unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* RFC: [PATCH] isearch enhancements: symbol mode; syntactic filtering
@ 2011-07-07 14:20 Daniel Colascione
  2011-07-07 14:53 ` Lennart Borgman
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Daniel Colascione @ 2011-07-07 14:20 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1184 bytes --]

New features
------------

- Syntactic filtering: control whether to match in comments, strings, 
and normal text.

- Symbol search: like word search, but looks only at symbol boundaries

- One ring: optionally share a search ring between normal and regular 
expression searches. The additional history storage described below 
allows this mode to work reliably.

Bugfixes
--------

- After an isearch settings change, we refresh the state stored at the 
to of the stack. This way, we don't lose track of settings after adding 
characters to the search string and removing them again.

The patch also changes some behavior:

- With the patch, we store isearch state in the history ring alongside 
the actual search strings.  This information allows us to exactly 
recreate searches when we pull them from history.

Other behavior changes
----------------------

- Case sensitivity is now displayed alongside other isearch settings in 
the isearch prompt.  Previously, the only indication we gave of case 
sensitivity was a fleeing message displayed briefly each time the user 
toggled the setting.

- isearch no longer prints "pending". I don't see why this message would 
be useful.

[-- Attachment #2: isearch-updates.patch --]
[-- Type: text/plain, Size: 32296 bytes --]

=== modified file 'lisp/isearch.el'
--- lisp/isearch.el	2011-07-04 12:15:16 +0000
+++ lisp/isearch.el	2011-07-07 14:17:08 +0000
@@ -57,6 +57,9 @@
 
 ;;; Code:
 
+(eval-when-compile
+  (require 'cl))
+
 \f
 ;; Some additional options and constants.
 
@@ -74,6 +77,11 @@
   :type 'boolean
   :group 'isearch)
 
+(defcustom search-one-ring nil
+  "Non-nil means to use one ring for both normal and regular expression searches."
+  :type 'boolean
+  :group 'isearch)
+
 (defcustom search-slow-window-lines 1
   "Number of lines in slow search display windows.
 These are the short windows used during incremental search on slow terminals.
@@ -399,6 +407,18 @@
 
 (defalias 'isearch-mode-help 'isearch-describe-mode)
 
+(defun* isearch-get-ring (&optional (regexp isearch-regexp))
+  "Return the symbol holding the appropriate search ring to use."
+  (if (and regexp (not search-one-ring))
+      'regexp-search-ring
+    'search-ring))
+
+(defun* isearch-get-yank-pointer (&optional (regexp isearch-regexp))
+  "Return the symbol holding the appropriate search ring yank pointer."
+  (if (and regexp (not search-one-ring))
+      'regexp-search-ring-yank-pointer
+    'search-ring-update))
+
 \f
 ;; Define isearch-mode keymap.
 
@@ -502,7 +522,15 @@
     (define-key map "\M-e" 'isearch-edit-string)
 
     (define-key map "\M-sr" 'isearch-toggle-regexp)
-    (define-key map "\M-sw" 'isearch-toggle-word)
+    (define-key map "\M-sw" 'isearch-cycle-word-mode)
+
+    ;; Shortcuts for useful programming language modes.
+    (define-key map "\M-_"  'isearch-toggle-identifier-mode)
+    (define-key map "\M-#"  'isearch-toggle-match-text)
+    
+    (define-key map "\M-sc" 'isearch-toggle-match-comments)
+    (define-key map "\M-ss" 'isearch-toggle-match-strings)
+    (define-key map "\M-st" 'isearch-toggle-match-text)
 
     (define-key map [?\M-%] 'isearch-query-replace)
     (define-key map [?\C-\M-%] 'isearch-query-replace-regexp)
@@ -530,14 +558,14 @@
 
 (defvar isearch-forward nil)	; Searching in the forward direction.
 (defvar isearch-regexp nil)	; Searching for a regexp.
-(defvar isearch-word nil)	; Searching for words.
-(defvar isearch-hidden nil) ; Non-nil if the string exists but is invisible.
+(defvar isearch-word nil)	; Searching for words or symbols.
+(defvar isearch-context-filter nil)    ; Where to find matches.
+(defvar isearch-hidden nil)     ; Non-nil if the string exists but is invisible.
 
 (defvar isearch-cmds nil
   "Stack of search status sets.
-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]")
+Each set is a plist containg isearch variables to restore and
+miscellaneous other information.")
 
 (defvar isearch-string "")  ; The current search string.
 (defvar isearch-message "") ; text-char-description version of isearch-string
@@ -558,8 +586,6 @@
 ;;   case in the search string is ignored.
 (defvar isearch-case-fold-search nil)
 
-(defvar isearch-last-case-fold-search nil)
-
 ;; Used to save default value while isearch is active
 (defvar isearch-original-minibuffer-message-timeout nil)
 
@@ -651,7 +677,7 @@
 
 Type \\[isearch-toggle-case-fold] to toggle search case-sensitivity.
 Type \\[isearch-toggle-regexp] to toggle regular-expression mode.
-Type \\[isearch-toggle-word] to toggle word mode.
+Type \\[isearch-cycle-word-mode] to cycle between normal, word, and symbol mode.
 Type \\[isearch-edit-string] to edit the search string in the minibuffer.
 
 Also supported is a search ring of the previous 16 search strings.
@@ -660,6 +686,11 @@
  ring.
 Type \\[isearch-complete] to complete the search string using the search ring.
 
+Type \\[isearch-toggle-match-comments] to toggle matching inside comments.
+Type \\[isearch-toggle-match-strings] to toggle matching inside strings.
+Type \\[isearch-toggle-match-text] to toggle matching outside comments\
+ and strings.
+
 Type \\[isearch-query-replace] to run `query-replace' with string to\
  replace from last search string.
 Type \\[isearch-query-replace-regexp] to run `query-replace-regexp'\
@@ -746,7 +777,10 @@
 ;;  "List of commands for which isearch-mode does not recursive-edit.")
 
 
-(defun isearch-mode (forward &optional regexp op-fun recursive-edit word-p)
+(defun* isearch-mode (forward &optional regexp op-fun recursive-edit word-p
+                              &key (regexp regexp) (op-fun op-fun)
+                              (recursive-edit recursive-edit) (word-p word-p)
+                              context-filter)
   "Start Isearch minor mode.
 It is called by the function `isearch-forward' and other related functions."
 
