source: branches/acode-rewrite/source/cocoa-ide/hemlock/src/isearchcoms.lisp

Last change on this file was 16082, checked in by Gary Byers, 11 years ago

Merge trunk changes into this branch. Expect some things to explode.

File size: 12.2 KB
RevLine 
[7844]1;;; -*- Mode: Lisp; Package: hemlock -*-
2;;;
3;;; Copyright (C) 2007 Clozure Associates
4
5(in-package :hemlock)
6
7(defmode "I-Search" :precedence :highest
8 ;; Make anything that's not otherwise overridden exit i-search.
9 :default-command "I-Search Exit and Redo")
10
11(add-hook abort-hook 'end-isearch-mode)
12
13(defhvar "Self Insert Command Name"
14 "The name of the command to handle quoted input (i.e. after c-q) in I-Search"
15 :value "I-Search Self Insert"
16 :mode "I-Search")
17
18(defcommand "Incremental Search" (p)
19 "Searches for input string as characters are provided.
20
21 These are the default I-Search command characters:
22 ^Q quotes the next character typed.
23 ^W extends the search string to include the the word after the point.
24 Delete cancels the last key typed.
25 ^G during a successful search aborts and returns point to where it started.
26 During a failing search, ^G backs up to last non-failing point.
27 ^S repeats forward, and ^R repeats backward.
28 ^R or ^S with empty string either changes the direction or yanks the previous search string.
29 Escape exits the search unless the string is empty.
30 Escape with an empty search string calls the non-incremental search command.
31
32 Other control characters cause exit and execution of the appropriate
33 command.
34"
35 "Set up Incremental Search mode"
36 (declare (ignore p))
37 (start-isearch-mode :forward))
38
39(defcommand "Reverse Incremental Search" (p)
40 "Searches for input string as characters are provided.
41
42 These are the default I-Search command characters:
43 ^Q quotes the next character typed.
44 ^W extends the search string to include the the word after the point.
45 Delete cancels the last key typed.
46 ^G during a successful search aborts and returns point to where it started.
47 During a failing search, ^G backs up to last non-failing point.
48 ^S repeats forward, and ^R repeats backward.
49 ^R or ^S with empty string either changes the direction or yanks the previous search string.
50 Escape exits the search unless the string is empty.
51 Escape with an empty search string calls the non-incremental search command.
52
53 Other control characters cause exit and execution of the appropriate
54 command.
55"
56 "Set up Incremental Search mode"
57 (declare (ignore p))
58 (start-isearch-mode :backward))
59
60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
61;;
62
63(defstruct (isearch-state (:conc-name "ISS-"))
64 direction
[16082]65 local-pattern
[7844]66 failure
67 wrapped-p
68 history
69 start-region)
70
[16082]71(defvar *global-search-pattern* nil "Used when *isearch-is-global*") ; can't use *last-search-pattern* because that's
72; used elsewhere
73
74(defun iss-pattern (state)
75 (if *isearch-is-global*
76 (or *global-search-pattern*
77 (iss-local-pattern state))
78 (iss-local-pattern state)))
79
80(defun iss-string (state)
81 (ignore-errors ; because iss-pattern might be nil
82 (hi::search-pattern-pattern (iss-pattern state))))
83
[7844]84(defun current-region-info ()
85 (list (copy-mark (current-point) :temporary)
86 (copy-mark (current-mark) :temporary)
87 (region-active-p)))
88
89(defun set-current-region-info (info)
90 (destructuring-bind (point mark active-p) info
91 (move-mark (current-point) point)
92 (move-mark (current-mark) mark)
93 (if active-p
94 (progn
95 (activate-region)
96 (note-current-selection-set-by-search))
97 (deactivate-region))))
98
99(defun %i-search-save-state (iss)
100 (push (list* (iss-string iss)
101 (iss-direction iss)
102 (iss-failure iss)
103 (iss-wrapped-p iss)
104 (current-region-info))
105 (iss-history iss)))
106
107(defun %i-search-pop-state (iss)
108 (destructuring-bind (string direction failure wrapped-p . region-info)
109 (pop (iss-history iss))
110 (setf (iss-failure iss) failure)
111 (setf (iss-wrapped-p iss) wrapped-p)
112 (%i-search-set-pattern iss :string string :direction direction)
113 (set-current-region-info region-info)))
114
115(defun %i-search-message (iss)
116 (when t ;(interactive)
117 (message "~:[~;Failing ~]~:[~;Wrapped ~]~:[Reverse I-Search~;I-Search~]: ~A"
118 (iss-failure iss)
119 (iss-wrapped-p iss)
120 (eq (iss-direction iss) :forward)
121 (or (iss-string iss) ""))))
122
123
124;; Minor errors that don't cause isearch mode to be exited, except while
125;; executing keyboard macros.
126(defun %i-search-perhaps-error (message)
127 message
128 (if t ;(interactive)
129 (beep)
130 (abort-current-command message)))
131
132;;;;
133;;
134
[8207]135(defun current-isearch-state ()
136 (or (value i-search-state)
137 (error "I-Search command invoked outside I-Search")))
138
[7844]139(defun start-isearch-mode (direction)
[8207]140 (let* ((buffer (current-buffer))
141 (iss (make-isearch-state :direction direction
[7844]142 :start-region (current-region-info))))
[16082]143 (when (iss-pattern iss)
144 (setf (hi::search-pattern-pattern (iss-pattern iss)) nil))
[8207]145 (setf (buffer-minor-mode buffer "I-Search") t)
146 (unless (hemlock-bound-p 'i-search-state :buffer buffer)
147 (defhvar "I-Search State"
148 "Internal variable containing current state of I-Search"
149 :buffer buffer))
[14812]150 (unless (region-active-p) ; We need the selection (if there is one) to stay put!
151 (push-new-buffer-mark (current-point)))
[7844]152 (setf (value i-search-state) iss)
153 (%i-search-message iss)))
154
155(defun end-isearch-mode ()
156 (setf (buffer-minor-mode (current-buffer) "I-Search") nil))
157
[14812]158(defcommand "I-Search Yank Selection" (p)
159 "Pull string from current selection into search string."
160 (declare (ignore p))
161 (let* ((iss (current-isearch-state)))
[16082]162 (i-search-extend iss (symbol-at-point (current-buffer)))))
[14812]163
[7844]164(defun i-search-backup (iss)
165 (if (iss-history iss)
166 (progn
167 (%i-search-pop-state iss)
168 (%i-search-message iss))
169 (%i-search-perhaps-error "I-Search Backup failed")))
170
171(defun i-search-revert (iss)
172 (loop while (iss-failure iss) do (%i-search-pop-state iss))
173 (%i-search-message iss))
174
175(defun i-search-repeat (iss)
176 (cond ((null (iss-string iss))
177 ;; No search string, so "repeat" really means fetch last successful search string
178 (if (zerop (length *last-search-string*))
179 (%i-search-perhaps-error "No previous search string")
180 (progn
181 (%i-search-save-state iss)
182 (%i-search-set-pattern iss :string *last-search-string*)
183 (%i-search-do-search iss (current-mark)))))
184 ((iss-failure iss)
185 (%i-search-save-state iss)
186 ;; If failed last time, "repeat" really means try again from the top.
187 (setf (iss-wrapped-p iss) t) ;; start saying "Wrapped i-search" to remind 'em.
188 (%i-search-do-search iss (if (eq (iss-direction iss) :forward)
189 (buffer-start-mark (current-buffer))
190 (buffer-end-mark (current-buffer)))))
191 (t
192 (%i-search-save-state iss)
193 ;; Have a non-empty string and a successful search, just find the next one!
194 (%i-search-do-search iss (current-point))))
195 (%i-search-message iss))
196
197(defun i-search-reverse (iss)
198 (%i-search-save-state iss)
199 (%i-search-set-pattern iss :direction (ecase (iss-direction iss)
200 (:forward :backward)
201 (:backward :forward)))
202 (let* ((mark (current-mark))
203 (point (current-point)))
204 (with-mark ((temp point))
205 (move-mark point mark)
206 (move-mark mark temp))
207 (when (iss-failure iss)
208 ;; if we were failing before, search immediately, otherwise wait til asked
209 (%i-search-do-search iss mark)))
210 (%i-search-message iss))
211
212(defun i-search-extend (iss extension)
213 (%i-search-save-state iss)
214 (let* ((new-string (concatenate 'simple-string (iss-string iss) extension)))
215 (%i-search-set-pattern iss :string new-string))
216 (unless (iss-failure iss) ;; Can't succeed now if failed before, so don't try
217 (with-mark ((temp (current-mark)))
218 (when (eq (iss-direction iss) :backward)
219 (or (character-offset temp (length extension))
220 (buffer-end temp)))
221 (%i-search-do-search iss temp)))
222 (%i-search-message iss))
223
224(defun i-search-exit (iss)
225 (let* ((string (iss-string iss)))
226 (when (and string (not (iss-failure iss)))
227 (setf *last-search-string* string)))
228 (end-isearch-mode)
229 (message ""))
230
231(defun %i-search-set-pattern (iss &key (string nil s-p) (direction nil d-p))
[16082]232 (let ((thisstring (if s-p (or string "") (iss-string iss))))
233 (when *isearch-is-global*
234 (setf *last-search-string* thisstring))
235 (when d-p
236 (setf (iss-direction iss) direction))
237 (setf *global-search-pattern*
238 (setf (iss-local-pattern iss) (new-search-pattern (if (value string-search-ignore-case)
239 :string-insensitive
240 :string-sensitive)
241 (iss-direction iss)
242 thisstring
243 (iss-pattern iss))))))
[7844]244
245;; Do a search for the current pattern starting at START going to
246;; end/beginning as per ISS-DIRECTION. Sets ISS-FAILURE depending on
247;; whether found or not. If successful, moves region to surround the
248;; found string (with point at the end for :forward search and at the
249;; beginning for :backward) and activates the region. If failed,
250;; leaves region unchanged. Never modifies START.
251(defun %i-search-do-search (iss start)
252 (let* ((temp (copy-mark start :temporary))
253 (found-offset (find-pattern temp (iss-pattern iss))))
254 (setf (iss-failure iss) (not found-offset))
255 (if (iss-failure iss)
256 (%i-search-perhaps-error "I-Search failed")
257 (let* ((point (current-point))
258 (mark (current-mark)))
259 (move-mark point temp)
260 (if (eq (iss-direction iss) :forward)
261 (character-offset point found-offset)
262 (character-offset temp found-offset))
263 (move-mark mark temp)
264 (activate-region)
265 (note-current-selection-set-by-search)))))
266
267;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
268;;
269
270(defcommand "I-Search Repeat Forward" (p)
271 "Repeat forward incremental search, or reverse direction if currently searching backward"
272 (declare (ignore p))
273 (let* ((iss (current-isearch-state)))
274 (if (eq (iss-direction iss) :forward)
275 (i-search-repeat iss)
276 (i-search-reverse iss))))
277
278(defcommand "I-Search Repeat Backward" (p)
279 "Repeat backward incremental search, or reverse direction if currently searching forward"
280 (declare (ignore p))
281 (let* ((iss (current-isearch-state)))
282 (if (eq (iss-direction iss) :backward)
283 (i-search-repeat iss)
284 (i-search-reverse iss))))
285
286(defcommand "I-Search Backup" (p)
287 "Undo last incremental search command"
288 (declare (ignore p))
289 (let* ((iss (current-isearch-state)))
290 (i-search-backup iss)))
291
292(defcommand "I-Search Yank Word" (p)
293 "Extend the search string to include the the word after the point."
294 (declare (ignore p))
295 (let* ((iss (current-isearch-state))
296 (point (current-point)))
297 (with-mark ((end point))
298 (if (word-offset end 1)
299 (i-search-extend iss (region-to-string (region point end)))
300 (%i-search-perhaps-error "No more words")))))
301
302(defcommand "I-Search Self Insert" (p)
303 "Add character typed to search string"
304 (declare (ignore p))
305 (let* ((iss (current-isearch-state))
306 (char (last-char-typed)))
307 (unless char (editor-error "Can't insert that character."))
308 (i-search-extend iss (string char))))
309
310(defcommand "I-Search Abort" (p)
311 "Abort incremental search mode if search is successful. Otherwise, revert to last
312successful search and continue searching."
313 (declare (ignore p))
314 (let* ((iss (current-isearch-state)))
315 (if (iss-failure iss)
316 (i-search-revert iss)
317 ;; Else move back to starting point and stop searching
318 (progn
319 (set-current-region-info (iss-start-region iss))
320 (abort-current-command "Search aborted")))))
321
322;; The transparent-p flag takes care of executing the key normally when we're done,
323;; as long as we don't take a non-local exit.
324(defcommand ("I-Search Exit and Redo" :transparent-p t) (p)
325 "Exit Incremental Search and then execute the key normally"
326 (declare (ignore p))
327 (let* ((iss (current-isearch-state)))
328 (i-search-exit iss)))
329
330(defcommand "I-Search Exit or Search" (p)
331 "Exit incremental search. If the search string is empty, switch to non-incremental search,
332otherwise just quit"
333 (declare (ignore p))
334 (let* ((iss (current-isearch-state))
335 (string (iss-string iss))
336 (direction (iss-direction iss)))
337 (i-search-exit iss)
338 (when (null string)
339 (if (eq direction :forward)
340 (forward-search-command nil)
341 (reverse-search-command nil)))))
342
343
344
Note: See TracBrowser for help on using the repository browser.