summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLev Lamberov <dogsleg@debian.org>2016-11-14 09:06:19 -0400
committerLev Lamberov <dogsleg@debian.org>2016-11-14 09:06:19 -0400
commitdb7a88e7431ff3fdc853961fee3c54c39523d8c9 (patch)
tree32806837e3e590cc9be231deb851b70a9cfdafb3
Import iedit_0.9.9.9.orig.tar.gz
[dgit import orig iedit_0.9.9.9.orig.tar.gz]
-rw-r--r--.gitignore3
-rw-r--r--README.org51
-rw-r--r--iedit-lib.el948
-rw-r--r--iedit-rect.el188
-rw-r--r--iedit-tests.el709
-rw-r--r--iedit.el766
-rwxr-xr-xrelease.sh22
7 files changed, 2687 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..180f445
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.elc
+.bzr
+.bzrignore
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..c9c309e
--- /dev/null
+++ b/README.org
@@ -0,0 +1,51 @@
+* Iedit - Edit multiple regions in the same way simultaneously
+
+This package includes Emacs minor modes (iedit-mode and
+iedit-rectangle-mode) based on a API library (iedit-lib) 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, with visual
+feedback as you type.
+
+Normal scenario of Iedit mode is like:
+
+ - Highlight certain contents - by press C-; (The default key binding) All
+ occurrences of a symbol, string or a region in the buffer 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 convinient 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 occurrences 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
+
+ - Restricting the search area to just the current line can be done by
+ pressing M-I.
+
+ - Restricting the search area to the lines near the current line can
+ be done by pressing M-{ and M-}. These will expand the search
+ region one line at a time from the top and bottom. Add a prefix
+ argument to go the opposite direction.
+
+Iedit-rectangle-mode provides rectangle support with *visible rectangle*
+highlighting, which is similar with cua mode rectangle support. But it's
+lighter weight and uses iedit mechanisms.
+
+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.
diff --git a/iedit-lib.el b/iedit-lib.el
new file mode 100644
index 0000000..555133e
--- /dev/null
+++ b/iedit-lib.el
@@ -0,0 +1,948 @@
+;;; iedit-lib.el --- APIs for editing multiple regions in the same way
+;;; simultaneously.
+
+;; Copyright (C) 2010, 2011, 2012 Victor Ren
+
+;; Time-stamp: <2016-06-24 14:02:51 Victor Ren>
+;; Author: Victor Ren <victorhge@gmail.com>
+;; Keywords: occurrence region simultaneous rectangle refactoring
+;; Version: 0.9.9
+;; X-URL: http://www.emacswiki.org/emacs/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 iedit APIs library that allow you to write your own minor mode.
+;; The functionalities of the APIs:
+;; - Create occurrence overlays
+;; - Navigate in the occurrence overlays
+;; - Modify the occurrences
+;; - Hide/unhide
+;; - Other basic support APIs
+
+;;; todo:
+;; - Update comments for APIs
+;; - Add more easy access keys for whole occurrence
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(defgroup iedit nil
+ "Edit multiple regions in the same way simultaneously."
+ :prefix "iedit-"
+ :group 'replace
+ :group 'convenience)
+
+(defface iedit-occurrence
+ '((t :inherit highlight))
+ "*Face used for the occurrences' default values."
+ :group 'iedit)
+
+(defface iedit-read-only-occurrence
+ '((t :inherit region))
+ "*Face used for the read-only occurrences' default values."
+ :group 'iedit)
+
+(defcustom iedit-case-sensitive-default t
+ "If no-nil, matching is case sensitive."
+ :type 'boolean
+ :group 'iedit)
+
+(defcustom iedit-transient-mark-sensitive t
+ "If no-nil, Iedit mode is sensitive to the Transient Mark mode.
+It means Iedit works as expected only when regions are
+highlighted. If you want to use iedit without Transient Mark
+mode, set it as nil."
+ :type 'boolean
+ :group 'iedit)
+
+(defcustom iedit-overlay-priority 200
+ "The priority of the overlay used to indicate matches."
+ :type 'integer
+ :group 'iedit)
+
+(defvar iedit-occurrences-overlays nil
+ "The occurrences slot contains a list of overlays used to
+indicate the position of each editable occurrence. In addition, the
+occurrence overlay is used to provide a different face
+configurable via `iedit-occurrence'.")
+
+(defvar iedit-read-only-occurrences-overlays nil
+ "The occurrences slot contains a list of overlays used to
+indicate the position of each read-only occurrence. In addition, the
+occurrence overlay is used to provide a different face
+configurable via `iedit-ready-only-occurrence'.")
+
+(defvar iedit-case-sensitive iedit-case-sensitive-default
+ "This is buffer local variable.
+If no-nil, matching is case sensitive.")
+
+(defvar iedit-unmatched-lines-invisible nil
+ "This is buffer local variable which indicates whether
+unmatched lines are hided.")
+
+(defvar iedit-forward-success t
+ "This is buffer local variable which indicates the moving
+forward or backward successful")
+
+(defvar iedit-before-modification-string ""
+ "This is buffer local variable which is the buffer substring
+that is going to be changed.")
+
+(defvar iedit-before-modification-undo-list nil
+ "This is buffer local variable which is the buffer undo list before modification.")
+
+;; `iedit-update-occurrences' gets called twice when change==0 and
+;; occurrence is zero-width (beg==end) -- for front and back insertion.
+(defvar iedit-skip-modification-once t
+ "Variable used to skip first modification hook run when
+insertion against a zero-width occurrence.")
+
+(defvar iedit-aborting nil
+ "This is buffer local variable which indicates Iedit mode is aborting.")
+
+(defvar iedit-aborting-hook nil
+ "Functions to call before iedit-abort. Normally it should be mode exit function.")
+
+(defvar iedit-post-undo-hook-installed nil
+ "This is buffer local variable which indicated if
+`iedit-post-undo' is installed in `post-command-hook'.")
+
+(defvar iedit-buffering nil
+ "This is buffer local variable which indicates iedit-mode is
+buffering, which means the modification to the current occurrence
+is not applied to other occurrences when it is true.")
+
+(defvar iedit-occurrence-context-lines 1
+ "The number of lines before or after the occurrence.")
+
+(make-variable-buffer-local 'iedit-occurrences-overlays)
+(make-variable-buffer-local 'iedit-read-only-occurrences-overlays)
+(make-variable-buffer-local 'iedit-unmatched-lines-invisible)
+(make-local-variable 'iedit-case-sensitive)
+(make-variable-buffer-local 'iedit-forward-success)
+(make-variable-buffer-local 'iedit-before-modification-string)
+(make-variable-buffer-local 'iedit-before-modification-undo-list)
+(make-variable-buffer-local 'iedit-skip-modification-once)
+(make-variable-buffer-local 'iedit-aborting)
+(make-variable-buffer-local 'iedit-buffering)
+(make-variable-buffer-local 'iedit-post-undo-hook-installed)
+(make-variable-buffer-local 'iedit-occurrence-context-lines)
+
+(defconst iedit-occurrence-overlay-name 'iedit-occurrence-overlay-name)
+(defconst iedit-invisible-overlay-name 'iedit-invisible-overlay-name)
+
+;;; Define Iedit mode map
+(defvar iedit-lib-keymap
+ (let ((map (make-sparse-keymap)))
+ ;; Default key bindings
+ (define-key map (kbd "TAB") 'iedit-next-occurrence)
+ (define-key map (kbd "<tab>") 'iedit-next-occurrence)
+ (define-key map (kbd "<S-tab>") 'iedit-prev-occurrence)
+ (define-key map (kbd "<S-iso-lefttab>") 'iedit-prev-occurrence)
+ (define-key map (kbd "<backtab>") 'iedit-prev-occurrence)
+ (define-key map (kbd "C-'") 'iedit-toggle-unmatched-lines-visible)
+ map)
+ "Keymap used while Iedit mode is enabled.")
+
+(defvar iedit-occurrence-keymap-default
+ (let ((map (make-sparse-keymap)))
+;; (set-keymap-parent map iedit-lib-keymap)
+ (define-key map (kbd "M-U") 'iedit-upcase-occurrences)
+ (define-key map (kbd "M-L") 'iedit-downcase-occurrences)
+ (define-key map (kbd "M-R") 'iedit-replace-occurrences)
+ (define-key map (kbd "M-SPC") 'iedit-blank-occurrences)
+ (define-key map (kbd "M-D") 'iedit-delete-occurrences)
+ (define-key map (kbd "M-N") 'iedit-number-occurrences)
+ (define-key map (kbd "M-B") 'iedit-toggle-buffering)
+ (define-key map (kbd "M-<") 'iedit-goto-first-occurrence)
+ (define-key map (kbd "M->") 'iedit-goto-last-occurrence)
+ (define-key map (kbd "C-?") 'iedit-help-for-occurrences)
+ (define-key map [remap keyboard-escape-quit] 'iedit-quit)
+ (define-key map [remap keyboard-quit] 'iedit-quit)
+ map)
+ "Default keymap used within occurrence overlays.")
+
+(defvar iedit-occurrence-keymap 'iedit-occurrence-keymap-default
+ "Keymap used within occurrence overlays.
+It should be set before occurrence overlay is created.")
+(make-local-variable 'iedit-occurrence-keymap)
+
+(defun iedit-help-for-occurrences ()
+ "Display `iedit-occurrence-keymap-default'"
+ (interactive)
+ (message (concat (substitute-command-keys "\\[iedit-upcase-occurrences]") "/"
+ (substitute-command-keys "\\[iedit-downcase-occurrences]") ":up/downcase "
+ (substitute-command-keys "\\[iedit-replace-occurrences]") ":replace "
+ (substitute-command-keys "\\[iedit-blank-occurrences]") ":blank "
+ (substitute-command-keys "\\[iedit-delete-occurrences]") ":delete "
+ (substitute-command-keys "\\[iedit-number-occurrences]") ":number "
+ (substitute-command-keys "\\[iedit-toggle-buffering]") ":buffering "
+ (substitute-command-keys "\\[iedit-goto-first-occurrence]") "/"
+ (substitute-command-keys "\\[iedit-goto-last-occurrence]") ":first/last "
+ )))
+
+(defun iedit-quit ()
+ "Quit the current mode."
+ (interactive)
+ (run-hooks 'iedit-aborting-hook))
+
+(defun iedit-make-markers-overlays (markers)
+ "Create occurrence overlays on a list of markers."
+ (setq iedit-occurrences-overlays
+ (mapcar #'(lambda (marker)
+ (iedit-make-occurrence-overlay (car marker) (cdr marker)))
+ markers)))
+
+(defun iedit-make-occurrences-overlays (occurrence-regexp beg end)
+ "Create occurrence overlays for `occurrence-regexp' in a region.
+Return the number of occurrences."
+ (setq iedit-aborting nil)
+ (setq iedit-occurrences-overlays nil)
+ (setq iedit-read-only-occurrences-overlays nil)
+ ;; Find and record each occurrence's markers and add the overlay to the occurrences
+ (let ((counter 0)
+ (case-fold-search (not iedit-case-sensitive)))
+ (save-excursion
+ (save-window-excursion
+ (goto-char end)
+ ;; todo: figure out why re-search-forward is slow without "recenter"
+ (recenter)
+ (goto-char beg)
+ (while (re-search-forward occurrence-regexp end t)
+ (let ((beginning (match-beginning 0))
+ (ending (match-end 0)))
+ (if (text-property-not-all beginning ending 'read-only nil)
+ (push (iedit-make-read-only-occurrence-overlay beginning ending)
+ iedit-read-only-occurrences-overlays)
+ (push (iedit-make-occurrence-overlay beginning ending)
+ iedit-occurrences-overlays))
+ (setq counter (1+ counter))))))
+ counter))
+
+(defun iedit-add-next-occurrence-overlay (occurrence-exp &optional point)
+ "Create next occurrence overlay for `occurrence-exp'."
+ (iedit-add-occurrence-overlay occurrence-exp point t))
+
+(defun iedit-add-previous-occurrence-overlay (occurrence-exp &optional point)
+ "Create previous occurrence overlay for `occurrence-exp'."
+ (iedit-add-occurrence-overlay occurrence-exp point nil))
+
+(defun iedit-add-occurrence-overlay (occurrence-exp point forward &optional bound)
+ "Create next or previous occurrence overlay for `occurrence-exp'.
+Return the start position of the new occurrence if successful."
+ (or point
+ (setq point (point)))
+ (let ((case-fold-search (not iedit-case-sensitive))
+ (pos nil))
+ (save-excursion
+ (goto-char point)
+ (if (not (if forward
+ (re-search-forward occurrence-exp bound t)
+ (re-search-backward occurrence-exp bound t)))
+ (message "No more matches.")
+ (setq pos (match-beginning 0))
+ (if (or (iedit-find-overlay-at-point (match-beginning 0) 'iedit-occurrence-overlay-name)
+ (iedit-find-overlay-at-point (match-end 0) 'iedit-occurrence-overlay-name))
+ (error "Conflict region"))
+ (push (iedit-make-occurrence-overlay (match-beginning 0)
+ (match-end 0))
+ iedit-occurrences-overlays)
+ (message "Add one match for \"%s\"." (iedit-printable occurrence-exp))
+ (when iedit-unmatched-lines-invisible
+ (iedit-show-all)
+ (iedit-hide-unmatched-lines iedit-occurrence-context-lines))
+ ))
+ pos))
+
+(defun iedit-add-region-as-occurrence (beg end)
+ "Add region as an occurrence.
+The length of the region must the same as other occurrences if
+there are."
+ (or (= beg end)
+ (error "No region"))
+ (if (null iedit-occurrences-overlays)
+ (push
+ (iedit-make-occurrence-overlay beg end)
+ iedit-occurrences-overlays)
+ (or (= (- end beg) (iedit-occurrence-string-length))
+ (error "Wrong region"))
+ (if (or (iedit-find-overlay-at-point beg 'iedit-occurrence-overlay-name)
+ (iedit-find-overlay-at-point end 'iedit-occurrence-overlay-name))
+ (error "Conflict region"))
+ (push (iedit-make-occurrence-overlay beg end)
+ iedit-occurrences-overlays)
+ )) ;; todo test this function
+
+(defun iedit-cleanup ()
+ "Clean up occurrence overlay, invisible overlay and local variables."
+ (remove-overlays nil nil iedit-occurrence-overlay-name t)
+ (iedit-show-all)
+ (setq iedit-occurrences-overlays nil)
+ (setq iedit-read-only-occurrences-overlays nil)
+ (setq iedit-aborting nil)
+ (setq iedit-before-modification-string "")
+ (setq iedit-before-modification-undo-list nil))
+
+(defun iedit-make-occurrence-overlay (begin end)
+ "Create an overlay for an occurrence in Iedit mode.
+Add the properties for the overlay: a face used to display a
+occurrence's default value, and modification hooks to update
+occurrences if the user starts typing."
+ (let ((occurrence (make-overlay begin end (current-buffer) nil t)))
+ (overlay-put occurrence iedit-occurrence-overlay-name t)
+ (overlay-put occurrence 'face 'iedit-occurrence)
+ (overlay-put occurrence 'keymap iedit-occurrence-keymap)
+ (overlay-put occurrence 'insert-in-front-hooks '(iedit-update-occurrences))
+ (overlay-put occurrence 'insert-behind-hooks '(iedit-update-occurrences))
+ (overlay-put occurrence 'modification-hooks '(iedit-update-occurrences))
+ (overlay-put occurrence 'priority iedit-overlay-priority)
+ occurrence))
+
+(defun iedit-make-read-only-occurrence-overlay (begin end)
+ "Create an overlay for an read-only occurrence in Iedit mode."
+ (let ((occurrence (make-overlay begin end (current-buffer) nil t)))
+ (overlay-put occurrence iedit-occurrence-overlay-name t)
+ (overlay-put occurrence 'face 'iedit-read-only-occurrence)
+ occurrence))
+
+(defun iedit-make-unmatched-lines-overlay (begin end)
+ "Create an overlay for lines between two occurrences in Iedit mode."
+ (let ((unmatched-lines-overlay (make-overlay begin end (current-buffer) nil t)))
+ (overlay-put unmatched-lines-overlay iedit-invisible-overlay-name t)
+ (overlay-put unmatched-lines-overlay 'invisible 'iedit-invisible-overlay-name)
+ ;; (overlay-put unmatched-lines-overlay 'intangible t)
+ unmatched-lines-overlay))
+
+(defun iedit-post-undo ()
+ "Check if it is time to abort iedit after undo command is executed.
+
+This is added to `post-command-hook' when undo command is executed
+in occurrences."
+ (if (iedit-same-length)
+ nil
+ (run-hooks 'iedit-aborting-hook))
+ (remove-hook 'post-command-hook 'iedit-post-undo t)
+ (setq iedit-post-undo-hook-installed nil))
+
+(defun iedit-reset-aborting ()
+ "Turning Iedit mode off and reset `iedit-aborting'.
+
+This is added to `post-command-hook' when aborting Iedit mode is
+decided. `iedit-aborting-hook' is postponed after the current
+command is executed for avoiding `iedit-update-occurrences'
+is called for a removed overlay."
+ (run-hooks 'iedit-aborting-hook)
+ (remove-hook 'post-command-hook 'iedit-reset-aborting t)
+ (setq iedit-aborting nil))
+
+;; There are two ways to update all occurrences. One is to redefine all key
+;; stroke map for overlay, the other is to figure out three basic modification
+;; in the modification hook. This function chooses the latter.
+(defun iedit-update-occurrences (occurrence after beg end &optional change)
+ "Update all occurrences.
+This modification hook is triggered when a user edits any
+occurrence and is responsible for updating all other
+occurrences. Refer to `modification-hooks' for more details.
+Current supported edits are insertion, yank, deletion and
+replacement. If this modification is going out of the
+occurrence, it will abort Iedit mode."
+ (if undo-in-progress
+ ;; If the "undo" change make occurrences different, it is going to mess up
+ ;; occurrences. So a check will be done after undo command is executed.
+ (when (not iedit-post-undo-hook-installed)
+ (add-hook 'post-command-hook 'iedit-post-undo nil t)
+ (setq iedit-post-undo-hook-installed t))
+ (when (not iedit-aborting)
+ ;; before modification
+ (if (null after)
+ (if (or (< beg (overlay-start occurrence))
+ (> end (overlay-end occurrence)))
+ (progn (setq iedit-aborting t) ; abort iedit-mode
+ (add-hook 'post-command-hook 'iedit-reset-aborting nil t))
+ (setq iedit-before-modification-string
+ (buffer-substring-no-properties beg end))
+ ;; Check if this is called twice before modification. When inserting
+ ;; into zero-width occurrence or between two conjoined occurrences,
+ ;; both insert-in-front-hooks and insert-behind-hooks will be
+ ;; called. Two calls will make `iedit-skip-modification-once' true.
+ (setq iedit-skip-modification-once (not iedit-skip-modification-once)))
+ ;; after modification
+ (when (not iedit-buffering)
+ (if iedit-skip-modification-once
+ ;; Skip the first hook
+ (setq iedit-skip-modification-once nil)
+ (setq iedit-skip-modification-once t)
+ (when (or (eq 0 change) ;; insertion
+ (eq beg end) ;; deletion
+ (not (string= iedit-before-modification-string
+ (buffer-substring-no-properties beg end))))
+ (iedit-update-occurrences-2 occurrence after beg end change))))))))
+
+(defun iedit-update-occurrences-2 (occurrence after beg end &optional change)
+ ""
+ (let ((inhibit-modification-hooks t)
+ (offset (- beg (overlay-start occurrence)))
+ (value (buffer-substring-no-properties beg end)))
+ (save-excursion
+ ;; insertion or yank
+ (if (= 0 change)
+ (dolist (another-occurrence iedit-occurrences-overlays)
+ (let* ((beginning (+ (overlay-start another-occurrence) offset))
+ (ending (+ beginning (- end beg))))
+ (when (not (eq another-occurrence occurrence))
+ (goto-char beginning)
+ (insert-and-inherit value)
+ ;; todo: reconsider this change Quick fix for
+ ;; multi-occur occur-edit-mode: multi-occur depend on
+ ;; after-change-functions to update original
+ ;; buffer. Since inhibit-modification-hooks is set to
+ ;; non-nil, after-change-functions hooks are not going
+ ;; to be called for the changes of other occurrences.
+ ;; So run the hook here.
+ (run-hook-with-args 'after-change-functions
+ beginning
+ ending
+ change))
+ (iedit-move-conjoined-overlays another-occurrence)))
+ ;; deletion
+ (dolist (another-occurrence (remove occurrence iedit-occurrences-overlays))
+ (let ((beginning (+ (overlay-start another-occurrence) offset)))
+ (delete-region beginning (+ beginning change))
+ (unless (eq beg end) ;; replacement
+ (goto-char beginning)
+ (insert-and-inherit value))
+ (run-hook-with-args 'after-change-functions
+ beginning
+ (+ beginning (- beg end))
+ change)))))))
+
+(defun iedit-next-occurrence ()
+ "Move forward to the next occurrence in the `iedit'.
+If the point is already in the last occurrences, you are asked to type
+another `iedit-next-occurrence', it starts again from the
+beginning of the buffer."
+ (interactive)
+ (let ((pos (point))
+ (in-occurrence (get-char-property (point) 'iedit-occurrence-overlay-name)))
+ (when in-occurrence
+ (setq pos (next-single-char-property-change pos 'iedit-occurrence-overlay-name)))
+ (setq pos (next-single-char-property-change pos 'iedit-occurrence-overlay-name))
+ (if (/= pos (point-max))
+ (setq iedit-forward-success t)
+ (if (and iedit-forward-success in-occurrence)
+ (progn (message "This is the last occurrence.")
+ (setq iedit-forward-success nil))
+ (progn
+ (if (get-char-property (point-min) 'iedit-occurrence-overlay-name)
+ (setq pos (point-min))
+ (setq pos (next-single-char-property-change
+ (point-min)
+ 'iedit-occurrence-overlay-name)))
+ (setq iedit-forward-success t)
+ (message "Located the first occurrence."))))
+ (when iedit-forward-success
+ (goto-char pos))))
+
+(defun iedit-prev-occurrence ()
+ "Move backward to the previous occurrence in the `iedit'.
+If the point is already in the first occurrences, you are asked to type
+another `iedit-prev-occurrence', it starts again from the end of
+the buffer."
+ (interactive)
+ (let ((pos (point))
+ (in-occurrence (get-char-property (point) 'iedit-occurrence-overlay-name)))
+ (when in-occurrence
+ (setq pos (previous-single-char-property-change pos 'iedit-occurrence-overlay-name)))
+ (setq pos (previous-single-char-property-change pos 'iedit-occurrence-overlay-name))
+ ;; At the start of the first occurrence
+ (if (or (and (eq pos (point-min))
+ (not (get-char-property (point-min) 'iedit-occurrence-overlay-name)))
+ (and (eq (point) (point-min))
+ in-occurrence))
+ (if (and iedit-forward-success in-occurrence)
+ (progn (message "This is the first occurrence.")
+ (setq iedit-forward-success nil))
+ (progn
+ (setq pos (previous-single-char-property-change (point-max) 'iedit-occurrence-overlay-name))
+ (if (not (get-char-property (- (point-max) 1) 'iedit-occurrence-overlay-name))
+ (setq pos (previous-single-char-property-change pos 'iedit-occurrence-overlay-name)))
+ (setq iedit-forward-success t)
+ (message "Located the last occurrence.")))
+ (setq iedit-forward-success t))
+ (when iedit-forward-success
+ (goto-char pos))))
+
+(defun iedit-goto-first-occurrence ()
+ "Move to the first occurrence."
+ (interactive)
+ (goto-char (iedit-first-occurrence))
+ (setq iedit-forward-success t)
+ (message "Located the first occurrence."))
+
+(defun iedit-first-occurrence ()
+ "return the position of the first occurrence."
+ (if (get-char-property (point-min) 'iedit-occurrence-overlay-name)
+ (point-min)
+ (next-single-char-property-change
+ (point-min) 'iedit-occurrence-overlay-name)))
+
+(defun iedit-goto-last-occurrence ()
+ "Move to the last occurrence."
+ (interactive)
+ (goto-char (iedit-last-occurrence))
+ (setq iedit-forward-success t)
+ (message "Located the last occurrence."))
+
+(defun iedit-last-occurrence ()
+ "return the position of the last occurrence."
+ (let ((pos (previous-single-char-property-change (point-max) 'iedit-occurrence-overlay-name)))
+ (if (not (get-char-property (- (point-max) 1) 'iedit-occurrence-overlay-name))
+ (setq pos (previous-single-char-property-change pos 'iedit-occurrence-overlay-name)))
+ pos))
+
+(defun iedit-toggle-unmatched-lines-visible (&optional arg)
+ "Toggle whether to display unmatched lines.
+A prefix ARG specifies how many lines before and after the
+occurrences are not hided; negative is treated the same as zero.
+
+If no prefix argument, the prefix argument last time or default
+value of `iedit-occurrence-context-lines' is used for this time."
+ (interactive "P")
+ (if (null arg)
+ ;; toggle visible
+ (progn (setq iedit-unmatched-lines-invisible (not iedit-unmatched-lines-invisible))
+ (if iedit-unmatched-lines-invisible
+ (iedit-hide-unmatched-lines iedit-occurrence-context-lines)
+ (iedit-show-all)))
+ ;; reset invisible lines
+ (setq arg (prefix-numeric-value arg))
+ (if (< arg 0)
+ (setq arg 0))
+ (unless (and iedit-unmatched-lines-invisible
+ (= arg iedit-occurrence-context-lines))
+ (when iedit-unmatched-lines-invisible
+ (remove-overlays nil nil iedit-invisible-overlay-name t))
+ (setq iedit-occurrence-context-lines arg)
+ (setq iedit-unmatched-lines-invisible t)
+ (iedit-hide-unmatched-lines iedit-occurrence-context-lines))))
+
+(defun iedit-show-all()
+ "Show hided lines."
+ (setq line-move-ignore-invisible nil)
+ (remove-from-invisibility-spec '(iedit-invisible-overlay-name . t))
+ (remove-overlays nil nil iedit-invisible-overlay-name t))
+
+(defun iedit-hide-unmatched-lines (context-lines)
+ "Hide unmatched lines using invisible overlay."
+ (let ((prev-occurrence-end 1)
+ (unmatched-lines nil))
+ (save-excursion
+ (goto-char (iedit-first-occurrence))
+ (while (/= (point) (point-max))
+ ;; Now at the beginning of an occurrence
+ (let ((current-start (point)))
+ (forward-line (- context-lines))
+ (let ((line-beginning (line-beginning-position)))
+ (if (> line-beginning prev-occurrence-end)
+ (push (list prev-occurrence-end (1- line-beginning)) unmatched-lines)))
+ ;; goto the end of the occurrence
+ (goto-char (next-single-char-property-change current-start 'iedit-occurrence-overlay-name)))
+ (let ((current-end (point)))
+ (forward-line context-lines)
+ (setq prev-occurrence-end (1+ (line-end-position)))
+ ;; goto the beginning of next occurrence
+ (goto-char (next-single-char-property-change current-end 'iedit-occurrence-overlay-name))))
+ (if (< prev-occurrence-end (point-max))
+ (push (list prev-occurrence-end (point-max)) unmatched-lines))
+ (when unmatched-lines
+ (set (make-local-variable 'line-move-ignore-invisible) t)
+ (add-to-invisibility-spec '(iedit-invisible-overlay-name . t))
+ (dolist (unmatch unmatched-lines)
+ (iedit-make-unmatched-lines-overlay (car unmatch) (cadr unmatch)))))
+ unmatched-lines))
+
+;;;; functions for overlay keymap
+(defun iedit-apply-on-occurrences (function &rest args)
+ "Call function for each occurrence."
+ (let ((inhibit-modification-hooks t))
+ (save-excursion
+ (dolist (occurrence iedit-occurrences-overlays)
+ (apply function (overlay-start occurrence) (overlay-end occurrence) args)))))
+
+(defun iedit-upcase-occurrences ()
+ "Covert occurrences to upper case."
+ (interactive "*")
+ (iedit-barf-if-buffering)
+ (iedit-apply-on-occurrences 'upcase-region))
+
+(when (require 'multiple-cursors-core nil t)
+ (defun iedit-switch-to-mc-mode ()
+ "Switch to multiple-cursors-mode. So that you can navigate
+out of the occurrence and edit simutaneously with multiple
+cursors."
+ (interactive "*")
+ (iedit-barf-if-buffering)
+ (let* ((ov (iedit-find-current-occurrence-overlay))
+ (offset (- (point) (overlay-start ov)))
+ (master (point)))
+ (mc/save-excursion
+ (dolist (occurrence iedit-occurrences-overlays)
+ (goto-char (+ (overlay-start occurrence) offset))
+ (unless (= master (point))
+ (mc/create-fake-cursor-at-point))
+ ))
+ (multiple-cursors-mode 1)
+ (run-hooks 'iedit-aborting-hook)))
+
+ (define-key iedit-occurrence-keymap-default (kbd "M-M") 'iedit-switch-to-mc-mode))
+
+(defun iedit-downcase-occurrences()
+ "Covert occurrences to lower case."
+ (interactive "*")
+ (iedit-barf-if-buffering)
+ (iedit-apply-on-occurrences 'downcase-region))
+
+(defun iedit-replace-occurrences(&optional to-string)
+ "Replace occurrences with STRING.
+This function preserves case."
+ (interactive "*")
+ (iedit-barf-if-buffering)
+ (let* ((ov (iedit-find-current-occurrence-overlay))
+ (offset (- (point) (overlay-start ov)))
+ (from-string (buffer-substring-no-properties
+ (overlay-start ov)
+ (overlay-end ov)))
+ (from-string-lowercase (downcase from-string))
+ (to-string (if (not to-string)
+ (read-string "Replace with: "
+ nil nil
+ from-string
+ nil)
+ to-string)))
+ (iedit-apply-on-occurrences
+ (lambda (beg end from-string-lowercase to-string)
+ (goto-char beg)
+ (search-forward from-string-lowercase end)
+ (replace-match to-string nil))
+ from-string-lowercase to-string)
+ (goto-char (+ (overlay-start ov) offset))))
+
+(defun iedit-blank-occurrences()
+ "Replace occurrences with blank spaces."
+ (interactive "*")
+ (iedit-barf-if-buffering)
+ (let* ((ov (car iedit-occurrences-overlays))
+ (offset (- (point) (overlay-start ov)))
+ (count (- (overlay-end ov) (overlay-start ov))))
+ (iedit-apply-on-occurrences
+ (lambda (beg end )
+ (delete-region beg end)
+ (goto-char beg)
+ (insert-and-inherit (make-string count 32))))
+ (goto-char (+ (overlay-start ov) offset))))
+
+(defun iedit-delete-occurrences()
+ "Delete occurrences."
+ (interactive "*")
+ (iedit-barf-if-buffering)
+ (iedit-apply-on-occurrences 'delete-region))
+
+;; todo: add cancel buffering function
+(defun iedit-toggle-buffering ()
+ "Toggle buffering.
+This is intended to improve iedit's response time. If the number
+of occurrences are huge, it might be slow to update all the
+occurrences for each key stoke. When buffering is on,
+modification is only applied to the current occurrence and will
+be applied to other occurrences when buffering is off."
+ (interactive "*")
+ (if iedit-buffering
+ (iedit-stop-buffering)
+ (iedit-start-buffering))
+ (message (concat "Modification Buffering "
+ (if iedit-buffering
+ "started."
+ "stopped."))))
+
+(defun iedit-start-buffering ()
+ "Start buffering."
+ (setq iedit-buffering t)
+ (setq iedit-before-modification-string (iedit-current-occurrence-string))
+ (setq iedit-before-modification-undo-list buffer-undo-list)
+ (message "Start buffering editing..."))
+
+(defun iedit-stop-buffering ()
+ "Stop buffering and apply the modification to other occurrences.
+If current point is not at any occurrence, the buffered
+modification is not going to be applied to other occurrences."
+ (let ((ov (iedit-find-current-occurrence-overlay)))
+ (when ov
+ (let* ((beg (overlay-start ov))
+ (end (overlay-end ov))
+ (modified-string (buffer-substring-no-properties beg end))
+ (offset (- (point) beg)) ;; delete-region moves cursor
+ (inhibit-modification-hooks t))
+ (when (not (string= iedit-before-modification-string modified-string))
+ (save-excursion
+ ;; Rollback the current modification and buffer-undo-list. This is
+ ;; to avoid the inconsistency if user undoes modifications
+ (delete-region beg end)
+ (goto-char beg)
+ (insert-and-inherit iedit-before-modification-string)
+ (setq buffer-undo-list iedit-before-modification-undo-list)
+ (dolist (occurrence iedit-occurrences-overlays) ; todo:extract as a function
+ (let ((beginning (overlay-start occurrence))
+ (ending (overlay-end occurrence)))
+ (delete-region beginning ending)
+ (unless (eq beg end) ;; replacement
+ (goto-char beginning)
+ (insert-and-inherit modified-string))
+ (iedit-move-conjoined-overlays occurrence))))
+ (goto-char (+ (overlay-start ov) offset))))))
+ (setq iedit-buffering nil)
+ (message "Buffered modification applied.")
+ (setq iedit-before-modification-undo-list nil))
+
+(defun iedit-move-conjoined-overlays (occurrence)
+ "This function keeps overlays conjoined after modification.
+After modification, conjoined overlays may be overlapped."
+ (let ((beginning (overlay-start occurrence))
+ (ending (overlay-end occurrence)))
+ (unless (= beginning (point-min))
+ (let ((previous-overlay (iedit-find-overlay-at-point
+ (1- beginning)
+ 'iedit-occurrence-overlay-name)))
+ (if previous-overlay ; two conjoined occurrences
+ (move-overlay previous-overlay
+ (overlay-start previous-overlay)
+ beginning))))
+ (unless (= ending (point-max))
+ (let ((next-overlay (iedit-find-overlay-at-point
+ ending
+ 'iedit-occurrence-overlay-name)))
+ (if next-overlay ; two conjoined occurrences
+ (move-overlay next-overlay ending (overlay-end next-overlay)))))))
+
+(defvar iedit-number-line-counter 1
+ "Occurrence number for 'iedit-number-occurrences.")
+
+(defun iedit-default-occurrence-number-format (start-at)
+ (concat "%"
+ (int-to-string
+ (length (int-to-string
+ (1- (+ (length iedit-occurrences-overlays) start-at)))))
+ "d "))
+
+(defun iedit-number-occurrences (start-at &optional format-string)
+ "Insert numbers in front of the occurrences.
+START-AT, if non-nil, should be a number from which to begin
+counting. FORMAT, if non-nil, should be a format string to pass
+to `format-string' along with the line count. When called
+interactively with a prefix argument, prompt for START-AT and
+FORMAT."
+ (interactive
+ (if current-prefix-arg
+ (let* ((start-at (read-number "Number to count from: " 1)))
+ (list start-at
+ (read-string "Format string: "
+ (iedit-default-occurrence-number-format
+ start-at))))
+ (list 1 nil)))
+ (iedit-barf-if-buffering)
+ (unless format-string
+ (setq format-string (iedit-default-occurrence-number-format start-at)))
+ (let ((iedit-number-occurrence-counter start-at)
+ (inhibit-modification-hooks t))
+ (save-excursion
+ (goto-char (iedit-first-occurrence))
+ (while (/= (point) (point-max))
+ (insert (format format-string iedit-number-occurrence-counter))
+ (iedit-move-conjoined-overlays (iedit-find-current-occurrence-overlay))
+ (setq iedit-number-occurrence-counter
+ (1+ iedit-number-occurrence-counter))
+ (goto-char (next-single-char-property-change (point) 'iedit-occurrence-overlay-name))
+ (goto-char (next-single-char-property-change (point) 'iedit-occurrence-overlay-name))))))
+
+
+;;; help functions
+(defun iedit-find-current-occurrence-overlay ()
+ "Return the current occurrence overlay at point or point - 1.
+This function is supposed to be called in overlay keymap."
+ (or (iedit-find-overlay-at-point (point) 'iedit-occurrence-overlay-name)
+ (iedit-find-overlay-at-point (1- (point)) 'iedit-occurrence-overlay-name)))
+
+(defun iedit-find-overlay-at-point (point property)
+ "Return the overlay with PROPERTY at POINT."
+ (let ((overlays (overlays-at point))
+ found)
+ (while (and overlays (not found))
+ (let ((overlay (car overlays)))
+ (if (overlay-get overlay property)
+ (setq found overlay)
+ (setq overlays (cdr overlays)))))
+ found))
+
+(defun iedit-same-column ()
+ "Return t if all occurrences are at the same column."
+ (save-excursion
+ (let ((column (progn (goto-char (overlay-start (car iedit-occurrences-overlays)))
+ (current-column)))
+ (overlays (cdr iedit-occurrences-overlays))
+ (same t))
+ (while (and overlays same)
+ (let ((overlay (car overlays)))
+ (if (/= (progn (goto-char (overlay-start overlay))
+ (current-column))
+ column)
+ (setq same nil)
+ (setq overlays (cdr overlays)))))
+ same)))
+
+(defun iedit-same-length ()
+ "Return t if all occurrences are the same length."
+ (save-excursion
+ (let ((length (iedit-occurrence-string-length))
+ (overlays (cdr iedit-occurrences-overlays))
+ (same t))
+ (while (and overlays same)
+ (let ((ov (car overlays)))
+ (if (/= (- (overlay-end ov) (overlay-start ov))
+ length)
+ (setq same nil)
+ (setq overlays (cdr overlays)))))
+ same)))
+
+;; This function might be called out of any occurrence
+(defun iedit-current-occurrence-string ()
+ "Return current occurrence string.
+Return nil if occurrence string is empty string."
+ (let ((ov (or (iedit-find-current-occurrence-overlay)
+ (car iedit-occurrences-overlays))))
+ (if ov
+ (let ((beg (overlay-start ov))
+ (end (overlay-end ov)))
+ (if (/= beg end)
+ (buffer-substring-no-properties beg end)
+ nil))
+ nil)))
+
+(defun iedit-occurrence-string-length ()
+ "Return the length of current occurrence string."
+ (let ((ov (car iedit-occurrences-overlays)))
+ (- (overlay-end ov) (overlay-start ov))))
+
+(defun iedit-find-overlay (beg end property &optional exclusive)
+ "Return a overlay with property in region, or out of the region if EXCLUSIVE is not nil."
+ (if exclusive
+ (or (iedit-find-overlay-in-region (point-min) beg property)
+ (iedit-find-overlay-in-region end (point-max) property))
+ (iedit-find-overlay-in-region beg end property)))
+
+(defun iedit-find-overlay-in-region (beg end property)
+ "Return a overlay with property in region."
+ (let ((overlays (overlays-in beg end))
+ found)
+ (while (and overlays (not found))
+ (let ((overlay (car overlays)))
+ (if (and (overlay-get overlay property)
+ (>= (overlay-start overlay) beg)
+ (<= (overlay-end overlay) end))
+ (setq found overlay)
+ (setq overlays (cdr overlays)))))
+ found))
+
+(defun iedit-cleanup-occurrences-overlays (beg end &optional inclusive)
+ "Remove deleted overlays from list `iedit-occurrences-overlays'."
+ (if inclusive
+ (remove-overlays beg end iedit-occurrence-overlay-name t)
+ (remove-overlays (point-min) beg iedit-occurrence-overlay-name t)
+ (remove-overlays end (point-max) iedit-occurrence-overlay-name t))
+ (let (overlays)
+ (dolist (overlay iedit-occurrences-overlays)
+ (if (overlay-buffer overlay)
+ (push overlay overlays)))
+ (setq iedit-occurrences-overlays overlays)))
+
+(defun iedit-printable (string)
+ "Return a omitted substring that is not longer than 50.
+STRING is already `regexp-quote'ed"
+ (let ((first-newline-index (string-match "$" string))
+ (length (length string)))
+ (if (and first-newline-index
+ (/= first-newline-index length))
+ (if (< first-newline-index 50)
+ (concat (substring string 0 first-newline-index) "...")
+ (concat (substring string 0 50) "..."))
+ (if (> length 50)
+ (concat (substring string 0 50) "...")
+ string))))
+
+(defun iedit-char-at-bol (&optional N)
+ "Get char position of the beginning of the current line. If `N'
+is given, move forward (or backward) that many lines (using
+`forward-line') and get the char position at the beginning of
+that line."
+ (save-excursion
+ (forward-line (if N N 0))
+ (point)))
+
+(defun iedit-char-at-eol (&optional N)
+ "Get char position of the end of the current line. If `N' is
+given, move forward (or backward) that many lines (using
+`forward-line') and get the char position at the end of that
+line."
+ (save-excursion
+ (forward-line (if N N 0))
+ (end-of-line)
+ (point)))
+
+(defun iedit-region-active ()
+ "Return t if region is active and not empty.
+If variable `iedit-transient-mark-sensitive' is t, active region
+means `transient-mark-mode' is on and mark is active. Otherwise,
+it just means mark is active."
+ (and (if iedit-transient-mark-sensitive
+ transient-mark-mode
+ t)
+ mark-active
+ (not (equal (mark) (point)))))
+
+(defun iedit-barf-if-lib-active()
+ "Signal error if Iedit lib is active."
+ (or (and (null iedit-occurrences-overlays)
+ (null iedit-read-only-occurrences-overlays))
+ (error "Iedit lib is active")))
+
+(defun iedit-barf-if-buffering()
+ "Signal error if Iedit lib is buffering."
+ (or (null iedit-buffering)
+ (error "Iedit is buffering")))
+
+(provide 'iedit-lib)
+
+;;; iedit-lib.el ends here
+
+;; LocalWords: iedit el MERCHANTABILITY kbd isearch todo ert Lindberg Tassilo
+;; LocalWords: eval rect 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
diff --git a/iedit-rect.el b/iedit-rect.el
new file mode 100644
index 0000000..f12a632
--- /dev/null
+++ b/iedit-rect.el
@@ -0,0 +1,188 @@
+;;; iedit-rect.el --- visible rectangle editing support based on Iedit.
+
+;; Copyright (C) 2010, 2011, 2012 Victor Ren
+
+;; Time-stamp: <2016-09-28 00:03:47 Victor Ren>
+;; Author: Victor Ren <victorhge@gmail.com>
+;; Keywords: occurrence region simultaneous rectangle refactoring
+;; Version: 0.9.9
+;; X-URL: http://www.emacswiki.org/emacs/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 also provides rectangle support with *visible rectangle*
+;; highlighting, which is similar with cua-mode rectangle support. But it is
+;; lighter weight and uses iedit mechanisms.
+
+;; 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.
+
+;;; todo:
+;; - Add restrict function back
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(require 'rect) ;; kill-rectangle
+(require 'iedit-lib)
+
+(defvar iedit-rectangle-mode nil) ;; Name of the minor mode
+
+(make-variable-buffer-local 'iedit-rectangle-mode)
+(or (assq 'iedit-rectangle-mode minor-mode-alist)
+ (nconc minor-mode-alist
+ (list '(iedit-rectangle-mode iedit-rectangle-mode))))
+
+
+;;; Default key bindings:
+(when (null (where-is-internal 'iedit-rectangle-mode))
+ (let ((key-def (lookup-key ctl-x-r-map "\r")))
+ (if key-def
+ (display-warning 'iedit (format "Iedit rect default key %S is occupied by %s."
+ (key-description [C-x r RET])
+ key-def)
+ :warning)
+ (define-key ctl-x-r-map "\r" 'iedit-rectangle-mode)
+ (message "Iedit-rect default key binding is %s" (key-description [C-x r RET])))))
+
+(defvar iedit-rectangle nil
+ "This buffer local variable which is the rectangle geometry if
+current mode is iedit-rect. Otherwise it is nil.
+\(car iedit-rectangle) is the top-left corner and
+\(cadr iedit-rectangle) is the bottom-right corner" )
+
+(make-variable-buffer-local 'iedit-rectangle)
+
+;;; Define Iedit rect mode map
+(defvar iedit-rect-keymap
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map iedit-occurrence-keymap-default)
+ (define-key map (kbd "M-K") 'iedit-kill-rectangle)
+ map)
+ "Keymap used within overlays in Iedit-rect mode.")
+
+(or (assq 'iedit-rectangle-mode minor-mode-map-alist)
+ (setq minor-mode-map-alist
+ (cons (cons 'iedit-rectangle-mode iedit-lib-keymap) minor-mode-map-alist)))
+
+
+;; Avoid to restore Iedit-rect mode when restoring desktop
+(add-to-list 'desktop-minor-mode-handlers
+ '(iedit-rectangle-mode . nil))
+
+;;;###autoload
+(defun iedit-rectangle-mode (&optional beg end)
+ "Toggle Iedit-rect mode.
+
+When Iedit-rect mode is on, a rectangle is started with visible
+rectangle highlighting. Rectangle editing support is based on
+Iedit mechanism.
+
+Commands:
+\\{iedit-rect-keymap}"
+ (interactive (when (iedit-region-active)
+ (list (region-beginning)
+ (region-end))))
+
+ ;; enforce skip modification once, errors may happen to cause this to be
+ ;; unset.
+ (setq iedit-skip-modification-once t)
+ (if iedit-rectangle-mode
+ (iedit-rectangle-done)
+ (iedit-barf-if-lib-active)
+ (if (and beg end)
+ (progn (setq mark-active nil)
+ (run-hooks 'deactivate-mark-hook)
+ (iedit-rectangle-start beg end))
+ (error "no region available."))))
+
+(defun iedit-rectangle-start (beg end)
+ "Start Iedit mode for the region as a rectangle."
+ (barf-if-buffer-read-only)
+ (setq beg (copy-marker beg))
+ (setq end (copy-marker end t))
+ (setq iedit-occurrences-overlays nil)
+ (setq iedit-initial-string-local nil)
+ (setq iedit-occurrence-keymap iedit-rect-keymap)
+ (save-excursion
+ (let ((beg-col (progn (goto-char beg) (current-column)))
+ (end-col (progn (goto-char end) (current-column))))
+ (when (< end-col beg-col)
+ (rotatef beg-col end-col))
+ (goto-char beg)
+ (while
+ (progn
+ (push (iedit-make-occurrence-overlay
+ (progn
+ (move-to-column beg-col t)
+ (point))
+ (progn
+ (move-to-column end-col t)
+ (point)))
+ iedit-occurrences-overlays)
+ (and (< (point) end) (forward-line 1))))))
+ (setq iedit-rectangle (list beg end))
+ (setq iedit-rectangle-mode
+ (propertize
+ (concat " Iedit-rect:"
+ (number-to-string (length iedit-occurrences-overlays)))
+ 'face
+ 'font-lock-warning-face))
+ (force-mode-line-update)
+ (add-hook 'before-revert-hook 'iedit-rectangle-done nil t)
+ (add-hook 'kbd-macro-termination-hook 'iedit-rectangle-done nil t)
+ (add-hook 'change-major-mode-hook 'iedit-rectangle-done nil t)
+ (add-hook 'iedit-aborting-hook 'iedit-rectangle-done nil t))
+
+(defun iedit-rectangle-done ()
+ "Exit Iedit mode.
+Save the current occurrence string locally and globally. Save
+the initial string globally."
+ (when iedit-buffering
+ (iedit-stop-buffering))
+ (iedit-cleanup)
+ (setq iedit-rectangle-mode nil)
+ (force-mode-line-update)
+ (remove-hook 'before-revert-hook 'iedit-rectangle-done t)
+ (remove-hook 'kbd-macro-termination-hook 'iedit-rectangle-done t)
+ (remove-hook 'change-major-mode-hook 'iedit-rectangle-done t)
+ (remove-hook 'iedit-aborting-hook 'iedit-rectangle-done t))
+
+(defun iedit-kill-rectangle(&optional fill)
+ "Kill the rectangle.
+The behavior is the same as `kill-rectangle' in rect mode."
+ (interactive "*P")
+ (or (and iedit-rectangle (iedit-same-column))
+ (error "Not a rectangle"))
+ (let ((inhibit-modification-hooks t))
+ (kill-rectangle (marker-position (car iedit-rectangle))
+ (marker-position (cadr iedit-rectangle)) fill)))
+
+(provide 'iedit-rect)
+
+;;; iedit-rect.el ends here
+
+;; LocalWords: iedit el MERCHANTABILITY kbd isearch todo ert Lindberg Tassilo
+;; LocalWords: eval rect 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
diff --git a/iedit-tests.el b/iedit-tests.el
new file mode 100644
index 0000000..efb9c44
--- /dev/null
+++ b/iedit-tests.el
@@ -0,0 +1,709 @@
+;;; iedit-tests.el --- iedit's automatic-tests
+
+;; Copyright (C) 2010, 2011, 2012 Victor Ren
+
+;; Time-stamp: <2016-09-20 00:28:29 Victor Ren>
+;; Author: Victor Ren <victorhge@gmail.com>
+;; Version: 0.97
+;; X-URL: http://www.emacswiki.org/emacs/Iedit
+
+;; 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 file is part of iedit.
+
+;;; Code:
+(require 'ert)
+(require 'iedit)
+(require 'iedit-rect)
+
+(ert-deftest iedit-compile-test ()
+ (let ((byte-compile-error-on-warn t ))
+ (should (byte-compile-file "~/.emacs.d/site-lisp/iedit/iedit.el"))
+ (delete-file "~/.emacs.d/site-lisp/iedit/iedit.elc" nil)))
+
+(defmacro with-iedit-test-buffer (buffer-name &rest body)
+ (declare (indent 1) (debug t))
+ `(progn
+ (when (get-buffer ,buffer-name)
+ (kill-buffer ,buffer-name))
+ (with-current-buffer (get-buffer-create ,buffer-name)
+ ;; Give the current temp buffer a window. Otherwise `recenter' will
+ ;; trigger an error message.
+ (progn (set-window-buffer nil ,buffer-name)
+ ,@body))))
+
+(defun marker-position-list (l)
+ "convert list of markers to positions"
+ (mapcar (lambda (m) (marker-position m)) l))
+
+(defun goto-word (word &optional beginning)
+ (goto-char (point-min))
+ (search-forward word)
+ (when beginning
+ (goto-char (- (point) (length word)))))
+
+(defun goto-word-beginning (word)
+ (goto-word word t))
+
+
+(defun with-iedit-test-fixture (input-buffer-string body)
+ "iedit test fixture"
+ (let ((old-transient-mark-mode transient-mark-mode)
+ (old-iedit-transient-sensitive iedit-transient-mark-sensitive))
+ (unwind-protect
+ (progn
+ (with-iedit-test-buffer "* iedit transient mark *"
+ (transient-mark-mode t)
+ (setq iedit-transient-mark-sensitive t)
+ (insert input-buffer-string)
+ (goto-char 1)
+ (iedit-mode)
+ (funcall body))
+ (with-iedit-test-buffer "* iedit NO transient mark *"
+ (setq iedit-transient-mark-sensitive nil)
+ (transient-mark-mode -1)
+ (insert input-buffer-string)
+ (goto-char 1)
+ (iedit-mode)
+ (funcall body)))
+ (transient-mark-mode old-transient-mark-mode)
+ (setq iedit-transient-mark-sensitive old-transient-mark-mode))))
+
+(ert-deftest iedit-mode-base-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (should (= 3 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "foo"))
+ (set-mark-command nil)
+ (forward-line 2)
+ (iedit-mode)
+ (should (= 2 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "foo"))
+ (iedit-mode)
+ (should (null iedit-occurrences-overlays)))))
+
+(ert-deftest iedit-mode-with-region-test ()
+ (with-iedit-test-fixture
+"foobar
+ foo
+ foo
+ bar
+foo"
+ (lambda ()
+ (iedit-mode)
+ (goto-char 1)
+ (set-mark-command nil)
+ (forward-char 3)
+ (iedit-mode)
+ (should (= 4 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "foo"))
+ (should (eq 'selection iedit-occurrence-type-local))
+ (goto-char 1)
+ (set-mark-command nil)
+ (forward-line 3)
+ (iedit-mode 4)
+ (should (= 1 (length iedit-occurrences-overlays))))))
+
+(ert-deftest iedit-mode-with-tag-pair-test ()
+ (with-iedit-test-fixture
+ "<div> foo </div>
+<div> bar </div>
+<div> foobar </div>
+div
+foobar
+ foo
+ bar
+foo"
+ (lambda ()
+ (iedit-mode)
+ (goto-char 2)
+ (iedit-mode)
+ (should (= 2 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "div"))
+ ;; (should (eq 'tag iedit-occurrence-type-local))
+ (iedit-mode)
+ (sgml-electric-tag-pair-mode t)
+ (iedit-mode)
+ (should (= 3 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "<div>"))
+ (should (eq 'symbol iedit-occurrence-type-local))
+ (sgml-electric-tag-pair-mode))))
+
+(ert-deftest iedit-move-conjointed-overlays-test ()
+ (with-iedit-test-fixture
+"foobar
+ foofoofoo
+ foofoo
+ foo"
+ (lambda ()
+ (iedit-mode)
+ (goto-char 1)
+ (set-mark-command nil)
+ (forward-char 3)
+ (iedit-mode)
+ (should (= 7 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "foo"))
+ (should (eq 'selection iedit-occurrence-type-local))
+ (goto-char 1)
+ (insert "123")
+ (should (string= (buffer-string)
+"123foobar
+ 123foo123foo123foo
+ 123foo123foo
+ 123foo"))
+ (forward-char 3)
+ (insert "456")
+ (should (string= (buffer-string)
+"123foo456bar
+ 123foo456123foo456123foo456
+ 123foo456123foo456
+ 123foo456")))))
+
+(ert-deftest iedit-overlay-at-end-of-buffer ()
+ (with-iedit-test-fixture
+ "foo
+foo"
+ (lambda ()
+ (iedit-mode)
+ (highlight-changes-mode 1)
+ (goto-char (point-min))
+ (goto-char (point-at-eol))
+ (iedit-mode)
+ (delete-region (point) (1- (point)))
+ (should (string= (buffer-string)
+ "fo
+fo"))
+ (insert "b")
+ (should (string= (buffer-string)
+ "fob
+fob")))))
+
+(ert-deftest iedit-mode-start-from-isearch-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (should (= 3 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "foo"))
+ (iedit-mode)
+ (forward-line 2)
+ (isearch-mode t)
+ (isearch-process-search-char ?f)
+ (isearch-process-search-char ?o)
+ (isearch-process-search-char ?o)
+ (call-interactively 'iedit-mode-from-isearch)
+ (should (string= iedit-initial-string-local "foo"))
+ (should (= 4 (length iedit-occurrences-overlays)))
+ (iedit-mode)
+ (should (null iedit-occurrences-overlays)))))
+
+(ert-deftest iedit-mode-last-local-occurrence-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (should (= 3 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "foo"))
+ (iedit-mode)
+ (goto-char 15)
+ (iedit-mode 4) ; last local
+ (should (string= iedit-initial-string-local "foo"))
+ (should (= 3 (length iedit-occurrences-overlays))))))
+
+(ert-deftest iedit-mode-last-global-occurrence-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (should (= 3 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "foo"))
+ (iedit-mode)
+ (with-temp-buffer
+ (set-window-buffer nil (current-buffer))
+ (insert "bar foo foo")
+ (goto-char 1)
+ (iedit-mode 16)
+ (should (string= iedit-initial-string-local "foo"))
+ (should (= 2 (length iedit-occurrences-overlays)))))))
+
+(ert-deftest iedit-execute-last-modification-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (should (= 3 (length iedit-occurrences-overlays)))
+ (should (string= iedit-initial-string-local "foo"))
+ (iedit-mode)
+ (with-temp-buffer
+ (insert "bar foo foo")
+ (should-error (iedit-execute-last-modification))))))
+
+(ert-deftest iedit-movement-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo "
+ (lambda ()
+ (iedit-goto-last-occurrence)
+ (should (= (point) 24))
+ (iedit-goto-first-occurrence)
+ (should (= (point) 1))
+ (iedit-next-occurrence)
+ (should (= (point) 7))
+ (iedit-next-occurrence)
+ (should (= (point) 24))
+ (iedit-next-occurrence)
+ (should (= (point) 24)) ;; (should (string= (current-message) "This is the last occurrence."))
+ (iedit-next-occurrence)
+ (should (= (point) 1)) ;; (should (string= (current-message) "Located the first occurrence."))
+ (iedit-next-occurrence)
+ (should (= (point) 7))
+ (goto-char (point-max))
+ (iedit-prev-occurrence)
+ (should (= (point) 27))
+ (iedit-prev-occurrence)
+ (should (= (point) 24))
+ (iedit-prev-occurrence)
+ (should (= (point) 7))
+ (iedit-prev-occurrence)
+ (should (= (point) 1))
+ (iedit-prev-occurrence)
+ (should (= (point) 1)) ;; (should (string= (current-message) "This is the first occurrence."))
+ (iedit-prev-occurrence)
+ (should (= (point) 24)) ;; (should (string= (current-message) "Located the last occurrence."))
+ )))
+
+(ert-deftest iedit-occurrence-update-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (insert "1")
+ (should (string= (buffer-string)
+"1foo
+ 1foo
+ barfoo
+ 1foo"))
+ (backward-delete-char 1)
+ (should (string= (buffer-string)
+"foo
+ foo
+ barfoo
+ foo"))
+ (capitalize-word 1)
+ (should (string= (buffer-string)
+"Foo
+ Foo
+ barfoo
+ Foo"))
+ ;; test insert from empty
+ (iedit-delete-occurrences)
+ (insert "1")
+ (should (string= (buffer-string)
+"1
+ 1
+ barfoo
+ 1")))))
+
+(ert-deftest iedit-occurrence-update-with-read-only-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (iedit-mode)
+ (put-text-property 1 2 'read-only t)
+ (iedit-mode)
+ (goto-char 2)
+ (should-error (insert "1"))
+ (should (string= (buffer-string)
+"foo
+ foo
+ barfoo
+ foo"))
+ (goto-char 7)
+ (insert "1")
+ (should (string= (buffer-string)
+"foo
+ 1foo
+ barfoo
+ 1foo"))
+ )))
+
+(ert-deftest iedit-aborting-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (kill-region (point) (+ 4 (point)))
+ (should (string= (buffer-string)
+" foo
+ barfoo
+ foo")))))
+
+(ert-deftest iedit-toggle-case-sensitive-test ()
+ (with-iedit-test-fixture
+"foo
+ Foo
+ barfoo
+ foo"
+ (lambda ()
+ (should (= 2 (length iedit-occurrences-overlays)))
+ (iedit-toggle-case-sensitive)
+ (should (= 3 (length iedit-occurrences-overlays)))
+ (iedit-next-occurrence)
+ (iedit-toggle-case-sensitive)
+ (should (= 1 (length iedit-occurrences-overlays))))))
+
+(ert-deftest iedit-apply-on-occurrences-test ()
+ "Test functions deal with the whole occurrences"
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (iedit-upcase-occurrences)
+ (should (string= (buffer-string)
+"FOO
+ FOO
+ barfoo
+ FOO"))
+ (iedit-downcase-occurrences)
+ (should (string= (buffer-string)
+"foo
+ foo
+ barfoo
+ foo"))
+ (iedit-replace-occurrences "bar")
+ (should (string= (buffer-string)
+"bar
+ bar
+ barfoo
+ bar"))
+ (iedit-number-occurrences 1)
+ (should (string= (buffer-string)
+"1 bar
+ 2 bar
+ barfoo
+ 3 bar")))))
+
+(ert-deftest iedit-blank-occurrences-test ()
+ "Test functions deal with the whole occurrences"
+ (with-iedit-test-fixture
+"foo foo barfoo foo"
+ (lambda ()
+ (iedit-blank-occurrences)
+ (should (string= (buffer-string) " barfoo ")))))
+
+(ert-deftest iedit-blank-occurrences-rectangle-test ()
+ "Test functions deal with the whole occurrences"
+ (with-iedit-test-fixture
+"foo
+ foo barfoo foo"
+ (lambda ()
+ (iedit-mode) ; turn off iedit
+ (goto-char 2)
+ (set-mark-command nil)
+ (goto-char 7)
+ (call-interactively 'iedit-rectangle-mode)
+ (iedit-blank-occurrences)
+ (should (string= (buffer-string) "f o
+ oo barfoo foo")))))
+
+(ert-deftest iedit-delete-occurrences-test ()
+ "Test functions deal with the whole occurrences"
+ (with-iedit-test-fixture
+"foo foo barfoo foo"
+ (lambda ()
+ (iedit-delete-occurrences)
+ (should (string= (buffer-string) " barfoo ")))))
+
+(ert-deftest iedit-toggle-buffering-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (iedit-toggle-buffering)
+ (insert "bar")
+ (should (string= (buffer-string)
+"barfoo
+ foo
+ barfoo
+ foo"))
+ (iedit-toggle-buffering)
+ (should (string= (buffer-string)
+"barfoo
+ barfoo
+ barfoo
+ barfoo"))
+ (should (= (point) 4))
+ (iedit-toggle-buffering)
+ (backward-delete-char 3)
+ (should (string= (buffer-string)
+"foo
+ barfoo
+ barfoo
+ barfoo"))
+ (goto-char 15) ;not in an occurrence
+ (should (null (iedit-find-current-occurrence-overlay)))
+ (iedit-toggle-buffering)
+ (should (string= (buffer-string)
+"foo
+ barfoo
+ barfoo
+ barfoo")))))
+
+(ert-deftest iedit-rectangle-start-test ()
+ (with-iedit-test-fixture
+ "foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (iedit-mode)
+ (set-mark-command nil)
+ (forward-char 3)
+ (forward-line 3)
+ (call-interactively 'iedit-rectangle-mode)
+ (should (equal (marker-position-list iedit-rectangle) '(1 19)))
+ (call-interactively 'iedit-rectangle-mode)
+ (goto-char (point-min))
+ (set-mark-command nil)
+ (goto-char (point-max))
+ (call-interactively 'iedit-rectangle-mode)
+ (should (equal (marker-position-list iedit-rectangle) '(1 33))))))
+
+(ert-deftest iedit-kill-rectangle-error-test ()
+ (with-iedit-test-fixture
+ "foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (iedit-mode)
+ (set-mark-command nil)
+ (goto-char 22)
+ (call-interactively 'iedit-rectangle-mode)
+ (should (iedit-same-column))
+ (should (equal (marker-position-list iedit-rectangle) '(1 22)))
+ (iedit-prev-occurrence)
+ (delete-char -1)
+ (should (not (iedit-same-column)))
+ (should-error (iedit-kill-rectangle)))))
+
+(ert-deftest iedit-expand-to-occurrence-test ()
+ (with-iedit-test-fixture
+ "a a
+a a a
+a a a"
+ (lambda()
+ (goto-char 5)
+ (iedit-restrict-current-line)
+ (call-interactively 'iedit-expand-down-to-occurrence)
+ (should (equal (length iedit-occurrences-overlays) 4))
+ (should (= (point) 11))
+ (call-interactively 'iedit-expand-up-to-occurrence)
+ (should (equal (length iedit-occurrences-overlays) 5))
+ (should (= (point) 3))
+ (call-interactively 'iedit-expand-up-to-occurrence)
+ (call-interactively 'iedit-expand-up-to-occurrence)
+ (should (equal (length iedit-occurrences-overlays) 6))
+ (should (= (point) 1))
+ (call-interactively 'iedit-expand-down-to-occurrence)
+ (call-interactively 'iedit-expand-down-to-occurrence)
+ (call-interactively 'iedit-expand-down-to-occurrence)
+ (should (equal (length iedit-occurrences-overlays) 8))
+ (should (= (point) 15)))))
+
+(ert-deftest iedit-kill-rectangle-test ()
+ (with-iedit-test-fixture
+"foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (iedit-mode)
+ (set-mark-command nil)
+ (goto-char 22)
+ (call-interactively 'iedit-rectangle-mode)
+ (should (iedit-same-column))
+ (should (equal (marker-position-list iedit-rectangle) '(1 22)))
+ (iedit-kill-rectangle)
+ (should (string= (buffer-string)
+"
+o
+arfoo
+ foo"))
+ (should (equal killed-rectangle '("foo" " fo" " b" " "))))))
+
+(ert-deftest iedit-kill-rectangle-fill-extra-spaces ()
+ "lines within rectangle shorter than rectangle right column
+ should have spaces filled in."
+ (with-iedit-test-fixture
+ "foo
+ foo
+ barfoo
+ foo"
+ (lambda ()
+ (iedit-mode)
+ (setq indent-tabs-mode nil)
+ (set-mark-command nil)
+ (goto-word "barfoo")
+ (call-interactively 'iedit-rectangle-mode)
+ (should (iedit-same-column))
+ (should (equal '(1 27) (marker-position-list iedit-rectangle))))))
+
+(ert-deftest iedit-restrict-defun-test ()
+ (with-iedit-test-fixture
+"a
+(defun foo (foo bar foo)
+\"foo bar foobar\" nil)
+ (defun bar (bar foo bar)
+ \"bar foo barfoo\" nil)"
+ (lambda ()
+ (iedit-mode)
+ (emacs-lisp-mode)
+ (goto-char 5)
+ (iedit-mode)
+ (iedit-restrict-function)
+ (should (= 1 (length iedit-occurrences-overlays)))
+ (iedit-mode)
+ (goto-char 13)
+ (iedit-mode-toggle-on-function)
+ (should (= 4 (length iedit-occurrences-overlays)))
+ (iedit-mode)
+ (iedit-mode)
+ (mark-defun)
+ (iedit-mode)
+ (should (= 4 (length iedit-occurrences-overlays))))))
+
+(ert-deftest iedit-transient-sensitive-test ()
+ (with-iedit-test-fixture
+"a
+(defun foo (foo bar foo)
+\"foo bar foobar\" nil)
+ (defun bar (bar foo bar)
+ \"bar foo barfoo\" nil)"
+ (lambda ()
+ (iedit-mode)
+ (emacs-lisp-mode)
+ (setq iedit-transient-mark-sensitive t)
+ (transient-mark-mode -1)
+ (goto-char 5)
+ (iedit-mode)
+ (iedit-restrict-function)
+ (should (= 1 (length iedit-occurrences-overlays)))
+ (iedit-mode)
+ (goto-char 13)
+ (iedit-mode 0)
+ (should (= 4 (length iedit-occurrences-overlays)))
+ (iedit-mode) ;;turn off iedit mode
+ (iedit-mode)
+ (mark-defun)
+ (iedit-mode)
+ (should (= 0 (length iedit-occurrences-overlays))))))
+
+(defvar iedit-printable-test-lists
+ '(("" "")
+ ("abc" "abc")
+ ("abc
+bcd" "abc...")
+ ("abc\n34" "abc...")
+ ("12345678901234567890123456789012345678901234567890abc" "12345678901234567890123456789012345678901234567890...")
+ ("12345678901234567890123456789012345678901234567890abc
+abcd" "12345678901234567890123456789012345678901234567890...")))
+
+(ert-deftest iedit-printable-test ()
+ (dolist (test iedit-printable-test-lists)
+ (should (string= (iedit-printable (car test)) (cadr test)))))
+
+(ert-deftest iedit-hide-unmatched-lines-test ()
+ "Test function iedit-hide-unmatched-lines."
+ (with-iedit-test-fixture
+ "foo
+foo
+a
+ foo bar
+a
+a
+bar foo
+a
+a
+a
+bar foo
+a
+a
+a
+a
+ foo bar
+a
+a
+a
+a
+a
+foo"
+ (lambda ()
+ (should (equal (iedit-hide-unmatched-lines 0) '((64 73) (47 54) (33 38) (21 24) (9 10))))
+ (iedit-show-all)
+ (should (equal (iedit-hide-unmatched-lines 1) '((66 71) (49 52) (35 36))))
+ (iedit-show-all)
+ (should (equal (iedit-hide-unmatched-lines 2) '((68 69)) ))
+ (iedit-show-all)
+ (should (equal (iedit-hide-unmatched-lines 3) nil)))))
+
+;; todo add a auto performance test
+(setq elp-function-list '(;; insert-and-inherit
+ ;; delete-region
+ ;; goto-char
+ ;; iedit-occurrence-update
+ ;; buffer-substring-no-properties
+ ;; string=
+ re-search-forward
+ ;; replace-match
+ text-property-not-all
+ iedit-make-occurrence-overlay
+ iedit-make-occurrences-overlays
+ match-beginning
+ match-end
+ push
+ ))
+
+
+;;; iedit-tests.el ends here
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
diff --git a/release.sh b/release.sh
new file mode 100755
index 0000000..68c47d0
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# Create a tar file from the current sources for e.g. Marmalade.
+
+VERSION="$(sed -ne 's/^;; Version: *\([0-9.]*\)/\1/p' iedit.el)"
+
+mkdir -p release
+mkdir -p "release/iedit-$VERSION"
+cp *.el README.org "release/iedit-$VERSION"
+echo "(define-package \"iedit\" \"$VERSION\" \"Edit multiple regions in the same way simultaneously.\" '())" > "release/iedit-$VERSION/iedit-pkg.el"
+
+AUTOLOADS="$(pwd)/release/iedit-$VERSION/iedit-autoloads.el"
+emacs -q --batch --eval \
+ "(let ((generated-autoload-file \"$AUTOLOADS\"))
+ (batch-update-autoloads))" \
+ "release/iedit-$VERSION"
+rm -f "release/iedit-$VERSION"/*.el~
+tar -C release -c "iedit-$VERSION" > "release/iedit-${VERSION}.tar"
+rm -rf "release/iedit-$VERSION"
+
+echo
+echo "Release read for upload in release/iedit-${VERSION}.tar"