gnu-emacs-sources
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

joccur 1.3.3 - Enhanced Occur-mode


From: Javier Oviedo
Subject: joccur 1.3.3 - Enhanced Occur-mode
Date: 21 Jun 2004 10:50:27 -0700

Updated joccur.el

;;; Change Log:
;;
;; Version 1.3.3 06/21/2004  Javier Oviedo
;; - Added "follow-mode." When searching previous and next matches in the joccur
;;   buffer, the user has the option of having the source buffer follow along.
;; - New functions joccur-next-follow and joccur-prev-follow are mapped to
;;   \M-n and \M-p respectively.
;;

##############################################################

;;; joccur.el --- An enhanced 'occur' implementation.

;; Author: Javier Oviedo <address@hidden>
;; Created:  17 Feb 2004

;; This program 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 of
;; the License, or (at your option) any later version.

;; This program 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 this program; if not, write to the Free
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
;; MA 02111-1307 USA

;;; Commentary:
;;
;; This package implements the functionality of "occur" but with some
;; modifications. The *JOccur* buffer will display the lines containing
;; the matching regexp with the original text properties.
;;
;; I used the occur-mode code in replace.el(GNU Emacs 21.3.50.8) as the model
;; for this package. Indeed, a few helper functions are copied directly from
;; replace.el and renamed. The remaining helper functions I have reimplemented.
;; In addition, the actual "occur" engine has been modified extensively.
;;
;; Comments and/or constructive criticism is always welcome.
;;

