home *** CD-ROM | disk | FTP | other *** search
- ;;;; buffers.jl -- High-level buffer/file handling
- ;;; Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
-
- ;;; This file is part of Jade.
-
- ;;; Jade is free software; you can redistribute it and/or modify it
- ;;; under the terms of the GNU General Public License as published by
- ;;; the Free Software Foundation; either version 2, or (at your option)
- ;;; any later version.
-
- ;;; Jade is distributed in the hope that it will be useful, but
- ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;;; GNU General Public License for more details.
-
- ;;; You should have received a copy of the GNU General Public License
- ;;; along with Jade; see the file COPYING. If not, write to
- ;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- (defvar auto-save-p t
- "When t files are auto-save'd regularly.")
- (defvar default-auto-save-interval 120
- "The number of seconds between each auto-save.")
-
- (defvar make-backup-files t
- "When non-nil backups of files are made when they are saved.")
- (defvar backup-by-copying nil
- "When non-nil all file backups are made by copying the file, not by
- renaming it.")
-
- (defvar amiga-use-file-req-p t
- "*AMIGA ONLY*
- When non-nil the normal ASL file requester is used when file names are
- prompted for.")
-
- (setq default-buffer (current-buffer))
-
- (defvar buffer-list (cons default-buffer nil)
- "List of buffers in most-recently-used order. Each window has it's own.")
- (set-window-variable 'buffer-list)
-
- (defvar standard-output default-buffer
- "Stream that `prin?' writes its output to by default")
- (defvar standard-input default-buffer
- "Stream that `read' takes it's input from by default")
-
- (defvar buffer-file-modtime 0
- "Holds the modification time of the file this buffer was loaded from")
- (set-buffer-variable 'buffer-file-modtime)
-
- (defun goto-buffer (buf)
- "(goto-buffer BUFFER)
- Switch the current buffer to BUFFER which can either be a buffer-object
- or a string naming an existing buffer. The selected buffer is moved to
- the head of the buffer list."
- (cond
- ((stringp buf)
- (setq buf (get-buffer buf)))
- ((bufferp buf))
- (t
- (title "error: bad arg to goto-buffer")
- (return)))
- (setq buffer-list (cons buf (delq buf buffer-list)))
- (set-current-buffer buf))
-
- (defun open-file (name &aux buf)
- "(open-file FILE-NAME)
- If no buffer containing file FILE-NAME exits try to create one.
- After creating a new buffer (named after the file's (not path) name)
- it first call the hook `read-file-hook' with arguments `(file-name buffer)'
- If this hook returns nil (ie, no members of the hook decided to read the
- file into memory) the file is read into the buffer verbatim.
- Once the file is in memory, through the hook or otherwise, this function
- then tries to initialise the correct editing mode for the file.
- `open-file' always returns the buffer holding the file, or nil if it
- doesn't exist."
- (unless (setq buf (get-file-buffer name))
- (when (setq buf (make-buffer (base-name name)))
- (add-buffer buf buffer-list)
- (with-buffer buf
- (unless (eval-hook 'read-file-hook name buf)
- (set-file-name buf name)
- (if (file-exists-p name)
- (read-buffer name)
- (title "New file")))
- (set-buffer-modified buf nil)
- (when auto-save-p
- (setq auto-save-interval default-auto-save-interval))
- (setq
- buffer-file-modtime (file-modtime name)
- last-save-time (current-time))
- (when (auto-save-file-newer-p name)
- (title "warning: auto-saved file is newer")
- (beep))
- (when (and (file-exists-p name) (not (file-writeable-p name)))
- (set-buffer-read-only buf t))
- (setq keymap-path (cons global-keymap nil))
- (eval-hook 'open-file-hook buf)
- (init-mode buf))))
- buf)
-
- (defun find-file (&optional name)
- "(find-file [FILE-NAME])
- Sets the current buffer to that containing the file FILE-NAME, if FILE-NAME
- is unspecified it will be prompted for. If the file is not already in memory
- `open-file' will be used to load it."
- (unless name
- (setq name (funcall (if (and (amiga-p) amiga-use-file-req-p) 'file-req 'prompt-for-file)
- "Find file: "
- (path-name (file-name)))))
- (when name
- (goto-buffer (open-file name))))
-
- (defun find-file-read-only (&optional name)
- "(find-file-read-only [FILE-NAME])
- Similar to `find-file' except that the buffer is edited in read-only mode."
- (unless name
- (setq name (funcall (if (and (amiga-p) amiga-use-file-req-p) 'file-req 'prompt-for-file)
- "Find file read-only:"
- (path-name (file-name)))))
- (when name
- (let
- ((buf (open-file name)))
- (when buf
- (set-buffer-read-only buf t)
- (goto-buffer buf)))))
-
- (defun open-alternate-file (name &optional buf)
- "(open-alternate-file FILE-NAME [BUFFER] )
- Kills BUFFER and returns a buffer containing FILE-NAME (through the
- `kill-buffer' and `open-file' functions)."
- (when (stringp name)
- (when (null buf)
- (setq buf (current-buffer)))
- (kill-buffer buf)
- (open-file name)))
-
- (defun find-alternate-file (&optional name)
- "(find-alternate-file [FILE-NAME])
- If FILE-NAME is unspecified one will be prompted for. The current buffer
- is killed and one editing FILE-NAME is found."
- (unless name
- (setq name (funcall (if (and (amiga-p) amiga-use-file-req-p) 'file-req 'prompt-for-file)
- "Find alternate file: "
- (path-name (file-name)))))
- (when name
- (goto-buffer (open-alternate-file name))))
-
- (defun write-file (buf &optional name)
- "(write-file BUFFER [FILE-NAME] )
- Writes the contents of BUFFER to the file FILE-NAME, or to the one
- that it is associated with."
- (unless (stringp name)
- (setq name (file-name buf)))
- (unless (eval-hook 'write-file-hook buf name)
- (when (and make-backup-files (file-regular-p name))
- (let
- ((backup-name (concat name ?~)))
- (if backup-by-copying
- (copy-file name backup-name)
- (when (and (file-owner-p name)
- (= (file-nlinks name) 1))
- (when (file-exists-p backup-name)
- (delete-file backup-name))
- (rename-file name backup-name)))))
- (write-buffer name buf)))
-
- (defun save-file (&optional buf &aux name)
- "(save-file [BUFFER])
- Saves the buffer BUFFER, or the current buffer, to the file that it is
- associated with, then sets the number of modifications made to this file
- to zero.
- Note: if no changes have been made to this buffer, it won't be saved."
- (unless (bufferp buf)
- (setq buf (current-buffer)))
- (with-buffer buf
- (if (not (buffer-modified-p))
- (title "no changes need to be saved!")
- (setq name (file-name))
- (when (and
- (> (file-modtime name) buffer-file-modtime)
- (not (ask-yes-or-no "File on disk has changed since it was loaded, save anyway")))
- (return nil))
- (when (write-file buf)
- (set-buffer-modified buf nil)
- (setq
- last-save-time (current-time)
- last-save-changes (buffer-changes)
- last-user-save-changes (buffer-changes))
- (setq buffer-file-modtime (file-modtime name))
- (delete-auto-save-file)
- (format t "Saved file %S." name)))))
-
- (defun save-file-as (&optional name buf &aux old-name)
- "(save-file-as [FILE-NAME] [BUFFER])
- Saves the buffer BUFFER, or the current one, to the file FILE-NAME,
- resetting the name of the buffer and the file that it is associated with
- to reflect FILE-NAME. Also sets the modification count to zero."
- (unless name
- (setq name
- (if (and (amiga-p) amiga-use-file-req-p)
- (file-req "Write file:" (path-name (file-name)) t)
- (prompt-for-file "Write file: " (path-name (file-name))))))
- (unless (bufferp buf)
- (setq buf (current-buffer)))
- (with-buffer buf
- (setq old-name (file-name))
- (set-file-name buf name)
- (set-buffer-name buf (base-name name))
- (when (write-file buf)
- (set-buffer-modified buf nil)
- (setq
- last-save-time (current-time)
- last-save-changes (buffer-changes)
- last-user-save-changes (buffer-changes))
- (setq buffer-file-modtime (file-modtime name))
- (delete-file (make-auto-save-name old-name))
- (format t "Saved file %S." name))))
-
- (defun insert-file (&optional name buf)
- "(insert-file [FILE-NAME] [BUFFER])
- Inserts the file FILE-NAME (may be prompted for) into the buffer BUFFER (or
- the current one) before the cursor position."
- (unless name
- (setq name (funcall (if (and (amiga-p) amiga-use-file-req-p) 'file-req 'prompt-for-file)
- "Insert file: "
- (path-name (file-name)))))
- (unless (bufferp buf)
- (setq buf (current-buffer)))
- (with-buffer buf
- (insert (read-file name))))
-
- (defun open-buffer (name &aux buf)
- "(open-buffer NAME)
- If no buffer called NAME exists, creates one and adds it to the main
- buffer-list. Always returns the buffer."
- (unless (setq buf (get-buffer name))
- (when (setq buf (make-buffer name))
- (add-buffer buf)))
- buf)
-
- (defun kill-buffer (&optional buf)
- "(kill-buffer [BUFFER])
- Destroys BUFFER (can be an actual buffer or name of a buffer), first
- checks whether or not we're allowed to with the function `check-changes'.
- If it can be deleted, all windows displaying this buffer are switched
- to the buffer at the head of the buffer-list, and BUFFER is removed
- from the buffer-list (if it was in it)."
- (or buf
- (setq buf (prompt-for-buffer (concat "Buffer to kill (default: "
- (buffer-name) ?\))))
- (return))
- (when (equal buf "")
- (setq buf (current-buffer)))
- (cond
- ((bufferp buf))
- ((stringp buf)
- (setq buf (get-buffer buf))))
- (when (and buf (check-changes buf))
- (remove-buffer buf)
- (unless (buffer-special-p buf)
- (kill-mode buf)
- (destroy-buffer buf))))
-
- (defun rotate-buffers-forward (&aux head end)
- "(rotate-buffers-forward)
- Moves the buffer at the head of the buffer-list to be last in the list, the
- new head of the buffer-list is displayed in the current window."
- (setq
- head (car buffer-list)
- end (nthcdr (1- (length buffer-list)) buffer-list))
- (rplacd end (cons head nil))
- (setq buffer-list (cdr buffer-list))
- (set-current-buffer (car buffer-list)))
-
- ;(defun rotate-buffers-backward (&aux end)
- ; "(rotate-buffers-backward)
- ;Moves the buffer at the end of the buffer-list to be first in the list, the
- ;new head of the buffer-list is displayed in the current window."
- ; (setq
- ; end (nthcdr (- 2 (length buffer-list)) buffer-list)
- ; buffer-list (cons (last buffer-list) buffer-list))
- ; (rplacd end nil)
- ; (set-current-buffer (car buffer-list)))
-
- (defun check-changes (&optional buf)
- "(check-changes [BUFFER])
- Returns t if it is ok to kill BUFFER, or the current buffer. If unsaved
- changes have been made to it the user is asked whether (s)he minds losing
- them."
- (or (not (buffer-modified-p buf))
- (ask-yes-or-no (format-string "OK to lose change(s) to buffer %s"
- (base-name (buffer-name buf))))))
-
- (defun goto-mark (mark)
- "(goto-mark MARK)
- Switches (if necessary) to the buffer containing MARK at the position
- of the mark. If the file containing MARK is not in memory then we
- attempt to load it with `open-file'."
- (when (markp mark)
- (let*
- ((file (mark-file mark))
- (pos (mark-pos mark)))
- (when (stringp file)
- (setq file (open-file file)))
- (set-auto-mark)
- (goto-buffer file)
- (goto pos))))
-
- (defun set-auto-mark ()
- "(set-auto-mark)
- Sets the mark `auto-mark' to the current position (buffer & cursor-pos)."
- (set-mark auto-mark (cursor-pos) (current-buffer))
- (title "Set auto-mark."))
-
- (defun swap-cursor-and-auto-mark ()
- "(swap-cursor-and-auto-mark)
- Sets the `auto-mark' to the current position and then sets the current
- position (buffer and cursor-pos) to the old value of `auto-mark'."
- (let*
- ((a-m-file (mark-file auto-mark))
- (a-m-pos (dup-pos (mark-pos auto-mark))))
- (set-auto-mark)
- (when (stringp a-m-file)
- (setq a-m-file (open-file a-m-file)))
- (set-current-buffer a-m-file)
- (goto a-m-pos)))
-
- (defun split-line-indent ()
- "(split-line-indent)
- Inserts a newline at the cursor position and then indents the new line
- created to the indentation of the one above it."
- (let*
- ((old-indent-pos (next-line 1 (indent-pos))))
- (split-line)
- (if (empty-line-p)
- (goto old-indent-pos)
- (set-indent-pos old-indent-pos))))
-
- (defun make-auto-save-name (name)
- "(make-auto-save-name FILE-NAME)
- Returns a string naming the file used to hold the auto-save'd file for
- file FILE-NAME."
- (concat (path-name name) ?# (base-name name) ?#))
-
- (defun auto-save-function (buf)
- "(auto-save-function BUFFER)
- Automatically called when BUFFER is due to be automatically saved.
- This function calls the hook `auto-save-hook', if this returns nil it then
- saves it to the file specified by `make-auto-save-name' appiled to the
- name of the file stored in BUFFER."
- (format t "Auto-saving %S..." (buffer-name buf))
- (refresh-all)
- (flush-output)
- (with-buffer buf
- (if (or (eval-hook 'auto-save-hook buf)
- (write-buffer (make-auto-save-name (file-name))))
- (format t "done.")
- (format t "*error* can't auto-save.")
- nil)))
-
- (defun delete-auto-save-file (&optional buf)
- "(delete-auto-save-file [BUFFER])
- Deletes the file used to store the auto-save'd copy of the file stored in
- BUFFER, if such a file exists."
- (let
- ((a-name (make-auto-save-name (file-name buf))))
- (when (file-exists-p a-name)
- (delete-file a-name))))
-
- (defun auto-save-file-newer-p (name)
- "(auto-save-file-newer-p FILE-NAME)
- Returns t if there exists an automatically saved copy of file FILE-NAME
- which is newer than FILE-NAME."
- (let*
- ((recover-name (make-auto-save-name name)))
- (> (file-modtime recover-name) (file-modtime name))))
-
- (defun recover-file (&optional buf)
- "(recover-file [BUFFER])
- Loads the auto-saved copy of the file stored in BUFFER into BUFFER
- overwriting its current contents (if any changes are to be lost the user
- will have to agree to this)."
- (let*
- ((recover-name (make-auto-save-name (file-name buf))))
- (unless buf
- (setq buf (current-buffer)))
- (when (and (file-exists-p recover-name) (check-changes buf))
- (with-buffer buf
- (read-buffer recover-name)
- (set-buffer-modified buf nil)
- (setq last-save-time (current-time))
- (title (concat "using " recover-name " as "(file-name buf)))))
- buf))
-
- (defun revert-buffer (&optional buf)
- "(revert-buffer [BUFFER])
- Restores the contents of BUFFER (or current buffer) to the contents of the
- file it was loaded from."
- (unless buf
- (setq buf (current-buffer)))
- (if (and (auto-save-file-newer-p (file-name buf))
- (ask-yes-or-no "auto-saved file is newer, use it?"))
- (recover-file buf)
- (when (check-changes buf)
- (with-buffer buf
- (unless (eval-hook 'read-file-hook (file-name buf) buf)
- (read-buffer (file-name buf)))
- (set-buffer-modified buf nil)
- (setq last-save-time (current-time))))))
-
- (defun switch-to-buffer ()
- "(switch-to-buffer)
- Prompt for the name of a buffer and display it in the current window."
- (let*
- ((def (buffer-name (nth 1 buffer-list)))
- (buf (prompt-for-buffer (concat "Switch to Buffer: (default: " def ?\)))))
- (when buf
- (goto-buffer (if (equal buf "") def buf)))))
-
- (defun goto-line ()
- "(goto-line)
- Prompt for a line number and move the cursor to it."
- (set-auto-mark)
- (goto (pos nil (read-from-string (prompt "Line: ")))))
-
- (defun file-newer-than-file-p (file1 file2)
- (> (file-modtime file1) (file-modtime file2)))
-
- (defun save-some-buffers ()
- "(save-some-buffers)
- Asks whether or not to save any modified buffers, returns t if no modified
- buffers are left."
- (let*
- ((bufs buffer-list)
- buf
- (unsaved-files-p nil))
- (while (consp bufs)
- (setq buf (car bufs))
- (when (and (buffer-modified-p buf) (not (buffer-special-p buf)))
- (if (ask-y-or-n (concat "Save buffer " (buffer-name buf)))
- (unless (save-file buf)
- (setq unsaved-files-p t))
- (setq unsaved-files-p t)))
- (setq bufs (cdr bufs)))
- (not unsaved-files-p)))
-
- (defun save-and-quit ()
- "(save-and-quit)
- Calls `save-some-buffers' and quits (after asking whether it's ok to lose any
- unsaved buffers)."
- (when (or (save-some-buffers) (ask-yes-or-no "Unsaved buffers exist: quit anyway?"))
- (throw 'quit 0)))
-