@@ -754,8 +788,8 @@
   (setq isearch-forward forward
 	isearch-regexp regexp
 	isearch-word word-p
+        isearch-context-filter context-filter
 	isearch-op-fun op-fun
-	isearch-last-case-fold-search isearch-case-fold-search
 	isearch-case-fold-search case-fold-search
 	isearch-string ""
 	isearch-message ""
@@ -834,7 +868,8 @@
 ;; Some high level utilities.  Others below.
 
 (defun isearch-update ()
-  ;; Called after each command to update the display.
+  ;; Called after each command to update the display and save
+  ;; isearch settings to the top of the stack.
   (if (and (null unread-command-events)
 	   (null executing-kbd-macro))
       (progn
@@ -875,6 +910,8 @@
   (setq ;; quit-flag nil  not for isearch-mode
    isearch-adjusted nil
    isearch-yank-flag nil)
+  (pop isearch-cmds)
+  (isearch-push-state)
   (when isearch-lazy-highlight
     (isearch-lazy-highlight-new-loop))
   ;; We must prevent the point moving to the end of composition when a
@@ -890,10 +927,12 @@
 NOPUSH is t and EDIT is t."
 
   (if isearch-resume-in-command-history
-      (let ((command `(isearch-resume ,isearch-string ,isearch-regexp
-				      ,isearch-word ,isearch-forward
-				      ,isearch-message
-				      ',isearch-case-fold-search)))
+      (let ((command
+             `(isearch-resume ,isearch-string ,isearch-regexp
+                              ,isearch-word ,isearch-forward
+                              ,isearch-message
+                              ',isearch-case-fold-search
+                              :context-filter ',isearch-context-filter)))
 	(unless (equal (car command-history) command)
 	  (setq command-history (cons command command-history)))))
 
@@ -959,10 +998,28 @@
 (defun isearch-update-ring (string &optional regexp)
   "Add STRING to the beginning of the search ring.
 REGEXP if non-nil says use the regexp search ring."
-  (add-to-history
-   (if regexp 'regexp-search-ring 'search-ring)
-   string
-   (if regexp regexp-search-ring-max search-ring-max)))
+  (let ((ring (isearch-get-ring regexp)))
+    (unless (zerop (length string))
+      (setq string (copy-sequence string))
+      (set-text-properties
+       0 (length string)
+       (list 'isearch-saved-state
+             (loop for var in isearch-persistent-state-variables
+                   collect (cons var (symbol-value var))))
+       string)
+      (unless (and (symbol-value ring)
+                   (equal string (car (symbol-value ring)))
+                   (equal (isearch-saved-state-from-string string)
+                          (isearch-saved-state-from-string
+                           (car (symbol-value ring)))))
+        (let (history-delete-duplicates)
+          (add-to-history
+           ring
+           string
+           (if (and regexp (not search-one-ring))
+               regexp-search-ring-max
+             search-ring-max)
+           t))))))
 
 ;; Switching buffers should first terminate isearch-mode.
 ;; ;; For Emacs 19, the frame switch event is handled.
@@ -976,74 +1033,64 @@
 \f
 ;; The search status structure and stack.
 
-(defsubst isearch-string-state (frame)
-  "Return the search string in FRAME."
-  (aref frame 0))
-(defsubst isearch-message-state (frame)
-  "Return the search string to display to the user in FRAME."
-  (aref frame 1))
-(defsubst isearch-point-state (frame)
-  "Return the point in FRAME."
-  (aref frame 2))
-(defsubst isearch-success-state (frame)
-  "Return the success flag in FRAME."
-  (aref frame 3))
-(defsubst isearch-forward-state (frame)
-  "Return the searching-forward flag in FRAME."
-  (aref frame 4))
-(defsubst isearch-other-end-state (frame)
-  "Return the other end of the match in FRAME."
-  (aref frame 5))
-(defsubst isearch-word-state (frame)
-  "Return the search-by-word flag in FRAME."
-  (aref frame 6))
-(defsubst isearch-error-state (frame)
-  "Return the regexp error message in FRAME, or nil if its regexp is valid."
-  (aref frame 7))
-(defsubst isearch-wrapped-state (frame)
-  "Return the search-wrapped flag in FRAME."
-  (aref frame 8))
-(defsubst isearch-barrier-state (frame)
-  "Return the barrier value in FRAME."
-  (aref frame 9))
-(defsubst isearch-case-fold-search-state (frame)
-  "Return the case-folding flag in FRAME."
-  (aref frame 10))
-(defsubst isearch-pop-fun-state (frame)
-  "Return the function restoring the mode-specific Isearch state in FRAME."
-  (aref frame 11))
+(eval-and-compile
+  (defconst isearch-persistent-state-variables
+    '(isearch-word
+      isearch-context-filter
+      isearch-case-fold-search
+      isearch-regexp)
+    "List of isearch variables saved between searches.")
+  
+  (defconst isearch-state-variables
+    (append
+     isearch-persistent-state-variables
+     '(isearch-string
+       isearch-message
+       isearch-success
+       isearch-forward
+       isearch-other-end
+       isearch-word
+       isearch-error
+       isearch-wrapped
+       isearch-barrier
+       isearch-context-filter
+       isearch-case-fold-search))
+    "List of isearch variables saved during searches."))
+
+(eval-when-compile
+  (loop for var in (list*
+                    'isearch-pop-fun
+                    'isearch-point
+                    isearch-state-variables)
+        for accessor = (intern (format "%s-state" var))
+        do (eval
+            `(defun ,accessor (cmd)
+               (cdr (assq ',var cmd))))))
 
 (defun isearch-top-state ()
-  (let ((cmd (car isearch-cmds)))
-    (setq isearch-string (isearch-string-state cmd)
-	  isearch-message (isearch-message-state cmd)
-	  isearch-success (isearch-success-state cmd)
-	  isearch-forward (isearch-forward-state cmd)
-	  isearch-other-end (isearch-other-end-state cmd)
-	  isearch-word (isearch-word-state cmd)
-	  isearch-error (isearch-error-state cmd)
-	  isearch-wrapped (isearch-wrapped-state cmd)
-	  isearch-barrier (isearch-barrier-state cmd)
-	  isearch-case-fold-search (isearch-case-fold-search-state cmd))
-    (if (functionp (isearch-pop-fun-state cmd))
-	(funcall (isearch-pop-fun-state cmd) cmd))
-    (goto-char (isearch-point-state cmd))))
+  (loop for (var . value) in (car isearch-cmds)
+        do (cond ((memq var isearch-state-variables)
+                  (set var value))
+                 ((eq var 'isearch-point)
+                  (goto-char value))
+                 ((and (eq var 'isearch-pop-fun)
+                       (functionp value))
+                  (funcall value (car isearch-cmds))))))
+
+(defun isearch-push-state ()
+  (push (nreverse
+         (list*
+          (cons 'isearch-pop-fun (if isearch-push-state-function
+                                     (funcall isearch-push-state-function)))
+          (cons 'isearch-point (point))
+          (loop for var in isearch-state-variables
+               collect (cons var (symbol-value var)))))
+        isearch-cmds))
 
 (defun isearch-pop-state ()
-  (setq isearch-cmds (cdr isearch-cmds))
+  (pop 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-error isearch-wrapped isearch-barrier
-		      isearch-case-fold-search
-		      (if isearch-push-state-function
-			  (funcall isearch-push-state-function)))
-	      isearch-cmds)))
-
 \f
 ;; Commands active while inside of the isearch minor mode.
 
@@ -1110,6 +1157,7 @@
 	      (isearch-new-word isearch-word)
 
 	      (isearch-regexp isearch-regexp)
+              (isearch-context-filter isearch-context-filter)
 	      (isearch-op-fun isearch-op-fun)
 	      (isearch-cmds isearch-cmds)
 	      (isearch-success isearch-success)
@@ -1150,20 +1198,21 @@
 	  (setq old-point (point) old-other-end isearch-other-end)
 
 	  (unwind-protect
-	      (let* ((message-log-max nil)
+	      (let* (message-log-max
+                     history-add-new-input ; we'll add to history manually
+                     (minibuffer-allow-text-properties t)
 		     ;; Binding minibuffer-history-symbol to nil is a work-around
 		     ;; for some incompatibility with gmhist.
-		     (minibuffer-history-symbol))
+		     minibuffer-history-symbol
+                     (ring-name (isearch-get-ring))
+                     (yank-pointer-name (isearch-get-yank-pointer)))
 		(setq isearch-new-string
                       (read-from-minibuffer
                        (isearch-message-prefix nil nil isearch-nonincremental)
                         (cons isearch-string (1+ (isearch-fail-pos)))
                        minibuffer-local-isearch-map nil
-                       (if isearch-regexp
-			   (cons 'regexp-search-ring
-				 (1+ (or regexp-search-ring-yank-pointer -1)))
-			 (cons 'search-ring
-			       (1+ (or search-ring-yank-pointer -1))))
+                       (cons ring-name
+                             (1+ (or (symbol-value yank-pointer-name) -1)))
                        nil t)
 		      isearch-new-message
 		      (mapconcat 'isearch-text-char-description
@@ -1181,7 +1230,9 @@
 			  isearch-regexp
 			  isearch-op-fun
 			  nil
-			  isearch-word)
+			  isearch-word
+                          :context-filter isearch-context-filter
+                          )
 
 	    ;; Copy new local values to isearch globals
 	    (setq isearch-string isearch-new-string
@@ -1191,11 +1242,8 @@
 
 	  ;; Empty isearch-string means use default.
 	  (if (= 0 (length isearch-string))
-	      (setq isearch-string (or (car (if isearch-regexp
-						regexp-search-ring
-					      search-ring))
-				       "")
-
+	      (setq isearch-string (or (car (symbol-value (isearch-get-ring)))
+				       "")                    
 		    isearch-message
 		    (mapconcat 'isearch-text-char-description
 			       isearch-string ""))
@@ -1204,6 +1252,10 @@
 	    ;; Only the string actually used should be saved.
 	    ))
 
+        ;; Update isearch variables from the supplied string in case
+        ;; it came from history.
+        (isearch-update-state-from-string)
+
 	;; This used to push the state as of before this C-s, but it adds
 	;; an inconsistent state where part of variables are from the
 	;; previous search (e.g. `isearch-success'), and part of variables
@@ -1213,7 +1265,7 @@
 	;; Reinvoke the pending search.
 	(isearch-search)
 	(isearch-push-state)		; this pushes the correct state
-	(isearch-update)
+        (isearch-update)
 	(if isearch-nonincremental
 	    (progn
 	      ;; (sit-for 1) ;; needed if isearch-done does: (message "")
@@ -1274,22 +1326,40 @@
       (isearch-pop-state))
     (isearch-update)))
 
+(put 'isearch-saved-state 'front-sticky t)
+
+(defun isearch-saved-state-from-string (str)
+  (or (get-text-property 0 'isearch-saved-state str)
+      (get-text-property (next-property-change 0 str (1- (length str)))
+                         'isearch-saved-state str)))
+
+(defun isearch-update-state-from-string ()
+  "Update isearch state variables from text properties.  We set
+these properties when adding strings to isearch history.  By side
+effect, set isearch-string to a copy of the current value with
+all text properties removed."
+  (unless (zerop (length isearch-string))
+    (loop for (var . value) in (isearch-saved-state-from-string
+                                isearch-string)
+          do (set var value))
+    (setq isearch-string (copy-sequence isearch-string))
+    (set-text-properties 0 (length isearch-string) nil isearch-string)))
+
 (defun isearch-repeat (direction)
   ;; Utility for isearch-repeat-forward and -backward.
   (if (eq isearch-forward (eq direction 'forward))
       ;; C-s in forward or C-r in reverse.
       (if (equal isearch-string "")
 	  ;; If search string is empty, use last one.
-	  (if (null (if isearch-regexp regexp-search-ring search-ring))
+	  (if (null (symbol-value (isearch-get-ring)))
 	      (setq isearch-error "No previous search string")
-	    (setq isearch-string
-		  (if isearch-regexp
-		      (car regexp-search-ring)
-		    (car search-ring))
-		  isearch-message
-		  (mapconcat 'isearch-text-char-description
-			     isearch-string "")
-		  isearch-case-fold-search isearch-last-case-fold-search))
+	    (progn
+              (setq isearch-string
+                    (car (symbol-value (isearch-get-ring))))
+              (isearch-update-state-from-string)
+              (setq isearch-message
+                    (mapconcat 'isearch-text-char-description
+                               isearch-string ""))))
 	;; If already have what to search for, repeat it.
 	(or isearch-success
 	    (progn
@@ -1342,25 +1412,81 @@
   (setq isearch-success t isearch-adjusted t)
   (isearch-update))
 
-(defun isearch-toggle-word ()
-  "Toggle word searching on or off."
-  (interactive)
-  (setq isearch-word (not isearch-word))
-  (setq isearch-success t isearch-adjusted t)
-  (isearch-update))
+(defun* isearch-cycle-word-mode (&optional (mode nil mode-supplied-p))
+  "Cycle isearch through normal, word, and symbol searching.  If
+MODE is supplied, set the word mode directly to the mode.  MODE
+should be either the symbol `word' for word-matching mode,
+`symbol' for symbol-matching mode, or nil for normal mode."
+  (interactive)
+  (setq isearch-word (cond
+                      (mode-supplied-p mode)
+                      ((eq isearch-word 'symbol) nil)
+                      (isearch-word 'symbol)
+                      (t 'word)))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
+(defalias 'isearch-toggle-word 'isearch-cycle-word-mode)
+
+(defun isearch-toggle-symbol-mode ()
+  (interactive)
+  (isearch-cycle-word-mode
+   (if (eq isearch-word 'symbol)
+       nil
+     'symbol)))
+
+(defun isearch-toggle-context-filter-flags (filter-flags)
+  "Toggle flags in `isearch-context-filter'.  If any flag in
+FILTER-FLAGS is present, remove all flags in FILTER-FLAGS.
+Otherwise, ensure all flags in FILTER-FLAGS are in
+`isearch-context-filter'"
+  (let ((flag-present (loop for flag in filter-flags
+                            thereis (memq flag isearch-context-filter)))
+        (stripped-filter (loop for old-flag in isearch-context-filter
+                               unless (memq old-flag filter-flags)
+                               collect old-flag)))
+    (setq isearch-context-filter
+          (if flag-present
+              stripped-filter
+            (append filter-flags stripped-filter))))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
+(defun isearch-toggle-match-comments ()
+  "Toggle matching inside comments on or off."
+  (interactive)
+  (isearch-toggle-context-filter-flags '(comment)))
+
+(defun isearch-toggle-match-strings ()
+  "Toggle matching inside comments on or off."
+  (interactive)
+  (isearch-toggle-context-filter-flags '(string)))
+
+(defun isearch-toggle-match-text ()
+  "Toggle matching inside comments on or off."
+  (interactive)
+  (isearch-toggle-context-filter-flags '(text)))
+
+(defun isearch-toggle-match-comments-and-strings ()
+  "Toggle matching inside comments and strings on or off."
+  (interactive)
+  (isearch-toggle-context-filter-flags '(comment string)))
+
+(defun isearch-toggle-identifier-mode ()
+  "Toggle matching whole symbols outside comments and strings on or off."
+  (interactive)
+  (if (or isearch-word isearch-context-filter)
+      (setq isearch-word nil
+            isearch-context-filter nil)
+    (isearch-toggle-symbol-mode)
+    (isearch-toggle-match-comments-and-strings)))
 
 (defun isearch-toggle-case-fold ()
   "Toggle case folding in searching on or off."
   (interactive)
   (setq isearch-case-fold-search
 	(if isearch-case-fold-search nil 'yes))
-  (let ((message-log-max nil))
-    (message "%s%s [case %ssensitive]"
-	     (isearch-message-prefix nil nil isearch-nonincremental)
-	     isearch-message
-	     (if isearch-case-fold-search "in" "")))
   (setq isearch-success t isearch-adjusted t)
-  (sit-for 1)
   (isearch-update))
 
 (defun isearch-query-replace (&optional delimited regexp-flag)
@@ -1382,7 +1508,16 @@
 	;; `exit-recursive-edit' in `isearch-done' that terminates
 	;; the execution of this command when it is non-nil.
 	;; We call `exit-recursive-edit' explicitly at the end below.
-	(isearch-recursive-edit nil))
+	(isearch-recursive-edit nil)
+        ;; Make perform-replace respect our contextual filtering.
+        (replace-search-function
+         `(lambda (&rest args)
+            (apply #'isearch-context-filter-search-function
+                   ',replace-search-function args)))
+        (replace-re-search-function
+         `(lambda (&rest args)
+            (apply #'isearch-context-filter-search-function
+                   ',replace-re-search-function args))))
     (isearch-done nil t)
     (isearch-clean-overlays)
     (if (and isearch-other-end
@@ -1398,7 +1533,11 @@
      (query-replace-read-to
       isearch-string
       (concat "Query replace"
-	      (if (or delimited isearch-word) " word" "")
+              (cond ((eq isearch-word 'symbol)
+                     " symbol")
+                    ((or delimited isearch-word)
+                     " word ")
+                    (t ""))
 	      (if isearch-regexp " regexp" "")
 	      (if (and transient-mark-mode mark-active) " in region" ""))
       isearch-regexp)
@@ -1421,12 +1560,14 @@
   (interactive
    (list
     (cond
-     (isearch-word (concat "\\b" (replace-regexp-in-string
-				  "\\W+" "\\W+"
-				  (replace-regexp-in-string
-				   "^\\W+\\|\\W+$" "" isearch-string)
-				  nil t)
-			   "\\b"))
+     (isearch-word
+      (concat (if (eq isearch-word 'symbol) "\\_<" "\\b")
+              (replace-regexp-in-string
+               "\\W+" "\\W+"
+               (replace-regexp-in-string
+                "^\\W+\\|\\W+$" "" isearch-string)
+               nil t)
+              (if (eq isearch-word 'symbol) "\\_>" "\\b")))
      (isearch-regexp isearch-string)
      (t (regexp-quote isearch-string)))
     (if current-prefix-arg (prefix-numeric-value current-prefix-arg))))
@@ -2060,12 +2201,10 @@
 
 (defun isearch-ring-adjust1 (advance)
   ;; Helper for isearch-ring-adjust
-  (let* ((ring (if isearch-regexp regexp-search-ring search-ring))
+  (let* ((ring (isearch-get-ring))
 	 (length (length ring))
-	 (yank-pointer-name (if isearch-regexp
-				'regexp-search-ring-yank-pointer
-			      'search-ring-yank-pointer))
-	 (yank-pointer (eval yank-pointer-name)))
+	 (yank-pointer-name (isearch-get-yank-pointer))
+	 (yank-pointer (symbol-value yank-pointer-name)))
     (if (zerop length)
 	()
       (set yank-pointer-name
@@ -2082,6 +2221,7 @@
   (isearch-ring-adjust1 advance)
   (if search-ring-update
       (progn
+        (isearch-update-state-from-string)
 	(isearch-search)
 	(isearch-push-state)
 	(isearch-update))
@@ -2104,7 +2244,7 @@
 (defun isearch-complete1 ()
   ;; Helper for isearch-complete and isearch-complete-edit
   ;; Return t if completion OK, nil if no completion exists.
-  (let* ((ring (if isearch-regexp regexp-search-ring search-ring))
+  (let* ((ring (symbol-value (isearch-get-ring)))
          (completion-ignore-case case-fold-search)
          (completion (try-completion isearch-string ring)))
     (cond
@@ -2182,6 +2322,51 @@
 	     (isearch-message-suffix c-q-hack ellipsis)))
     (if c-q-hack m (let ((message-log-max nil)) (message "%s" m)))))
 
+(defun isearch-context-filter-match-p ()
+  "Return t if the current location satisfies `isearch-context-filter'."
+  ;; Compute the parse state only when we might not match it.  Use
+  ;; parse-partial-sexp, not syntax-ppss, to avoid confusing match
+  ;; highlighting.
+  (loop with parse-state = (if isearch-context-filter
+                               (parse-partial-sexp (point-min) (point)))
+        for condition in isearch-context-filter
+        never (case condition
+                (string   (nth 3 parse-state))
+                (comment  (nth 4 parse-state))
+                (text     (not (nth 8 parse-state))))))
+
+(defun isearch-context-filter-search-function (search-func pat bound noerror)
+  "Wrap SEARCH-FUNC so as to enforce `isearch-context-filter'. "
+
+  (let (found)
+    (while (and (setq found (funcall search-func pat bound noerror))
+                (not (isearch-context-filter-match-p))))
+    found))
+
+(defun isearch-describe-context-filter (context-filter)
+  "Return a string describing isearch context-filter CONTEXT-FILTER."
+  (let ((want-comments (not  (memq 'comment context-filter)))
+        (want-strings (not (memq 'string context-filter)))
+        (want-text (not (memq 'text context-filter))))
+    (cond ((null context-filter) "everywhere")
+          ((not (or want-comments want-strings want-text))
+           "[no match]")
+          (want-text
+           (concat "no-"
+                   (cond (want-strings
+                          "comment")
+                         (want-comments
+                          "string")
+                         (t
+                          "string-or-comment"))))
+          (t
+           (cond ((and want-strings want-comments)
+                  "string-and-comment")
+                 (want-strings
+                  "string")
+                 (want-comments
+                  "comment"))))))
+
 (defun isearch-message-prefix (&optional _c-q-hack ellipsis nonincremental)
   ;; If about to search, and previous search regexp was invalid,
   ;; check that it still is.  If it is valid now,
@@ -2194,16 +2379,33 @@
   ;; If currently failing, display no ellipsis.
   (or isearch-success (setq ellipsis nil))
   (let ((m (concat (if isearch-success "" "failing ")
-		   (if isearch-adjusted "pending " "")
-		   (if (and isearch-wrapped
+                   (if (and isearch-wrapped
 			    (not isearch-wrap-function)
 			    (if isearch-forward
 				(> (point) isearch-opoint)
 			      (< (point) isearch-opoint)))
 		       "over")
 		   (if isearch-wrapped "wrapped ")
-		   (if isearch-word "word " "")
+                   (cond ((eq isearch-word 'symbol) "symbol ")
+                         (isearch-word "word ")
+                         (t ""))
+
+                   ;; Display a message for whichever option isn't the
+                   ;; default.
+                   (cond ((and isearch-case-fold-search
+                               (not case-fold-search))
+                          "case-insensitive ")
+                         ((and case-fold-search
+                               (not isearch-case-fold-search))
+                          "case-sensitive ")
+                         (t ""))
+                   
 		   (if isearch-regexp "regexp " "")
+                   (if isearch-context-filter
+                       (concat (isearch-describe-context-filter
+                                isearch-context-filter)
+                               " ")
+                     "")
 		   (if multi-isearch-next-buffer-current-function "multi " "")
 		   (or isearch-message-prefix-add "")
 		   (if nonincremental "search" "I-search")
@@ -2234,12 +2436,24 @@
 This returned function will be used by `isearch-search-string' to
 search for the first occurrence of STRING or its translation.")
 
+(defun isearch-symbol-search-forward (string bound noerror)
+  (re-search-forward
+   (concat "\\_<" (regexp-quote string) "\\_>") bound noerror))
+
+(defun isearch-symbol-search-backward (string bound noerror)
+  (re-search-backward
+   (concat "\\_<" (regexp-quote string) "\\_>") bound noerror))
+
 (defun isearch-search-fun ()
   "Return the function to use for the search.
 Can be changed via `isearch-search-fun-function' for special needs."
   (if isearch-search-fun-function
       (funcall isearch-search-fun-function)
     (cond
+     ((eq isearch-word 'symbol)
+      (if isearch-forward
+          'isearch-symbol-search-forward
+        'isearch-symbol-search-backward))
      (isearch-word
       ;; Use lax versions to not fail at the end of the word while
       ;; the user adds and removes characters in the search string
@@ -2259,7 +2473,8 @@
 If found, move point to the end of the occurrence,
 update the match data, and return point."
   (let* ((func (isearch-search-fun))
-         (pos1 (save-excursion (funcall func string bound noerror)))
+         (pos1 (save-excursion (isearch-context-filter-search-function
+                                func string bound noerror)))
          pos2)
     (when (and
 	   ;; Avoid "obsolete" warnings for translation-table-for-input.
@@ -2282,7 +2497,8 @@
         (when translated
           (save-match-data
             (save-excursion
-              (if (setq pos2 (funcall func translated bound noerror))
+              (if (setq pos2 (isearch-context-filter-search-function
+                              func translated bound noerror))
                   (setq match-data (match-data t)))))
           (when (and pos2
                      (or (not pos1)
@@ -2323,8 +2539,8 @@
 	  (if (or (not isearch-success)
 		  (bobp) (eobp)
 		  (= (match-beginning 0) (match-end 0))
-		  (funcall isearch-filter-predicate
-			   (match-beginning 0) (match-end 0)))
+                  (funcall isearch-filter-predicate
+                           (match-beginning 0) (match-end 0)))
 	      (setq retry nil)))
 	(setq isearch-just-started nil)
 	(if isearch-success
@@ -2702,8 +2918,8 @@
 	  (if (or (not success)
 		  (= (point) bound) ; like (bobp) (eobp) in `isearch-search'.
 		  (= (match-beginning 0) (match-end 0))
-		  (funcall isearch-filter-predicate
-			   (match-beginning 0) (match-end 0)))
+                  (funcall isearch-filter-predicate
+                           (match-beginning 0) (match-end 0)))
 	      (setq retry nil)))
 	success)
     (error nil)))
@@ -2776,7 +2992,8 @@
 		    (run-at-time lazy-highlight-interval nil
 				 'isearch-lazy-highlight-update)))))))))
 
-(defun isearch-resume (string regexp word forward message case-fold)
+(defun* isearch-resume (string regexp word
+                        forward message case-fold &rest args)
   "Resume an incremental search.
 STRING is the string or regexp searched for.
 REGEXP non-nil means the resumed search was a regexp search.
@@ -2784,7 +3001,7 @@
 FORWARD non-nil means resume a forward search.
 MESSAGE is the echo-area message recorded for the search resumed.
 CASE-FOLD non-nil means the search was case-insensitive."
-  (isearch-mode forward regexp nil nil word)
+  (apply #'isearch-mode forward regexp nil nil word args)
   (setq isearch-string string
 	isearch-message message
 	isearch-case-fold-search case-fold)

=== modified file 'lisp/replace.el'
--- lisp/replace.el	2011-07-02 13:53:53 +0000
+++ lisp/replace.el	2011-07-07 05:55:54 +0000
@@ -1797,10 +1797,10 @@
 
     (if delimited-flag
 	(setq search-function 're-search-forward
-	      search-string (concat "\\b"
+	      search-string (concat (if (eq delimited-flag 'symbol) "\\_<" "\\b")
 				    (if regexp-flag from-string
 				      (regexp-quote from-string))
-				    "\\b")))
+                                    (if (eq delimited-flag 'symbol) "\\_>" "\\b"))))
     (when query-replace-lazy-highlight
       (setq isearch-lazy-highlight-last-string nil))
 
@@ -1926,7 +1926,9 @@
 			 (with-output-to-temp-buffer "*Help*"
 			   (princ
 			    (concat "Query replacing "
-				    (if delimited-flag "word " "")
+                                    (cond ((eq delimited-flag 'symbol) "symbol ")
+                                          (delimited-flag "word ")
+                                          (t ""))				    
 				    (if regexp-flag "regexp " "")
 				    from-string " with "
 				    next-replacement ".\n\n"


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2011-07-08 22:34 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-07 14:20 RFC: [PATCH] isearch enhancements: symbol mode; syntactic filtering Daniel Colascione
2011-07-07 14:53 ` Lennart Borgman
2011-07-07 20:51 ` Stefan Monnier
2011-07-07 21:39   ` Daniel Colascione
2011-07-08  0:20 ` Juri Linkov
2011-07-08  0:49   ` Daniel Colascione
2011-07-08 19:27     ` Juri Linkov
2011-07-08  3:23   ` Drew Adams
2011-07-08 13:04     ` Stefan Monnier
2011-07-08 21:05 ` Lennart Borgman
2011-07-08 21:19   ` Drew Adams
2011-07-08 21:50     ` Juri Linkov
2011-07-08 22:34       ` Drew Adams

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).