[comp.emacs] Gnu emacs isearch with interactive toggle of literal vs regexp search

klm@cme-durer.ARPA (Ken Manheimer) (02/18/88)

I just finished porting an isearch hack i find indispensible.  I
originally stole the idea from gosling/unipress emacs isearch and
implemented it for gnu emacs 16.xx .  The variation allows you to
interactively toggle an incremental search between treating the search
string literally (vanilla isearch) and treating the string as a
regexp simply by typing a \C-t at any time during the search.  The
modified isearch also resumes a prior search (eg, when you enter a
search-resume-char (\C-s) as the first character of the search) using
the mode, literal vs regexp, it was in last.  We just got 18.49 up
here and i just spent some time incorporating my changes into the new
version.  I'm posting here the entire modified isearch function as
well as an incidental constant declaration that probably ought to go
into loaddefs.el .  The standard place for the isearch function is
isearch.el .

One immediate benefit of combining the literal and regexp incremental
searches is that you don't need to dedicate as many keys to have both
kinds of incremental search immediately available.  More importantly,
it allows you to employ regexp search when you didn't expect you'd
need it, like for some long, tedious literal string that all of a
sudden needs a wildcard char - you just toggle and add the wildcard
on.  This sacrifices the compartmentalization of the literal and
regexp recommencement strings, but i find it *much* preferable.
Since the search string is shared between the two modes, in addition
to adding the declaration below for `isearch-regexp-toggle' to
loaddefs.el, you should also dispose of the definition for
`search-last-regexp' from the same file, as it's no longer used by
isearch. 

Here's what you need to do:
-=-				   -=-				   -=-
Add to loaddefs.el:
(defconst isearch-regexp-toggle ?\C-t "\
Character that toggles between literal and regexp incremental searching.")
-=-				   -=-				   -=-
Don't forget to cut out the declaration for `search-last-regexp' from loaddefs.
-=-				   -=-				   -=-
Substitute for isearch function in isearch.el:

(defun 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))
	(inhibit-quit t))  ;Prevent ^G from quitting immediately.
    (isearch-push-state)
    (save-window-excursion
     (catch 'search-done
       (while t
	 (or (>= unread-command-char 0)
	     (progn
	       (or (input-pending-p)
		   (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
		 invalid-regexp nil)			; ~~ klm 17-Feb-1988
	   ;; 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))
		      (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)
			     (signal 'quit nil))
		    ;; If search is failing, rub out until it is once more
		    ;;  successful.
		    (while (not success) (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.
			  (progn
			    (setq regexp resume-regexp-isearch)
			    (setq search-string 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 "")
		      (isearch-search))
		  (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)
		    (isearch-pop)))
		 ((= char isearch-regexp-toggle)
		  ;; C-t means change between treating search string as
		  ;; literal to treating it as regexp and vice versa
		  (setq resume-regexp-isearch (setq regexp (not regexp)))
		  (if other-end (goto-char other-end))
		  (if (not (equal search-string "")) (isearch-search)))
		 (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
					     (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 possibiities 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)))))
		      (isearch-search)))
		  (isearch-push-state))))))
     (setq found-start (window-start (selected-window)))
     (setq found-point (point)))
    (if (> (length search-string) 0)
	(setq search-last-string search-string))
    ;; If there was movement, mark the starting position.
    ;; Maybe should test difference between and set mark iff > threshold.
    (if (/= (point) opoint)
	(push-mark opoint)
      (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))))
-=-				   -=-				   -=-
Now you probably will have to reload and dump emacs, because
loaddefs.el and isearch.el are both cooked in...
-=-				   -=-				   -=-
I hope you like this approach.  I checked for any overt bugs, and have
been using the version i adapted the changes from for over a year, but
if i let anything get by please let me know the details and i'll try
to correct them asap.  (Actually, i'll be disappearing for a week in a
few days, but will be diligent about following up on this before and afterwards...)

Cheers,

Ken Manheimer	 klm@cme-durer.arpa	or	..!uunet!cme-durer.arpa!klm
National Bureau of standards
Metrology A127				(301) 975-3539
Gaithersburg, MD 20899

Nirvana?  That's a place where the powers that be and their friends hang out.
	- "Zonker Harris"