;; Installation:
;;
;; 1. Place joccur.el in your emacs load-path
;; 2. Add (require 'joccur) to your .emacs file
;;

;; Usage:
;;
;; M-x joccur regexp
;;

;;; Change Log:
;;
;; Version 1.3.3 06/21/2004  Javier Oviedo
;; - Added "follow-mode." When searching previous and next matches in the joccur
;;   buffer, the user has the option of having the source buffer follow along.
;; - New functions joccur-next-follow and joccur-prev-follow are mapped to
;;   \M-n and \M-p respectively.
;;
;; Version 1.3.2 05/18/2004  Javier Oviedo
;; - The regexp used for joccur will default to the current-word if nothing
;;   is typed or selected from the regexp-history list. The regexp-history
;;   list is then updated with regexp chosen, if it is not already in the
;;   list.
;;
;; Version 1.3.1 03/11/2004  Javier Oviedo
;; - Fixed a joccur-mode-mouse-goto/joccur-mode-goto-occurrence bug. 
;;   These functions are bound to enter, mouse-2, etc.
;; - Added marker and text property for the entire line in the joccur buffer.
;;   Previously there were only markers in the joccur buffer on the matches.
;;   Now it is possible to jump to the line when the cursor is not on an
;;   exact regexp match.
;; - Added additional key definitions to the joccur-mode-map.
;;
;; Version 1.3 03/08/2004  Javier Oviedo
;; - Restructered joccur-engine using markers for each individual occurance
;;   of REGEXP...modified to better support multiple occurances per line.
;;
;; Version 1.2.3 03/04/2004  Javier Oviedo
;; - Cleaned up byte compiler. Added let-bindings and defvars.
;;
;; Version 1.2.2 02/25/2004  Javier Oviedo
;; - Made a joccur-regexp buffer-local variable. This allows switching between
;;   multiple joccur buffers with different regexp.
;; - Made a joccur-input-buffer buffer-local variable.
;; - Made overlays buffer-local. This allows each buffer to maintain the
;;   current highlighted/overlay match when switching between multiple joccur
;;   buffers.
;;
;; Version 1.2.1 02/24/2004  Javier Oviedo
;; - Joccur now handles the generic case of any font-lock-support-mode,
;;   instead of just lazy-lock-mode.
;;
;; Version 1.2 02/23/2004  Javier Oviedo
;; - Implemented multi-occur for joccur...called multi-joccur
;; - Handle multiple occurances of REGEXP in a line
;; - Various functionality improvements(additional overlays, etc.)
;;
;; Version 1.1 02/18/2004  Javier Oviedo
;; - Adding highlight of regexp when displaying occurance in originating buffer.
;; - Changed joccur-match-face.
;; - Added joccur-display-face and joccur-title-face
;; - Overlays are destroyed on killing/deleting of window/buffer
;; - Unique Joccur buffer created every time...based on search buffer and 
regexp.
;; - Introduced "Change Log" and "Usage" sections.
;;
;; Version 1.0 02/17/2004
;; - Introduced joccur package.
;;


(defconst joccur-version "1.3.3"
  "This version of joccur.el.")

(defconst joccur-buf "*JOccur*"
  "Name of joccur buffer to use.")

(defvar joccur-buf-cur nil
  "Name of current joccur buffer to use.")

(defvar joccur-input-buffer nil
  "Name of input buffer to run joccur on.")

(defvar joccur-input-buffer-local nil
  "Name of input buffer for the current joccur buffer.")

(defvar joccur-match-overlay nil
  "The overlay used to highlight matched text in the *JOccur* buffer.")

(defvar joccur-cur-match-overlay nil
  "The overlay used to highlight matched text in the *JOccur* buffer.")

(defvar joccur-display-overlay nil
  "The overlay used to highlight matched text in the originating buffer.")

(defvar joccur-regexp nil
  "The current joccur regexp.")

(defvar joccur-regexp-local nil
  "The regexp for the current joccur buffer.")


(defvar joccur-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "v" 'joccur-mode-display-occurrence)
    (define-key map "p" 'joccur-prev)
    (define-key map "n" 'joccur-next)
    (define-key map [mouse-2] 'joccur-mode-mouse-goto)
    (define-key map "\C-c\C-c" 'joccur-mode-goto-occurrence)
    (define-key map "\C-m" 'joccur-mode-goto-occurrence)
    (define-key map "o" 'joccur-mode-goto-occurrence-other-window)
    (define-key map "\C-o" 'joccur-mode-display-occurrence)
    (define-key map "\M-n" 'joccur-next-follow)
    (define-key map "\M-p" 'joccur-prev-follow)
    (define-key map "r" 'joccur-rename-buffer)
    (define-key map "c" 'clone-buffer)
    (define-key map "g" 'revert-buffer)
    (define-key map "q" 'joccur-delete-window)
    (define-key map "z" 'joccur-kill-this-buffer)
    map)
  "Keymap for `joccur-mode'.")

(defface joccur-title-face
  '((t (:slant italic :underline t)))
  "Face for Joccur mode."
  :group 'joccur)

(defface joccur-match-face
  '((t (:weight bold :slant italic :foreground "black" :background 
"SteelBlue1")))
  "Face for Joccur mode."
  :group 'joccur)

(defface joccur-cur-match-face
  '((t (:weight bold :slant italic :foreground "black" :background "yellow")))
  "Face for Joccur mode."
  :group 'joccur)

(defface joccur-display-face
  '((t (:weight bold :slant italic :foreground "black" :background "pink")))
  "Face for Joccur mode."
  :group 'joccur)

(defcustom joccur-title-highlight 'joccur-title-face
  "*Face used for title line in the *JOccur* buffer.
If the value is nil, don't highlight."
  :type 'face
  :group 'matching)

(defcustom joccur-match-highlight 'joccur-match-face
  "*Face used to higlight regexp matches in the *JOccur* buffer.
If the value is nil, don't highlight."
  :type 'face
  :group 'matching)

(defcustom joccur-cur-match-highlight 'joccur-cur-match-face
  "*Face used to higlight regexp matches in the *JOccur* buffer.
If the value is nil, don't highlight."
  :type 'face
  :group 'matching)

(defcustom joccur-display-highlight 'joccur-display-face
  "*Face used to higlight regexp matches in the *JOccur* buffer.
If the value is nil, don't highlight."
  :type 'face
  :group 'matching)

(defcustom joccur-mode-hook '(turn-on-font-lock)
  "Hook run when entering JOccur mode."
  :type 'hook
  :group 'matching)


(put 'joccur-mode 'mode-class 'special)
(defun joccur-mode ()
  "Major mode for output from \\[joccur].
\\<joccur-mode-map>Move point to one of the items in this buffer, then use
\\[joccur-mode-goto-joccurrence] to go to the joccurrence that the item refers 
to.
Alternatively, click \\[joccur-mode-mouse-goto] on an item to go to it.
\\{joccur-mode-map}"
  (interactive)
  (kill-all-local-variables)
  (use-local-map joccur-mode-map)
  (setq major-mode 'joccur-mode)
  (setq mode-name "Joccur")
  (setq case-fold-search t)
  (run-hooks 'joccur-mode-hook))

(defun joccur-mode-mouse-goto (event)
  "In JOccur mode, go to the occurrence whose line you click on."
  (interactive "e")
  (let (pos)
    (save-excursion
      (set-buffer (window-buffer (posn-window (event-end event))))
      (save-excursion
        (goto-char (posn-point (event-end event)))
        (setq pos (joccur-mode-find-occurrence))))
    (pop-to-buffer (marker-buffer pos))
    (goto-char pos)))

(defun joccur-mode-find-occurrence ()
  (let ((pos (get-text-property (point) 'joccur-target)))
    (unless pos
      (error "No occurrence on this line"))
    (unless (buffer-live-p (marker-buffer pos))
      (error "Buffer for this occurrence was killed"))
    (setq joccur-buf-cur (buffer-name))
    (joccur-mode-display-highlight pos)
    pos))

(defun joccur-mode-goto-occurrence ()
  "Go to the occurrence the current line describes."
  (interactive)
  (let ((pos (joccur-mode-find-occurrence)))
    (pop-to-buffer (marker-buffer pos))
    (goto-char pos)))

(defun joccur-mode-goto-occurrence-other-window ()
  "Go to the occurrence the current line describes, in another window."
  (interactive)
  (let ((pos (joccur-mode-find-occurrence)))
    (switch-to-buffer-other-window (marker-buffer pos))
    (goto-char pos)))

(defun joccur-mode-display-occurrence ()
  "Display in another window the occurrence the current line describes."
  (interactive)
  (let ((pos (joccur-mode-find-occurrence))
        window
        ;; Bind these to ensure `display-buffer' puts it in another window.
        same-window-buffer-names
        same-window-regexps)
    (setq window (display-buffer (marker-buffer pos)))
    ;; This is the way to set point in the proper window.
    (save-selected-window
      (select-window window)
      (goto-char pos))))

(defun joccur-mode-display-highlight(pos)
  "Create or move the `joccur-display-highlight' overlay."
  (interactive)
  (save-excursion
    (with-current-buffer (marker-buffer pos)
      (goto-char pos)
      (with-current-buffer joccur-buf-cur
        (setq joccur-regexp joccur-regexp-local))
      (re-search-forward joccur-regexp nil t)
      (let ((matchbeg (match-beginning 0))
            (matchend (match-end 0)))
        (if joccur-display-overlay
            (move-overlay joccur-display-overlay matchbeg matchend)
          (set (make-local-variable 'joccur-display-overlay) t)
          (setq joccur-display-overlay (make-overlay matchbeg matchend))
          (overlay-put joccur-display-overlay 'face joccur-display-highlight)
          (overlay-put joccur-display-overlay 'priority 1))))))

(defun joccur-mode-display-unhighlight ()
  "Delete the `joccur-display-highlight' overlay."
  (interactive)
  (with-current-buffer joccur-input-buffer-local
    (if joccur-display-overlay
        (delete-overlay joccur-display-overlay))
    (setq joccur-display-overlay nil)))

(defun joccur-mode-match-unhighlight ()
  "Delete the `joccur-display-highlight' overlay."
  (interactive)
  (if joccur-match-overlay
      (delete-overlay joccur-match-overlay))
  (setq joccur-match-overlay nil))

(defun joccur-delete-window ()
  (interactive)
  (joccur-mode-display-unhighlight)
  (delete-window))

(defun joccur-kill-this-buffer ()
  (interactive)
  (joccur-mode-display-unhighlight)
  (kill-buffer-and-window))

(defun joccur-next ()
  "Move to the next match in a JOccur mode buffer."
  (interactive)
  (let (joccur-next-match)
    (if (eq (count-lines (point-min) (point)) 0)
        (forward-line 1))
    (setq joccur-regexp joccur-regexp-local)
    (setq joccur-next-match (re-search-forward joccur-regexp nil t))
    (if (not joccur-next-match)
        (let ()
          (error "No more matches"))
      (let ((matchbeg (match-beginning 0))
            (matchend (match-end 0)))
        (goto-char (1- (point)))
        (setq joccur-buf-cur (current-buffer))
        (if joccur-cur-match-overlay
            (move-overlay joccur-cur-match-overlay
                          matchbeg matchend joccur-buf-cur)
          (set (make-local-variable 'joccur-cur-match-overlay) t)
          (setq joccur-cur-match-overlay
                (make-overlay matchbeg matchend joccur-buf-cur))
          (overlay-put joccur-cur-match-overlay
                       'face joccur-cur-match-highlight)
          (overlay-put joccur-cur-match-overlay 'priority 2))))))

(defun joccur-prev ()
  "Move to the previous match in a JOccur mode buffer."
  (interactive)
  (setq joccur-regexp joccur-regexp-local)
  (re-search-backward joccur-regexp nil t)
  (if (eq (count-lines (point-min) (point)) 1)
      (let ()
        (forward-line 1)
        (joccur-next)
        (error "No earlier Matches"))
    (let ((matchbeg (match-beginning 0))
          (matchend (match-end 0)))
      (setq joccur-buf-cur (current-buffer))
      (if joccur-cur-match-overlay
          (move-overlay joccur-cur-match-overlay
                        matchbeg matchend joccur-buf-cur)
        (set (make-local-variable 'joccur-cur-match-overlay) t)
        (setq joccur-cur-match-overlay
              (make-overlay matchbeg matchend joccur-buf-cur))
        (overlay-put joccur-cur-match-overlay
                     'face joccur-cur-match-highlight)
        (overlay-put joccur-cur-match-overlay 'priority 2)))))

(defun joccur-next-follow ()
  "Move to the next match in a JOccur mode buffer and have the source buffer
update to that match."
  (interactive)
  (joccur-next)
  (joccur-mode-display-occurrence))

(defun joccur-prev-follow ()
  "Move to the previous match in a JOccur mode buffer and have the source buffer
update to that match."
  (interactive)
  (joccur-prev)
  (joccur-mode-display-occurrence))

(defun joccur-read-primary-args ()
  (list (let* ((default (current-word))
               (input
                (read-from-minibuffer
                 (if default
                     (format "List lines matching regexp (default `%s'): "
                             default)
                   "List lines matching regexp: ")
                 nil
                 nil
                 nil
                 'regexp-history)))
          (if (not (equal default regexp-history))
              (setq regexp-history
                    (cons default regexp-history)))
          (if (equal input "")
              default
            input))
        (when current-prefix-arg
          (prefix-numeric-value current-prefix-arg))))

(defun joccur-rename-buffer (&optional unique-p)
  "Rename the current *JOccur* buffer to *JOccur: original-buffer-name*.
Here `original-buffer-name' is the buffer name were occur was originally run.
When given the prefix argument, the renaming will not clobber the existing
buffer(s) of that name, but use `generate-new-buffer-name' instead.
You can add this to `joccur-hook' if you always want a separate *JOccur*
buffer for each buffer where you invoke `joccur'."
  (interactive "P")
  (with-current-buffer
      (if (eq major-mode 'joccur-mode)
          (current-buffer)
        (get-buffer "*JOccur*"))
    (let (joccur-buf-unique)
      (setq joccur-buf-unique (concat "*JOccur: "
                                      joccur-input-buffer
                                      " - "
                                      joccur-regexp
                                      "*"))
      (rename-buffer joccur-buf-unique unique-p))))

(defun joccur (regexp &optional nlines)
  "Show all lines in the current buffer containing a match for REGEXP.

If a match spreads across multiple lines, all those lines are shown.

The lines are shown in a buffer named `*JOccur*'.
It serves as a menu to find any of the occurrences in this buffer.
\\<joccur-mode-map>\\[describe-mode] in that buffer will explain how.

Searches are done in a case-insensitive manner"
  (interactive (joccur-read-primary-args))
  (joccur-1 regexp (list (current-buffer)) t))


(defun joccur-1 (regexp bufs-list joccur-unique-buffer)
  (setq joccur-regexp regexp)
  (get-buffer-create joccur-buf)
  (let (active-bufs)
    (setq active-bufs (delq nil (mapcar #'(lambda (buf)
                                            (when (buffer-live-p buf) buf))
                                        bufs-list)))
    (dolist (input-buf active-bufs)
      (joccur-engine input-buf))
    (display-buffer joccur-buf)
    (pop-to-buffer joccur-buf)
    (setq truncate-lines t)
    (set (make-local-variable 'joccur-regexp-local) joccur-regexp)
    (set (make-local-variable 'joccur-input-buffer-local) joccur-input-buffer)
    (beginning-of-buffer)
    (if joccur-unique-buffer
        (joccur-rename-buffer joccur-unique-buffer))))

  
(defun joccur-engine (input-buf &optional unique-buf)
  (setq joccur-input-buffer (buffer-name input-buf))
  (let ((lines 1)
        (matches 0)
        (marker nil)
        (match-line-marker nil)
        (joccur-font-lock-support-mode nil)
        (joccur-buf-start 0)
        (matchpt nil)
        (matchbeg-input nil)
        (matchend-input nil)
        (matchbeg-output nil)
        (matchend-output nil)
        (beg-output nil)
        (end-output nil)
        (curMatch nil)
        (joccur-buf-output nil))
    (save-excursion
      (with-current-buffer joccur-buf
        (joccur-mode)
        (setq buffer-read-only nil)
        (if unique-buf
            (erase-buffer))
        (goto-char (setq joccur-buf-start (point-max)))
        (with-current-buffer joccur-input-buffer
          (setq joccur-font-lock-support-mode font-lock-support-mode)
          (if joccur-font-lock-support-mode
              (let ()
                (setq font-lock-support-mode nil)
                (font-lock-mode)
                (font-lock-mode)))
          (goto-char (point-min)) ;; begin searching in the buffer
          (while (not (eobp))
            (when (setq matchpt (re-search-forward joccur-regexp nil t))
              (setq matches (1+ matches)) ;; increment match count
              (setq curMatch (buffer-substring (line-beginning-position)
                                               (line-end-position)))
              (setq lines (count-lines (point-min) matchpt))
              (setq joccur-buf-output
                    (concat (format "%7d: " lines) curMatch "\n"))
              (setq match-line-marker (make-marker))
              (set-marker match-line-marker (point-at-bol))
              (with-current-buffer joccur-buf
                (setq beg-output (point))
                (insert joccur-buf-output)
                (setq end-output (point))
                (add-text-properties
                 beg-output (1- (point)) '(mouse-face highlight))
                (add-text-properties
                 beg-output end-output
                 `(joccur-target ,match-line-marker
                                 help-echo
                                 "mouse-2: go to this occurrence"))
                (goto-char beg-output))
              (save-excursion
                (goto-char (point-at-bol))
                (while (re-search-forward joccur-regexp (point-at-eol) t)
                  (setq matchbeg-input (match-beginning 0)
                        matchend-input (match-end 0))
                  (setq marker (make-marker))
                  (set-marker marker matchbeg-input)
                  (with-current-buffer joccur-buf
                    (re-search-forward joccur-regexp (point-at-eol) t)
                    (setq matchbeg-output (match-beginning 0)
                          matchend-output (match-end 0))
                    (add-text-properties
                     matchbeg-output matchend-output
                     `(joccur-target ,marker
                                     help-echo
                                     "mouse-2: go to this occurrence"))
                    (set (make-local-variable 'joccur-match-overlay) t)
                    (setq joccur-match-overlay
                          (make-overlay matchbeg-output matchend-output))
                    (overlay-put joccur-match-overlay
                                 'face joccur-match-highlight)
                    (overlay-put joccur-match-overlay 'priority 1))))
              (with-current-buffer joccur-buf
                (goto-char end-output)))
            (forward-line 1))
          (if joccur-font-lock-support-mode
              (setq font-lock-support-mode joccur-font-lock-support-mode)))
        (insert "\n")
        (goto-char joccur-buf-start)
        (save-excursion
          (let ((beg joccur-buf-start)
                end)
            (insert (format "%d lines matching \"%s\" in buffer: %s\n"
                            matches joccur-regexp joccur-input-buffer))
            (setq end (point))
            (add-text-properties
             beg end
             (append `(font-lock-face ,joccur-title-highlight)))))
        (setq buffer-read-only t)))))

(defun multi-joccur (bufs regexp &optional nlines)
  "Show all lines in buffers BUFS containing a match for REGEXP.
This function acts on multiple buffers; otherwise, it is exactly like
`joccur'."
  (interactive
   (cons
    (let* ((bufs (list (read-buffer "First buffer to search: "
                                    (current-buffer) t)))
           (buf nil)
           (ido-ignore-item-temp-list bufs))
      (while (not (string-equal
                   (setq buf (read-buffer
                              (if (eq read-buffer-function 'ido-read-buffer)
                                  "Next buffer to search (C-j to end): "
                                "Next buffer to search (RET to end): ")
                              nil t))
                   ""))
        (add-to-list 'bufs buf)
        (setq ido-ignore-item-temp-list bufs))
      (nreverse (mapcar #'get-buffer bufs)))
    (joccur-read-primary-args)))
  (joccur-1 regexp bufs nil))


(provide 'joccur)

;;; joccur.el ends here


reply via email to

[Prev in Thread] Current Thread [Next in Thread]