source: release/1.11/source/cocoa-ide/hemlock/src/isearchcoms.lisp

Last change on this file was 16688, checked in by R. Matthew Emerson, 9 years ago

Merge copyright/license header changes to 1.11 release branch.

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