diff options
| author | Juri Linkov | 2010-03-30 19:03:08 +0300 |
|---|---|---|
| committer | Juri Linkov | 2010-03-30 19:03:08 +0300 |
| commit | dc2d2590b24f7e4ee648b5d073ba744fbda7a4d8 (patch) | |
| tree | 8d15261ebed74c762df72b14eb4023534c784520 | |
| parent | 47c88c067f98772d5b505d7b6ad3d0909da5f68a (diff) | |
| download | emacs-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/ChangeLog | 5 | ||||
| -rw-r--r-- | doc/emacs/search.texi | 3 | ||||
| -rw-r--r-- | etc/TODO | 2 | ||||
| -rw-r--r-- | lisp/ChangeLog | 17 | ||||
| -rw-r--r-- | lisp/replace.el | 90 | ||||
| -rw-r--r-- | test/ChangeLog | 4 | ||||
| -rw-r--r-- | test/occur-testsuite.el | 209 |
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 @@ | |||
| 1 | 2010-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 | |||
| 1 | 2010-03-30 Eli Zaretskii <eliz@gnu.org> | 6 | 2010-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 | |||
| 1311 | buffer that contains a match for it. To limit the search to part of | 1311 | buffer that contains a match for it. To limit the search to part of |
| 1312 | the buffer, narrow to that part (@pxref{Narrowing}). A numeric | 1312 | the buffer, narrow to that part (@pxref{Narrowing}). A numeric |
| 1313 | argument @var{n} specifies that @var{n} lines of context are to be | 1313 | argument @var{n} specifies that @var{n} lines of context are to be |
| 1314 | displayed before and after each matching line. Currently, | 1314 | displayed 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)} |
| @@ -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 @@ | |||
| 1 | 2010-03-30 Juri Linkov <juri@jurta.org> | 1 | 2010-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 | |||
| 18 | 2010-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 @@ | |||
| 1 | 2010-03-30 Juri Linkov <juri@jurta.org> | ||
| 2 | |||
| 3 | * occur-testsuite.el (occur-tests): Add tests for context lines. | ||
| 4 | |||
| 1 | 2010-03-23 Juri Linkov <juri@jurta.org> | 5 | 2010-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 "\ | ||
| 112 | ax | ||
| 113 | b | ||
| 114 | c | ||
| 115 | d | ||
| 116 | ex | ||
| 117 | f | ||
| 118 | g | ||
| 119 | hx | ||
| 120 | " "\ | ||
| 121 | 3 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 "\ | ||
| 134 | a | ||
| 135 | bx | ||
| 136 | c | ||
| 137 | d | ||
| 138 | ex | ||
| 139 | f | ||
| 140 | " "\ | ||
| 141 | 2 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 "\ | ||
| 152 | ax | ||
| 153 | bx | ||
| 154 | c | ||
| 155 | dx | ||
| 156 | e | ||
| 157 | f | ||
| 158 | gx | ||
| 159 | h | ||
| 160 | i | ||
| 161 | j | ||
| 162 | kx | ||
| 163 | " "\ | ||
| 164 | 5 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 "\ | ||
| 179 | a | ||
| 180 | b | ||
| 181 | cx | ||
| 182 | d | ||
| 183 | e | ||
| 184 | f | ||
| 185 | gx | ||
| 186 | h | ||
| 187 | i | ||
| 188 | " "\ | ||
| 189 | 2 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 | |||
| 203 | b | ||
| 204 | cx | ||
| 205 | d | ||
| 206 | e | ||
| 207 | f | ||
| 208 | gx | ||
| 209 | h | ||
| 210 | |||
| 211 | " "\ | ||
| 212 | 2 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 "\ | ||
| 225 | ax | ||
| 226 | bx | ||
| 227 | c | ||
| 228 | d | ||
| 229 | ex | ||
| 230 | fx | ||
| 231 | g | ||
| 232 | h | ||
| 233 | i | ||
| 234 | jx | ||
| 235 | kx | ||
| 236 | " "\ | ||
| 237 | 3 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 "\ | ||
| 252 | ax | ||
| 253 | bx | ||
| 254 | c | ||
| 255 | d | ||
| 256 | e | ||
| 257 | f | ||
| 258 | gx | ||
| 259 | hx | ||
| 260 | " "\ | ||
| 261 | 2 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 "\ | ||
| 274 | a | ||
| 275 | bx | ||
| 276 | c | ||
| 277 | d | ||
| 278 | e | ||
| 279 | fx | ||
| 280 | g | ||
| 281 | h | ||
| 282 | ix | ||
| 283 | " "\ | ||
| 284 | 3 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 "\ | ||
| 298 | a | ||
| 299 | bx | ||
| 300 | c | ||
| 301 | dx | ||
| 302 | e | ||
| 303 | f | ||
| 304 | gx | ||
| 305 | h | ||
| 306 | " "\ | ||
| 307 | 3 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'. |
| 112 | Each element has the format: | 319 | Each element has the format: |
| 113 | \(REGEXP NLINES INPUT-BUFFER-STRING OUTPUT-BUFFER-STRING).") | 320 | \(REGEXP NLINES INPUT-BUFFER-STRING OUTPUT-BUFFER-STRING).") |