source: release/1.5/source/cocoa-ide/hemlock/src/search1.lisp

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

r13585 from trunk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.5 KB
RevLine 
[6]1;;; -*- Log: Hemlock.Log; Package: Hemlock-Internals -*-
2;;;
3;;; **********************************************************************
4;;; This code was written as part of the CMU Common Lisp project at
5;;; Carnegie Mellon University, and has been placed in the public domain.
6;;;
7#+CMU (ext:file-comment
8 "$Header$")
9;;;
10;;; **********************************************************************
11;;;
12;;; Searching and replacing functions for Hemlock.
13;;; Originally written by Skef Wholey, Rewritten by Rob MacLachlan.
14;;;
15
16(in-package :hemlock-internals)
17
18
19
20;;; The search pattern structure is used only by simple searches, more
21;;; complex ones make structures which include it.
22
23(defstruct (search-pattern (:print-function %print-search-pattern)
24 (:constructor internal-make-search-pattern))
25 kind ; The kind of pattern to search for.
26 direction ; The direction to search in.
27 pattern ; The search pattern to use.
28 search-function ; The function to call to search.
29 reclaim-function) ; The function to call to reclaim this pattern.
30
31(setf (documentation 'search-pattern-p 'function)
32 "Returns true if its argument is a Hemlock search-pattern object,
33 Nil otherwise.")
34
35(defun %print-search-pattern (object stream depth)
36 (let ((*print-level* (and *print-level* (- *print-level* depth)))
37 (*print-case* :downcase))
38 (declare (special *print-level* *print-case*))
39 (write-string "#<Hemlock " stream)
40 (princ (search-pattern-direction object) stream)
41 (write-char #\space stream)
42 (princ (search-pattern-kind object) stream)
43 (write-string " Search-Pattern for ")
44 (prin1 (search-pattern-pattern object) stream)
45 (write-char #\> stream)
46 (terpri stream)))
47
48(defvar *search-pattern-experts* (make-hash-table :test #'eq)
49 "Holds an eq hashtable which associates search kinds with the functions
50 that know how to make patterns of that kind.")
51(defvar *search-pattern-documentation* ()
52 "A list of all the kinds of search-pattern that are defined.")
53
54;;; define-search-kind -- Internal
55;;;
56;;; This macro is used to define a new kind of search pattern. Kind
57;;; is the kind of search pattern to define. Lambda-list is the argument
58;;; list for the expert-function to be built and forms it's body.
59;;; The arguments passed are the direction, the pattern, and either
60;;; an old search-pattern of the same type or nil. Documentation
61;;; is put on the search-pattern-documentation property of the kind
62;;; keyword.
63;;;
[12635]64(defmacro define-search-kind (kind lambda-list documentation &body forms)
65 `(progn
66 (push ,documentation *search-pattern-documentation*)
67 (setf (gethash ,kind *search-pattern-experts*)
[6]68 #'(lambda ,lambda-list ,@forms))))
69
70
71;;; new-search-pattern -- Public
72;;;
73;;; This function deallocates any old search-pattern and then dispatches
74;;; to the correct expert.
75;;;
76(defun new-search-pattern (kind direction pattern &optional
77 result-search-pattern)
78 "Makes a new Hemlock search pattern of kind Kind to search direction
79 using Pattern. Direction is either :backward or :forward.
80 If supplied, result-search-pattern is a pattern to destroy to make
81 the new one. The variable *search-pattern-documentation* contains
82 documentation for each kind."
83 (unless (or (eq direction :forward) (eq direction :backward))
84 (error "~S is not a legal search direction." direction))
85 (when result-search-pattern
86 (funcall (search-pattern-reclaim-function result-search-pattern)
87 result-search-pattern)
88 (unless (eq kind (search-pattern-kind result-search-pattern))
89 (setq result-search-pattern nil)))
90 (let ((expert (gethash kind *search-pattern-experts*)))
91 (unless expert
92 (error "~S is not a defined search pattern kind." kind))
[12170]93 (funcall expert direction pattern result-search-pattern)))
94
95(defun new-search-vector (vec access-fn)
96 (let* ((max 0))
97 (declare (fixnum max))
98 (dotimes (i (length vec))
99 (let* ((code (funcall access-fn vec i)))
100 (when (> code max)
[6]101 (setq max code))))
102 (make-array (the fixnum (1+ max)))))
103
104(eval-when (:compile-toplevel :execute)
[12170]105
[6]106(defmacro dispose-search-vector (vec)
107 vec)
108); eval-when (:compile-toplevel :execute)
109
110
111;;;; macros used by various search kinds:
112
113;;; search-once-forward-macro -- Internal
114;;;
115;;; Passes search-fun strings, starts and lengths to do a forward
116;;; search. The other-args are passed through to the searching
117;;; function after after everything else The search-fun is
118;;; expected to return NIL if nothing is found, or it index where the
119;;; match ocurred. Something non-nil is returned if something is
120;;; found and line and start are set to where it was found.
121;;;
122(defmacro search-once-forward-macro (line start search-fun &rest other-args)
123 `(do* ((l ,line)
124 (chars (line-chars l) (line-chars l))
125 (len (length chars) (length chars))
126 (start-pos ,start 0)
127 (index 0))
128 (())
129 (declare (simple-string chars) (fixnum start-pos len)
130 (type (or fixnum null) index))
131 (setq index (,search-fun chars start-pos len ,@other-args))
132 (when index
133 (setq ,start index ,line l)
134 (return t))
135 (setq l (line-next l))
136 (when (null l) (return nil))))
137
138
139;;; search-once-backward-macro -- Internal
140;;;
141;;; Like search-once-forward-macro, except it goes backwards. Length
142;;; is not passed to the search function, since it won't need it.
143;;;
144(defmacro search-once-backward-macro (line start search-fun &rest other-args)
145 `(do* ((l ,line)
146 (chars (line-chars l) (line-chars l))
147 (start-pos (1- ,start) (1- (length chars)))
148 (index 0))
149 (())
150 (declare (simple-string chars) (fixnum start-pos)
151 (type (or fixnum null) index))
152 (setq index (,search-fun chars start-pos ,@other-args))
153 (when index
154 (setq ,start index ,line l)
155 (return t))
156 (setq l (line-previous l))
157 (when (null l) (return nil))))
158
159
160
161;;;; String Searches.
162;;;
163;;; We use the Boyer-Moore algorithm for string searches.
164;;;
165
166;;; sensitive-string-search-macro -- Internal
167;;;
168;;; This macro does a case-sensitive Boyer-Moore string search.
169;;;
170;;; Args:
171;;; String - The string to search in.
172;;; Start - The place to start searching at.
173;;; Length - NIL if going backward, the length of String if going forward.
174;;; Pattern - A simple-vector of characters. A simple-vector is used
175;;; rather than a string because it is believed that simple-vector access
176;;; will be faster in most implementations.
177;;; Patlen - The length of Pattern.
178;;; Last - (1- Patlen)
179;;; Jumps - The jump vector as given by compute-boyer-moore-jumps
180;;; +/- - The function to increment with, either + (forward) or -
181;;; (backward)
[12170]182;;; -/+ - Like +/-, only the other way around.
183(eval-when (:compile-toplevel :execute)
[6]184(defmacro sensitive-string-search-macro (string start length pattern patlen
[12170]185 last jumps +/- -/+)
[6]186 (let* ((jumpslen (gensym))
187 (charcode (gensym)))
188 `(do ((scan (,+/- ,start ,last))
189 (,jumpslen (length ,jumps))
190 (patp ,last))
191 (,(if length `(>= scan ,length) '(minusp scan)))
192 (declare (fixnum scan patp))
193 (let ((char (schar ,string scan)))
194 (cond
195 ((char= char (svref ,pattern patp))
196 (if (zerop patp)
[12170]197 (return scan)
198 (setq scan (,-/+ scan 1) patp (1- patp))))
199 (t
200 ;; If mismatch consult jump table to find amount to skip.
[6]201 (let* ((,charcode (search-char-code char))
202 (jump (if (< ,charcode ,jumpslen)
203 (svref ,jumps ,charcode)
204 ,patlen)))
[12170]205 (declare (fixnum jump))
[6]206 (if (> jump (- ,patlen patp))
207 (setq scan (,+/- scan jump))
208 (setq scan (,+/- scan (- ,patlen patp)))))
209 (setq patp ,last)))))))
210
211
212;;; insensitive-string-search-macro -- Internal
213;;;
214;;; This macro is very similar to the case sensitive one, except that
215;;; we do the search for a hashed string, and then when we find a match
216;;; we compare the uppercased search string with the found string uppercased
[12170]217;;; and only say we win when they match too.
218;;;
219(defmacro insensitive-string-search-macro (string start length pattern
220 folded-string patlen last
221 jumps +/- -/+)
222 (let* ((jumpslen (gensym)))
223 `(do ((scan (,+/- ,start ,last))
224 (,jumpslen (length ,jumps))
225 (patp ,last))
226 (,(if length `(>= scan ,length) '(minusp scan)))
227 (declare (fixnum scan patp))
[6]228 (let ((hash (search-hash-code (schar ,string scan))))
229 (declare (fixnum hash))
230 (cond
231 ((= hash (the fixnum (svref ,pattern patp)))
232 (if (zerop patp)
233 (if (do ((i ,last (1- i)))
234 (())
[12170]235 (when (char/=
236 (search-char-upcase (schar ,string (,+/- scan i)))
[6]237 (schar ,folded-string i))
[12170]238 (return nil))
239 (when (zerop i) (return t)))
240 (return scan)
241 (setq scan (,+/- scan ,patlen) patp ,last))
242 (setq scan (,-/+ scan 1) patp (1- patp))))
243 (t
244 ;; If mismatch consult jump table to find amount to skip.
[6]245 (let ((jump (if (< hash ,jumpslen)
246 (svref ,jumps hash)
[12170]247 ,patlen)))
[6]248 (declare (fixnum jump))
249 (if (> jump (- ,patlen patp))
250 (setq scan (,+/- scan jump))
251 (setq scan (,+/- scan (- ,patlen patp)))))
252 (setq patp ,last)))))))
253
254
255;;;; Searching for strings with newlines in them:
256;;;
257;;; Due to the buffer representation, search-strings with embedded
258;;; newlines need to be special-cased. What we do is break
259;;; the search string up into lines and then searching for a line with
260;;; the correct prefix. This is actually a faster search.
261;;; For this one we just have one big hairy macro conditionalized for
262;;; both case-sensitivity and direction. Have fun!!
263
264;;; newline-search-macro -- Internal
265;;;
266;;; Do a search for a string containing newlines. Line is the line
267;;; to start on, and Start is the position to start at. Pattern and
268;;; optionally Pattern2, are simple-vectors of things that represent
269;;; each line in the pattern, and are passed to Test-Fun. Pattern
270;;; must contain simple-strings so we can take the length. Test-Fun is a
271;;; thing to compare two strings and see if they are equal. Forward-p
272;;; tells whether to go forward or backward.
273;;;
274(defmacro newline-search-macro (line start test-fun pattern forward-p
275 &optional pattern2)
276 `(let* ((patlen (length ,pattern))
277 (first (svref ,pattern 0))
278 (firstlen (length first))
279 (l ,line)
280 (chars (line-chars l))
281 (len (length chars))
282 ,@(if pattern2 `((other (svref ,pattern2 0)))))
283 (declare (simple-string first chars) (fixnum firstlen patlen len))
284 ,(if forward-p
285 ;; If doing a forward search, go to the next line if we could not
286 ;; match due to the start position.
287 `(when (< (- len ,start) firstlen)
288 (setq l (line-next l)))
289 ;; If doing a backward search, go to the previous line if the current
290 ;; line could not match the last line in the pattern, and then go
291 ;; back the 1- number of lines in the pattern to avoid a possible
292 ;; match across the starting point.
293 `(let ((1-len (1- patlen)))
294 (declare (fixnum 1-len))
295 (when (< ,start (length (the simple-string
296 (svref ,pattern 1-len))))
297 (setq l (line-previous l)))
298 (dotimes (i 1-len)
299 (when (null l) (return nil))
300 (setq l (line-previous l)))))
301 (do* ()
302 ((null l))
303 (setq chars (line-chars l) len (length chars))
304 ;; If the end of this line is the first line in the pattern then check
305 ;; to see if the other lines match.
306 (when (and (>= len firstlen)
307 (,test-fun chars first other
308 :start1 (- len firstlen) :end1 len
309 :end2 firstlen))
310 (when
311 (do ((m (line-next l) (line-next m))
312 (i 2 (1+ i))
313 (next (svref ,pattern 1) (svref ,pattern i))
314 ,@(if pattern2
315 `((another (svref ,pattern2 1)
316 (svref ,pattern2 i))))
317 (len 0)
318 (nextlen 0)
319 (chars ""))
320 ((null m))
321 (declare (simple-string next chars) (fixnum len nextlen i))
322 (setq chars (line-chars m) nextlen (length next)
323 len (length chars))
324 ;; When on last line of pattern, check if prefix of line.
325 (when (= i patlen)
326 (return (and (>= len nextlen)
327 (,test-fun chars next another :end1 nextlen
328 :end2 nextlen))))
329 (unless (,test-fun chars next another :end1 len
330 :end2 nextlen)
331 (return nil)))
332 (setq ,line l ,start (- len firstlen))
333 (return t)))
334 ;; If not, try the next line
335 (setq l ,(if forward-p '(line-next l) '(line-previous l))))))
336
337
338;;;; String-comparison macros that are passed to newline-search-macro
339
340;;; case-sensitive-test-fun -- Internal
341;;;
342;;; Just thows away the extra arg and calls string=.
343;;;
344(defmacro case-sensitive-test-fun (string1 string2 ignore &rest keys)
345 (declare (ignore ignore))
346 `(string= ,string1 ,string2 ,@keys))
347
348;;; case-insensitive-test-fun -- Internal
349;;;
350;;; First compare the characters hashed with hashed-string2 and then
351;;; only if they agree do an actual compare with case-folding.
352;;;
353(defmacro case-insensitive-test-fun (string1 string2 hashed-string2
354 &key end1 (start1 0) end2)
355 `(when (= (- ,end1 ,start1) ,end2)
356 (do ((i 0 (1+ i)))
357 ((= i ,end2)
358 (dotimes (i ,end2 t)
359 (when (char/= (search-char-upcase (schar ,string1 (+ ,start1 i)))
360 (schar ,string2 i))
361 (return nil))))
362 (when (/= (search-hash-code (schar ,string1 (+ ,start1 i)))
363 (svref ,hashed-string2 i))
364 (return nil)))))
365); eval-when (:compile-toplevel :execute)
366
367
[12170]368;;; compute-boyer-moore-jumps -- Internal
[6]369;;;
370;;; Compute return a jump-vector to do a Boyer-Moore search for
[12143]371;;; the "string" of things in Vector. Access-fun is a function
[6]372;;; that aref's vector and returns a number.
[13586]373;;;
[6]374(defun compute-boyer-moore-jumps (vec access-fun)
375 (declare (simple-vector vec))
376 (let ((jumps (new-search-vector vec access-fun))
377 (len (length vec)))
378 (declare (simple-vector jumps))
[13586]379 (when (zerop len) (editor-error "Zero length search string not allowed."))
380 ;; The default jump is the length of the search string.
[6]381 (dotimes (i (length jumps))
382 (setf (aref jumps i) len))
383 ;; For chars in the string the jump is the distance from the end.
384 (dotimes (i len)
385 (setf (aref jumps (funcall access-fun vec i)) (- len i 1)))
386 jumps))
387
388
389
390
391;;;; Case insensitive searches
392
393;;; In order to avoid case folding, we do a case-insensitive hash of
394;;; each character. We then search for string in this translated
395;;; character set, and reject false successes by checking of the found
396;;; string is string-equal the the original search string.
397;;;
398
399(defstruct (string-insensitive-search-pattern
400 (:include search-pattern)
401 (:conc-name string-insensitive-)
402 (:print-function %print-search-pattern))
403 jumps
404 hashed-string
405 folded-string)
406
407;;; Search-Hash-String -- Internal
408;;;
409;;; Return a simple-vector containing the search-hash-codes of the
410;;; characters in String.
411;;;
412(defun search-hash-string (string)
413 (declare (simple-string string))
414 (let* ((len (length string))
415 (result (make-array len)))
416 (declare (fixnum len) (simple-vector result))
417 (dotimes (i len result)
418 (setf (aref result i) (search-hash-code (schar string i))))))
419
420;;; make-insensitive-newline-pattern -- Internal
421;;;
422;;; Make bash in fields in a string-insensitive-search-pattern to
423;;; do a search for a string with newlines in it.
424;;;
425(defun make-insensitive-newline-pattern (pattern folded-string)
426 (declare (simple-string folded-string))
427 (let* ((len (length folded-string))
428 (num (1+ (count #\newline folded-string :end len)))
429 (hashed (make-array num))
430 (folded (make-array num)))
431 (declare (simple-vector hashed folded) (fixnum len num))
432 (do ((prev 0 nl)
433 (i 0 (1+ i))
434 (nl (position #\newline folded-string :end len)
435 (position #\newline folded-string :start nl :end len)))
436 ((null nl)
437 (let ((piece (subseq folded-string prev len)))
438 (setf (aref folded i) piece)
439 (setf (aref hashed i) (search-hash-string piece))))
440 (let ((piece (subseq folded-string prev nl)))
441 (setf (aref folded i) piece)
442 (setf (aref hashed i) (search-hash-string piece)))
443 (incf nl))
444 (setf (string-insensitive-folded-string pattern) folded
445 (string-insensitive-hashed-string pattern) hashed)))
446
447
448
449(define-search-kind :string-insensitive (direction pattern old)
450 ":string-insensitive - Pattern is a string to do a case-insensitive
451 search for."
452 (unless old (setq old (make-string-insensitive-search-pattern)))
453 (setf (search-pattern-kind old) :string-insensitive
454 (search-pattern-direction old) direction
455 (search-pattern-pattern old) pattern)
456 (let* ((folded-string (string-upcase pattern)))
457 (declare (simple-string folded-string))
458 (cond
459 ((find #\newline folded-string)
460 (make-insensitive-newline-pattern old folded-string)
461 (setf (search-pattern-search-function old)
462 (if (eq direction :forward)
463 #'insensitive-find-newline-once-forward-method
[8774]464 #'insensitive-find-newline-once-backward-method))
[6]465 (setf (search-pattern-reclaim-function old) #'identity))
466 (t
467 (case direction
468 (:forward
469 (setf (search-pattern-search-function old)
470 #'insensitive-find-string-once-forward-method))
471 (t
472 (setf (search-pattern-search-function old)
473 #'insensitive-find-string-once-backward-method)
474 (setq folded-string (nreverse folded-string))))
475 (let ((hashed-string (search-hash-string folded-string)))
476 (setf (string-insensitive-hashed-string old) hashed-string
477 (string-insensitive-folded-string old) folded-string)
478 (setf (string-insensitive-jumps old)
479 (compute-boyer-moore-jumps hashed-string #'svref))
480 (setf (search-pattern-reclaim-function old)
481 #'(lambda (p)
482 (dispose-search-vector (string-insensitive-jumps p))))))))
483 old)
484
485
486(defun insensitive-find-string-once-forward-method (pattern line start)
487 (let* ((hashed-string (string-insensitive-hashed-string pattern))
488 (folded-string (string-insensitive-folded-string pattern))
489 (jumps (string-insensitive-jumps pattern))
490 (patlen (length hashed-string))
491 (last (1- patlen)))
492 (declare (simple-vector jumps hashed-string) (simple-string folded-string)
493 (fixnum patlen last))
494 (when (search-once-forward-macro
495 line start insensitive-string-search-macro
496 hashed-string folded-string patlen last jumps + -)
497 (values line start patlen))))
498
499(defun insensitive-find-string-once-backward-method (pattern line start)
500 (let* ((hashed-string (string-insensitive-hashed-string pattern))
501 (folded-string (string-insensitive-folded-string pattern))
502 (jumps (string-insensitive-jumps pattern))
503 (patlen (length hashed-string))
504 (last (1- patlen)))
505 (declare (simple-vector jumps hashed-string) (simple-string folded-string)
506 (fixnum patlen last))
507 (when (search-once-backward-macro
508 line start insensitive-string-search-macro
509 nil hashed-string folded-string patlen last jumps - +)
510 (values line (- start last) patlen))))
511
512(eval-when (:compile-toplevel :execute)
513(defmacro def-insensitive-newline-search-method (name direction)
514 `(defun ,name (pattern line start)
515 (let* ((hashed (string-insensitive-hashed-string pattern))
516 (folded-string (string-insensitive-folded-string pattern))
517 (patlen (length (the string (search-pattern-pattern pattern)))))
518 (declare (simple-vector hashed folded-string))
519 (when (newline-search-macro line start case-insensitive-test-fun
520 folded-string ,direction hashed)
521 (values line start patlen)))))
522); eval-when (:compile-toplevel :execute)
523
524(def-insensitive-newline-search-method
525 insensitive-find-newline-once-forward-method t)
526(def-insensitive-newline-search-method
527 insensitive-find-newline-once-backward-method nil)
528
529
530;;;; And Snore, case sensitive.
531;;;
532;;; This is horribly repetitive, but if I introduce another level of
533;;; macroexpansion I will go Insaaaane....
534;;;
535(defstruct (string-sensitive-search-pattern
536 (:include search-pattern)
537 (:conc-name string-sensitive-)
538 (:print-function %print-search-pattern))
539 string
540 jumps)
541
542;;; make-sensitive-newline-pattern -- Internal
543;;;
544;;; The same, only more sensitive (it hurts when you do that...)
545;;;
546(defun make-sensitive-newline-pattern (pattern string)
547 (declare (simple-vector string))
548 (let* ((string (coerce string 'simple-string))
549 (len (length string))
550 (num (1+ (count #\newline string :end len)))
551 (sliced (make-array num)))
552 (declare (simple-string string) (simple-vector sliced) (fixnum len num))
553 (do ((prev 0 nl)
554 (i 0 (1+ i))
555 (nl (position #\newline string :end len)
556 (position #\newline string :start nl :end len)))
557 ((null nl)
558 (setf (aref sliced i) (subseq string prev len)))
559 (setf (aref sliced i) (subseq string prev nl))
560 (incf nl))
561 (setf (string-sensitive-string pattern) sliced)))
562
563
564
565(define-search-kind :string-sensitive (direction pattern old)
566 ":string-sensitive - Pattern is a string to do a case-sensitive
567 search for."
568 (unless old (setq old (make-string-sensitive-search-pattern)))
569 (setf (search-pattern-kind old) :string-sensitive
570 (search-pattern-direction old) direction
571 (search-pattern-pattern old) pattern)
572 (let* ((string (coerce pattern 'simple-vector)))
573 (declare (simple-vector string))
574 (cond
575 ((find #\newline string)
576 (make-sensitive-newline-pattern old string)
[8774]577 (setf (search-pattern-search-function old)
[6]578 (if (eq direction :forward)
579 #'sensitive-find-newline-once-forward-method
580 #'sensitive-find-newline-once-backward-method))
581 (setf (search-pattern-reclaim-function old) #'identity))
582 (t
583 (case direction
584 (:forward
585 (setf (search-pattern-search-function old)
586 #'sensitive-find-string-once-forward-method))
587 (t
588 (setf (search-pattern-search-function old)
589 #'sensitive-find-string-once-backward-method)
590 (setq string (nreverse string))))
591 (setf (string-sensitive-string old) string)
592 (setf (string-sensitive-jumps old)
593 (compute-boyer-moore-jumps
594 string #'(lambda (v i) (char-code (svref v i)))))
595 (setf (search-pattern-reclaim-function old)
596 #'(lambda (p)
597 (dispose-search-vector (string-sensitive-jumps p)))))))
598 old)
599
600
601
602(defun sensitive-find-string-once-forward-method (pattern line start)
603 (let* ((string (string-sensitive-string pattern))
604 (jumps (string-sensitive-jumps pattern))
605 (patlen (length string))
606 (last (1- patlen)))
607 (declare (simple-vector jumps string) (fixnum patlen last))
608 (when (search-once-forward-macro
609 line start sensitive-string-search-macro
610 string patlen last jumps + -)
611 (values line start patlen))))
612
613(defun sensitive-find-string-once-backward-method (pattern line start)
614 (let* ((string (string-sensitive-string pattern))
615 (jumps (string-sensitive-jumps pattern))
616 (patlen (length string))
617 (last (1- patlen)))
618 (declare (simple-vector jumps string) (fixnum patlen last))
619 (when (search-once-backward-macro
620 line start sensitive-string-search-macro
621 nil string patlen last jumps - +)
622 (values line (- start last) patlen))))
623
624(eval-when (:compile-toplevel :execute)
625(defmacro def-sensitive-newline-search-method (name direction)
[8428]626 `(defun ,name (pattern line start)
[6]627 (let* ((string (string-sensitive-string pattern))
628 (patlen (length (the string (search-pattern-pattern pattern)))))
629 (declare (simple-vector string))
[8428]630 (when (newline-search-macro line start case-sensitive-test-fun
631 string ,direction)
[12635]632 (values line start patlen)))))
633); eval-when (:compile-toplevel :execute)
[6582]634
[6]635(def-sensitive-newline-search-method
636 sensitive-find-newline-once-forward-method t)
637(def-sensitive-newline-search-method
638 sensitive-find-newline-once-backward-method nil)
[8428]639
640
[12635]641(defun find-pattern (mark search-pattern &optional stop-mark)
642 "Find a match of Search-Pattern starting at Mark. Mark is moved to
643 point before the match and the number of characters matched is returned.
644 If there is no match for the pattern then Mark is not modified and NIL
645 is returned.
646 If stop-mark is specified, NIL is returned and mark is not moved if
647 the point before the match is after stop-mark for forward search or
[6]648 before stop-mark for backward search"
649 (close-line)
650 (multiple-value-bind (line start matched)
651 (funcall (search-pattern-search-function search-pattern)
652 search-pattern (mark-line mark)
653 (mark-charpos mark))
654 (when (and matched
655 (or (null stop-mark)
656 (if (eq (search-pattern-direction search-pattern) :forward)
657 (or (< (line-number line) (line-number (mark-line stop-mark)))
[6582]658 (and (eq line (mark-line stop-mark))
659 (<= start (mark-charpos stop-mark))))
660 (or (< (line-number (mark-line stop-mark)) (line-number line))
661 (and (eq (mark-line stop-mark) line)
662 (<= (mark-charpos stop-mark) start))))))
663 (move-to-position mark start line)
664 matched)))
665
666;;; replace-pattern -- Public
667;;;
668;;;
669(defun replace-pattern (mark search-pattern replacement &optional n)
670 "Replaces N occurrences of the Search-Pattern with the Replacement string
671 in the text starting at the given Mark. If N is Nil, all occurrences
672 following the Mark are replaced."
673 (close-line)
674 (do* ((replacement (coerce replacement 'simple-string))
675 (new (length (the simple-string replacement)))
676 (fun (search-pattern-search-function search-pattern))
677 (forward-p (eq (search-pattern-direction search-pattern) :forward))
678 (n (if n (1- n) -1) (1- n))
679 (m (copy-mark mark :temporary)) line start matched)
680 (())
681 (multiple-value-setq (line start matched)
682 (funcall fun search-pattern (mark-line m) (mark-charpos m)))
683 (unless matched (return m))
684 (setf (mark-line m) line (mark-charpos m) start)
685 (delete-characters m matched)
686 (insert-string m replacement)
687 (when forward-p (character-offset m new))
688 (when (zerop n) (return m))
689 (close-line)))
Note: See TracBrowser for help on using the repository browser.