# HG changeset patch # User Eli Zaretskii # Date 1092487719 0 # Node ID 99e8e40ec202e4ee007abe931bca137e8ee488c8 # Parent 00e5170987b500cc35af01b97e11d51b1a5cc58c Remove accidental changes of March 4. Fix backing up when a regexp isearch is made more general. Use symbolic accessor functions for isearch stack frames to make usage clearer. (search-whitespace-regexp): Made groups in documentation shy (as is the group in the default value). (isearch-fallback): New function, addresses problems with regexps liberalized by `\|', adds support for liberalization by `\}' (the general repetition construct), and incorporates behavior for `*'/`?'. (isearch-}-char): New command, calls `isearch-fallback' with arguments appropriate to a typed `}'. (isearch-*-char, isearch-|-char): Now just call `isearch-fallback' appropriately. (isearch-mode-map): Bind `}' to `isearch-}-char'. (isearch-string, isearch-message,string, isearch-point, isearch-success, isearch-forward-flag, isearch-other-end, isearch-word, isearch-invalid-regexp, isearch-wrapped, isearch-barrier, isearch-within-brackets, isearch-case-fold-search): New inline functions to read fields of a stack frame. diff -r 00e5170987b5 -r 99e8e40ec202 lisp/isearch.el --- a/lisp/isearch.el Sat Aug 14 12:47:42 2004 +0000 +++ b/lisp/isearch.el Sat Aug 14 12:48:39 2004 +0000 @@ -153,9 +153,9 @@ (defcustom search-whitespace-regexp "\\(?:\\s-+\\)" "*If non-nil, regular expression to match a sequence of whitespace chars. This applies to regular expression incremental search. -You might want to use something like \"[ \\t\\r\\n]+\" instead. -In the Customization buffer, that is `[' followed by a space, -a tab, a carriage return (control-M), a newline, and `]+'." +You might want to use something like \"\\\\(?:[ \\t\\r\\n]+\\\\)\" instead. +In the Customization buffer, that is `\\(?:[' followed by a space, +a tab, a carriage return (control-M), a newline, and `]+\\)'." :type 'regexp :group 'isearch) @@ -298,11 +298,11 @@ (define-key map "\M-\C-y" 'isearch-yank-char) (define-key map "\C-y" 'isearch-yank-line) - ;; Define keys for regexp chars * ? |. + ;; Define keys for regexp chars * ? } |. ;; Nothing special for + because it matches at least once. (define-key map "*" 'isearch-*-char) (define-key map "?" 'isearch-*-char) - (define-key map "{" 'isearch-{-char) + (define-key map "}" 'isearch-}-char) (define-key map "|" 'isearch-|-char) ;; Turned off because I find I expect to get the global definition--rms. @@ -372,9 +372,9 @@ (defvar isearch-cmds nil "Stack of search status sets. -Each set is a list of the form: - (STRING MESSAGE POINT SUCCESS FORWARD OTHER-END WORD - INVALID-REGEXP WRAPPED BARRIER WITHIN-BRACKETS CASE-FOLD-SEARCH)") +Each set is a vector of the form: + [STRING MESSAGE POINT SUCCESS FORWARD OTHER-END WORD + INVALID-REGEXP WRAPPED BARRIER WITHIN-BRACKETS CASE-FOLD-SEARCH]") (defvar isearch-string "") ; The current search string. (defvar isearch-message "") ; text-char-description version of isearch-string @@ -774,6 +774,74 @@ ;; (handle-switch-frame (car (cdr last-command-char)))) +;; The search status structure and stack. + +(defsubst isearch-string (frame) + "Return the search string in FRAME." + (aref 0 frame)) +(defsubst isearch-message-string (frame) + "Return the search string to display to the user in FRAME." + (aref 1 frame)) +(defsubst isearch-point (frame) + "Return the point in FRAME." + (aref 2 frame)) +(defsubst isearch-success (frame) + "Return the success flag in FRAME." + (aref 3 frame)) +(defsubst isearch-forward-flag (frame) + "Return the searching-forward flag in FRAME." + (aref 4 frame)) +(defsubst isearch-other-end (frame) + "Return the other end of the match in FRAME." + (aref 5 frame)) +(defsubst isearch-word (frame) + "Return the search-by-word flag in FRAME." + (aref 6 frame)) +(defsubst isearch-invalid-regexp (frame) + "Return the regexp error message in FRAME, or nil if its regexp is valid." + (aref 7 frame)) +(defsubst isearch-wrapped (frame) + "Return the search-wrapped flag in FRAME." + (aref 8 frame)) +(defsubst isearch-barrier (frame) + "Return the barrier value in FRAME." + (aref 9 frame)) +(defsubst isearch-within-brackets (frame) + "Return the in-character-class flag in FRAME." + (aref 10 frame)) +(defsubst isearch-case-fold-search (frame) + "Return the case-folding flag in FRAME." + (aref 11 frame)) + +(defun isearch-top-state () + (let ((cmd (car isearch-cmds))) + (setq isearch-string (isearch-string cmd) + isearch-message (isearch-message-string cmd) + isearch-success (isearch-success cmd) + isearch-forward (isearch-forward-flag cmd) + isearch-other-end (isearch-other-end cmd) + isearch-word (isearch-word cmd) + isearch-invalid-regexp (isearch-invalid-regexp cmd) + isearch-wrapped (isearch-wrapped cmd) + isearch-barrier (isearch-barrier cmd) + isearch-within-brackets (isearch-within-brackets cmd) + isearch-case-fold-search (isearch-case-fold-search cmd)) + (goto-char (isearch-point cmd)))) + +(defun isearch-pop-state () + (setq isearch-cmds (cdr isearch-cmds)) + (isearch-top-state)) + +(defun isearch-push-state () + (setq isearch-cmds + (cons (vector isearch-string isearch-message (point) + isearch-success isearch-forward isearch-other-end + isearch-word + isearch-invalid-regexp isearch-wrapped isearch-barrier + isearch-within-brackets isearch-case-fold-search) + isearch-cmds))) + + ;; Commands active while inside of the isearch minor mode. (defun isearch-exit () @@ -1249,53 +1317,93 @@ (isearch-update)) -(defun isearch-{-char () - "Handle \{ specially in regexps." - (interactive) - (isearch-*-char t)) - -;; *, ?, and | chars can make a regexp more liberal. +;; *, ?, }, and | chars can make a regexp more liberal. ;; They can make a regexp match sooner or make it succeed instead of failing. ;; So go back to place last successful search started ;; or to the last ^S/^R (barrier), whichever is nearer. ;; + needs no special handling because the string must match at least once. -(defun isearch-*-char (&optional want-backslash) - "Handle * and ? specially in regexps. -When WANT-BACKSLASH is non-nil, do special handling for \{." - (interactive) - (if isearch-regexp - (let ((idx (length isearch-string))) - (while (and (> idx 0) - (eq (aref isearch-string (1- idx)) ?\\)) - (setq idx (1- idx))) - ;; * and ? are special when not preceded by \. - ;; { is special when it is preceded by \. - (when (= (mod (- (length isearch-string) idx) 2) - (if want-backslash 1 0)) - (setq isearch-adjusted t) - ;; Get the isearch-other-end from before the last search. - ;; We want to start from there, - ;; so that we don't retreat farther than that. - ;; (car isearch-cmds) is after last search; - ;; (car (cdr isearch-cmds)) is from before it. - (let ((cs (nth 5 (car (cdr isearch-cmds))))) - (setq cs (or cs isearch-barrier)) - (goto-char - (if isearch-forward - (max cs isearch-barrier) - (min cs isearch-barrier))))))) +(defun isearch-backslash (str) + "Return t if STR ends in an odd number of backslashes." + (= (mod (- (length str) (string-match "\\\\*\\'" str)) 2) 1)) + +(defun isearch-fallback (want-backslash &optional allow-invalid to-barrier) + "Return point to previous successful match to allow regexp liberalization. +\\ +Respects \\[isearch-repeat-forward] and \\[isearch-repeat-backward] by +stopping at `isearch-barrier' as needed. + +Do nothing if a backslash is escaping the liberalizing character. If +WANT-BACKSLASH is non-nil, invert this behavior (for \\} and \\|). + +Do nothing if regexp has recently been invalid unless optional ALLOW-INVALID +non-nil. + +If optional TO-BARRIER non-nil, ignore previous matches and go exactly to the +barrier." + ;; (eq (not a) (not b)) makes all non-nil values equivalent + (when (and isearch-regexp (eq (not (isearch-backslash isearch-string)) + (not want-backslash)) + ;; We have to check 2 stack frames because the last might be + ;; invalid just because of a backslash. + (or (not isearch-invalid-regexp) + (not (isearch-invalid-regexp (cadr isearch-cmds))) + allow-invalid)) + (if to-barrier + (progn (goto-char isearch-barrier) + (setq isearch-adjusted t)) + (let* ((stack isearch-cmds) + (previous (cdr stack)) ; lookbelow in the stack + (frame (car stack))) + ;; Walk down the stack looking for a valid regexp (as of course only + ;; they can be the previous successful match); this conveniently + ;; removes all bracket-sets and groups that might be in the way, as + ;; well as partial \{\} constructs that the code below leaves behind. + ;; Also skip over postfix operators -- though horrid, + ;; 'ab?\{5,6\}+\{1,2\}*' is perfectly legal. + (while (and previous + (or (isearch-invalid-regexp frame) + (let* ((string (isearch-string frame)) + (lchar (aref string (1- (length string))))) + ;; The operators aren't always operators; check + ;; backslashes. This doesn't handle the case of + ;; operators at the beginning of the regexp not + ;; being special, but then we should fall back to + ;; the barrier anyway because it's all optional. + (if (isearch-backslash + (isearch-string (car previous))) + (eq lchar ?\}) + (memq lchar '(?* ?? ?+)))))) + (setq stack previous previous (cdr previous) frame (car stack))) + (when stack + ;; `stack' now refers the most recent valid regexp that is not at + ;; all optional in its last term. Now dig one level deeper and find + ;; what matched before that. + (let ((last-other-end (or (isearch-other-end (car previous)) + isearch-barrier))) + (goto-char (if isearch-forward + (max last-other-end isearch-barrier) + (min last-other-end isearch-barrier))) + (setq isearch-adjusted t)))))) (isearch-process-search-char last-command-char)) - -(defun isearch-|-char () - "If in regexp search, jump to the barrier." +;; * and ? are special when not preceded by \. +(defun isearch-*-char () + "Maybe back up to handle * and ? specially in regexps." (interactive) - (if isearch-regexp - (progn - (setq isearch-adjusted t) - (goto-char isearch-barrier))) - (isearch-process-search-char last-command-char)) + (isearch-fallback nil)) + +;; } is special when it is preceded by \. +(defun isearch-}-char () + "Handle \\} specially in regexps." + (interactive) + (isearch-fallback t t)) + +;; | is special when it is preceded by \. +(defun isearch-|-char () + "If in regexp search, jump to the barrier unless in a group." + (interactive) + (isearch-fallback t nil t)) (defun isearch-unread-key-sequence (keylist) "Unread the given key-sequence KEYLIST. @@ -1775,38 +1883,6 @@ (insert isearch-string)))) -;; The search status stack (and isearch window-local variables, not used). -;; Need a structure for this. - -(defun isearch-top-state () - (let ((cmd (car isearch-cmds))) - (setq isearch-string (car cmd) - isearch-message (car (cdr cmd)) - isearch-success (nth 3 cmd) - isearch-forward (nth 4 cmd) - isearch-other-end (nth 5 cmd) - isearch-word (nth 6 cmd) - isearch-invalid-regexp (nth 7 cmd) - isearch-wrapped (nth 8 cmd) - isearch-barrier (nth 9 cmd) - isearch-within-brackets (nth 10 cmd) - isearch-case-fold-search (nth 11 cmd)) - (goto-char (car (cdr (cdr cmd)))))) - -(defun isearch-pop-state () - (setq isearch-cmds (cdr isearch-cmds)) - (isearch-top-state)) - -(defun isearch-push-state () - (setq isearch-cmds - (cons (list isearch-string isearch-message (point) - isearch-success isearch-forward isearch-other-end - isearch-word - isearch-invalid-regexp isearch-wrapped isearch-barrier - isearch-within-brackets isearch-case-fold-search) - isearch-cmds))) - - ;; Message string (defun isearch-message (&optional c-q-hack ellipsis) @@ -1936,9 +2012,9 @@ (if isearch-success nil ;; Ding if failed this time after succeeding last time. - (and (nth 3 (car isearch-cmds)) + (and (isearch-success (car isearch-cmds)) (ding)) - (goto-char (nth 2 (car isearch-cmds))))) + (goto-char (isearch-point (car isearch-cmds))))) ;; Called when opening an overlay, and we are still in isearch.