Mercurial > hg > xemacs-beta
diff lisp/vm/vm-search18.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/vm/vm-search18.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,476 @@ +;; Incremental search through a mail folder +;; For version 18 of FSF Emacs only. +;; Copyright (C) 1985, 1986 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY. No author or distributor +;; accepts responsibility to anyone for the consequences of using it +;; or for whether it serves any particular purpose or works at all, +;; unless he says so in writing. Refer to the GNU Emacs General Public +;; License for full details. + +;; Everyone is granted permission to copy, modify and redistribute +;; GNU Emacs, but only under the conditions described in the +;; GNU Emacs General Public License. A copy of this license is +;; supposed to have been given to you along with GNU Emacs so you +;; can know your rights and responsibilities. It should be in a +;; file named COPYING. Among other things, the copyright notice +;; and this notice must be preserved on all copies. + + +(provide 'vm-search) + +;; Adapted for the VM mail reader, Kyle Jones, May 1989 + + +;; This function does all the work of incremental search. +;; The functions attached to ^R and ^S are trivial, +;; merely calling this one, but they are always loaded by default +;; whereas this file can optionally be autoloadable. +;; This is the only entry point in this file. + +(defun vm-isearch (forward &optional regexp) + (let ((search-string "") + (search-message "") + (cmds nil) + (success t) + (wrapped nil) + (barrier (point)) + adjusted + (invalid-regexp nil) + (slow-terminal-mode (and (<= (baud-rate) search-slow-speed) + (> (window-height) + (* 4 search-slow-window-lines)))) + (other-end nil) ;Start of last match if fwd, end if backwd. + (small-window nil) ;if t, using a small window + (found-point nil) ;to restore point from a small window + ;; This is the window-start value found by the search. + (found-start nil) + (opoint (point)) + (vm-ml-message-new vm-ml-message-new) + (vm-ml-message-unread vm-ml-message-unread) + (vm-ml-message-read vm-ml-message-read) + (vm-ml-message-edited vm-ml-message-edited) + (vm-ml-message-replied vm-ml-message-replied) + (vm-ml-message-forwarded vm-ml-message-forwarded) + (vm-ml-message-filed vm-ml-message-filed) + (vm-ml-message-written vm-ml-message-written) + (vm-ml-message-marked vm-ml-message-marked) + (vm-ml-message-deleted vm-ml-message-deleted) + (vm-ml-message-number vm-ml-message-number) + (vm-message-pointer vm-message-pointer) + (inhibit-quit t)) ;Prevent ^G from quitting immediately. + (vm-isearch-push-state) + (save-window-excursion + (catch 'search-done + (while t + (or (>= unread-command-char 0) + (progn + (or (input-pending-p) + (vm-isearch-message)) + (if (and slow-terminal-mode + (not (or small-window (pos-visible-in-window-p)))) + (progn + (setq small-window t) + (setq found-point (point)) + (move-to-window-line 0) + (let ((window-min-height 1)) + (split-window nil (if (< search-slow-window-lines 0) + (1+ (- search-slow-window-lines)) + (- (window-height) + (1+ search-slow-window-lines))))) + (if (< search-slow-window-lines 0) + (progn (vertical-motion (- 1 search-slow-window-lines)) + (set-window-start (next-window) (point)) + (set-window-hscroll (next-window) + (window-hscroll)) + (set-window-hscroll (selected-window) 0)) + (other-window 1)) + (goto-char found-point))))) + (let ((char (if quit-flag + ?\C-g + (read-char)))) + (setq quit-flag nil adjusted nil) + ;; Meta character means exit search. + (cond ((and (>= char 128) + search-exit-option) + (setq unread-command-char char) + (throw 'search-done t)) + ((eq char search-exit-char) + ;; Esc means exit search normally. + ;; Except, if first thing typed, it means do nonincremental + (if (= 0 (length search-string)) + (vm-nonincremental-search forward regexp)) + (throw 'search-done t)) + ((= char ?\C-g) + ;; ^G means the user tried to quit. + (ding) + (discard-input) + (if success + ;; If search is successful, move back to starting point + ;; and really do quit. + (progn (goto-char opoint) + (vm-update-search-position) + (signal 'quit nil)) + ;; If search is failing, rub out until it is once more + ;; successful. + (while (not success) (vm-isearch-pop)))) + ((or (eq char search-repeat-char) + (eq char search-reverse-char)) + (if (eq forward (eq char search-repeat-char)) + ;; C-s in forward or C-r in reverse. + (if (equal search-string "") + ;; If search string is empty, use last one. + (setq search-string + (if regexp + search-last-regexp search-last-string) + search-message + (mapconcat 'text-char-description + search-string "")) + ;; If already have what to search for, repeat it. + (or success + (progn (goto-char (if forward (point-min) (point-max))) + (setq wrapped t)))) + ;; C-s in reverse or C-r in forward, change direction. + (setq forward (not forward))) + (setq barrier (point)) ; For subsequent \| if regexp. + (setq success t) + (or (equal search-string "") + (vm-isearch-search)) + (vm-isearch-push-state)) + ((= char search-delete-char) + ;; Rubout means discard last input item and move point + ;; back. If buffer is empty, just beep. + (if (null (cdr cmds)) + (ding) + (vm-isearch-pop))) + (t + (cond ((or (eq char search-yank-word-char) + (eq char search-yank-line-char)) + ;; ^W means gobble next word from buffer. + ;; ^Y means gobble rest of line from buffer. + (let ((word (save-excursion + (and (not forward) other-end + (goto-char other-end)) + (buffer-substring + (point) + (save-excursion + (if (eq char search-yank-line-char) + (end-of-line) + (forward-word 1)) + (point)))))) + (setq search-string (concat search-string word) + search-message + (concat search-message + (mapconcat 'text-char-description + word ""))))) + ;; Any other control char => + ;; unread it and exit the search normally. + ((and search-exit-option + (/= char search-quote-char) + (or (= char ?\177) + (and (< char ? ) (/= char ?\t) (/= char ?\r)))) + (setq unread-command-char char) + (throw 'search-done t)) + (t + ;; Any other character => add it to the + ;; search string and search. + (cond ((= char search-quote-char) + (setq char (read-quoted-char + (vm-isearch-message t)))) + ((= char ?\r) + ;; unix braindeath + (setq char ?\n))) + (setq search-string (concat search-string + (char-to-string char)) + search-message (concat search-message + (text-char-description char))))) + (if (and (not success) + ;; unsuccessful regexp search may become + ;; successful by addition of characters which + ;; make search-string valid + (not regexp)) + nil + ;; If a regexp search may have been made more + ;; liberal, retreat the search start. + ;; Go back to place last successful search started + ;; or to the last ^S/^R (barrier), whichever is nearer. + (and regexp success cmds + (cond ((memq char '(?* ??)) + (setq adjusted t) + (let ((cs (nth (if forward + 5 ; other-end + 2) ; saved (point) + (car (cdr cmds))))) + ;; (car cmds) is after last search; + ;; (car (cdr cmds)) is from before it. + (setq cs (or cs barrier)) + (goto-char + (if forward + (max cs barrier) + (min cs barrier))))) + ((eq char ?\|) + (setq adjusted t) + (goto-char barrier)))) + ;; In reverse regexp search, adding a character at + ;; the end may cause zero or many more chars to be + ;; matched, in the string following point. + ;; Allow all those possibilities without moving point as + ;; long as the match does not extend past search origin. + (if (and regexp (not forward) (not adjusted) + (condition-case () + (looking-at search-string) + (error nil)) + (<= (match-end 0) (min opoint barrier))) + (setq success t invalid-regexp nil + other-end (match-end 0)) + ;; Not regexp, not reverse, or no match at point. + (if (and other-end (not adjusted)) + (goto-char (if forward other-end + (min opoint barrier (1+ other-end))))) + (vm-isearch-search))) + (vm-isearch-push-state)))))) + (setq found-start (window-start (selected-window))) + (setq found-point (point))) + (if (> (length search-string) 0) + (if regexp + (setq search-last-regexp search-string) + (setq search-last-string search-string))) + (message "") + (if small-window + (goto-char found-point) + ;; Exiting the save-window-excursion clobbers this; restore it. + (set-window-start (selected-window) found-start t)))) + +(defun vm-isearch-message (&optional c-q-hack ellipsis) + ;; If about to search, and previous search regexp was invalid, + ;; check that it still is. If it is valid now, + ;; let the message we display while searching say that it is valid. + (and invalid-regexp ellipsis + (condition-case () + (progn (re-search-forward search-string (point) t) + (setq invalid-regexp nil)) + (error nil))) + ;; If currently failing, display no ellipsis. + (or success (setq ellipsis nil)) + (let ((m (concat (if success "" "failing ") + (if wrapped "wrapped ") + (if regexp "regexp " "") + "VM I-search" + (if forward ": " " backward: ") + search-message + (if c-q-hack "^Q" "") + (if invalid-regexp + (concat " [" invalid-regexp "]") + "")))) + (aset m 0 (upcase (aref m 0))) + (let ((cursor-in-echo-area ellipsis)) + (if c-q-hack m (message "%s" m))))) + +(defun vm-isearch-pop () + (setq cmds (cdr cmds)) + (let ((cmd (car cmds))) + (setq search-string (car cmd) + search-message (car (cdr cmd)) + success (nth 3 cmd) + forward (nth 4 cmd) + other-end (nth 5 cmd) + invalid-regexp (nth 6 cmd) + wrapped (nth 7 cmd) + barrier (nth 8 cmd) +; unused now +; vm-ml-attributes-string (nth 9 cmd) + vm-ml-message-number (nth 10 cmd) + vm-message-pointer (nth 11 cmd)) + (if vm-summary-buffer + (save-excursion + (set-buffer vm-summary-buffer) + (setq + ; unused now + ;vm-ml-attributes-string (nth 9 cmd) + vm-ml-message-number (nth 10 cmd)))) + (goto-char (car (cdr (cdr cmd)))) + (vm-set-summary-pointer (car vm-message-pointer)))) + +(defun vm-isearch-push-state () + (setq cmds (cons (list search-string search-message (point) + success forward other-end invalid-regexp + wrapped barrier +; unused now +; vm-ml-attributes-string + nil + vm-ml-message-number + vm-message-pointer) + cmds))) + +(defun vm-isearch-search () + (vm-isearch-message nil t) + (condition-case lossage + (let ((inhibit-quit nil)) + (if regexp (setq invalid-regexp nil)) + (setq success + (funcall + (if regexp + (if forward 're-search-forward 're-search-backward) + (if forward 'search-forward 'search-backward)) + search-string nil t)) + (if success + (setq other-end + (if forward (match-beginning 0) (match-end 0))))) + (quit (setq unread-command-char ?\C-g) + (setq success nil)) + (invalid-regexp (setq invalid-regexp (car (cdr lossage))) + (if (string-match "\\`Premature \\|\\`Unmatched \\|\\`Invalid " + invalid-regexp) + (setq invalid-regexp "incomplete input")))) + (if success + (vm-update-search-position) + ;; Ding if failed this time after succeeding last time. + (and (nth 3 (car cmds)) + (ding)) + (goto-char (nth 2 (car cmds))))) + +;; This is called from incremental-search +;; if the first input character is the exit character. +;; The interactive-arg-reader uses free variables `forward' and `regexp' +;; which are bound by `incremental-search'. + +;; We store the search string in `search-string' +;; which has been bound already by `incremental-search' +;; so that, when we exit, it is copied into `search-last-string'. + +(defun vm-nonincremental-search (forward regexp) + (let (message char function string inhibit-quit + (cursor-in-echo-area t)) + ;; Prompt assuming not word search, + (setq message (if regexp + (if forward "VM Regexp search: " + "VM Regexp search backward: ") + (if forward "VM Search: " "VM Search backward: "))) + (message "%s" message) + ;; Read 1 char and switch to word search if it is ^W. + (setq char (read-char)) + (if (eq char search-yank-word-char) + (setq message (if forward "VM Word search: " "VM Word search backward: ")) + ;; Otherwise let that 1 char be part of the search string. + (setq unread-command-char char)) + (setq function + (if (eq char search-yank-word-char) + (if forward 'word-search-forward 'word-search-backward) + (if regexp + (if forward 're-search-forward 're-search-backward) + (if forward 'search-forward 'search-backward)))) + ;; Read the search string with corrected prompt. + (setq string (read-string message)) + ;; Empty means use default. + (if (= 0 (length string)) + (setq string search-last-string) + ;; Set last search string now so it is set even if we fail. + (setq search-last-string string)) + ;; Since we used the minibuffer, we should be available for redo. + (setq command-history (cons (list function string) command-history)) + ;; Go ahead and search. + (funcall function string))) + +(defun vm-update-search-position (&optional record-change) + (if (and (>= (point) (vm-start-of (car vm-message-pointer))) + (<= (point) (vm-end-of (car vm-message-pointer)))) + nil + (let ((mp vm-message-list) + (point (point))) + (while mp + (if (and (>= point (vm-start-of (car mp))) + (<= point (vm-end-of (car mp)))) + (if record-change + (progn + (vm-record-and-change-message-pointer vm-message-pointer mp) + (setq mp nil)) + (setq vm-message-pointer mp mp nil)) + (setq mp (cdr mp)))) + (setq vm-need-summary-pointer-update t) + (vm-update-summary-and-mode-line)))) + +(defun vm-isearch-forward (&optional arg) + "Incrementally search forward through the current folder's messages. +Usage is identical to the standard Emacs incremental search. +When the search terminates the message containing point will be selected. + +If the variable vm-search-using-regexps is non-nil, regular expressions +are understood; nil means the search will be for the input string taken +literally. Specifying a prefix ARG interactively toggles the value of +vm-search-using-regexps for this search." + (interactive "P") + (vm-follow-summary-cursor) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-empty) + (vm-error-if-virtual-folder) + (vm-display (current-buffer) t '(vm-isearch-forward) '(vm-isearch-forward)) + (let ((clip-head (point-min)) + (clip-tail (point-max)) + (old-w (selected-window))) + (unwind-protect + (progn (select-window (get-buffer-window (current-buffer))) + (widen) + (vm-isearch t (if arg + (not vm-search-using-regexps) + vm-search-using-regexps)) + (vm-update-search-position t) + ;; vm-show-current-message only adjusts (point-max), + ;; it doesn't change (point-min). + (narrow-to-region + (if (< (point) (vm-vheaders-of (car vm-message-pointer))) + (vm-start-of (car vm-message-pointer)) + (vm-vheaders-of (car vm-message-pointer))) + (point-max)) + (vm-show-current-message) + (setq vm-system-state 'reading) + ;; turn the clipping unwind into a noop + (setq clip-head (point-min)) + (setq clip-tail (point-max))) + (narrow-to-region clip-head clip-tail) + (select-window old-w)))) + +(defun vm-isearch-backward (&optional arg) + "Incrementally search backward through the current folder's messages. +Usage is identical to the standard Emacs incremental search. +When the search terminates the message containing point will be selected. + +If the variable vm-search-using-regexps is non-nil, regular expressions +are understood; nil means the search will be for the input string taken +literally. Specifying a prefix ARG interactively toggles the value of +vm-search-using-regexps for this search." + (interactive "P") + (vm-follow-summary-cursor) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-empty) + (vm-error-if-virtual-folder) + (vm-display (current-buffer) t '(vm-isearch-backward) + (list 'vm-isearch-backward 'searching-message)) + (let ((clip-head (point-min)) + (clip-tail (point-max)) + (old-w (selected-window))) + (unwind-protect + (progn (select-window (get-buffer-window (current-buffer))) + (widen) + (vm-isearch nil (if arg + (not vm-search-using-regexps) + vm-search-using-regexps)) + (vm-update-search-position t) + ;; vm-show-current-message only adjusts (point-max), + ;; it doesn't change (point-min). + (narrow-to-region + (if (< (point) (vm-vheaders-of (car vm-message-pointer))) + (vm-start-of (car vm-message-pointer)) + (vm-vheaders-of (car vm-message-pointer))) + (point-max)) + (vm-show-current-message) + (setq vm-system-state 'reading) + ;; turn the clipping unwind into a noop + (setq clip-head (point-min)) + (setq clip-tail (point-max))) + (narrow-to-region clip-head clip-tail) + (select-window old-w))))