diff options
| author | Tino Calancha | 2016-02-24 12:35:46 +1100 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2016-02-24 12:35:46 +1100 |
| commit | e1d749bd7e0d68ab063eae3927caede6039a33cf (patch) | |
| tree | 3b16fbd693e0184b8c4d5e5322d71f28c903a4e6 | |
| parent | bbd86c5642bd62c43d72391669f28eaa14459fd5 (diff) | |
| download | emacs-e1d749bd7e0d68ab063eae3927caede6039a33cf.tar.gz emacs-e1d749bd7e0d68ab063eae3927caede6039a33cf.zip | |
Allow undoing changes while doing query-replace
* doc/lispref/searching.texi (Search and Replace): Mention
undo (bug#21684).
* lisp/replace.el (query-replace-help): Document undo.
(perform-replace): Implement undo while replacing text.
| -rw-r--r-- | doc/lispref/searching.texi | 8 | ||||
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/replace.el | 117 |
3 files changed, 121 insertions, 9 deletions
diff --git a/doc/lispref/searching.texi b/doc/lispref/searching.texi index 1243d720bc3..644716a95c7 100644 --- a/doc/lispref/searching.texi +++ b/doc/lispref/searching.texi | |||
| @@ -1805,6 +1805,14 @@ Answer this question and all subsequent questions in the series with | |||
| 1805 | @item backup | 1805 | @item backup |
| 1806 | Move back to the previous place that a question was asked about. | 1806 | Move back to the previous place that a question was asked about. |
| 1807 | 1807 | ||
| 1808 | @item undo | ||
| 1809 | Undo last replacement and move back to the place where that | ||
| 1810 | replacement was performed. | ||
| 1811 | |||
| 1812 | @item undo-all | ||
| 1813 | Undo all replacements and move back to the place where the first | ||
| 1814 | replacement was performed. | ||
| 1815 | |||
| 1808 | @item edit | 1816 | @item edit |
| 1809 | Enter a recursive edit to deal with this question---instead of any | 1817 | Enter a recursive edit to deal with this question---instead of any |
| 1810 | other action that would normally be taken. | 1818 | other action that would normally be taken. |
| @@ -435,6 +435,11 @@ is intended for adding to 'kill-emacs-query-functions'. | |||
| 435 | in favor of the global `M-s h' bindings introduced in Emacs-23.1. | 435 | in favor of the global `M-s h' bindings introduced in Emacs-23.1. |
| 436 | They'll disappear soon. | 436 | They'll disappear soon. |
| 437 | 437 | ||
| 438 | +++ | ||
| 439 | ** New bindings for 'query-replace-map'. | ||
| 440 | `undo', undo the last replacement; bound to `u'. | ||
| 441 | `undo-all', undo all replacements; bound to `U'. | ||
| 442 | |||
| 438 | 443 | ||
| 439 | * Changes in Specialized Modes and Packages in Emacs 25.1 | 444 | * Changes in Specialized Modes and Packages in Emacs 25.1 |
| 440 | 445 | ||
diff --git a/lisp/replace.el b/lisp/replace.el index 488eff7928a..b7e26c96fc5 100644 --- a/lisp/replace.el +++ b/lisp/replace.el | |||
| @@ -1824,6 +1824,8 @@ C-w to delete match and recursive edit, | |||
| 1824 | C-l to clear the screen, redisplay, and offer same replacement again, | 1824 | C-l to clear the screen, redisplay, and offer same replacement again, |
| 1825 | ! to replace all remaining matches in this buffer with no more questions, | 1825 | ! to replace all remaining matches in this buffer with no more questions, |
| 1826 | ^ to move point back to previous match, | 1826 | ^ to move point back to previous match, |
| 1827 | u to undo previous replacement, | ||
| 1828 | U to undo all replacements, | ||
| 1827 | E to edit the replacement string. | 1829 | E to edit the replacement string. |
| 1828 | In multi-buffer replacements type `Y' to replace all remaining | 1830 | In multi-buffer replacements type `Y' to replace all remaining |
| 1829 | matches in all remaining buffers with no more questions, | 1831 | matches in all remaining buffers with no more questions, |
| @@ -1853,6 +1855,8 @@ in the current buffer." | |||
| 1853 | (define-key map "\C-l" 'recenter) | 1855 | (define-key map "\C-l" 'recenter) |
| 1854 | (define-key map "!" 'automatic) | 1856 | (define-key map "!" 'automatic) |
| 1855 | (define-key map "^" 'backup) | 1857 | (define-key map "^" 'backup) |
| 1858 | (define-key map "u" 'undo) | ||
| 1859 | (define-key map "U" 'undo-all) | ||
| 1856 | (define-key map "\C-h" 'help) | 1860 | (define-key map "\C-h" 'help) |
| 1857 | (define-key map [f1] 'help) | 1861 | (define-key map [f1] 'help) |
| 1858 | (define-key map [help] 'help) | 1862 | (define-key map [help] 'help) |
| @@ -1878,7 +1882,7 @@ The valid answers include `act', `skip', `act-and-show', | |||
| 1878 | `act-and-exit', `exit', `exit-prefix', `recenter', `scroll-up', | 1882 | `act-and-exit', `exit', `exit-prefix', `recenter', `scroll-up', |
| 1879 | `scroll-down', `scroll-other-window', `scroll-other-window-down', | 1883 | `scroll-down', `scroll-other-window', `scroll-other-window-down', |
| 1880 | `edit', `edit-replacement', `delete-and-edit', `automatic', | 1884 | `edit', `edit-replacement', `delete-and-edit', `automatic', |
| 1881 | `backup', `quit', and `help'. | 1885 | `backup', `undo', `undo-all', `quit', and `help'. |
| 1882 | 1886 | ||
| 1883 | This keymap is used by `y-or-n-p' as well as `query-replace'.") | 1887 | This keymap is used by `y-or-n-p' as well as `query-replace'.") |
| 1884 | 1888 | ||
| @@ -2132,6 +2136,10 @@ It must return a string." | |||
| 2132 | (noedit nil) | 2136 | (noedit nil) |
| 2133 | (keep-going t) | 2137 | (keep-going t) |
| 2134 | (stack nil) | 2138 | (stack nil) |
| 2139 | (search-string-replaced nil) ; last string matching `from-string' | ||
| 2140 | (next-replacement-replaced nil) ; replacement string | ||
| 2141 | ; (substituted regexp) | ||
| 2142 | (last-was-undo) | ||
| 2135 | (replace-count 0) | 2143 | (replace-count 0) |
| 2136 | (skip-read-only-count 0) | 2144 | (skip-read-only-count 0) |
| 2137 | (skip-filtered-count 0) | 2145 | (skip-filtered-count 0) |
| @@ -2328,8 +2336,28 @@ It must return a string." | |||
| 2328 | (match-beginning 0) (match-end 0) | 2336 | (match-beginning 0) (match-end 0) |
| 2329 | start end search-string | 2337 | start end search-string |
| 2330 | regexp-flag delimited-flag case-fold-search backward) | 2338 | regexp-flag delimited-flag case-fold-search backward) |
| 2331 | ;; Bind message-log-max so we don't fill up the message log | 2339 | ;; Obtain the matched groups: needed only when |
| 2332 | ;; with a bunch of identical messages. | 2340 | ;; regexp-flag non nil. |
| 2341 | (when (and last-was-undo regexp-flag) | ||
| 2342 | (setq last-was-undo nil | ||
| 2343 | real-match-data | ||
| 2344 | (save-excursion | ||
| 2345 | (goto-char (match-beginning 0)) | ||
| 2346 | (looking-at search-string) | ||
| 2347 | (match-data t real-match-data)))) | ||
| 2348 | ;; Matched string and next-replacement-replaced | ||
| 2349 | ;; stored in stack. | ||
| 2350 | (setq search-string-replaced (buffer-substring-no-properties | ||
| 2351 | (match-beginning 0) | ||
| 2352 | (match-end 0)) | ||
| 2353 | next-replacement-replaced | ||
| 2354 | (query-replace-descr | ||
| 2355 | (save-match-data | ||
| 2356 | (set-match-data real-match-data) | ||
| 2357 | (match-substitute-replacement | ||
| 2358 | next-replacement nocasify literal)))) | ||
| 2359 | ;; Bind message-log-max so we don't fill up the | ||
| 2360 | ;; message log with a bunch of identical messages. | ||
| 2333 | (let ((message-log-max nil) | 2361 | (let ((message-log-max nil) |
| 2334 | (replacement-presentation | 2362 | (replacement-presentation |
| 2335 | (if query-replace-show-replacement | 2363 | (if query-replace-show-replacement |
| @@ -2342,8 +2370,8 @@ It must return a string." | |||
| 2342 | (query-replace-descr from-string) | 2370 | (query-replace-descr from-string) |
| 2343 | (query-replace-descr replacement-presentation))) | 2371 | (query-replace-descr replacement-presentation))) |
| 2344 | (setq key (read-event)) | 2372 | (setq key (read-event)) |
| 2345 | ;; Necessary in case something happens during read-event | 2373 | ;; Necessary in case something happens during |
| 2346 | ;; that clobbers the match data. | 2374 | ;; read-event that clobbers the match data. |
| 2347 | (set-match-data real-match-data) | 2375 | (set-match-data real-match-data) |
| 2348 | (setq key (vector key)) | 2376 | (setq key (vector key)) |
| 2349 | (setq def (lookup-key map key)) | 2377 | (setq def (lookup-key map key)) |
| @@ -2354,7 +2382,8 @@ It must return a string." | |||
| 2354 | (concat "Query replacing " | 2382 | (concat "Query replacing " |
| 2355 | (if delimited-flag | 2383 | (if delimited-flag |
| 2356 | (or (and (symbolp delimited-flag) | 2384 | (or (and (symbolp delimited-flag) |
| 2357 | (get delimited-flag 'isearch-message-prefix)) | 2385 | (get delimited-flag |
| 2386 | 'isearch-message-prefix)) | ||
| 2358 | "word ") "") | 2387 | "word ") "") |
| 2359 | (if regexp-flag "regexp " "") | 2388 | (if regexp-flag "regexp " "") |
| 2360 | (if backward "backward " "") | 2389 | (if backward "backward " "") |
| @@ -2381,6 +2410,73 @@ It must return a string." | |||
| 2381 | (message "No previous match") | 2410 | (message "No previous match") |
| 2382 | (ding 'no-terminate) | 2411 | (ding 'no-terminate) |
| 2383 | (sit-for 1))) | 2412 | (sit-for 1))) |
| 2413 | ((or (eq def 'undo) (eq def 'undo-all)) | ||
| 2414 | (if (null stack) | ||
| 2415 | (progn | ||
| 2416 | (message "Nothing to undo") | ||
| 2417 | (ding 'no-terminate) | ||
| 2418 | (sit-for 1)) | ||
| 2419 | (let ((stack-idx 0) | ||
| 2420 | (stack-len (length stack)) | ||
| 2421 | (num-replacements 0) | ||
| 2422 | search-string | ||
| 2423 | next-replacement) | ||
| 2424 | (while (and (< stack-idx stack-len) | ||
| 2425 | stack | ||
| 2426 | (null replaced)) | ||
| 2427 | (let* ((elt (nth stack-idx stack))) | ||
| 2428 | (setq | ||
| 2429 | stack-idx (1+ stack-idx) | ||
| 2430 | replaced (nth 1 elt) | ||
| 2431 | ;; Bind swapped values | ||
| 2432 | ;; (search-string <--> replacement) | ||
| 2433 | search-string (nth (if replaced 4 3) elt) | ||
| 2434 | next-replacement (nth (if replaced 3 4) elt) | ||
| 2435 | search-string-replaced search-string | ||
| 2436 | next-replacement-replaced next-replacement) | ||
| 2437 | |||
| 2438 | (when (and (= stack-idx stack-len) | ||
| 2439 | (null replaced) | ||
| 2440 | (zerop num-replacements)) | ||
| 2441 | (message "Nothing to undo") | ||
| 2442 | (ding 'no-terminate) | ||
| 2443 | (sit-for 1)) | ||
| 2444 | |||
| 2445 | (when replaced | ||
| 2446 | (setq stack (nthcdr stack-idx stack)) | ||
| 2447 | (goto-char (nth 0 elt)) | ||
| 2448 | (set-match-data (nth 2 elt)) | ||
| 2449 | (setq real-match-data | ||
| 2450 | (save-excursion | ||
| 2451 | (goto-char (match-beginning 0)) | ||
| 2452 | (looking-at search-string) | ||
| 2453 | (match-data t (nth 2 elt))) | ||
| 2454 | noedit | ||
| 2455 | (replace-match-maybe-edit | ||
| 2456 | next-replacement nocasify literal | ||
| 2457 | noedit real-match-data backward) | ||
| 2458 | replace-count (1- replace-count) | ||
| 2459 | real-match-data | ||
| 2460 | (save-excursion | ||
| 2461 | (goto-char (match-beginning 0)) | ||
| 2462 | (looking-at next-replacement) | ||
| 2463 | (match-data t (nth 2 elt)))) | ||
| 2464 | ;; Set replaced nil to keep in loop | ||
| 2465 | (when (eq def 'undo-all) | ||
| 2466 | (setq replaced nil | ||
| 2467 | stack-len (- stack-len stack-idx) | ||
| 2468 | stack-idx 0 | ||
| 2469 | num-replacements | ||
| 2470 | (1+ num-replacements)))))) | ||
| 2471 | (when (and (eq def 'undo-all) | ||
| 2472 | (null (zerop num-replacements))) | ||
| 2473 | (message "Undid %d %s" num-replacements | ||
| 2474 | (if (= num-replacements 1) | ||
| 2475 | "replacement" | ||
| 2476 | "replacements")) | ||
| 2477 | (ding 'no-terminate) | ||
| 2478 | (sit-for 1))) | ||
| 2479 | (setq replaced nil last-was-undo t))) | ||
| 2384 | ((eq def 'act) | 2480 | ((eq def 'act) |
| 2385 | (or replaced | 2481 | (or replaced |
| 2386 | (setq noedit | 2482 | (setq noedit |
| @@ -2503,9 +2599,12 @@ It must return a string." | |||
| 2503 | (match-beginning 0) | 2599 | (match-beginning 0) |
| 2504 | (match-end 0) | 2600 | (match-end 0) |
| 2505 | (current-buffer)) | 2601 | (current-buffer)) |
| 2506 | (match-data t))) | 2602 | (match-data t)) |
| 2507 | stack)))))) | 2603 | search-string-replaced |
| 2508 | 2604 | next-replacement-replaced) | |
| 2605 | stack) | ||
| 2606 | (setq next-replacement-replaced nil | ||
| 2607 | search-string-replaced nil)))))) | ||
| 2509 | (replace-dehighlight)) | 2608 | (replace-dehighlight)) |
| 2510 | (or unread-command-events | 2609 | (or unread-command-events |
| 2511 | (message "Replaced %d occurrence%s%s" | 2610 | (message "Replaced %d occurrence%s%s" |