aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuri Linkov2010-03-30 19:03:08 +0300
committerJuri Linkov2010-03-30 19:03:08 +0300
commitdc2d2590b24f7e4ee648b5d073ba744fbda7a4d8 (patch)
tree8d15261ebed74c762df72b14eb4023534c784520
parent47c88c067f98772d5b505d7b6ad3d0909da5f68a (diff)
downloademacs-dc2d2590b24f7e4ee648b5d073ba744fbda7a4d8.tar.gz
emacs-dc2d2590b24f7e4ee648b5d073ba744fbda7a4d8.zip
Make occur handle multi-line matches cleanly with context.
http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01280.html * replace.el (occur-accumulate-lines): Add optional arg `pt'. (occur-engine): Add local variables `ret', `prev-after-lines', `prev-lines'. Use more arguments for `occur-context-lines'. Set first elem of its returned list to `data', and the second elem to `prev-after-lines'. Don't print the separator line. In the end, print remaining context after-lines. (occur-context-lines): Add new arguments `begpt', `endpt', `lines', `prev-lines', `prev-after-lines'. Rewrite to combine after-lines of the previous match with before-lines of the current match and not overlap them. Return a list with two values: the output line and the list of context after-lines. * search.texi (Other Repeating Search): Remove line that `occur' can not handle multiline matches. * occur-testsuite.el (occur-tests): Add tests for context lines.
-rw-r--r--doc/emacs/ChangeLog5
-rw-r--r--doc/emacs/search.texi3
-rw-r--r--etc/TODO2
-rw-r--r--lisp/ChangeLog17
-rw-r--r--lisp/replace.el90
-rw-r--r--test/ChangeLog4
-rw-r--r--test/occur-testsuite.el209
7 files changed, 308 insertions, 22 deletions
diff --git a/doc/emacs/ChangeLog b/doc/emacs/ChangeLog
index e23f129a168..b13b3ac1eec 100644
--- a/doc/emacs/ChangeLog
+++ b/doc/emacs/ChangeLog
@@ -1,3 +1,8 @@
12010-03-30 Juri Linkov <juri@jurta.org>
2
3 * search.texi (Other Repeating Search): Remove line that `occur'
4 can not handle multiline matches.
5
12010-03-30 Eli Zaretskii <eliz@gnu.org> 62010-03-30 Eli Zaretskii <eliz@gnu.org>
2 7
3 * mule.texi (International): Mention support of bidirectional editing. 8 * mule.texi (International): Mention support of bidirectional editing.
diff --git a/doc/emacs/search.texi b/doc/emacs/search.texi
index 8c49a3fa699..890dd48df9f 100644
--- a/doc/emacs/search.texi
+++ b/doc/emacs/search.texi
@@ -1311,8 +1311,7 @@ Prompt for a regexp, and display a list showing each line in the
1311buffer that contains a match for it. To limit the search to part of 1311buffer that contains a match for it. To limit the search to part of
1312the buffer, narrow to that part (@pxref{Narrowing}). A numeric 1312the buffer, narrow to that part (@pxref{Narrowing}). A numeric
1313argument @var{n} specifies that @var{n} lines of context are to be 1313argument @var{n} specifies that @var{n} lines of context are to be
1314displayed before and after each matching line. Currently, 1314displayed before and after each matching line.
1315@code{occur} can not correctly handle multiline matches.
1316 1315
1317@kindex RET @r{(Occur mode)} 1316@kindex RET @r{(Occur mode)}
1318@kindex o @r{(Occur mode)} 1317@kindex o @r{(Occur mode)}
diff --git a/etc/TODO b/etc/TODO
index 6ce2947bd93..f21d105b2cd 100644
--- a/etc/TODO
+++ b/etc/TODO
@@ -128,8 +128,6 @@ for users to customize.
128 128
129** Enhance scroll-bar to handle tall line (similar to line-move). 129** Enhance scroll-bar to handle tall line (similar to line-move).
130 130
131** Make occur handle multi-line matches cleanly with context.
132
133** In Custom buffers, put the option that turns a mode on or off first, 131** In Custom buffers, put the option that turns a mode on or off first,
134 using a heuristic of some kind? 132 using a heuristic of some kind?
135 133
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 6ef607e8025..d3c5610972b 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,22 @@
12010-03-30 Juri Linkov <juri@jurta.org> 12010-03-30 Juri Linkov <juri@jurta.org>
2 2
3 Make occur handle multi-line matches cleanly with context.
4 http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01280.html
5
6 * replace.el (occur-accumulate-lines): Add optional arg `pt'.
7 (occur-engine): Add local variables `ret', `prev-after-lines',
8 `prev-lines'. Use more arguments for `occur-context-lines'.
9 Set first elem of its returned list to `data', and the second elem
10 to `prev-after-lines'. Don't print the separator line.
11 In the end, print remaining context after-lines.
12 (occur-context-lines): Add new arguments `begpt', `endpt',
13 `lines', `prev-lines', `prev-after-lines'. Rewrite to combine
14 after-lines of the previous match with before-lines of the
15 current match and not overlap them. Return a list with two
16 values: the output line and the list of context after-lines.
17
182010-03-30 Juri Linkov <juri@jurta.org>
19
3 * replace.el (occur-accumulate-lines): Fix a bug where the first 20 * replace.el (occur-accumulate-lines): Fix a bug where the first
4 context line at the beginning of the buffer was missing. 21 context line at the beginning of the buffer was missing.
5 22
diff --git a/lisp/replace.el b/lisp/replace.el
index a74da4b89b5..14a1869b4f9 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -1005,8 +1005,10 @@ which means to discard all text properties."
1005 :group 'matching 1005 :group 'matching
1006 :version "22.1") 1006 :version "22.1")
1007 1007
1008(defun occur-accumulate-lines (count &optional keep-props) 1008(defun occur-accumulate-lines (count &optional keep-props pt)
1009 (save-excursion 1009 (save-excursion
1010 (when pt
1011 (goto-char pt))
1010 (let ((forwardp (> count 0)) 1012 (let ((forwardp (> count 0))
1011 result beg end moved) 1013 result beg end moved)
1012 (while (not (or (zerop count) 1014 (while (not (or (zerop count)
@@ -1189,12 +1191,15 @@ See also `multi-occur'."
1189 (when (buffer-live-p buf) 1191 (when (buffer-live-p buf)
1190 (let ((matches 0) ;; count of matched lines 1192 (let ((matches 0) ;; count of matched lines
1191 (lines 1) ;; line count 1193 (lines 1) ;; line count
1194 (prev-after-lines nil) ;; context lines of prev match
1195 (prev-lines nil) ;; line number of prev match endpt
1192 (matchbeg 0) 1196 (matchbeg 0)
1193 (origpt nil) 1197 (origpt nil)
1194 (begpt nil) 1198 (begpt nil)
1195 (endpt nil) 1199 (endpt nil)
1196 (marker nil) 1200 (marker nil)
1197 (curstring "") 1201 (curstring "")
1202 (ret nil)
1198 (inhibit-field-text-motion t) 1203 (inhibit-field-text-motion t)
1199 (headerpt (with-current-buffer out-buf (point)))) 1204 (headerpt (with-current-buffer out-buf (point))))
1200 (with-current-buffer buf 1205 (with-current-buffer buf
@@ -1271,14 +1276,17 @@ See also `multi-occur'."
1271 ;; The simple display style 1276 ;; The simple display style
1272 out-line 1277 out-line
1273 ;; The complex multi-line display style. 1278 ;; The complex multi-line display style.
1274 (occur-context-lines out-line nlines keep-props) 1279 (setq ret (occur-context-lines
1275 ))) 1280 out-line nlines keep-props begpt endpt
1281 lines prev-lines prev-after-lines))
1282 ;; Set first elem of the returned list to `data',
1283 ;; and the second elem to `prev-after-lines'.
1284 (setq prev-after-lines (nth 1 ret))
1285 (nth 0 ret))))
1276 ;; Actually insert the match display data 1286 ;; Actually insert the match display data
1277 (with-current-buffer out-buf 1287 (with-current-buffer out-buf
1278 (let ((beg (point)) 1288 (let ((beg (point))
1279 (end (progn (insert data) (point)))) 1289 (end (progn (insert data) (point)))))))
1280 (unless (= nlines 0)
1281 (insert "-------\n")))))
1282 (goto-char endpt)) 1290 (goto-char endpt))
1283 (if endpt 1291 (if endpt
1284 (progn 1292 (progn
@@ -1289,7 +1297,13 @@ See also `multi-occur'."
1289 (if (and (bolp) (eolp)) 1 0))) 1297 (if (and (bolp) (eolp)) 1 0)))
1290 ;; On to the next match... 1298 ;; On to the next match...
1291 (forward-line 1)) 1299 (forward-line 1))
1292 (goto-char (point-max)))))) 1300 (goto-char (point-max)))
1301 (setq prev-lines (1- lines)))
1302 ;; Flush remaining context after-lines.
1303 (when prev-after-lines
1304 (with-current-buffer out-buf
1305 (insert (apply #'concat (occur-engine-add-prefix
1306 prev-after-lines)))))))
1293 (when (not (zerop matches)) ;; is the count zero? 1307 (when (not (zerop matches)) ;; is the count zero?
1294 (setq globalcount (+ globalcount matches)) 1308 (setq globalcount (+ globalcount matches))
1295 (with-current-buffer out-buf 1309 (with-current-buffer out-buf
@@ -1345,18 +1359,60 @@ See also `multi-occur'."
1345;; Generate context display for occur. 1359;; Generate context display for occur.
1346;; OUT-LINE is the line where the match is. 1360;; OUT-LINE is the line where the match is.
1347;; NLINES and KEEP-PROPS are args to occur-engine. 1361;; NLINES and KEEP-PROPS are args to occur-engine.
1362;; LINES is line count of the current match,
1363;; PREV-LINES is line count of the previous match,
1364;; PREV-AFTER-LINES is a list of after-context lines of the previous match.
1348;; Generate a list of lines, add prefixes to all but OUT-LINE, 1365;; Generate a list of lines, add prefixes to all but OUT-LINE,
1349;; then concatenate them all together. 1366;; then concatenate them all together.
1350(defun occur-context-lines (out-line nlines keep-props) 1367(defun occur-context-lines (out-line nlines keep-props begpt endpt
1351 (apply #'concat 1368 lines prev-lines prev-after-lines)
1352 (nconc 1369 ;; Find after- and before-context lines of the current match.
1353 (occur-engine-add-prefix 1370 (let ((before-lines
1354 (nreverse (cdr (occur-accumulate-lines 1371 (nreverse (cdr (occur-accumulate-lines
1355 (- (1+ (abs nlines))) keep-props)))) 1372 (- (1+ (abs nlines))) keep-props begpt))))
1356 (list out-line) 1373 (after-lines
1357 (if (> nlines 0) 1374 (cdr (occur-accumulate-lines
1358 (occur-engine-add-prefix 1375 (1+ nlines) keep-props endpt)))
1359 (cdr (occur-accumulate-lines (1+ nlines) keep-props))))))) 1376 separator)
1377
1378 ;; Combine after-lines of the previous match
1379 ;; with before-lines of the current match.
1380
1381 (when prev-after-lines
1382 ;; Don't overlap prev after-lines with current before-lines.
1383 (if (>= (+ prev-lines (length prev-after-lines))
1384 (- lines (length before-lines)))
1385 (setq prev-after-lines
1386 (butlast prev-after-lines
1387 (- (length prev-after-lines)
1388 (- lines prev-lines (length before-lines) 1))))
1389 ;; Separate non-overlapping context lines with a dashed line.
1390 (setq separator "-------\n")))
1391
1392 (when prev-lines
1393 ;; Don't overlap current before-lines with previous match line.
1394 (if (<= (- lines (length before-lines))
1395 prev-lines)
1396 (setq before-lines
1397 (nthcdr (- (length before-lines)
1398 (- lines prev-lines 1))
1399 before-lines))
1400 ;; Separate non-overlapping before-context lines.
1401 (unless (> nlines 0)
1402 (setq separator "-------\n"))))
1403
1404 (list
1405 ;; Return a list where the first element is the output line.
1406 (apply #'concat
1407 (append
1408 (and prev-after-lines
1409 (occur-engine-add-prefix prev-after-lines))
1410 (and separator (list separator))
1411 (occur-engine-add-prefix before-lines)
1412 (list out-line)))
1413 ;; And the second element is the list of context after-lines.
1414 (if (> nlines 0) after-lines))))
1415
1360 1416
1361;; It would be nice to use \\[...], but there is no reasonable way 1417;; It would be nice to use \\[...], but there is no reasonable way
1362;; to make that display both SPC and Y. 1418;; to make that display both SPC and Y.
diff --git a/test/ChangeLog b/test/ChangeLog
index ee69172241d..87847a43ada 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,7 @@
12010-03-30 Juri Linkov <juri@jurta.org>
2
3 * occur-testsuite.el (occur-tests): Add tests for context lines.
4
12010-03-23 Juri Linkov <juri@jurta.org> 52010-03-23 Juri Linkov <juri@jurta.org>
2 6
3 * occur-testsuite.el: New file. 7 * occur-testsuite.el: New file.
diff --git a/test/occur-testsuite.el b/test/occur-testsuite.el
index d817805dd67..a4756c49e2a 100644
--- a/test/occur-testsuite.el
+++ b/test/occur-testsuite.el
@@ -107,7 +107,214 @@ fx
107 :fx 107 :fx
108 : 108 :
109") 109")
110 ) 110 ;; * Test non-overlapping context lines with matches at bob/eob.
111 ("x" 1 "\
112ax
113b
114c
115d
116ex
117f
118g
119hx
120" "\
1213 matches for \"x\" in buffer: *temp*
122 1:ax
123 :b
124-------
125 :d
126 5:ex
127 :f
128-------
129 :g
130 8:hx
131")
132 ;; * Test non-overlapping context lines with matches not at bob/eob.
133 ("x" 1 "\
134a
135bx
136c
137d
138ex
139f
140" "\
1412 matches for \"x\" in buffer: *temp*
142 :a
143 2:bx
144 :c
145-------
146 :d
147 5:ex
148 :f
149")
150 ;; * Test overlapping context lines with matches at bob/eob.
151 ("x" 2 "\
152ax
153bx
154c
155dx
156e
157f
158gx
159h
160i
161j
162kx
163" "\
1645 matches for \"x\" in buffer: *temp*
165 1:ax
166 2:bx
167 :c
168 4:dx
169 :e
170 :f
171 7:gx
172 :h
173 :i
174 :j
175 11:kx
176")
177 ;; * Test overlapping context lines with matches not at bob/eob.
178 ("x" 2 "\
179a
180b
181cx
182d
183e
184f
185gx
186h
187i
188" "\
1892 matches for \"x\" in buffer: *temp*
190 :a
191 :b
192 3:cx
193 :d
194 :e
195 :f
196 7:gx
197 :h
198 :i
199")
200 ;; * Test overlapping context lines with empty first and last line..
201 ("x" 2 "\
202
203b
204cx
205d
206e
207f
208gx
209h
210
211" "\
2122 matches for \"x\" in buffer: *temp*
213 :
214 :b
215 3:cx
216 :d
217 :e
218 :f
219 7:gx
220 :h
221 :
222")
223 ;; * Test multi-line overlapping context lines.
224 ("x\n.x" 2 "\
225ax
226bx
227c
228d
229ex
230fx
231g
232h
233i
234jx
235kx
236" "\
2373 matches for \"x^J.x\" in buffer: *temp*
238 1:ax
239 :bx
240 :c
241 :d
242 5:ex
243 :fx
244 :g
245 :h
246 :i
247 10:jx
248 :kx
249")
250 ;; * Test multi-line non-overlapping context lines.
251 ("x\n.x" 2 "\
252ax
253bx
254c
255d
256e
257f
258gx
259hx
260" "\
2612 matches for \"x^J.x\" in buffer: *temp*
262 1:ax
263 :bx
264 :c
265 :d
266-------
267 :e
268 :f
269 7:gx
270 :hx
271")
272 ;; * Test non-overlapping negative (before-context) lines.
273 ("x" -2 "\
274a
275bx
276c
277d
278e
279fx
280g
281h
282ix
283" "\
2843 matches for \"x\" in buffer: *temp*
285 :a
286 2:bx
287-------
288 :d
289 :e
290 6:fx
291-------
292 :g
293 :h
294 9:ix
295")
296 ;; * Test overlapping negative (before-context) lines.
297 ("x" -3 "\
298a
299bx
300c
301dx
302e
303f
304gx
305h
306" "\
3073 matches for \"x\" in buffer: *temp*
308 :a
309 2:bx
310 :c
311 4:dx
312 :e
313 :f
314 7:gx
315")
316
317)
111 "List of tests for `occur'. 318 "List of tests for `occur'.
112Each element has the format: 319Each element has the format:
113\(REGEXP NLINES INPUT-BUFFER-STRING OUTPUT-BUFFER-STRING).") 320\(REGEXP NLINES INPUT-BUFFER-STRING OUTPUT-BUFFER-STRING).")