emacs-devel
[Top][All Lists]
Advanced

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

isearch multiple buffers


From: Juri Linkov
Subject: isearch multiple buffers
Date: Sat, 06 Oct 2007 22:52:46 +0300
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/23.0.50 (gnu/linux)

Below is a patch that implements a feature allowing isearch to search
multiple files/buffers.  I wrote documentation strings and comments
in every new function, so for details please look there.

This general feature is implemented in isearch.el and can be used by other
packages.  add-log.el is the first package that uses it, and below is also
a patch for add-log.el that allows C-s to search through a set of all
ChangeLog files in one directory:

Index: lisp/add-log.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/add-log.el,v
retrieving revision 1.196
diff -c -r1.196 add-log.el
*** lisp/add-log.el     20 Sep 2007 14:06:13 -0000      1.196
--- lisp/add-log.el     6 Oct 2007 19:43:40 -0000
***************
*** 760,766 ****
         'change-log-resolve-conflict)
    (set (make-local-variable 'adaptive-fill-regexp) "\\s *")
    (set (make-local-variable 'font-lock-defaults)
!        '(change-log-font-lock-keywords t nil nil backward-paragraph)))
  
  ;; It might be nice to have a general feature to replace this.  The idea I
  ;; have is a variable giving a regexp matching text which should not be
--- 760,792 ----
         'change-log-resolve-conflict)
    (set (make-local-variable 'adaptive-fill-regexp) "\\s *")
    (set (make-local-variable 'font-lock-defaults)
!        '(change-log-font-lock-keywords t nil nil backward-paragraph))
!   (add-hook 'isearch-mode-hook 'isearch-buffers-init nil t)
!   (set (make-local-variable 'isearch-search-fun-function)
!        'isearch-buffers-search-fun)
!   (set (make-local-variable 'isearch-wrap-function)
!        'isearch-buffers-wrap)
!   (set (make-local-variable 'isearch-push-state-function)
!        'isearch-buffers-push-state)
!   (set (make-local-variable 'isearch-buffers-next-buffer-function)
!        'change-log-isearch-next-buffer))
! 
! (defun change-log-isearch-next-buffer (&optional buffer wrap)
!   "Return the next buffer for multiple buffers isearch.
! A sequence of buffers is formed by ChangeLog files with decreasing
! numeric file name suffixes in the directory of the initial ChangeLog
! file were isearch was started."
!   (let* ((name (change-log-name))
!        (files (cons name (sort (file-expand-wildcards (concat name 
"[-.][0-9]*"))
!                                (lambda (a b)
!                                  (version< (substring b (length name))
!                                            (substring a (length name)))))))
!        (files (if isearch-forward files (reverse files))))
!     (find-file-noselect
!      (if wrap
!        (car files)
!        (cadr (member (file-name-nondirectory (buffer-file-name buffer))
!                    files))))))
  
  ;; It might be nice to have a general feature to replace this.  The idea I
  ;; have is a variable giving a regexp matching text which should not be

Index: lisp/isearch.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/isearch.el,v
retrieving revision 1.303
diff -c -r1.303 isearch.el
*** lisp/isearch.el     29 Aug 2007 05:28:05 -0000      1.303
--- lisp/isearch.el     6 Oct 2007 19:48:51 -0000
***************
*** 2035,2042 ****
                           (if isearch-forward (< pos2 pos1) (> pos2 pos1))))
              (setq pos1 pos2)
              (set-match-data match-data)))))
!     (if pos1
!       (goto-char pos1))
      pos1))
  
  (defun isearch-search ()
--- 2035,2046 ----
                           (if isearch-forward (< pos2 pos1) (> pos2 pos1))))
              (setq pos1 pos2)
              (set-match-data match-data)))))
!     (when pos1
!       ;; When using multiple buffers isearch, switch to the new buffer here,
!       ;; because `save-excursion' above doesn't allow doing it inside funcall.
!       (if isearch-buffers-current-buffer
!         (switch-to-buffer isearch-buffers-current-buffer))
!       (goto-char pos1))
      pos1))
  
  (defun isearch-search ()
***************
*** 2243,2248 ****
--- 2247,2344 ----
              (setq isearch-hidden t)))))))
  
  
+ ;; Search multiple buffers
+ 
+ (defvar isearch-buffers-current-buffer nil
+   "The buffer where the search currently stays.
+ The value is nil when the search still is in the initial buffer.")
+ 
+ (defvar isearch-buffers-next-buffer-function nil
+   "Function to call to get the next buffer to search.
+ The first argument of this function is the CURRENT-BUFFER that defined
+ the base buffer relative to which to find the next buffer.  When the
+ isearch direction is backward (when isearch-forward is nil), this
+ function should return the previous buffer.
+ 
+ If the second argument of this function WRAP is non-nil, then it
+ should return the first buffer in a buffer sequence; and for the
+ backward search, it should return the last buffer in a sequence.")
+ 
+ (defun isearch-buffers-init ()
+   "Set up isearch to search multiple buffers.
+ Intended to be added to `isearch-mode-hook'."
+   (setq isearch-buffers-current-buffer nil))
+ 
+ (defun isearch-buffers-search-fun ()
+   "Return the proper search function, for isearch in multiple buffers."
+   (lambda (string bound noerror)
+     (let ((search-fun
+          ;; Use standard functions to search within one buffer
+          (cond
+           (isearch-word
+            (if isearch-forward 'word-search-forward 'word-search-backward))
+           (isearch-regexp
+            (if isearch-forward 're-search-forward 're-search-backward))
+           (t
+            (if isearch-forward 'search-forward 'search-backward))))
+         found buffer)
+       (or
+        ;; 1. First try searching in the initial buffer
+        (funcall search-fun string bound noerror)
+        ;; 2. If the above search fails, start visiting next/prev buffers
+        ;; successively, and search the string in them.  Do this only
+        ;; when bound is nil (i.e. not while lazy-highlighting search
+        ;; strings in the current buffer).
+        (unless bound
+        (if isearch-buffers-current-buffer
+            (condition-case nil
+                (progn
+                  (while (not found)
+                    (setq buffer (funcall isearch-buffers-next-buffer-function 
buffer))
+                    (with-current-buffer buffer
+                      (goto-char (if isearch-forward (point-min) (point-max)))
+                      (setq isearch-barrier (point) isearch-opoint (point))
+                      ;; After visiting the next/prev buffer search the
+                      ;; string in them again, until the function in
+                      ;; isearch-buffers-next-buffer-function raises an error
+                      ;; at the beginning/end of the buffer list.
+                      (setq found (funcall search-fun string bound noerror))))
+                  (if buffer (setq isearch-buffers-current-buffer buffer))
+                  ;; Return point of the new search result
+                  found)
+              ;; Return nil when isearch-buffers-next-buffer-function fails
+              (error nil))
+          (signal 'search-failed (list string "initial buffer"))))))))
+ 
+ (defun isearch-buffers-wrap ()
+   "Wrap the multiple buffers search when search is failed.
+ Switch buffer to the first buffer for a forward search,
+ or to the last buffer for a backward search.
+ Set `isearch-buffers-current-buffer' to the current buffer to display
+ the isearch suffix message [initial buffer] only when isearch leaves
+ the initial buffer."
+   (if isearch-buffers-current-buffer
+       (progn
+       (switch-to-buffer
+        (funcall isearch-buffers-next-buffer-function (current-buffer) t))
+       (goto-char (if isearch-forward (point-min) (point-max))))
+     (setq isearch-buffers-current-buffer (current-buffer))
+     (setq isearch-wrapped nil)))
+ 
+ (defun isearch-buffers-push-state ()
+   "Save a function restoring the state of multiple buffers search.
+ Save the current buffer to the additional state parameter in the
+ search status stack."
+   `(lambda (cmd)
+      (isearch-buffers-pop-state cmd ,(current-buffer))))
+ 
+ (defun isearch-buffers-pop-state (cmd buffer)
+   "Restore the multiple buffers search state.
+ Switch to the buffer restored from the search status stack."
+   (unless (equal buffer (current-buffer))
+     (switch-to-buffer (setq isearch-buffers-current-buffer buffer))))
+ 
+ 
  ;; General utilities
  
  (defun isearch-no-upper-case-p (string regexp-flag)

-- 
Juri Linkov
http://www.jurta.org/emacs/




reply via email to

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