[gnu.emacs] view-buffer-mode.el

julian@uhccux.uhcc.hawaii.edu (Julian Cowley) (01/17/90)

Here is something which I wrote a year back to view buffers
without using view.el.  It has been slightly extended in the
year since.  I am aware that there might be some discrepancy
between the naming of functions between this and view.el, but I
am too lazy to check.  The reason that I wrote a replacment is
that I found view.el too cumbersome, especially with its use of
recursive edit, and I wanted something closer to the standard
`more' functions of Unix.  Hope you folks find it useful.

julian@uhccux.uhcc.hawaii.edu
julian@uhccux.bitnet
"I press Execute." -- KT

;;; Simple mode for the viewing the current buffer
;;;
;;; Written by Julian Cowley <julian@uhccux.uhcc.hawaii.edu> Jan. 1989
;;; This program is now in the public domain as of 22 Feb. 1989.
;;; 
;;; This is a simple replacement for view-mode that is
;;; intentionally small, doesn't use recursive edit, etc.  While
;;; it is really a major mode, view-buffer-mode makes almost no
;;; disturbances to the buffer, and is able to appear/disappear
;;; upon command like minor modes do.  In addition, whenever view
;;; buffer mode is active, the word "View" will be present on the
;;; mode line right after the name of the major mode, also
;;; similar to minor modes.
;;;
;;; Suggested key binding: M-#
;;;
;;; (global-set-key "\M-#" 'view-buffer-mode)
;;;
;;; An interesting thing to do is to automatically go into
;;; view-buffer-mode whenever you find a read-only file.  You can
;;; do this by putting the following in your .emacs:
;;;
;;; (setq find-file-hooks '((lambda () 
;;;	                      (if buffer-read-only 
;;;                               (view-buffer-mode)))))
;;;
;;; If you want to be able to use C-x C-r (find-file-read-only)
;;; to find a file that you have write access to and go straight
;;; into view mode, you must redefine find-file-read-only so that
;;; it sets the buffer read-only *before* it runs the
;;; find-file-hooks.  Put the following redefinition in your
;;; .emacs, or better yet, byte-compile it and place it in a file
;;; that gets loaded upon start up.  If you have write access to
;;; the Emacs sources, replace the distribution version in simple.el
;;; with this.
;;;
;;; (defun find-file-read-only (filename)
;;;   "Edit file FILENAME but don't save without confirmation.
;;; Like find-file but marks buffer as read-only."
;;;   (interactive "fFind file read-only: ")
;;;   (let ((find-file-hooks (append '((lambda ()
;;; 					 (setq buffer-read-only t)))
;;;				     find-file-hooks)))
;;;     (find-file filename)))
;;;
;;; Please send comments/suggestions/bugs to me at the above address.

(defvar view-buffer-mode-map nil
  "The keymap for View buffer mode.")

(defvar old-local-map nil
  "Temporary storage for the buffer's local map while View buffer
mode is in effect.")

(defvar old-buffer-read-only nil
  "Temporary storage for the buffer's buffer-read-only variable
while View buffer mode is in effect.")

(make-variable-buffer-local 'viewing-buffer)
(make-variable-buffer-local 'old-local-map)
(make-variable-buffer-local 'old-buffer-read-only)
(or (assq 'viewing-buffer minor-mode-alist)
    (nconc minor-mode-alist
	   '((viewing-buffer " View"))))

(if view-buffer-mode-map
    ()
  (setq view-buffer-mode-map (make-sparse-keymap))
  (define-key view-buffer-mode-map " " 'scroll-up)
  (define-key view-buffer-mode-map "\r" 'scroll-up-line)
  (define-key view-buffer-mode-map "\C-d" 'scroll-up-half-screen)
  (define-key view-buffer-mode-map "d" 'scroll-up-half-screen)
  (define-key view-buffer-mode-map "s" 'search-forward)
  (define-key view-buffer-mode-map "r" 'search-backward)
  (define-key view-buffer-mode-map "/" 're-search-forward)
  (define-key view-buffer-mode-map "\\" 're-search-backward)
  (define-key view-buffer-mode-map "\C-?" 'scroll-down)
  (define-key view-buffer-mode-map "." 'beginning-of-buffer)
  (define-key view-buffer-mode-map "e" 'end-of-buffer)
  (define-key view-buffer-mode-map "<" 'beginning-of-buffer)
  (define-key view-buffer-mode-map ">" 'end-of-buffer)
  (define-key view-buffer-mode-map "g" 'goto-line)
  (define-key view-buffer-mode-map "=" 'what-line)
  (define-key view-buffer-mode-map "n" 'next-occurrence)
  (define-key view-buffer-mode-map "p" 'previous-occurrence)
  (define-key view-buffer-mode-map "?" 'describe-view-buffer-mode)
  (define-key view-buffer-mode-map "\C-hm" 'describe-view-buffer-mode))

(defun view-buffer-mode ()
  "Mode to view the current buffer.  Toggles on each call.

The bindings are:\\{view-buffer-mode-map}"
  (interactive)
  (if viewing-buffer
      ;; if we are currently viewing a buffer, restore the
      ;; original buffer
      (progn
	(use-local-map old-local-map)
	(setq buffer-read-only old-buffer-read-only
	      viewing-buffer nil))
    ;; save the local map and the state of buffer-read-only
    (setq old-local-map (current-local-map))
    (use-local-map view-buffer-mode-map)
    (setq old-buffer-read-only buffer-read-only
	  buffer-read-only t
	  viewing-buffer t))
  ;; make sure to update the mode line
  (set-buffer-modified-p (buffer-modified-p)))

(defun describe-view-buffer-mode ()
  "Display documentation of View buffer mode."
  (interactive)
  (with-output-to-temp-buffer "*Help*"
    (princ "View Buffer Mode:\n")
    (princ (documentation 'view-buffer-mode))
    (print-help-return-message)))

(defun scroll-up-line ()
  (interactive)
  (scroll-up 1))

(defun scroll-up-half-screen ()
  (interactive)
  (scroll-up (/ (window-height) 2)))

;; the following two functions are handy to use with M-x occur

(defun next-occurrence ()
  "Find the next occurrence from the Occur buffer, if any."
  (interactive)
  (let ((occbuf (get-buffer "*Occur*")))
    (if (or (null occbuf)
	    (progn (set-buffer occbuf)
		   (not (eq major-mode 'occur-mode))))
	(error "Occur buffer does not exist.")
      ;; skip past the current match
      (forward-line 1)
      ;; look for the next line number
      (if (re-search-forward "^[ 0-9]+" nil t)
	  (let ((occwin (get-buffer-window occbuf)))
	    ;; must be at beginning of line in order for
	    ;; occur-mode-goto-occurrence to work.
	    (beginning-of-line)
	    (if occwin
		(set-window-point occwin (point)))
	    (occur-mode-goto-occurrence))
	(message "End of matches.")
	;; go back to the line where we started
	(forward-line -1)))))

(defun previous-occurrence ()
  "Find the previous occurrence from the Occur buffer, if any."
  (interactive)
  (let ((occbuf (get-buffer "*Occur*")))
    (if (or (null occbuf)
	    (progn (set-buffer occbuf)
		   (not (eq major-mode 'occur-mode))))
	(error "Occur buffer does not exist.")
      ;; look for the previous line number
      (if (re-search-backward "^[ 0-9]+" nil t)
	  (let ((occwin (get-buffer-window occbuf)))
	    (if occwin
		(set-window-point occwin (point)))
	    (occur-mode-goto-occurrence))
	(message "Beginning of matches.")))))