Skip to content
Newer
Older
100644 772 lines (681 sloc) 31.5 KB
e9a9485 updates
anybody authored
1 ;;; iedit.el --- Edit multiple regions with the same content simultaneously.
2
02946e0 @victorhge Update contributors and spell check
authored
3 ;; Copyright (C) 2010, 2011, 2012 Victor Ren
e9a9485 updates
anybody authored
4
6e49225 @victorhge Add iedit-toggle-case-sensitive
authored
5 ;; Time-stamp: <2012-01-30 21:44:34 Victor Ren>
e9a9485 updates
anybody authored
6 ;; Author: Victor Ren <victorhge@gmail.com>
7 ;; Keywords: occurrence region replace simultaneous
9b3aaa8 @victorhge Fix occurrence from history does not exist problem
authored
8 ;; Version: 0.92
9a891d7 updates
anybody authored
9 ;; X-URL: http://www.emacswiki.org/emacs/iedit.el
5ad28b5 updates
anybody authored
10 ;; Compatibility: GNU Emacs: 22.x, 23.x, 24.x
e9a9485 updates
anybody authored
11
12 ;; This file is not part of GNU Emacs, but it is distributed under
13 ;; the same terms as GNU Emacs.
14
15 ;; GNU Emacs is free software: you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation, either version 3 of the License, or
18 ;; (at your option) any later version.
19
20 ;; GNU Emacs is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 ;; GNU General Public License for more details.
24
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27
28 ;;; Commentary:
29
30 ;; This package provides a more intuitive way of replace-string operation:
31 ;;
32 ;; - Select the occurrence in the buffer
da0a247 @tsdh Add option to match only complete words, not inside words
tsdh authored
33 ;; In Transient Mark mode, just mark a region, the content of the
e9a9485 updates
anybody authored
34 ;; region will be used as the occurrence. (if Transient Mark mode is disabled,
35 ;; using C-u C-x C-x or C-SPC C-SPC to activate it just for this one time).
36 ;;
37 ;; - Start iedit minor mode - by press C-;
38 ;; All occurrences of the content in the buffer are highlighted
39 ;;
40 ;; - Edit one of the contents
41 ;; The change is applied to other contents simultaneously
42 ;;
43 ;; - Finish - by pressing C-; again
44
f808bf1 @victorhge Merge from lewang/master and update comments
authored
45 ;; If Transient Mark mode is disabled or the region is not active, the current
6d96992 @victorhge Remove duplicated comments
authored
46 ;; symbol (returns from `current-word') is used as the occurrence by default.
e9a9485 updates
anybody authored
47
48 ;; You can also switch to iedit mode from isearch mode directly. The current
49 ;; search string is used as the occurrence.
50
51 ;; If you would like to replace-string on certain region, use "narrowing" first.
52
53 ;;; Suggested key bindings:
54 ;;
55 ;; (define-key global-map (kbd "C-;") 'iedit-mode)
56 ;; (define-key isearch-mode-map (kbd "C-;") 'iedit-mode)
57
58 ;;; todo:
9b3aaa8 @victorhge Fix occurrence from history does not exist problem
authored
59 ;; - profile to find bottleneck for huge file
60 ;; - Add more easy access keys for whole occurrence
02946e0 @victorhge Update contributors and spell check
authored
61 ;; - C-n,C-p is slow when unmatched lines are hided.
5ad28b5 updates
anybody authored
62 ;; - toggle blank line between matched lines?
ec6785b @victorhge host on git
authored
63 ;; - ert unit test
e9a9485 updates
anybody authored
64
ec6785b @victorhge host on git
authored
65 ;;; Contributors
66 ;; Adam Lindberg <eproxus@gmail.com> added a case sensitivity option that can be toggled.
02946e0 @victorhge Update contributors and spell check
authored
67
68 ;; Tassilo Horn <tassilo@member.fsf.org> added an option to match only complete
69 ;; words, not inside words
70
9b3aaa8 @victorhge Fix occurrence from history does not exist problem
authored
71 ;; Le Wang <l26wang@gmail.com> proposed to match only complete symbols, not
7d18e5d @victorhge Add a help command C-? for occurrences
authored
72 ;; inside symbols, contributed iedit-rect mode
ec6785b @victorhge host on git
authored
73
e9a9485 updates
anybody authored
74 ;;; Code:
75
76 (eval-when-compile (require 'cl))
77
78 (defgroup iedit nil
79 "Edit multiple regions with the same content simultaneously."
80 :prefix "iedit-"
81 :group 'replace
82 :group 'convenience)
83
84 (defcustom iedit-occurrence-face 'highlight
85 "*Face used for the occurrences' default values."
86 :type 'face
87 :group 'iedit)
88
fc71952 @victorhge Boolean `t'
authored
89 (defcustom iedit-current-symbol-default t
f808bf1 @victorhge Merge from lewang/master and update comments
authored
90 "If no-nil, use current symbol by default for the occurrence."
e9a9485 updates
anybody authored
91 :type 'boolean
92 :group 'iedit)
93
fc71952 @victorhge Boolean `t'
authored
94 (defcustom iedit-case-sensitive-default t
5ad28b5 updates
anybody authored
95 "If no-nil, matching is case sensitive"
96 :type 'boolean
97 :group 'iedit)
98
f808bf1 @victorhge Merge from lewang/master and update comments
authored
99 (defcustom iedit-only-at-symbol-boundaries t
100 "If no-nil, matches have to start and end at symbol boundaries.
da0a247 @tsdh Add option to match only complete words, not inside words
tsdh authored
101 For example, when invoking iedit-mode on the \"in\" in the
102 sentence \"The king in the castle...\", the \"king\" is not
103 edited."
104 :type 'boolean
105 :group 'iedit)
106
e9a9485 updates
anybody authored
107 (defcustom iedit-unmatched-lines-invisible-default nil
9a891d7 updates
anybody authored
108 "If no-nil, hide lines that do not cover any occurrences by
109 default."
e9a9485 updates
anybody authored
110 :type 'boolean
111 :group 'iedit)
112
113 (defvar iedit-mode-hook nil
114 "Function(s) to call after starting up an iedit.")
115
116 (defvar iedit-mode-end-hook nil
117 "Function(s) to call after terminating an iedit.")
118
119 (defvar iedit-mode nil) ;; Name of the minor mode
120
121 (make-variable-buffer-local 'iedit-mode)
122
123 (or (assq 'iedit-mode minor-mode-alist)
124 (nconc minor-mode-alist
9bc57d2 @victorhge make iedit-skipped-modification-once buffer local
authored
125 (list '(iedit-mode iedit-mode))))
e9a9485 updates
anybody authored
126
127 (defvar iedit-occurrences-overlays nil
9a891d7 updates
anybody authored
128 "The occurrences slot contains a list of overlays used to
129 indicate the position of each occurrence. In addition, the
130 occurrence overlay is used to provide a different face
131 configurable via `iedit-occurrence-face'.")
e9a9485 updates
anybody authored
132
6e49225 @victorhge Add iedit-toggle-case-sensitive
authored
133 (defvar iedit-case-sensitive iedit-case-sensitive-default
5ad28b5 updates
anybody authored
134 "This is buffer local variable. If no-nil, matching is case
135 sensitive.")
136
e9a9485 updates
anybody authored
137 (defvar iedit-unmatched-lines-invisible nil
9a891d7 updates
anybody authored
138 "This is buffer local variable which indicates whether
139 unmatched lines are hided.")
e9a9485 updates
anybody authored
140
141 (defvar iedit-last-occurrence-in-history nil
142 "This is buffer local variable which is the occurrence when
9a891d7 updates
anybody authored
143 iedit mode is turned off last time.")
144
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
145 (defvar iedit-current-occurrence-complete-symbol nil
146 "This is buffer local variable which indicates the occurrence
147 only matches complete symbol.")
148
9a891d7 updates
anybody authored
149 (defvar iedit-forward-success t
039c0e2 @victorhge Postpone iedit-done after commands are excuted.
authored
150 "This is buffer local variable which indicates the moving
9a891d7 updates
anybody authored
151 forward or backward successful")
e9a9485 updates
anybody authored
152
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
153 (defvar iedit-before-modification-string ""
154 "This is buffer local variable which is the buffer substring that is going to be changed.")
d4b9fd5 @victorhge don't call hooks when there is only text properites change
authored
155
c53a885 @victorhge Add bufferring modification functionalities
authored
156 (defvar iedit-before-modification-undo-list nil
157 "This is buffer local variable which is the buffer undo list before modification.")
039c0e2 @victorhge Postpone iedit-done after commands are excuted.
authored
158 ;; `iedit-occurrence-update' gets called twice when change==0 and occurrence
a75c446 fix bug with autopair and iedit-rect
Le Wang authored
159 ;; is zero-width (beg==end)
039c0e2 @victorhge Postpone iedit-done after commands are excuted.
authored
160 ;; -- for front and back insertion.
a75c446 fix bug with autopair and iedit-rect
Le Wang authored
161 (defvar iedit-skipped-modification-once nil
162 "Variable used to skip first modification hook run when insertion against a zero-width occurrence.")
039c0e2 @victorhge Postpone iedit-done after commands are excuted.
authored
163
164 (defvar iedit-aborting nil
165 "This is buffer local variable which indicates iedit-mode is aborting.")
166
c53a885 @victorhge Add bufferring modification functionalities
authored
167 (defvar iedit-buffering nil
168 "This is buffer local variable which indicates iedit-mode is
169 buffering, which means the modification to the current
170 occurrence is not applied to other occurrences when it is true.")
171
e9a9485 updates
anybody authored
172 (make-variable-buffer-local 'iedit-occurrences-overlays)
173 (make-variable-buffer-local 'iedit-unmatched-lines-invisible)
5ad28b5 updates
anybody authored
174 (make-variable-buffer-local 'iedit-case-sensitive)
9a891d7 updates
anybody authored
175 (make-variable-buffer-local 'iedit-last-occurrence-in-history)
176 (make-variable-buffer-local 'iedit-forward-success)
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
177 (make-variable-buffer-local 'iedit-before-modification-string)
c53a885 @victorhge Add bufferring modification functionalities
authored
178 (make-variable-buffer-local 'iedit-before-modification-undo-list)
9bc57d2 @victorhge make iedit-skipped-modification-once buffer local
authored
179 (make-variable-buffer-local 'iedit-skipped-modification-once)
039c0e2 @victorhge Postpone iedit-done after commands are excuted.
authored
180 (make-variable-buffer-local 'iedit-aborting)
c53a885 @victorhge Add bufferring modification functionalities
authored
181 (make-variable-buffer-local 'iedit-buffering)
e9a9485 updates
anybody authored
182
183 (defconst iedit-occurrence-overlay-name 'iedit-occurrence-overlay-name)
184 (defconst iedit-invisible-overlay-name 'iedit-invisible-overlay-name)
5ad28b5 updates
anybody authored
185
186 ;;; Define iedit help map.
187 (eval-when-compile (require 'help-macro))
188
189 (defvar iedit-help-map
190 (let ((map (make-sparse-keymap)))
191 (define-key map (char-to-string help-char) 'iedit-help-for-help)
192 (define-key map [help] 'iedit-help-for-help)
193 (define-key map [f1] 'iedit-help-for-help)
194 (define-key map "?" 'iedit-help-for-help)
195 (define-key map "b" 'iedit-describe-bindings)
196 (define-key map "k" 'iedit-describe-key)
197 (define-key map "m" 'iedit-describe-mode)
198 (define-key map "q" 'help-quit)
199 map)
200 "Keymap for characters following the Help key for iedit mode.")
201
9bc57d2 @victorhge make iedit-skipped-modification-once buffer local
authored
202 (make-help-screen
203 iedit-help-for-help-internal
204 (purecopy "Type a help option: [bkm] or ?")
205 "You have typed %THIS-KEY%, the help character. Type a Help option:
5ad28b5 updates
anybody authored
206 \(Type \\<help-map>\\[help-quit] to exit the Help command.)
207
208 b Display all Iedit key bindings.
209 k KEYS Display full documentation of Iedit key sequence.
210 m Display documentation of Iedit mode.
211
212 You can't type here other help keys available in the global help map,
213 but outside of this help window when you type them in Iedit mode,
214 they exit Iedit mode before displaying global help."
9bc57d2 @victorhge make iedit-skipped-modification-once buffer local
authored
215 iedit-help-map)
5ad28b5 updates
anybody authored
216
217 (defun iedit-help-for-help ()
218 "Display Iedit help menu."
219 (interactive)
220 (let (same-window-buffer-names same-window-regexps)
221 (iedit-help-for-help-internal)))
222
223 (defun iedit-describe-bindings ()
224 "Show a list of all keys defined in Iedit mode, and their definitions.
225 This is like `describe-bindings', but displays only Iedit keys."
226 (interactive)
227 (let (same-window-buffer-names same-window-regexps)
228 (with-help-window "*Help*"
229 (with-current-buffer standard-output
c53a885 @victorhge Add bufferring modification functionalities
authored
230 (princ "Iedit Mode Bindings:
231 ")
da47784 @victorhge Add iedit mode easy access keys
authored
232 (princ (substitute-command-keys "\\{iedit-occurrence-local-map}"))))))
5ad28b5 updates
anybody authored
233
234 (defun iedit-describe-key ()
235 "Display documentation of the function invoked by iedit key."
236 (interactive)
237 (let (same-window-buffer-names same-window-regexps)
238 (call-interactively 'describe-key)))
239
240 (defun iedit-describe-mode ()
241 "Display documentation of iedit mode."
242 (interactive)
243 (let (same-window-buffer-names same-window-regexps)
244 (describe-function 'iedit-mode)))
245
246 ;;; Define iedit mode map
1d070b7 move init code for iedit-mode-map inside defvar
Le Wang authored
247 (defvar iedit-mode-map
652087d @victorhge fix warning: the function `make-sparse-key-map' is not known to be
authored
248 (let ((map (make-sparse-keymap)))
1d070b7 move init code for iedit-mode-map inside defvar
Le Wang authored
249 ;; Default key bindings
652087d @victorhge fix warning: the function `make-sparse-key-map' is not known to be
authored
250 (define-key map (kbd "TAB") 'iedit-next-occurrence)
251 (define-key map (kbd "<S-tab>") 'iedit-prev-occurrence)
252 (define-key map (kbd "<S-iso-lefttab>") 'iedit-prev-occurrence)
253 (define-key map (kbd "<backtab>") 'iedit-prev-occurrence)
254 (define-key map (kbd "C-'") 'iedit-toggle-unmatched-lines-visible)
255 (define-key map (char-to-string help-char) iedit-help-map)
256 (define-key map [help] iedit-help-map)
257 (define-key map [f1] iedit-help-map)
1d070b7 move init code for iedit-mode-map inside defvar
Le Wang authored
258 map)
e9a9485 updates
anybody authored
259 "Keymap used while iedit mode is enabled.")
260
da47784 @victorhge Add iedit mode easy access keys
authored
261 (defvar iedit-occurrence-local-map
262 (let ((map (make-sparse-keymap)))
263 (set-keymap-parent map iedit-mode-map)
264 (define-key map (kbd "M-u") 'iedit-upcase-occurrences)
265 (define-key map (kbd "M-l") 'iedit-downcase-occurrences)
266 (define-key map (kbd "M-r") 'iedit-replace-occurrences)
6e49225 @victorhge Add iedit-toggle-case-sensitive
authored
267 (define-key map (kbd "M-C") 'iedit-clear-occurrences)
268 (define-key map (kbd "M-c") 'iedit-toggle-case-sensitive)
c53a885 @victorhge Add bufferring modification functionalities
authored
269 (define-key map (kbd "M-D") 'iedit-delete-occurrences)
270 (define-key map [C-return] 'iedit-toggle-buffering)
7d18e5d @victorhge Add a help command C-? for occurrences
authored
271 (define-key map (kbd "C-?") 'iedit-help-for-occurrences)
da47784 @victorhge Add iedit mode easy access keys
authored
272 map)
273 "Keymap used within overlays.")
274
7d18e5d @victorhge Add a help command C-? for occurrences
authored
275 (defun iedit-help-for-occurrences ()
276 "Display iedit-occurrence-local-map."
277 (interactive)
c53a885 @victorhge Add bufferring modification functionalities
authored
278 (message "M-u/l:up/downcase M-r:replace M-c:clear M-D:delete C-return:buffering C-?:help"))
7d18e5d @victorhge Add a help command C-? for occurrences
authored
279
e9a9485 updates
anybody authored
280 (or (assq 'iedit-mode minor-mode-map-alist)
281 (setq minor-mode-map-alist
5ad28b5 updates
anybody authored
282 (cons (cons 'iedit-mode iedit-mode-map) minor-mode-map-alist)))
e9a9485 updates
anybody authored
283
284 ;;;###autoload
285 (defun iedit-mode (&optional arg)
286 "Toggle iedit mode.
287 If iedit mode is off, turn iedit mode on, off otherwise.
288
289 In Transient Mark mode, when iedit mode is turned on, all the
f808bf1 @victorhge Merge from lewang/master and update comments
authored
290 occurrences of the current region are highlighted. If one
e9a9485 updates
anybody authored
291 occurrence is modified, the change are propagated to all other
292 occurrences simultaneously.
293
294 If Transient Mark mode is disabled or the region is not active,
6d96992 @victorhge Remove duplicated comments
authored
295 the current symbol (returns from `current-word') is used as the
296 occurrence by default. The occurrences of the current
297 symbol, but not include occurrences that are part of other
298 symbols, are highlighted. This is good for renaming refactoring
299 during programming. If you still want to match all the
300 occurrences, even though they are parts of other symbols, you may
301 have to select the symbol first.
e9a9485 updates
anybody authored
302
303 You can also switch to iedit mode from isearch mode directly. The
304 current search string is used as occurrence. All occurrences of
305 the current search string are highlighted.
306
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
307 With a universal prefix argument and no active region, the
308 occurrence when iedit is turned off last time is used as
309 occurrence. This is intended to recover last iedit which is
310 turned off by mistake.
311
312 With a universal prefix argument and region active, interactively
313 edit region as a string rectangle.
e9a9485 updates
anybody authored
314
315 Commands:
da47784 @victorhge Add iedit mode easy access keys
authored
316 \\{iedit-occurrence-local-map}"
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
317 (interactive "*P")
e9a9485 updates
anybody authored
318 (if iedit-mode
319 (iedit-done)
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
320 (let (occurrence rect-string)
321 (cond ((and arg
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
322 (or (not transient-mark-mode) (not mark-active)
323 (equal (mark) (point)))
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
324 iedit-last-occurrence-in-history)
e9a9485 updates
anybody authored
325 (setq occurrence iedit-last-occurrence-in-history))
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
326 ((and arg
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
327 transient-mark-mode mark-active (not (equal (mark) (point))))
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
328 (setq rect-string t))
e9a9485 updates
anybody authored
329 ((and transient-mark-mode mark-active (not (equal (mark) (point))))
df6c2ad fix search to use regexp-quote when needed
Le Wang authored
330 (setq occurrence (regexp-quote (buffer-substring-no-properties
331 (mark) (point)))))
e9a9485 updates
anybody authored
332 ((and isearch-mode (not (string= isearch-string "")))
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
333 (setq occurrence (regexp-quote (buffer-substring-no-properties
334 (point) isearch-other-end)))
e9a9485 updates
anybody authored
335 (isearch-exit))
f808bf1 @victorhge Merge from lewang/master and update comments
authored
336 ((and iedit-current-symbol-default (current-word t))
df6c2ad fix search to use regexp-quote when needed
Le Wang authored
337 (setq occurrence (regexp-quote (current-word)))
f808bf1 @victorhge Merge from lewang/master and update comments
authored
338 (when iedit-only-at-symbol-boundaries
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
339 (setq iedit-current-occurrence-complete-symbol t)
f808bf1 @victorhge Merge from lewang/master and update comments
authored
340 (setq occurrence (concat "\\_<" (regexp-quote occurrence) "\\_>"))))
e9a9485 updates
anybody authored
341 (t (error "No candidate of the occurrence, cannot enable iedit mode.")))
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
342 (if rect-string
343 (let ((beg (region-beginning))
344 (end (region-end)))
345 (deactivate-mark)
346 (iedit-rectangle beg end))
347 (deactivate-mark)
6e49225 @victorhge Add iedit-toggle-case-sensitive
authored
348 (setq iedit-case-sensitive iedit-case-sensitive-default)
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
349 (iedit-start occurrence)))))
e9a9485 updates
anybody authored
350
f808bf1 @victorhge Merge from lewang/master and update comments
authored
351 (defun iedit-start (occurrence-exp)
352 "Start an iedit for the occurrence-exp in the current buffer."
e9a9485 updates
anybody authored
353 (setq iedit-occurrences-overlays nil)
354 (setq iedit-unmatched-lines-invisible iedit-unmatched-lines-invisible-default)
9b3aaa8 @victorhge Fix occurrence from history does not exist problem
authored
355 (setq iedit-aborting nil)
e9a9485 updates
anybody authored
356 ;; Find and record each occurrence's markers and add the overlay to the occurrences
5ad28b5 updates
anybody authored
357 (let ((counter 0)
f808bf1 @victorhge Merge from lewang/master and update comments
authored
358 (case-fold-search (not iedit-case-sensitive)))
359 (save-excursion
360 (goto-char (point-min))
361 (while (re-search-forward occurrence-exp nil t)
362 (push (iedit-make-occurrence-overlay (match-beginning 0) (match-end 0))
363 iedit-occurrences-overlays)
9b3aaa8 @victorhge Fix occurrence from history does not exist problem
authored
364 (setq counter (1+ counter)))
365 (if (= 0 counter)
366 (error "0 matches for \"%s\""
367 (if (> (length occurrence-exp) 50)
368 (concat (substring occurrence-exp 0 50) "...")
369 occurrence-exp))
370 (setq iedit-occurrences-overlays (nreverse iedit-occurrences-overlays))
371 (if iedit-unmatched-lines-invisible
372 (iedit-hide-unmatched-lines)))
da0a247 @tsdh Add option to match only complete words, not inside words
tsdh authored
373 (message "%d matches for \"%s\""
374 counter
e9a9485 updates
anybody authored
375 (if (> (length occurrence-exp) 50)
376 (concat (substring occurrence-exp 0 50) "...")
9b3aaa8 @victorhge Fix occurrence from history does not exist problem
authored
377 occurrence-exp))))
378 (setq iedit-mode (propertize " Iedit" 'face 'font-lock-warning-face))
379 (force-mode-line-update)
380 (run-hooks 'iedit-mode-hook)
381 ;; (add-hook 'mouse-leave-buffer-hook 'iedit-done)
382 (add-hook 'kbd-macro-termination-hook 'iedit-done))
5ad28b5 updates
anybody authored
383
039c0e2 @victorhge Postpone iedit-done after commands are excuted.
authored
384
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
385 (defun iedit-rectangle (beg end)
386 "Start an iedit for the region as a rectangle"
387 (setq iedit-mode (propertize " Iedit-RECT" 'face 'font-lock-warning-face))
388 (setq iedit-occurrences-overlays nil)
389 (force-mode-line-update)
390 (run-hooks 'iedit-mode-hook)
391 (add-hook 'kbd-macro-termination-hook 'iedit-done)
c53a885 @victorhge Add bufferring modification functionalities
authored
392 (save-excursion
393 (let ((beg-col (progn (goto-char beg) (current-column)))
394 (end-col (progn (goto-char end) (current-column))))
395 (when (< end-col beg-col)
396 (rotatef beg-col end-col))
397 (goto-char beg)
398 (loop do (progn
399 (push (iedit-make-occurrence-overlay
400 (progn
401 (move-to-column beg-col t)
402 (point))
403 (progn
404 (move-to-column end-col t)
405 (point)))
406 iedit-occurrences-overlays)
407 (forward-line 1))
408 until (> (point) end))
409 (setq iedit-occurrences-overlays (nreverse iedit-occurrences-overlays)))))
ec765ee implement interactive string-rectangle mode with universal argument a…
Le Wang authored
410
e9a9485 updates
anybody authored
411 (defun iedit-done ()
412 "Exit iedit mode."
c53a885 @victorhge Add bufferring modification functionalities
authored
413 (if iedit-buffering
414 (iedit-stop-buffering))
17ce973 @victorhge Clean up code
authored
415 (let* ((ov (car iedit-occurrences-overlays))
416 (beg (overlay-start ov))
417 (end (overlay-end ov)))
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
418 (setq iedit-last-occurrence-in-history
17ce973 @victorhge Clean up code
authored
419 (if (and ov (/= beg end))
420 (let ((substring (buffer-substring-no-properties beg end)))
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
421 (if iedit-current-occurrence-complete-symbol
422 (concat "\\_<" substring "\\_>")
423 substring))
424 nil)
425 ))
426 (setq iedit-current-occurrence-complete-symbol nil)
e9a9485 updates
anybody authored
427 (remove-overlays (point-min) (point-max) iedit-occurrence-overlay-name t)
428 (remove-overlays (point-min) (point-max) iedit-invisible-overlay-name t)
429 (setq iedit-occurrences-overlays nil)
c53a885 @victorhge Add bufferring modification functionalities
authored
430 (setq iedit-aborting nil)
431 (setq iedit-before-modification-string "")
432 (setq iedit-before-modification-undo-list nil)
5ad28b5 updates
anybody authored
433 ;; (remove-hook 'mouse-leave-buffer-hook 'iedit-done)
e9a9485 updates
anybody authored
434 (remove-hook 'kbd-macro-termination-hook 'iedit-done)
435 (setq iedit-mode nil)
436 (force-mode-line-update)
437 (run-hooks 'iedit-mode-end-hook))
438
439 (defun iedit-make-occurrence-overlay (begin end)
440 "Create an overlay for an occurrence in iedit mode.
441 Add the properties for the overlay: a face used to display a
442 occurrence's default value, and modification hooks to update
443 occurrences if the user starts typing."
444 (let ((occurrence (make-overlay begin end (current-buffer) nil t)))
445 (overlay-put occurrence iedit-occurrence-overlay-name t)
446 (overlay-put occurrence 'face iedit-occurrence-face)
da47784 @victorhge Add iedit mode easy access keys
authored
447 (overlay-put occurrence 'local-map iedit-occurrence-local-map)
e9a9485 updates
anybody authored
448 (overlay-put occurrence 'insert-in-front-hooks '(iedit-occurrence-update))
449 (overlay-put occurrence 'insert-behind-hooks '(iedit-occurrence-update))
450 (overlay-put occurrence 'modification-hooks '(iedit-occurrence-update))
451 occurrence))
452
453 (defun iedit-make-unmatched-lines-overlay (begin end)
454 "Create an overlay for lines between two occurrences in iedit mode."
455 (let ((unmatched-lines-overlay (make-overlay begin end (current-buffer) nil t)))
456 (overlay-put unmatched-lines-overlay iedit-invisible-overlay-name t)
457 (overlay-put unmatched-lines-overlay 'invisible t)
458 (overlay-put unmatched-lines-overlay 'intangible t)
459 unmatched-lines-overlay))
460
6e49225 @victorhge Add iedit-toggle-case-sensitive
authored
461 (defun iedit-reset-aborting ()
462 "Turning iedit-mode off and reset iedit-aborting. `iedit-done'
463 is postponed after the command is executed for avoiding
464 iedit-occurrence-update is called for a removed overlay."
465 (iedit-done)
466 (remove-hook 'post-command-hook 'iedit-reset-aborting t)
467 (setq iedit-aborting nil))
468
e9a9485 updates
anybody authored
469 (defun iedit-occurrence-update (occurrence after beg end &optional change)
470 "Update all occurrences.
9a891d7 updates
anybody authored
471 This modification hook is triggered when a user edits any
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
472 occurrence and is responsible for updating all other occurrences.
473 Current supported edits are insertion, yank, deletion and replacement.
474 If this modification is going out of the occurrence, it will
7d18e5d @victorhge Add a help command C-? for occurrences
authored
475 exit iedit mode."
039c0e2 @victorhge Postpone iedit-done after commands are excuted.
authored
476 (when (and (not iedit-aborting )
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
477 (not undo-in-progress)) ; undo will do all the update
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
478 ;; before modification
13ecba1 Merge branch 'upstream-master'
Le Wang authored
479 (if (null after)
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
480 (if (or (< beg (overlay-start occurrence))
481 (> end (overlay-end occurrence)))
0edc86f @victorhge Fix the last occurrence problem when it is a complete symbol.
authored
482 (progn (setq iedit-aborting t) ; abort iedit-mode
483 (add-hook 'post-command-hook 'iedit-reset-aborting nil t))
17ce973 @victorhge Clean up code
authored
484 (setq iedit-before-modification-string
485 (buffer-substring-no-properties beg end)))
20c1075 remove conditions on setting `iedit-before-modification-string'.
Le Wang authored
486 ;; after modification
c53a885 @victorhge Add bufferring modification functionalities
authored
487 (when (not iedit-buffering)
9bc57d2 @victorhge make iedit-skipped-modification-once buffer local
authored
488 ;; Check if we are inserting into zero-width occurrence. If so, then
489 ;; TWO modification hooks will be called -- "insert-in-front-hooks" and
490 ;; "insert-behind-hooks". We need to run just once.
c53a885 @victorhge Add bufferring modification functionalities
authored
491 (if (and (= beg (overlay-start occurrence))
492 (= end (overlay-end occurrence))
493 (= change 0)
494 (not iedit-skipped-modification-once))
495 (setq iedit-skipped-modification-once t)
496 (setq iedit-skipped-modification-once nil)
497 (when (or (eq 0 change) ;; insertion
498 (eq beg end) ;; deletion
499 (not (string= iedit-before-modification-string
500 (buffer-substring-no-properties beg end))))
501 (let ((inhibit-modification-hooks t) ; todo: extract this as a function
502 (offset (- beg (overlay-start occurrence)))
503 (value (buffer-substring-no-properties beg end)))
504 (save-excursion
505 ;; insertion or yank
506 (if (eq 0 change)
507 (dolist (another-occurrence (remove occurrence iedit-occurrences-overlays))
508 (progn
509 (goto-char (+ (overlay-start another-occurrence) offset))
510 (insert-and-inherit value)))
511 ;; deletion
b81b9d6 @victorhge Clean code.
authored
512 (dolist (another-occurrence (remove occurrence iedit-occurrences-overlays))
c53a885 @victorhge Add bufferring modification functionalities
authored
513 (let* ((beginning (+ (overlay-start another-occurrence) offset))
514 (ending (+ beginning change)))
515 (delete-region beginning ending)
516 (unless (eq beg end) ;; replacement
517 (goto-char beginning)
518 (insert-and-inherit value)))))))))))))
17ce973 @victorhge Clean up code
authored
519 ;; (elp-instrument-list '(insert-and-inherit delete-region goto-char iedit-occurrence-update buffer-substring-no-properties string= re-search-forward replace-match))
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
520
7d18e5d @victorhge Add a help command C-? for occurrences
authored
521 ;; slowest version:
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
522 ;; (defun iedit-occurrence-update (occurrence after beg end &optional change)
523 ;; "Update all occurrences.
524 ;; This modification hook is triggered when a user edits any
525 ;; occurrence and is responsible for updating all other
526 ;; occurrences."
527 ;; (when (and after (not undo-in-progress)) ; undo will do all the work
528 ;; (let ((value (buffer-substring-no-properties
529 ;; (overlay-start occurrence) (overlay-end occurrence)))
530 ;; (inhibit-modification-hooks t))
531 ;; (save-excursion
b81b9d6 @victorhge Clean code.
authored
532 ;; (dolist (another-occurrence iedit-occurrences-overlays)
533 ;; (if (not (eq another-occurrence occurrence))
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
534 ;; (progn
b81b9d6 @victorhge Clean code.
authored
535 ;; (goto-char (overlay-start another-occurrence))
536 ;; (delete-region (overlay-start another-occurrence)
537 ;; (overlay-end another-occurrence))
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
538 ;; (insert value))))))))
539
540 ;; ;; todo \\_<
541 ;; (defun iedit-occurrence-update (occurrence after beg end &optional change)
542 ;; "Update all occurrences.
543 ;; This modification hook is triggered when a user edits any
544 ;; occurrence and is responsible for updating all other
545 ;; occurrences."
546 ;; (when (not undo-in-progress) ; undo will do all the work
13ecba1 Merge branch 'upstream-master'
Le Wang authored
547 ;; (if (null after)
c8dac6b @victorhge exit iedit mode when change is not inside of occurrence.
authored
548 ;; (if (or (< beg (overlay-start occurrence))
549 ;; (> end (overlay-end occurrence)))
550 ;; (iedit-done)
551 ;; (setq iedit-before-modification-string
552 ;; (buffer-substring-no-properties
553 ;; (overlay-start occurrence) (overlay-end occurrence))))
554 ;; (let ((value (buffer-substring-no-properties
555 ;; (overlay-start occurrence) (overlay-end occurrence)))
556 ;; (inhibit-modification-hooks t))
557 ;; (save-excursion
558 ;; (goto-char (overlay-end occurrence))
559 ;; (while (re-search-forward iedit-before-modification-string nil t)
560 ;; (replace-match value nil nil))
561 ;; (goto-char (point-min))
562 ;; (while (re-search-forward iedit-before-modification-string (overlay-start occurrence) t)
563 ;; (replace-match value nil nil)))))))
e9a9485 updates
anybody authored
564
565 (defun iedit-next-occurrence ()
9a891d7 updates
anybody authored
566 "Move forward to the next occurrence in the `iedit'.
ec6785b @victorhge host on git
authored
567 If the point is already in the last occurrences, you are asked to type
9a891d7 updates
anybody authored
568 another `iedit-next-occurrence', it starts again from the
569 beginning of the buffer."
e9a9485 updates
anybody authored
570 (interactive)
9a891d7 updates
anybody authored
571 (let ((pos (point))
572 (in-occurrence (get-char-property (point) 'iedit-occurrence-overlay-name)))
573 (when in-occurrence
574 (setq pos (next-single-char-property-change pos 'iedit-occurrence-overlay-name)))
575 (setq pos (next-single-char-property-change pos 'iedit-occurrence-overlay-name))
da0a247 @tsdh Add option to match only complete words, not inside words
tsdh authored
576
9a891d7 updates
anybody authored
577 (if (/= pos (point-max))
578 (setq iedit-forward-success t)
579 (if (and iedit-forward-success in-occurrence)
580 (progn (message "This is the last occurrence.")
581 (setq iedit-forward-success nil))
da0a247 @tsdh Add option to match only complete words, not inside words
tsdh authored
582 (progn
9a891d7 updates
anybody authored
583 (if (get-char-property (point-min) 'iedit-occurrence-overlay-name)
584 (setq pos (point-min))
585 (setq pos (next-single-char-property-change (point-min) 'iedit-occurrence-overlay-name)))
586 (setq iedit-forward-success t)
587 (message "Located the first occurrence."))))
588 (when iedit-forward-success
589 (goto-char pos))))
e9a9485 updates
anybody authored
590
591 (defun iedit-prev-occurrence ()
9a891d7 updates
anybody authored
592 "Move backward to the previous occurrence in the `iedit'.
ec6785b @victorhge host on git
authored
593 If the point is already in the first occurrences, you are asked to type
9a891d7 updates
anybody authored
594 another `iedit-prev-occurrence', it starts again from the end of
595 the buffer."
e9a9485 updates
anybody authored
596 (interactive)
9a891d7 updates
anybody authored
597 (let ((pos (point))
598 (in-occurrence (get-char-property (point) 'iedit-occurrence-overlay-name)))
599 (when in-occurrence
600 (setq pos (previous-single-char-property-change pos 'iedit-occurrence-overlay-name)))
601 (setq pos (previous-single-char-property-change pos 'iedit-occurrence-overlay-name))
5ad28b5 updates
anybody authored
602 ;; At the start of the first occurrence
9a891d7 updates
anybody authored
603 (if (or (and (eq pos (point-min))
604 (not (get-char-property (point-min) 'iedit-occurrence-overlay-name)))
da0a247 @tsdh Add option to match only complete words, not inside words
tsdh authored
605 (and (eq (point) (point-min))
9a891d7 updates
anybody authored
606 in-occurrence))
607 (if (and iedit-forward-success in-occurrence)
608 (progn (message "This is the first occurrence.")
609 (setq iedit-forward-success nil))
da0a247 @tsdh Add option to match only complete words, not inside words
tsdh authored
610 (progn
9a891d7 updates
anybody authored
611 (setq pos (previous-single-char-property-change (point-max) 'iedit-occurrence-overlay-name))
612 (if (not (get-char-property (- (point-max) 1) 'iedit-occurrence-overlay-name))
613 (setq pos (previous-single-char-property-change pos 'iedit-occurrence-overlay-name)))
614 (setq iedit-forward-success t)
615 (message "Located the last occurrence.")))
616 (setq iedit-forward-success t))
617 (when iedit-forward-success
618 (goto-char pos))))
e9a9485 updates
anybody authored
619
620 (defun iedit-toggle-unmatched-lines-visible ()
621 "Toggle whether to display unmatched lines."
622 (interactive)
623 (setq iedit-unmatched-lines-invisible (not iedit-unmatched-lines-invisible))
624 (if iedit-unmatched-lines-invisible
625 (iedit-hide-unmatched-lines)
626 (remove-overlays (point-min) (point-max) iedit-invisible-overlay-name t)))
627
6e49225 @victorhge Add iedit-toggle-case-sensitive
authored
628 (defun iedit-hide-unmatched-lines ()
629 "Hide unmatched lines using invisible overlay."
630 (let ((prev-occurrence-end 0)
631 (unmatched-lines nil))
632 (save-excursion
633 (dolist (overlay iedit-occurrences-overlays)
634 (goto-char (overlay-start overlay))
635 (let ((line-beginning (line-beginning-position)))
636 (if (> line-beginning (1+ prev-occurrence-end))
637 (push (list (1+ prev-occurrence-end) (1- line-beginning)) unmatched-lines)))
638 (goto-char (overlay-end overlay))
639 (setq prev-occurrence-end (line-end-position)))
640 (if (< prev-occurrence-end (point-max))
641 (push (list (1+ prev-occurrence-end) (point-max)) unmatched-lines))
642 (when unmatched-lines
643 (dolist (unmatch unmatched-lines)
644 (iedit-make-unmatched-lines-overlay (car unmatch) (cadr unmatch)))))))
645
646 ;;;; functions for overlay local-map
7d18e5d @victorhge Add a help command C-? for occurrences
authored
647 (defun iedit-foreach-occurrence-call (function &optional string)
da47784 @victorhge Add iedit mode easy access keys
authored
648 "Call function for each occurrence."
649 (let* ((ov (car iedit-occurrences-overlays))
650 (beg (overlay-start ov))
651 (end (overlay-end ov)))
652 (when (/= beg end)
653 (let ((inhibit-modification-hooks t))
654 (dolist (occurrence iedit-occurrences-overlays)
655 (if string
656 (funcall function (overlay-start occurrence) (overlay-end occurrence) string)
7d18e5d @victorhge Add a help command C-? for occurrences
authored
657 (funcall function (overlay-start occurrence) (overlay-end occurrence))))))))
da47784 @victorhge Add iedit mode easy access keys
authored
658
659 (defun iedit-upcase-occurrences ()
660 "Covert occurrences to upper case."
661 (interactive)
7d18e5d @victorhge Add a help command C-? for occurrences
authored
662 (iedit-foreach-occurrence-call 'upcase-region))
da47784 @victorhge Add iedit mode easy access keys
authored
663
664 (defun iedit-downcase-occurrences()
665 "Covert occurrences to lower case."
666 (interactive)
7d18e5d @victorhge Add a help command C-? for occurrences
authored
667 (iedit-foreach-occurrence-call 'downcase-region))
da47784 @victorhge Add iedit mode easy access keys
authored
668
669 (defun iedit-replace-occurrences(string)
670 "Replace occurrences with STRING."
671 (interactive "sString: ")
7d18e5d @victorhge Add a help command C-? for occurrences
authored
672 (iedit-foreach-occurrence-call
da47784 @victorhge Add iedit mode easy access keys
authored
673 (lambda (beg end string)
674 (save-excursion
675 (delete-region beg end)
676 (goto-char beg)
c53a885 @victorhge Add bufferring modification functionalities
authored
677 (insert-and-inherit string)))
da47784 @victorhge Add iedit mode easy access keys
authored
678 string))
679
680 (defun iedit-clear-occurrences()
681 "Replace occurrences with blank spaces."
682 (interactive)
7d18e5d @victorhge Add a help command C-? for occurrences
authored
683 (iedit-foreach-occurrence-call
da47784 @victorhge Add iedit mode easy access keys
authored
684 (lambda (beg end)
685 (save-excursion
686 (delete-region beg end)
687 (goto-char beg)
688 (insert-char 32 (- end beg))))))
689
690 (defun iedit-delete-occurrences()
691 "Delete occurrences."
692 (interactive)
7d18e5d @victorhge Add a help command C-? for occurrences
authored
693 (iedit-foreach-occurrence-call 'delete-region))
da47784 @victorhge Add iedit mode easy access keys
authored
694
c53a885 @victorhge Add bufferring modification functionalities
authored
695 (defun iedit-toggle-buffering ()
696 "Toggle buffering."
697 (interactive)
698 (if iedit-buffering
699 (iedit-stop-buffering)
700 (iedit-start-buffering)))
701
702 (defun iedit-start-buffering ()
703 "Start buffering."
704 (setq iedit-buffering t)
705 (let* ((ov (iedit-find-current-occurrence-overlay))
706 (beg (overlay-start ov))
707 (end (overlay-end ov)))
708 (setq iedit-before-modification-string
709 (buffer-substring-no-properties beg end))
710 (setq iedit-before-modification-undo-list buffer-undo-list)
711 (setq iedit-mode (propertize " Iedit-B" 'face 'font-lock-warning-face))
712 (force-mode-line-update)
713 (message "Iedit-mode buffering.")))
714
715 (defun iedit-stop-buffering ()
716 "Stop buffering and apply the modification to other occurrences."
717 (let* ((inhibit-modification-hooks t)
718 (ov (iedit-find-current-occurrence-overlay))
719 (beg (overlay-start ov))
720 (end (overlay-end ov))
721 (modified-string (buffer-substring-no-properties beg end))
722 (offset (- (point) beg))) ;; delete-region move cursor
723 (when (not (string= iedit-before-modification-string modified-string))
724 (save-excursion
9bc57d2 @victorhge make iedit-skipped-modification-once buffer local
authored
725 ;; Rollback the current modification and buffer-undo-list. This is to
726 ;; avoid the inconsistency if user undoes modifications
727 (delete-region beg end)
728 (goto-char beg)
729 (insert-and-inherit iedit-before-modification-string)
730 (setq buffer-undo-list iedit-before-modification-undo-list)
731 (dolist (occurrence iedit-occurrences-overlays) ; todo:extract as a function
732 (let ((beginning (overlay-start occurrence))
733 (ending (overlay-end occurrence)))
734 (delete-region beginning ending)
735 (unless (eq beg end) ;; replacement
736 (goto-char beginning)
737 (insert-and-inherit modified-string)))))
c53a885 @victorhge Add bufferring modification functionalities
authored
738 (goto-char (+ (overlay-start ov) offset))))
739 (setq iedit-buffering nil)
740 (setq iedit-mode (propertize " Iedit" 'face 'font-lock-warning-face))
741 (force-mode-line-update)
742 (setq iedit-before-modification-undo-list nil)
9bc57d2 @victorhge make iedit-skipped-modification-once buffer local
authored
743 (message "Iedit-mode buffering stopped."))
c53a885 @victorhge Add bufferring modification functionalities
authored
744
745 (defun iedit-find-current-occurrence-overlay ()
746 "Always return the current occurrence overlay at point or point - 1,
747 since this function is supposed to be called in overlay local-map."
748 (or (iedit-find-overlay (point) 'iedit-occurrence-overlay-name)
749 (iedit-find-overlay (1- (point)) 'iedit-occurrence-overlay-name)))
750
751 (defun iedit-find-overlay (point property)
752 "Return the overlay with PROPERTY at POINT."
753 (let ((overlays (overlays-at point))
754 found)
755 (while (and overlays (not found))
756 (let ((overlay (car overlays)))
757 (if (overlay-get overlay property)
758 (setq found overlay)
759 (setq overlays (cdr overlays)))))
760 found))
761
6e49225 @victorhge Add iedit-toggle-case-sensitive
authored
762 (defun iedit-toggle-case-sensitive ()
763 "Toggle case-sensitive matching occurrences."
764 (interactive)
765 (iedit-done)
766 (setq iedit-case-sensitive (not iedit-case-sensitive))
767 (iedit-start iedit-last-occurrence-in-history))
768
e9a9485 updates
anybody authored
769 (provide 'iedit)
770
771 ;;; iedit.el ends here
Something went wrong with that request. Please try again.