From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Juri Linkov Newsgroups: gmane.emacs.devel Subject: isearch multiple buffers Date: Sat, 06 Oct 2007 22:52:46 +0300 Organization: JURTA Message-ID: <87odfcggvl.fsf@jurta.org> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1191700576 25214 80.91.229.12 (6 Oct 2007 19:56:16 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Sat, 6 Oct 2007 19:56:16 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Oct 06 21:56:13 2007 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1IeFky-0007e6-HR for ged-emacs-devel@m.gmane.org; Sat, 06 Oct 2007 21:56:13 +0200 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1IeFkt-00045Q-MY for ged-emacs-devel@m.gmane.org; Sat, 06 Oct 2007 15:56:07 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1IeFkq-000455-81 for emacs-devel@gnu.org; Sat, 06 Oct 2007 15:56:04 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1IeFkp-00044d-6j for emacs-devel@gnu.org; Sat, 06 Oct 2007 15:56:03 -0400 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1IeFkp-00044Y-2R for emacs-devel@gnu.org; Sat, 06 Oct 2007 15:56:03 -0400 Original-Received: from mailman.kiev.sovam.com ([89.162.150.100]) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1IeFkm-0002eZ-Rx for emacs-devel@gnu.org; Sat, 06 Oct 2007 15:56:01 -0400 Original-Received: from relay01.kiev.sovam.com ([62.64.120.200]) by mailman.kiev.sovam.com with esmtp (Exim 4.63 (FreeBSD)) (envelope-from ) id 1IeIXf-000AMq-PI for emacs-devel@gnu.org; Sat, 06 Oct 2007 22:54:39 +0000 Original-Received: from [83.170.232.243] (helo=smtp.svitonline.com) by relay01.kiev.sovam.com with esmtp (Exim 4.67) (envelope-from ) id 1IeFkg-00072c-Cu for emacs-devel@gnu.org; Sat, 06 Oct 2007 22:55:56 +0300 User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/23.0.50 (gnu/linux) X-Scanner-Signature: 347b60a7d50e4b51b7e2953e35c97cdf X-DrWeb-checked: yes X-SpamTest-Envelope-From: juri@jurta.org X-SpamTest-Group-ID: 00000000 X-SpamTest-Header: Not Detected X-SpamTest-Info: Profiles 1563 [Oct 05 2007] X-SpamTest-Info: helo_type=3 X-SpamTest-Info: {HEADERS: header Content-Type found without required header Content-Transfer-Encoding} X-SpamTest-Method: none X-SpamTest-Rate: 25 X-SpamTest-Status: Not detected X-SpamTest-Status-Extended: not_detected X-SpamTest-Version: SMTP-Filter Version 3.0.0 [0255], KAS30/Release X-Detected-Kernel: FreeBSD 6.x (1) X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:80343 Archived-At: 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/