Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 485 lines (411 sloc) 18.995 kb
d2684ee7 »
2012-08-06 Remove duplicate definations
1 ;;; iedit.el --- Edit multiple regions in the same way simultaneously.
2
3 ;; Copyright (C) 2010, 2011, 2012 Victor Ren
4
29869ebe »
2012-09-05 Add buffer local variable iedit-occurrence-keymap
5 ;; Time-stamp: <2012-09-05 09:47:56 Victor Ren>
d2684ee7 »
2012-08-06 Remove duplicate definations
6 ;; Author: Victor Ren <victorhge@gmail.com>
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
7 ;; Keywords: occurrence region simultaneous refactoring
d2684ee7 »
2012-08-06 Remove duplicate definations
8 ;; Version: 0.97
9 ;; X-URL: http://www.emacswiki.org/emacs/Iedit
10 ;; Compatibility: GNU Emacs: 22.x, 23.x, 24.x
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 is an Emacs minor mode and allows you to edit one occurrence of
31 ;; some text in a buffer (possibly narrowed) or region, and simultaneously have
32 ;; other occurrences edited in the same way.
33 ;;
34 ;; Normal scenario of iedit-mode is like:
35 ;;
36 ;; - Highlight certain contents - by press C-;
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
37 ;; All occurrences of a symbol, string in the buffer or a region may be
38 ;; highlighted corresponding to current mark, point and prefix argument.
39 ;; Refer to the document of `iedit-mode' for details.
d2684ee7 »
2012-08-06 Remove duplicate definations
40 ;;
41 ;; - Edit one of the occurrences
42 ;; The change is applied to other occurrences simultaneously.
43 ;;
44 ;; - Finish - by pressing C-; again
45 ;;
46 ;; You can also use Iedit mode as a quick way to temporarily show only the
47 ;; buffer lines that match the current text being edited. This gives you the
48 ;; effect of a temporary `keep-lines' or `occur'. To get this effect, hit C-'
49 ;; when in Iedit mode - it toggles hiding non-matching lines.
50 ;;
51 ;; Renaming refactoring is convenient in Iedit mode
52 ;;
53 ;; - The symbol under point is selected as occurrence by default and only
54 ;; complete symbols are matched
55 ;; - With digit prefix argument 0, only symbols in current function are matched
56 ;; - Restricting symbols in current region can be done by pressing C-; again
57 ;; - Last renaming refactoring is remembered and can be applied to other buffers
58 ;; later
59 ;;
60 ;; There are also some other facilities you may never think about. Refer to the
61 ;; document of function `iedit-mode' (C-h f iedit-mode RET) for more details.
62
63 ;; The code was developed and fully tested on Gnu Emacs 24.0.93, partially
64 ;; tested on Gnu Emacs 22. If you have any compatible problem, please let me
65 ;; know.
66
67 ;;; todo:
68 ;; - Add more easy access keys for whole occurrence
69
70 ;;; Contributors
71 ;; Adam Lindberg <eproxus@gmail.com> added a case sensitivity option that can be toggled.
72
73 ;; Tassilo Horn <tassilo@member.fsf.org> added an option to match only complete
74 ;; words, not inside words
75
76 ;; Le Wang <l26wang@gmail.com> proposed to match only complete symbols, not
77 ;; inside symbols, contributed rectangle support
78
79 ;;; Code:
80
81 (eval-when-compile (require 'cl))
82 (require 'iedit-lib)
83
84 (defcustom iedit-current-symbol-default t
85 "If no-nil, use current symbol by default for the occurrence."
86 :type 'boolean
87 :group 'iedit)
88
89 (defcustom iedit-only-at-symbol-boundaries t
90 "If no-nil, matches have to start and end at symbol boundaries.
91 For example, when invoking command `iedit-mode' on the \"in\" in the
92 sentence \"The king in the castle...\", the \"king\" is not
93 edited."
94 :type 'boolean
95 :group 'iedit)
96
21a60f2e »
2012-08-31 Add toogle key option
97 (defcustom iedit-toogle-key-default (kbd "C-;")
98 "If no-nil, the key is inserted into global-map, isearch-mode-map, esc-map and help-map."
99 :type 'vector
100 :group 'iedit)
101
d2684ee7 »
2012-08-06 Remove duplicate definations
102 (defvar iedit-mode-hook nil
103 "Function(s) to call after starting up an iedit.")
104
105 (defvar iedit-mode-end-hook nil
106 "Function(s) to call after terminating an iedit.")
107
108 (defvar iedit-mode nil) ;; Name of the minor mode
109
110 (defvar iedit-only-complete-symbol-local nil
111 "This is buffer local variable which indicates the occurrence
112 only matches complete symbol.")
113
114 (defvar iedit-only-complete-symbol-global nil
115 "This is global variable which indicates the last global occurrence
116 only matches complete symbol.")
117
118 (defvar iedit-last-occurrence-local nil
119 "This is buffer local variable which is the occurrence when
120 Iedit mode is turned off last time.")
121
122 (defvar iedit-last-occurrence-global nil
123 "This is global variable which is the occurrence when
124 Iedit mode is turned off last time.")
125
126 (defvar iedit-last-initial-string-global nil
127 "This is a global variable which is the last initial occurrence string.")
128
129 (defvar iedit-initial-string-local nil
130 "This is buffer local variable which is the initial string to start Iedit mode.")
131
132 (make-variable-buffer-local 'iedit-mode)
133 (make-variable-buffer-local 'iedit-only-complete-symbol-local)
134 (make-variable-buffer-local 'iedit-last-occurrence-local)
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
135 (make-variable-buffer-local 'iedit-initial-string-local)
d2684ee7 »
2012-08-06 Remove duplicate definations
136
137 (or (assq 'iedit-mode minor-mode-alist)
138 (nconc minor-mode-alist
139 (list '(iedit-mode iedit-mode))))
140
b94c75e8 »
2012-08-24 Move iedit-help-keymap to iedit.el
141 ;;; Define iedit help map.
142 (eval-when-compile (require 'help-macro))
143
144 (defvar iedit-help-map
145 (let ((map (make-sparse-keymap)))
146 (define-key map (char-to-string help-char) 'iedit-help-for-help)
147 (define-key map [help] 'iedit-help-for-help)
148 (define-key map [f1] 'iedit-help-for-help)
149 (define-key map "?" 'iedit-help-for-help)
150 (define-key map "b" 'iedit-describe-bindings)
151 (define-key map "k" 'iedit-describe-key)
152 (define-key map "m" 'iedit-describe-mode)
153 (define-key map "q" 'help-quit)
154 map)
155 "Keymap for characters following the Help key for Iedit mode.")
156
157 (make-help-screen
158 iedit-help-for-help-internal
159 (purecopy "Type a help option: [bkm] or ?")
160 "You have typed %THIS-KEY%, the help character. Type a Help option:
161 \(Type \\<help-map>\\[help-quit] to exit the Help command.)
162
163 b Display all Iedit key bindings.
164 k KEYS Display full documentation of Iedit key sequence.
165 m Display documentation of Iedit mode.
166
167 You can't type here other help keys available in the global help map,
168 but outside of this help window when you type them in Iedit mode,
169 they exit Iedit mode before displaying global help."
170 iedit-help-map)
171
172 (defun iedit-help-for-help ()
173 "Display Iedit help menu."
174 (interactive)
175 (let (same-window-buffer-names same-window-regexps)
176 (iedit-help-for-help-internal)))
177
178 (defun iedit-describe-bindings ()
179 "Show a list of all keys defined in Iedit mode, and their definitions.
180 This is like `describe-bindings', but displays only Iedit keys."
181 (interactive)
182 (let (same-window-buffer-names
183 same-window-regexps
184 (keymap (substitute-command-keys "\\{iedit-mode-keymap}\\{iedit-mode-occurrence-keymap}")))
185 (with-help-window "*Help*"
186 (with-current-buffer standard-output
187 (princ "Iedit Mode Bindings: ")
188 (princ keymap)))))
189
190 (defun iedit-describe-key ()
191 "Display documentation of the function invoked by Iedit mode key."
192 (interactive)
193 (let (same-window-buffer-names same-window-regexps)
194 (call-interactively 'describe-key)))
195
d2684ee7 »
2012-08-06 Remove duplicate definations
196 (defun iedit-describe-mode ()
197 "Display documentation of Iedit mode."
198 (interactive)
199 (let (same-window-buffer-names same-window-regexps)
200 (describe-function 'iedit-mode)))
201
202 ;;; Default key bindings:
21a60f2e »
2012-08-31 Add toogle key option
203 (define-key global-map iedit-toogle-key-default 'iedit-mode)
204 (define-key isearch-mode-map iedit-toogle-key-default 'iedit-mode)
205 (define-key esc-map iedit-toogle-key-default 'iedit-execute-last-modification)
206 (define-key help-map iedit-toogle-key-default 'iedit-mode-toggle-on-function)
d2684ee7 »
2012-08-06 Remove duplicate definations
207
208 ;; Avoid to restore Iedit mode when restoring desktop
209 (add-to-list 'desktop-minor-mode-handlers
210 '(iedit-mode . nil))
211
212 ;;; Define iedit help map.
213 (eval-when-compile (require 'help-macro))
214
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
215 (defvar iedit-mode-occurrence-keymap
d2684ee7 »
2012-08-06 Remove duplicate definations
216 (let ((map (make-sparse-keymap)))
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
217 (set-keymap-parent map iedit-occurrence-keymap-default)
d2684ee7 »
2012-08-06 Remove duplicate definations
218 (define-key map (kbd "M-H") 'iedit-restrict-function)
219 (define-key map (kbd "M-C") 'iedit-toggle-case-sensitive)
220 map)
221 "Keymap used within overlays in Iedit mode.")
222
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
223 (defvar iedit-mode-keymap
224 (let ((map (make-sparse-keymap)))
225 (set-keymap-parent map iedit-lib-keymap)
b94c75e8 »
2012-08-24 Move iedit-help-keymap to iedit.el
226 (define-key map (char-to-string help-char) iedit-help-map)
227 (define-key map [help] iedit-help-map)
228 (define-key map [f1] iedit-help-map)
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
229 map)
230 "Keymap used while Iedit mode is enabled.")
d2684ee7 »
2012-08-06 Remove duplicate definations
231
232 ;;; Define Iedit mode map
233 (or (assq 'iedit-mode minor-mode-map-alist)
234 (setq minor-mode-map-alist
235 (cons (cons 'iedit-mode iedit-mode-keymap) minor-mode-map-alist)))
236
237 ;; Avoid to restore Iedit mode when restoring desktop
238 (add-to-list 'desktop-minor-mode-handlers
239 '(iedit-mode . nil))
240
241 ;;;###autoload
242 (defun iedit-mode (&optional arg)
243 "Toggle Iedit mode.
244 This command behaves differently, depending on the mark, point,
245 prefix argument and variable `iedit-transient-mark-sensitive'.
246
247 If Iedit mode is off, turn Iedit mode on.
248
249 When Iedit mode is turned on, all the occurrences of the current
250 region in the buffer (possibly narrowed) or a region are
251 highlighted. If one occurrence is modified, the change are
252 propagated to all other occurrences simultaneously.
253
254 If region is not active, the current symbol (returns from
255 `current-word') is used as the occurrence by default. The
256 occurrences of the current symbol, but not include occurrences
257 that are part of other symbols, are highlighted. If you still
258 want to match all the occurrences, even though they are parts of
259 other symbols, you may have to mark the symbol first.
260
261 In the above two situations, with digit prefix argument 0, only
262 occurrences in current function are matched. This is good for
263 renaming refactoring in programming.
264
265 You can also switch to Iedit mode from isearch mode directly. The
266 current search string is used as occurrence. All occurrences of
267 the current search string are highlighted.
268
269 With an universal prefix argument, the occurrence when Iedit mode
270 is turned off last time in current buffer is used as occurrence.
271 This is intended to recover last Iedit mode which is turned off.
272 If region active, Iedit mode is limited within the current
273 region.
274
275 With repeated universal prefix argument, the occurrence when
276 Iedit mode is turned off last time (might be in other buffer) is used
277 as occurrence. If region active, Iedit mode is limited within
278 the current region.
279
280 If Iedit mode is on and region is active, Iedit mode is
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
281 restricted in the region, e.g. the occurrences outside of the
282 region is excluded.
d2684ee7 »
2012-08-06 Remove duplicate definations
283
284 If Iedit mode is on and region is active, with an universal
285 prefix argument, Iedit mode is restricted outside of the region,
286 e.g. the occurrences in the region is excluded.
287
288 Turn off Iedit mode in other situations.
289
290 Commands:
b94c75e8 »
2012-08-24 Move iedit-help-keymap to iedit.el
291 \\{iedit-mode-keymap}
292 Keymap used within overlays:
293 \\{iedit-mode-occurrence-keymap}"
d2684ee7 »
2012-08-06 Remove duplicate definations
294 (interactive "P")
295 (if iedit-mode
296 (progn
297 (iedit-mode-on-action arg)
298 (setq iedit-only-complete-symbol-global iedit-only-complete-symbol-local))
299 (iedit-barf-if-lib-active)
300 (let (occurrence
301 complete-symbol
302 (beg (if (eq major-mode 'occur-edit-mode) ; skip the first occurrence
303 (next-single-char-property-change 1 'read-only)
304 (point-min)))
305 (end (point-max)))
306 (cond ((and arg
307 (= 4 (prefix-numeric-value arg))
308 iedit-last-occurrence-local)
309 (setq occurrence iedit-last-occurrence-local)
310 (setq complete-symbol iedit-only-complete-symbol-local))
311 ((and arg
312 (= 16 (prefix-numeric-value arg))
313 iedit-last-initial-string-global)
314 (setq occurrence iedit-last-initial-string-global)
315 (setq complete-symbol iedit-only-complete-symbol-global))
316 ((iedit-region-active)
317 (setq occurrence (buffer-substring-no-properties
318 (mark) (point))))
319 ((and isearch-mode (not (string= isearch-string "")))
320 (setq occurrence (buffer-substring-no-properties
321 (point) isearch-other-end))
322 (isearch-exit))
323 ((and iedit-current-symbol-default (current-word t))
324 (setq occurrence (current-word))
325 (when iedit-only-at-symbol-boundaries
326 (setq complete-symbol t)))
327 (t (error "No candidate of the occurrence, cannot enable Iedit mode")))
328 (when arg
329 (if (= 0 (prefix-numeric-value arg))
330 (save-excursion
331 (mark-defun)
332 (setq beg (region-beginning))
333 (setq end (region-end)))
334 (when (iedit-region-active)
335 (setq beg (region-beginning))
336 (setq end (region-end)))))
337 (setq iedit-only-complete-symbol-local complete-symbol)
8ff86b76 »
2012-08-31 Replace "(set-mark nil)" with (setq mark-active nil)"
338 (setq mark-active nil)
339 (run-hooks 'deactivate-mark-hook)
d2684ee7 »
2012-08-06 Remove duplicate definations
340 (setq iedit-case-sensitive-local iedit-case-sensitive-default)
341 (iedit-start occurrence beg end))))
342
343 (defun iedit-start (occurrence-exp beg end)
344 "Start Iedit mode for the OCCURRENCE-EXP in the current buffer."
345 (setq iedit-unmatched-lines-invisible iedit-unmatched-lines-invisible-default)
346 (setq iedit-initial-string-local occurrence-exp)
347 (iedit-refresh occurrence-exp beg end)
348 (run-hooks 'iedit-mode-hook)
349 (add-hook 'kbd-macro-termination-hook 'iedit-done nil t)
350 (add-hook 'change-major-mode-hook 'iedit-done nil t)
351 (add-hook 'iedit-aborting-hook 'iedit-done nil t))
352
353 (defun iedit-refresh (occurrence-exp beg end)
354 "Refresh Iedit mode."
355 (setq occurrence-exp (regexp-quote occurrence-exp))
29869ebe »
2012-09-05 Add buffer local variable iedit-occurrence-keymap
356 (setq iedit-occurrence-keymap iedit-mode-occurrence-keymap)
d2684ee7 »
2012-08-06 Remove duplicate definations
357 (when iedit-only-complete-symbol-local
358 (setq occurrence-exp (concat "\\_<" occurrence-exp "\\_>")))
359 (setq iedit-mode
360 (propertize
361 (concat " Iedit:"
362 (number-to-string
29869ebe »
2012-09-05 Add buffer local variable iedit-occurrence-keymap
363 (iedit-make-occurrences-overlays occurrence-exp beg end)))
d2684ee7 »
2012-08-06 Remove duplicate definations
364 'face
365 'font-lock-warning-face))
366 (force-mode-line-update))
367
368 (defun iedit-done ()
369 "Exit Iedit mode.
370 Save the current occurrence string locally and globally. Save
371 the initial string globally."
372 (when iedit-buffering
373 (iedit-stop-buffering))
374 (setq iedit-last-occurrence-local (iedit-current-occurrence-string))
375 (setq iedit-last-occurrence-global iedit-last-occurrence-local)
376 (setq iedit-last-initial-string-global iedit-initial-string-local)
377 (setq iedit-case-sensitive-global iedit-case-sensitive-local)
378
1c410fc2 »
2012-08-10 Fix iedit-number-occurrences with conjointed occurrences problem
379 (iedit-cleanup)
d2684ee7 »
2012-08-06 Remove duplicate definations
380
381 (setq iedit-mode nil)
382 (force-mode-line-update)
383 (remove-hook 'kbd-macro-termination-hook 'iedit-done t)
384 (remove-hook 'change-major-mode-hook 'iedit-done t)
385 (remove-hook 'iedit-aborting-hook 'iedit-done t)
386 (run-hooks 'iedit-mode-end-hook))
387
388 (defun iedit-mode-on-action (&optional arg)
389 "Turn off Iedit mode or restrict it in a region if region is active."
390 (if (iedit-region-active)
391 ;; Restrict iedit-mode
392 (let ((beg (region-beginning))
393 (end (region-end)))
394 (if (null (iedit-find-overlay beg end 'iedit-occurrence-overlay-name arg))
395 (iedit-done)
396 (iedit-restrict-region beg end arg)
397 (iedit-first-occurrence)))
398 (iedit-done)))
399
400
401 ;;;###autoload
402 (defun iedit-mode-toggle-on-function ()
403 "Toggle Iedit mode on current function."
404 (interactive)
405 (iedit-mode 0))
406
407 (defun iedit-execute-last-modification (&optional arg)
408 "Apply last modification in Iedit mode to the current buffer or an active region."
409 (interactive "*P")
410 (or (and iedit-last-initial-string-global
411 (not (string= iedit-last-initial-string-global iedit-last-occurrence-global)))
412 (error "No modification available"))
413 (let ((occurrence-exp (regexp-quote iedit-last-initial-string-global))
414 (replacement iedit-last-occurrence-global)
415 (case-fold-search (not iedit-case-sensitive-global))
416 beg end)
417 (when case-fold-search
418 (setq occurrence-exp (downcase occurrence-exp))
419 (setq replacement (downcase replacement)))
420 (if iedit-only-complete-symbol-global
421 (setq occurrence-exp (concat "\\_<" occurrence-exp "\\_>")))
422 (when (iedit-region-active)
423 (setq beg (region-beginning))
424 (setq end (region-end)))
425 (perform-replace occurrence-exp replacement t t nil nil nil beg end)))
426
427 (defun iedit-apply-global-modification ()
428 "Apply last global modification."
429 (interactive "*")
430 (if (and iedit-last-initial-string-global
431 (string= iedit-initial-string-local iedit-last-initial-string-global)
432 (not (string= iedit-last-initial-string-global iedit-last-occurrence-global)))
433 (iedit-replace-occurrences iedit-last-occurrence-global)
434 (message "No global modification available.")))
435
436
437 (defun iedit-restrict-function(&optional arg)
438 "Restricting Iedit mode in current function."
439 (interactive "P")
440 (save-excursion
441 (mark-defun)
76eeb87b »
2012-08-10 Add feekback message for iedit-restrict-function
442 (iedit-restrict-region (region-beginning) (region-end) arg))
443 (message "Restricted in current function, %d matches."
444 (length iedit-occurrences-overlays)))
d2684ee7 »
2012-08-06 Remove duplicate definations
445
446 (defun iedit-restrict-region (beg end &optional inclusive)
447 "Restricting Iedit mode in a region."
448 (when iedit-buffering
449 (iedit-stop-buffering))
450 (setq iedit-last-occurrence-local (iedit-current-occurrence-string))
8ff86b76 »
2012-08-31 Replace "(set-mark nil)" with (setq mark-active nil)"
451 (setq mark-active nil)
452 (run-hooks 'deactivate-mark-hook)
d2684ee7 »
2012-08-06 Remove duplicate definations
453 (iedit-show-all)
454 (iedit-cleanup-occurrences-overlays beg end inclusive)
455 (if iedit-unmatched-lines-invisible
456 (iedit-hide-unmatched-lines iedit-occurrence-context-lines))
457 (setq iedit-mode (propertize
458 (concat " Iedit:" (number-to-string
459 (length iedit-occurrences-overlays)))
460 'face 'font-lock-warning-face))
461 (force-mode-line-update))
462
463 (defun iedit-toggle-case-sensitive ()
464 "Toggle case-sensitive matching occurrences.
465 Todo: how about region"
466 (interactive)
467 (setq iedit-case-sensitive-local (not iedit-case-sensitive-local))
468 (if iedit-buffering
469 (iedit-stop-buffering))
470 (setq iedit-last-occurrence-local (iedit-current-occurrence-string))
471 (when iedit-last-occurrence-local
472 (remove-overlays nil nil iedit-occurrence-overlay-name t)
473 (iedit-show-all)
474 (iedit-refresh iedit-last-occurrence-local (point-min) (point-max))))
475
476 (provide 'iedit)
477
478 ;;; iedit.el ends here
479
480 ;; LocalWords: iedit el MERCHANTABILITY kbd isearch todo ert Lindberg Tassilo
6f80f881 »
2012-08-24 Differentiate lib keymap and occurrence keymap
481 ;; LocalWords: eval defgroup defcustom boolean defvar assq alist nconc
d2684ee7 »
2012-08-06 Remove duplicate definations
482 ;; LocalWords: substring cadr keymap defconst purecopy bkm defun princ prev
483 ;; LocalWords: iso lefttab backtab upcase downcase concat setq autoload arg
484 ;; LocalWords: refactoring propertize cond goto nreverse progn rotatef eq elp
485 ;; LocalWords: dolist pos unmatch args ov sReplace iedit's cdr quote'ed
Something went wrong with that request. Please try again.