diff options
author | Lev Lamberov <dogsleg@debian.org> | 2016-11-14 09:06:19 -0400 |
---|---|---|
committer | Lev Lamberov <dogsleg@debian.org> | 2016-11-14 09:06:19 -0400 |
commit | db7a88e7431ff3fdc853961fee3c54c39523d8c9 (patch) | |
tree | 32806837e3e590cc9be231deb851b70a9cfdafb3 |
Import iedit_0.9.9.9.orig.tar.gz
[dgit import orig iedit_0.9.9.9.orig.tar.gz]
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | README.org | 51 | ||||
-rw-r--r-- | iedit-lib.el | 948 | ||||
-rw-r--r-- | iedit-rect.el | 188 | ||||
-rw-r--r-- | iedit-tests.el | 709 | ||||
-rw-r--r-- | iedit.el | 766 | ||||
-rwxr-xr-x | release.sh | 22 |
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" |