diff options
Diffstat (limited to 'iedit.el')
-rw-r--r-- | iedit.el | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/iedit.el b/iedit.el new file mode 100644 index 0000000..f44b9a3 --- /dev/null +++ b/iedit.el @@ -0,0 +1,766 @@ +;;; iedit.el --- Edit multiple regions in the same way simultaneously. + +;; Copyright (C) 2010, 2011, 2012 Victor Ren + +;; Time-stamp: <2016-09-20 00:04:51 Victor Ren> +;; Author: Victor Ren <victorhge@gmail.com> +;; Keywords: occurrence region simultaneous refactoring +;; Version: 0.9.9 +;; X-URL: http://www.emacswiki.org/emacs/Iedit +;; https://github.com/victorhge/iedit +;; Compatibility: GNU Emacs: 22.x, 23.x, 24.x + +;; This file is not part of GNU Emacs, but it is distributed under +;; the same terms as GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package is an Emacs minor mode and allows you to edit one occurrence of +;; some text in a buffer (possibly narrowed) or region, and simultaneously have +;; other occurrences edited in the same way. +;; +;; Normal scenario of iedit-mode is like: +;; +;; - Highlight certain contents - by press C-; (The default key binding) +;; All occurrences of a symbol, string in the buffer or a region may be +;; highlighted corresponding to current mark, point and prefix argument. +;; Refer to the document of `iedit-mode' for details. +;; +;; - Edit one of the occurrences +;; The change is applied to other occurrences simultaneously. +;; +;; - Finish - by pressing C-; again +;; +;; You can also use Iedit mode as a quick way to temporarily show only the +;; buffer lines that match the current text being edited. This gives you the +;; effect of a temporary `keep-lines' or `occur'. To get this effect, hit C-' +;; when in Iedit mode - it toggles hiding non-matching lines. +;; +;; Renaming refactoring is convenient in Iedit mode +;; +;; - The symbol under point is selected as occurrence by default and only +;; complete symbols are matched +;; - With digit prefix argument 0, only symbols in current function are matched +;; - Restricting symbols in current region can be done by pressing C-; again +;; - Last renaming refactoring is remembered and can be applied to other buffers +;; later +;; +;; There are also some other facilities you may never think about. Refer to the +;; document of function `iedit-mode' (C-h f iedit-mode RET) for more details. + +;; The code was developed and fully tested on Gnu Emacs 24.0.93, partially +;; tested on Gnu Emacs 22. If you have any compatible problem, please let me +;; know. + +;;; Contributors +;; Adam Lindberg <eproxus@gmail.com> added a case sensitivity option that can be toggled. + +;; Tassilo Horn <tassilo@member.fsf.org> added an option to match only complete +;; words, not inside words + +;; Le Wang <l26wang@gmail.com> proposed to match only complete symbols, not +;; inside symbols, contributed rectangle support + +;;; Code: + +(eval-when-compile + (require 'cl) + (require 'sgml-mode)) +(require 'iedit-lib) + +(defcustom iedit-toggle-key-default (kbd "C-;") + "If no-nil, the key is inserted into global-map, +isearch-mode-map, esc-map and help-map." + :type 'vector + :group 'iedit) + +(defvar iedit-mode-hook nil + "Function(s) to call after starting up an iedit.") + +(defvar iedit-mode-end-hook nil + "Function(s) to call after terminating an iedit.") + +(defvar iedit-mode nil) ;; Name of the minor mode + +(defvar iedit-use-symbol-boundaries t + "If no-nil, matches have to start and end at symbol boundaries. Otherwise, +matches starts and end at word bondaries.") + +(defvar iedit-occurrence-type-local 'symbol + "This is buffer local variable which indicates the occurrence +type. It might be (symbol word other).") + +(defvar iedit-occurrence-type-global 'symbol + "This is global variable which indicates the last global occurrence +type. It might be (symbol word other).") + +(defvar iedit-last-occurrence-local nil + "This is buffer local variable which is the occurrence when +Iedit mode is turned off last time.") + +(defvar iedit-last-occurrence-global nil + "This is global variable which is the occurrence when +Iedit mode is turned off last time.") + +(defvar iedit-last-initial-string-global nil + "This is a global variable which is the last initial occurrence string.") + +(defvar iedit-initial-string-local nil + "This is buffer local variable which is the initial string to start Iedit mode.") +(defvar iedit-initial-region nil + "This is buffer local variable which is the initial region +where Iedit mode is started from.") + +(defvar iedit-num-lines-to-expand-up 0 + "This is a global variable indicating how many lines up from +point should be included in the replacement region.") + +(defvar iedit-num-lines-to-expand-down 0 + "This is a global variable indicating how many lines down from +point should be included in the replacement region.") + +(defvar iedit-default-occurrence-local nil + "This is a function which returns a string as occurrence candidate. +It is called in `iedit-default-occurrence'. This buffer local +varialbe can be configured in some modes. An example of how to +use this variable: +(add-hook 'perl-mode-hook + '(lambda () + (setq iedit-default-occurrence-local + '(lambda () + (let* ((bound (bounds-of-thing-at-point 'symbol)) + (prefix-char (char-after (1- (car bound))))) + (if (memq prefix-char '(?$ ?% ?@ ?*)) + (progn + (setq iedit-occurrence-type-local 'regexp) + (concat (regexp-quote (buffer-substring-no-properties (1- (car bound)) (cdr bound))) \"\\\\_>\")) + (buffer-substring-no-properties (car bound) (cdr bound)))))))) +'$%@*' will be included in the occurrences in perl mode.") + +(make-variable-buffer-local 'iedit-mode) +(make-variable-buffer-local 'iedit-use-symbol-boundaries) +(make-variable-buffer-local 'iedit-occurrence-type-local) +(make-variable-buffer-local 'iedit-last-occurrence-local) +(make-variable-buffer-local 'iedit-initial-string-local) +(make-variable-buffer-local 'iedit-initial-region) +(make-variable-buffer-local 'iedit-default-occurrence-local) + +(or (assq 'iedit-mode minor-mode-alist) + (nconc minor-mode-alist + (list '(iedit-mode iedit-mode)))) + +;;; Define iedit help map. +(eval-when-compile (require 'help-macro)) + +(defvar iedit-help-map + (let ((map (make-sparse-keymap))) + (define-key map (vector (event-convert-list `(,help-char))) 'iedit-help-for-help) + (define-key map [help] 'iedit-help-for-help) + (define-key map [f1] 'iedit-help-for-help) + (define-key map "?" 'iedit-help-for-help) + (define-key map "b" 'iedit-describe-bindings) + (define-key map "k" 'iedit-describe-key) + (define-key map "m" 'iedit-describe-mode) + (define-key map "q" 'help-quit) + map) + "Keymap for characters following the Help key for Iedit mode.") + +(make-help-screen + iedit-help-for-help-internal + (purecopy "Type a help option: [bkm] or ?") + "You have typed %THIS-KEY%, the help character. Type a Help option: +\(Type \\<help-map>\\[help-quit] to exit the Help command.) + +b Display all Iedit key bindings. +k KEYS Display full documentation of Iedit key sequence. +m Display documentation of Iedit mode. + +You can't type here other help keys available in the global help map, +but outside of this help window when you type them in Iedit mode, +they exit Iedit mode before displaying global help." + iedit-help-map) + +(defun iedit-help-for-help () + "Display Iedit help menu." + (interactive) + (let (same-window-buffer-names same-window-regexps) + (iedit-help-for-help-internal))) + +(defun iedit-describe-bindings () + "Show a list of all keys defined in Iedit mode, and their definitions. +This is like `describe-bindings', but displays only Iedit keys." + (interactive) + (let (same-window-buffer-names + same-window-regexps + (keymap (substitute-command-keys "\\{iedit-mode-keymap}\\{iedit-mode-occurrence-keymap}"))) + (with-help-window "*Help*" + (with-current-buffer standard-output + (princ "Iedit Mode Bindings: ") + (princ keymap))))) + +(defun iedit-describe-key () + "Display documentation of the function invoked by Iedit mode key." + (interactive) + (let (same-window-buffer-names same-window-regexps) + (call-interactively 'describe-key))) + +(defun iedit-describe-mode () + "Display documentation of Iedit mode." + (interactive) + (let (same-window-buffer-names same-window-regexps) + (describe-function 'iedit-mode))) + +;;; Default key bindings: +(when (and iedit-toggle-key-default (null (where-is-internal 'iedit-mode))) + (let ((key-def (lookup-key (current-global-map) iedit-toggle-key-default))) + (if key-def + (display-warning 'iedit (format "Iedit default key %S is occupied by %s." + (key-description iedit-toggle-key-default) + key-def) + :warning) + (define-key global-map iedit-toggle-key-default 'iedit-mode) + (define-key isearch-mode-map iedit-toggle-key-default 'iedit-mode-from-isearch) + (define-key esc-map iedit-toggle-key-default 'iedit-execute-last-modification) + (define-key help-map iedit-toggle-key-default 'iedit-mode-toggle-on-function) + (message "Iedit default key binding is %s" (key-description iedit-toggle-key-default))))) + +;; Avoid to restore Iedit mode when restoring desktop +(add-to-list 'desktop-minor-mode-handlers + '(iedit-mode . nil)) + +;;; Define iedit help map. +(eval-when-compile (require 'help-macro)) + +(defvar iedit-mode-occurrence-keymap + (let ((map (make-sparse-keymap))) + (set-keymap-parent map iedit-occurrence-keymap-default) + (define-key map (kbd "M-H") 'iedit-restrict-function) + (define-key map (kbd "M-I") 'iedit-restrict-current-line) + (define-key map (kbd "M-{") 'iedit-expand-up-a-line) + (define-key map (kbd "M-}") 'iedit-expand-down-a-line) + (define-key map (kbd "M-p") 'iedit-expand-up-to-occurrence) + (define-key map (kbd "M-n") 'iedit-expand-down-to-occurrence) + (define-key map (kbd "M-G") 'iedit-apply-global-modification) + (define-key map (kbd "M-C") 'iedit-toggle-case-sensitive) + map) + "Keymap used within overlays in Iedit mode.") + +(defvar iedit-mode-keymap + (let ((map (make-sparse-keymap))) + (set-keymap-parent map iedit-lib-keymap) + (define-key map (vector (event-convert-list `(,help-char))) iedit-help-map) + (define-key map [help] iedit-help-map) + (define-key map [f1] iedit-help-map) + (define-key map (kbd "M-;") 'iedit-toggle-selection) + map) + "Keymap used while Iedit mode is enabled.") + +;;; Define Iedit mode map +(or (assq 'iedit-mode minor-mode-map-alist) + (setq minor-mode-map-alist + (cons (cons 'iedit-mode iedit-mode-keymap) minor-mode-map-alist))) + +;;;###autoload +(defun iedit-mode (&optional arg) + "Toggle Iedit mode. +This command behaves differently, depending on the mark, point, +prefix argument and variable `iedit-transient-mark-sensitive'. + +If Iedit mode is off, turn Iedit mode on. + +When Iedit mode is turned on, all the occurrences of the current +region in the buffer (possibly narrowed) or a region are +highlighted. If one occurrence is modified, the change are +propagated to all other occurrences simultaneously. + +If region is not active, `iedit-default-occurrence' is called to +get an occurrence candidate, according to the thing at point. It +might be url, email address, markup tag or current symbol(or +word) . + +In the above two situations, with digit prefix argument 0, only +occurrences in current function are matched. This is good for +renaming refactoring in programming. + +You can also switch to Iedit mode from isearch mode directly. The +current search string is used as occurrence. All occurrences of +the current search string are highlighted. + +With an universal prefix argument, the occurrence when Iedit mode +is turned off last time in current buffer is used as occurrence. +This is intended to recover last Iedit mode which is turned off. +If region active, Iedit mode is limited within the current +region. + +With repeated universal prefix argument, the occurrence when +Iedit mode is turned off last time (might be in other buffer) is +used as occurrence. If region active, Iedit mode is limited +within the current region. + +With digital prefix argument 1, Iedit mode is limited on the +current symbol or the active region, which means just one +instance is highlighted. This behavior serves as a start point +of incremental selection work flow. + +If Iedit mode is on and region is active, Iedit mode is +restricted in the region, e.g. the occurrences outside of the +region is excluded. + +If Iedit mode is on and region is active, with an universal +prefix argument, Iedit mode is restricted outside of the region, +e.g. the occurrences in the region is excluded. + +Turn off Iedit mode in other situations. + +Commands: +\\{iedit-mode-keymap} +Keymap used within overlays: +\\{iedit-mode-occurrence-keymap}" + (interactive "P") + (if iedit-mode + (iedit-mode-on-action arg) + (iedit-barf-if-lib-active) + (let (occurrence + (beg (if (eq major-mode 'occur-edit-mode) ; skip the first occurrence + (next-single-char-property-change 1 'read-only) + (point-min))) + (end (point-max))) + ;; Get the occurrence and iedit-occurrence-type-local + (cond ((and arg + (= 4 (prefix-numeric-value arg)) + iedit-last-occurrence-local) + (setq occurrence iedit-last-occurrence-local)) + ((and arg + (= 16 (prefix-numeric-value arg)) + iedit-last-initial-string-global) + (setq occurrence iedit-last-initial-string-global) + (setq iedit-occurrence-type-local iedit-occurrence-type-global)) + ((iedit-region-active) + (setq occurrence (buffer-substring-no-properties + (mark) (point))) + (setq iedit-occurrence-type-local 'selection)) + (t (setq occurrence (iedit-default-occurrence)) + (unless occurrence + (error "No candidate of the occurrence, cannot enable Iedit mode")))) + ;; Get the scope + (when arg + (cond ((= 0 (prefix-numeric-value arg)) + (save-excursion + (mark-defun) + (setq beg (region-beginning)) + (setq end (region-end)))) + ((and (= 1 (prefix-numeric-value arg)) + (not (iedit-region-active))) + (let ((region (bounds-of-thing-at-point 'symbol))) + (setq beg (car region)) + (setq end (cdr region)))) + ((iedit-region-active) + (setq beg (region-beginning)) + (setq end (region-end))))) + (setq mark-active nil) + (run-hooks 'deactivate-mark-hook) + (setq iedit-initial-string-local occurrence) + (iedit-start (iedit-regexp-quote occurrence) beg end) + (unless iedit-occurrences-overlays + ;; (message "No matches found for %s" (iedit-regexp-quote occurrence)) + (iedit-done))))) + +(defun iedit-mode-from-isearch (regexp) + "Start Iedit mode using last search string as the regexp." + (interactive + (let ((regexp (cond + ((functionp isearch-regexp-function) + (funcall isearch-regexp-function isearch-string)) + (isearch-regexp-function (word-search-regexp isearch-string)) + (isearch-regexp isearch-string) + (t (regexp-quote isearch-string))))) + (list regexp))) + (or isearch-success + (error "No match" )) + (if (or isearch-regexp isearch-regexp-function) + nil + (setq iedit-initial-string-local isearch-string)) + (let ((iedit-case-sensitive (not isearch-case-fold-search))) + (isearch-exit) + (setq mark-active nil) + (run-hooks 'deactivate-mark-hook) + (when iedit-mode + (iedit-cleanup)) + (iedit-start regexp (point-min) (point-max)) + ;; TODO: reconsider how to avoid the loop in iedit-same-length + (cond ((not iedit-occurrences-overlays) + (message "No matches found for %s" regexp) + (iedit-done)) + ((not (iedit-same-length)) + (message "Matches are not the same length.") + (iedit-done))))) + +(defun iedit-start (occurrence-regexp beg end) + "Start Iedit mode for the `occurrence-regexp' in the current buffer." + ;; enforce skip modification once, errors may happen to cause this to be + ;; unset. + (setq iedit-skip-modification-once t) + (setq iedit-initial-region (list beg end)) + (let ((counter 0)) + (if (eq iedit-occurrence-type-local 'markup-tag) + (progn + (setq iedit-occurrence-keymap iedit-occurrence-keymap-default) + (iedit-make-markers-overlays iedit-occurrences-overlays) + (setq counter 2)) + (setq iedit-occurrence-keymap iedit-mode-occurrence-keymap) + (setq counter (iedit-make-occurrences-overlays occurrence-regexp beg end))) + (message "%d matches for \"%s\"" + counter + (iedit-printable occurrence-regexp)) + (setq iedit-mode + (propertize + (concat " Iedit:" (number-to-string counter)) + 'face + 'font-lock-warning-face)) + (force-mode-line-update)) + (run-hooks 'iedit-mode-hook) + (add-hook 'before-revert-hook 'iedit-done nil t) + (add-hook 'kbd-macro-termination-hook 'iedit-done nil t) + (add-hook 'change-major-mode-hook 'iedit-done nil t) + (add-hook 'iedit-aborting-hook 'iedit-done nil t)) + +(defun iedit-default-occurrence() + "This function returns a string as occurrence candidate. +The candidate depends on the thing at point." + (let (occurrence-str) + (cond + ((thing-at-point 'url) + (setq occurrence-str (thing-at-point 'url)) + (setq iedit-occurrence-type-local 'url)) + + ((thing-at-point 'email) + (setq occurrence-str (thing-at-point 'email)) + (setq iedit-occurrence-type-local 'email)) + + (iedit-default-occurrence-local + (setq occurrence-str (funcall iedit-default-occurrence-local))) + ;; Try to mark sgml pair anyway + ((and (not (bound-and-true-p sgml-electric-tag-pair-mode)) + (setq occurrence-str (iedit-mark-sgml-pair))) + (setq iedit-occurrence-type-local 'markup-tag)) + + ((and iedit-use-symbol-boundaries ;option + (thing-at-point 'symbol)) + (setq occurrence-str (thing-at-point 'symbol)) + (setq iedit-occurrence-type-local 'symbol)) + + ((thing-at-point 'word) + (setq occurrence-str (thing-at-point 'word)) + (setq iedit-occurrence-type-local 'word))) + occurrence-str)) + +(defun iedit-regexp-quote (exp) + "Return a regexp string." + (cl-case iedit-occurrence-type-local + ('symbol (concat "\\_<" (regexp-quote exp) "\\_>")) + ('word (concat "\\<" (regexp-quote exp) "\\>")) + ('regexp exp) + ( t (regexp-quote exp)))) + +(defun iedit-mark-sgml-pair () + "Check if the cursor is on a markup tag. +If the cursor is on a markup tag, the postion of the opening and +closing markup tags are saved in `iedit-occurrence-overlays' +temporarily. + +The code is adpated from +`sgml-electric-tag-pair-before-change-function'. + +Return the tag if succeeded, nil if failed." + (condition-case err + (save-excursion + (skip-chars-backward "[:alnum:]-_.:") + (if (or (eq (char-before) ?<) + (and (eq (char-before) ?/) + (eq (char-before (1- (point))) ?<))) + (let* ((endp (eq (char-before) ?/)) + (cl-start (point)) + (cl-end (progn (skip-chars-forward "[:alnum:]-_.:") (point))) + (match + (if endp + (when (sgml-skip-tag-backward 1) (forward-char 1) t) + (with-syntax-table sgml-tag-syntax-table + (up-list -1) + (when (sgml-skip-tag-forward 1) + (backward-sexp 1) + (forward-char 2) + t))))) + (when (and match + (/= cl-end cl-start) + (equal (buffer-substring cl-start cl-end) + (buffer-substring (point) + (save-excursion + (skip-chars-forward "[:alnum:]-_.:") + (point)))) + (or (not endp) (eq (char-after cl-end) ?>))) + (push (cons cl-start cl-end) iedit-occurrences-overlays) + (push (cons (point) (+ (point) (- cl-end cl-start))) iedit-occurrences-overlays) + (buffer-substring cl-start cl-end))))) + (error nil))) + +(defun iedit-done () + "Exit Iedit mode. +Save the current occurrence string locally and globally. Save +the initial string globally." + (when iedit-buffering + (iedit-stop-buffering)) + (setq iedit-last-occurrence-local (iedit-current-occurrence-string)) + (setq iedit-occurrence-type-global iedit-occurrence-type-local) + (setq iedit-last-occurrence-global iedit-last-occurrence-local) + (setq iedit-last-initial-string-global iedit-initial-string-local) + (if iedit-last-occurrence-local + (kill-new iedit-last-occurrence-local)) ; Make occurrence the latest kill in the kill ring. + (setq iedit-num-lines-to-expand-up 0) + (setq iedit-num-lines-to-expand-down 0) + + (iedit-cleanup) + + (setq iedit-initial-string-local nil) + (setq iedit-mode nil) + (force-mode-line-update) + (remove-hook 'before-revert-hook 'iedit-done t) + (remove-hook 'kbd-macro-termination-hook 'iedit-done t) + (remove-hook 'change-major-mode-hook 'iedit-done t) + (remove-hook 'iedit-aborting-hook 'iedit-done t) + (run-hooks 'iedit-mode-end-hook)) + +(defun iedit-mode-on-action (&optional arg) + "Turn off Iedit mode or restrict it in a region if region is active." + (if (iedit-region-active) + (iedit-restrict-region (region-beginning) (region-end) arg) + (iedit-done))) + + +;;;###autoload +(defun iedit-mode-toggle-on-function () + "Toggle Iedit mode on current function." + (interactive) + (iedit-mode 0)) + +(defun iedit-execute-last-modification (&optional arg) + "Apply last modification in Iedit mode to the current buffer or an active region." + (interactive "*P") + (or (and iedit-last-initial-string-global + (not (string= iedit-last-initial-string-global iedit-last-occurrence-global))) + (error "No modification available")) + (let ((occurrence-exp (regexp-quote iedit-last-initial-string-global)) + (replacement iedit-last-occurrence-global) + (case-fold-search (not iedit-case-sensitive)) + beg end) + (when case-fold-search + (setq occurrence-exp (downcase occurrence-exp)) + (setq replacement (downcase replacement))) + ;; `iedit-regexp-quote' depends on iedit-occurrence-type-local + (setq iedit-occurrence-type-local iedit-occurrence-type-global) + (setq occurrence-exp (iedit-regexp-quote occurrence-exp)) + (when (iedit-region-active) + (setq beg (region-beginning)) + (setq end (region-end))) + (perform-replace occurrence-exp replacement t t nil nil nil beg end))) + +(defun iedit-apply-global-modification () + "Apply last global modification." + (interactive "*") + (if (and iedit-last-initial-string-global + (string= iedit-initial-string-local iedit-last-initial-string-global) + (not (string= iedit-last-initial-string-global iedit-last-occurrence-global))) + (iedit-replace-occurrences iedit-last-occurrence-global) + (message "No global modification available."))) + +(defun iedit-toggle-selection () + "Select or deselect the occurrence under point." + (interactive) + (iedit-barf-if-buffering) + (let ((ov (iedit-find-current-occurrence-overlay))) + (if ov + (iedit-restrict-region (overlay-start ov) (overlay-end ov) t) + (let ((current-occurrence-string (iedit-current-occurrence-string))) + (when (not (null current-occurrence-string)) + (save-excursion + (goto-char (if (> (point) (length current-occurrence-string)) + ( - (point) (length current-occurrence-string)) + (point-min))) + (iedit-add-next-occurrence-overlay + (iedit-regexp-quote current-occurrence-string))) + (setq iedit-mode (propertize + (concat " Iedit:" (number-to-string + (length iedit-occurrences-overlays))) + 'face 'font-lock-warning-face)) + (force-mode-line-update)))))) + +(defun iedit-restrict-function(&optional arg) + "Restricting Iedit mode in current function." + (interactive "P") + (save-excursion + (mark-defun) + (iedit-restrict-region (region-beginning) (region-end) arg)) + (message "Restricted in current function, %d matches." + (length iedit-occurrences-overlays))) + +(defun iedit-restrict-current-line () + "Restrict Iedit mode to current line." + (interactive) + (iedit-restrict-region (iedit-char-at-bol) (iedit-char-at-eol)) + (setq iedit-num-lines-to-expand-up 0 + iedit-num-lines-to-expand-down 0) + (message "Restricted to current line, %d match%s." + (length iedit-occurrences-overlays) + (if (= 1 (length iedit-occurrences-overlays)) "" "es"))) + +(defun iedit-expand-by-a-line (where amount) + "Expands the top or bottom of the search region upwards or +downwards by `amount' lines. The region being acted upon is +controlled with `where' ('top to act on the top, anything else +for the bottom). With a prefix, collapses the top or bottom of +the search region by `amount' lines." + (interactive "P") + (let ((occurrence (iedit-current-occurrence-string))) + (iedit-cleanup) + (if (eq where 'top) + (setq iedit-num-lines-to-expand-up + (max 0 (+ amount iedit-num-lines-to-expand-up))) + (setq iedit-num-lines-to-expand-down + (max 0 (+ amount iedit-num-lines-to-expand-down)))) + (iedit-start (iedit-regexp-quote occurrence) + (iedit-char-at-bol (- iedit-num-lines-to-expand-up)) + (iedit-char-at-eol iedit-num-lines-to-expand-down)) + (message "Now looking -%d/+%d lines around current line, %d match%s." + iedit-num-lines-to-expand-up + iedit-num-lines-to-expand-down + (length iedit-occurrences-overlays) + (if (= 1 (length iedit-occurrences-overlays)) "" "es")))) + +(defun iedit-expand-up-a-line (&optional arg) + "After start iedit-mode only on current symbol or the active +region, this function expands the search region upwards by one +line. With a prefix, bring the top of the region back down one +line." + (interactive "P") + (iedit-expand-by-a-line 'top + (if arg -1 1))) + +(defun iedit-expand-down-a-line (&optional arg) + "After start iedit-mode only on current symbol or the active +region, this function expands the search region downwards by one +line. With a prefix, bring the bottom of the region back up one +line." + (interactive "P") + (iedit-expand-by-a-line 'bottom + (if arg -1 1))) + +(defun iedit-expand-down-to-occurrence (&optional arg) + "Expand the search region downwards until reaching a new occurrence. +If no such occurrence can be found, throw an error. With a +prefix, bring the bottom of the region back up one occurrence." + (interactive "P") + (if arg + (progn (iedit-restrict-region + (iedit-first-occurrence) + (1- (iedit-last-occurrence))) + (when iedit-mode + (goto-char (iedit-last-occurrence)))) + (iedit-expand-to-occurrence t))) + +(defun iedit-expand-up-to-occurrence (&optional arg) + "Expand the search region upwards until reaching a new occurrence. +If no such occurrence can be found, throw an error. With a +prefix, bring the top of the region back down one occurrence." + (interactive "P") + (if arg + (progn (iedit-restrict-region + (+ (iedit-occurrence-string-length) (iedit-first-occurrence)) + (+ (iedit-occurrence-string-length) (iedit-last-occurrence))) + (when iedit-mode + (goto-char (iedit-first-occurrence)))) + (iedit-expand-to-occurrence nil))) + +(defun iedit-expand-to-occurrence (forward) + "Expand to next or previous occurrence." + (let ((pos (iedit-add-occurrence-overlay + (iedit-regexp-quote (iedit-current-occurrence-string)) + (if forward + (1+ (iedit-last-occurrence)) + (iedit-first-occurrence)) + forward))) + (when pos + (goto-char pos) + (setq iedit-mode (propertize + (concat " Iedit:" (number-to-string + (length iedit-occurrences-overlays))) + 'face 'font-lock-warning-face)) + (force-mode-line-update)))) + +(defun iedit-restrict-region (beg end &optional inclusive) + "Restricting Iedit mode in a region." + (if (null (iedit-find-overlay beg end 'iedit-occurrence-overlay-name inclusive)) + (iedit-done) + (when iedit-buffering + (iedit-stop-buffering)) + (setq iedit-last-occurrence-local (iedit-current-occurrence-string)) + (setq mark-active nil) + (run-hooks 'deactivate-mark-hook) + (iedit-show-all) + (iedit-cleanup-occurrences-overlays beg end inclusive) + (if iedit-unmatched-lines-invisible + (iedit-hide-unmatched-lines iedit-occurrence-context-lines)) + (setq iedit-mode (propertize + (concat " Iedit:" (number-to-string + (length iedit-occurrences-overlays))) + 'face 'font-lock-warning-face)) + (force-mode-line-update))) + +(defun iedit-toggle-case-sensitive () + "Toggle case-sensitive matching occurrences. " + (interactive) + (setq iedit-case-sensitive (not iedit-case-sensitive)) + (if iedit-buffering + (iedit-stop-buffering)) + (setq iedit-last-occurrence-local (iedit-current-occurrence-string)) + (when iedit-last-occurrence-local + (remove-overlays nil nil iedit-occurrence-overlay-name t) + (iedit-show-all) + (let* ((occurrence-regexp (iedit-regexp-quote iedit-last-occurrence-local)) + (begin (car iedit-initial-region)) + (end (cadr iedit-initial-region)) + (counter (iedit-make-occurrences-overlays occurrence-regexp begin end))) + (message "iedit %s. %d matches for \"%s\"" + (if iedit-case-sensitive + "is case sensitive" + "ignores case") + counter + (iedit-printable occurrence-regexp)) + (setq iedit-mode + (propertize + (concat " Iedit:" (number-to-string counter)) + 'face + 'font-lock-warning-face)) + (force-mode-line-update)))) + +(provide 'iedit) + +;;; iedit.el ends here + +;; LocalWords: iedit el MERCHANTABILITY kbd isearch todo ert Lindberg Tassilo +;; LocalWords: eval defgroup defcustom boolean defvar assq alist nconc +;; LocalWords: substring cadr keymap defconst purecopy bkm defun princ prev +;; LocalWords: iso lefttab backtab upcase downcase concat setq autoload arg +;; LocalWords: refactoring propertize cond goto nreverse progn rotatef eq elp +;; LocalWords: dolist pos unmatch args ov sReplace iedit's cdr quote'ed |