branch: elpa/evil-iedit-state commit 76a8967a41fc3dd0c9510ca3a34ec4034084076a Author: syl20bnr <sylvain.ben...@gmail.com> Commit: syl20bnr <sylvain.ben...@gmail.com>
Version 1.0 --- README.md | 111 ++++++++++++++++++++++++ evil-iedit-state.el | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) diff --git a/README.md b/README.md new file mode 100644 index 0000000000..a9ae03f654 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# evil-iedit-state + +<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc/generate-toc again --> +**Table of Contents** + +- [evil-iedit-state](#evil-iedit-state) + - [Install](#install) + - [Package manager](#package-manager) + - [Manually](#manually) + - [Key bindings](#key-bindings) + - [State transitions](#state-transitions) + - [In iedit state](#in-iedit-state) + - [In iedit-insert state](#in-iedit-insert-state) + +<!-- markdown-toc end --> + +This package adds two new evil states: +- iedit state +- iedit-insert state + +It has also a nice integration with [expand-region][] for quick edit +of the current selected text by pressing <kbd>e</kbd>. + +## Install + +### Package manager + +**will be available soon** + +You can either install `evil-iedit-state` from [MELPA][melpa-link]: + +``` + M-x package-install evil-iedit-state +``` + +Or add it to your `Cask` file: + +```elisp +(source melpa) + +(depends-on "evil-iedit-state") +``` + +### Manually + +Add `evil-iedit-state.el` to your load path. `evil-iedit-state` requires +both `iedit` and `evil` to be installed. + +## Key bindings + +### State transitions + + Key Binding | From | To +-------------------|:------------------:|:-------------------------: +<kbd>SPC s e</kbd> | normal or visual | iedit +<kbd>e</kbd> | expand-region | iedit +<kbd>ESC</kbd> | iedit | normal +<kbd>C-g</kbd> | iedit | normal +<kbd>fd</kbd> | iedit | normal +<kbd>ESC</kbd> | iedit-insert | iedit +<kbd>C-g</kbd> | iedit-insert | normal +<kbd>fd</kbd> | iedit-insert | normal + +To sum-up, in `iedit-insert state` you have to press <kbd>ESC</kbd> twice to +go back to the `normal state`. You can also at any time press <kbd>C-g</kbd> +or <kbd>fd</kbd> to return to `normal state`. + +**Note:** evil commands which switch to `insert state` will switch in +`iedit-insert state`. + +### In iedit state + +`iedit state` inherits from `normal state`, the following key bindings are +specific to `iedit state`. + + Key Binding | Description +------------------|------------------------------------------------------------ +<kbd>ESC</kbd> | go back to `normal state` +<kbd>TAB</kbd> | toggle current occurrence +<kbd>0</kbd> | go to the beginning of the current occurrence +<kbd>$</kbd> | go to the end of the current occurrence +<kbd>#</kbd> | prefix all occurrences with an increasing number (<kbd>C-u</kbd> to choose the starting number). +<kbd>A</kbd> | go to the end of the current occurrence and switch to `iedit-insert state` +<kbd>D</kbd> | delete the occurrences +<kbd>F</kbd> | restrict the scope to the function +<kbd>gg</kbd> | go to first occurrence +<kbd>G</kbd> | go to last occurrence +<kbd>I</kbd> | go to the beginning of the current occurrence and switch to `iedit-insert state` +<kbd>J</kbd> | increase the edition scope by one line below +<kbd>K</kbd> | increase the edition scope by one line above +<kbd>L</kbd> | restrict the scope to the current line +<kbd>n</kbd> | go to next occurrence +<kbd>N</kbd> | go to previous occurrence +<kbd>p</kbd> | replace occurrences with last yanked (copied) text +<kbd>S</kbd> | (substitute) delete the occurrences and switch to `iedit-insert state` +<kbd>V</kbd> | toggle visibility of lines with no occurrence +<kbd>U</kbd> | Up-case the occurrences +<kbd>C-U</kbd> | down-case the occurrences + +**Note:** <kbd>0</kbd>, <kbd>$</kbd>, <kbd>A</kbd> and <kbd>I</kbd> have the +default Vim behavior when used outside of an occurrence. + +### In iedit-insert state + + Key Binding | Description +---------------------------|------------------------------------------------------------ +<kbd>ESC</kbd> | go back to `iedit state` +<kbd>C-g</kbd> | go back to `normal state` + +[iedit]: https://github.com/tsdh/iedit +[expand-region]: https://github.com/magnars/expand-region.el diff --git a/evil-iedit-state.el b/evil-iedit-state.el new file mode 100644 index 0000000000..ca65cf5444 --- /dev/null +++ b/evil-iedit-state.el @@ -0,0 +1,238 @@ +;;; evil-iedit-state.el --- Evil states to interface iedit mode. + +;; Copyright (C) 2014 syl20bnr +;; +;; Author: Sylvain Benner <sylvain.ben...@gmail.com> +;; Keywords: convenience editing evil iedit mnemonic +;; Created: 12 Dec 2014 +;; Version: 1.0 +;; Package-Requires: ((evil "1.0.9") (iedit "0.97")) +;; URL: https://github.com/syl20bnr/evil-iedit-state + +;; This file is not part of GNU Emacs. + +;; This program 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. + +;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Adds two new Evil states `iedit' and `iedit insert' with expand-region +;; integration. + +;; For more info visit: https://github.com/syl20bnr/evil-iedit-state + +(require 'evil) +(require 'iedit) + +(evil-define-state iedit + "`iedit state' interfacing iedit mode." + :tag " <E> " + :enable (normal) + :cursor box + :message "-- IEDIT --" + ;; force iedit mode + (if (evil-replace-state-p) (call-interactively 'iedit-mode))) + +(evil-define-state iedit-insert + "Replace insert state in `iedit state'." + :tag " <Ei> " + :enable (insert) + :cursor (bar . 2) + :message "-- IEDIT-INSERT --") + +(defun evil-iedit-state/iedit-mode (&optional arg) + "Start `iedit-mode'." + (interactive "P") + (if (fboundp 'ahs-clear) (ahs-clear)) + (iedit-mode arg) + (evil-iedit-state)) + +(defun evil-iedit-state/quit-iedit-mode () + "Quit iedit-mode and return to `normal state'." + (interactive) + (iedit-done) + (evil-normal-state)) + +(defmacro evil-iedit-state||swith-to-insert-state-after-command (command &optional interactive) + "Call COMMAND and switch to iedit-insert state. +If INTERACTIVE is non-nil then COMMAND is called interactively." + `(progn + (if ,interactive + (call-interactively ',command) + (funcall ',command)) + ;; required to correctly update the cursors + (evil-iedit-state) + (evil-iedit-insert-state))) + +(defun evil-iedit-state//goto-overlay-start () + "Return the position of the start of the current overlay." + (let ((overlay (iedit-find-current-occurrence-overlay))) + (if overlay + (goto-char (overlay-start overlay)) + (call-interactively 'evil-digit-argument-or-evil-beginning-of-line)))) + +(defun evil-iedit-state//goto-overlay-end () + "Return the position of the end of the current overlay." + (let ((overlay (iedit-find-current-occurrence-overlay))) + (if overlay + (goto-char (overlay-end overlay)) + (call-interactively 'evil-end-of-line)))) + +(defun evil-iedit-state/evil-beginning-of-line (count) + "Go to the beginning of the current overlay." + (interactive "p") + (evil-iedit-state//goto-overlay-start)) + +(defun evil-iedit-state/evil-end-of-line () + "Go to the beginning of the current overlay." + (interactive) + (evil-iedit-state//goto-overlay-end)) + +(defun evil-iedit-state/evil-append-line () + "Put the point at then end of current overlay and switch to +`iedit-insert state'." + (interactive) + (evil-iedit-state||swith-to-insert-state-after-command + evil-iedit-state//goto-overlay-end)) + +(defun evil-iedit-state/evil-insert-line () + "Put the point at then end of current overlay and switch to +`iedit-insert state'." + (interactive) + (evil-iedit-state||swith-to-insert-state-after-command + evil-iedit-state//goto-overlay-start)) + +(defun evil-iedit-state/substitute () + "Wipe all the occurrences and switch in `iedit-insert state'" + (interactive) + (iedit-delete-occurrences) + (evil-iedit-insert-state)) + +(defun evil-iedit-state/evil-change () + "Wipe all the occurrences and switch in `iedit-insert state'" + (interactive) + (evil-iedit-state||swith-to-insert-state-after-command evil-change t)) + +(defun evil-iedit-state/evil-append () + "Append and switch to `iedit-insert state'" + (interactive) + (evil-iedit-state||swith-to-insert-state-after-command evil-append t)) + +(defun evil-iedit-state/evil-open-below () + "Insert new line below and switch to `iedit-insert state'" + (interactive) + (evil-iedit-state||swith-to-insert-state-after-command evil-open-below t)) + +(defun evil-iedit-state/evil-open-above () + "Insert new line above and switch to `iedit-insert state'" + (interactive) + (evil-iedit-state||swith-to-insert-state-after-command evil-open-above t)) + +(defun evil-iedit-state/evil-substitute () + "Append and switch to `iedit-insert state'" + (interactive) + (evil-iedit-state||swith-to-insert-state-after-command evil-substitute t)) + +(defun evil-iedit-state/paste-replace (count) + "Replace the selection with the yanked text." + (interactive "P") + (iedit-delete-occurrences) + (evil-paste-before count)) + +;; expand-region integration, add an "e" command +(eval-after-load 'expand-region + '(progn + (defun evil-iedit-state/iedit-mode-from-expand-region (&optional arg) + "Start `iedit-mode'." + (interactive "P") + (evil-iedit-state/iedit-mode arg) + ;; hack to leave expand-region temporary overlay map + ;; we choose a letter that is not in `iedit state' + (setq unread-command-events (listify-key-sequence "kj"))) + + (defadvice er/prepare-for-more-expansions-internal + (around iedit/prepare-for-more-expansions-internal activate) + ad-do-it + (let ((default-msg (car ad-return-value)) + (default-bindings (cdr ad-return-value))) + (message "%s" default-bindings) + (setq ad-return-value + (cons (concat default-msg ", e to edit") + (add-to-list 'default-bindings + '("e" evil-iedit-state/iedit-mode-from-expand-region)))))))) + +;; redefine iedit-done to prevent iedit from putting the occurrence on the +;; kill-ring for no useful reason. +(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-last-occurrence-global iedit-last-occurrence-local) + (setq iedit-last-initial-string-global iedit-initial-string-local) + ;; this is the hack + ;; (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 '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)) + +(define-key evil-iedit-state-map "#" 'iedit-number-occurrences) +(define-key evil-iedit-state-map "$" 'evil-iedit-state/evil-end-of-line) +(evil-redirect-digit-argument evil-iedit-state-map "0" 'evil-iedit-state/evil-beginning-of-line) +(define-key evil-iedit-state-map "a" 'evil-iedit-state/evil-append) +(define-key evil-iedit-state-map "A" 'evil-iedit-state/evil-append-line) +(define-key evil-iedit-state-map "c" 'evil-iedit-state/evil-change) +(define-key evil-iedit-state-map "D" 'iedit-delete-occurrences) +(define-key evil-iedit-state-map "F" 'iedit-restrict-function) +(define-key evil-iedit-state-map "gg" 'iedit-goto-first-occurrence) +(define-key evil-iedit-state-map "G" 'iedit-goto-last-occurrence) +(define-key evil-iedit-state-map "i" 'evil-iedit-insert-state) +(define-key evil-iedit-state-map "I" 'evil-iedit-state/evil-insert-line) +(define-key evil-iedit-state-map "J" 'iedit-expand-down-a-line) +(define-key evil-iedit-state-map "K" 'iedit-expand-up-a-line) +(define-key evil-iedit-state-map "L" 'iedit-restrict-current-line) +(define-key evil-iedit-state-map "n" 'iedit-next-occurrence) +(define-key evil-iedit-state-map "N" 'iedit-prev-occurrence) +(define-key evil-iedit-state-map "o" 'evil-iedit-state/evil-open-below) +(define-key evil-iedit-state-map "O" 'evil-iedit-state/evil-open-above) +(define-key evil-iedit-state-map "p" 'evil-iedit-state/paste-replace) +(define-key evil-iedit-state-map "s" 'evil-iedit-state/evil-substitute) +(define-key evil-iedit-state-map "S" 'evil-iedit-state/substitute) +(define-key evil-iedit-state-map "V" 'iedit-toggle-unmatched-lines-visible) +(define-key evil-iedit-state-map "U" 'iedit-upcase-occurrences) +(define-key evil-iedit-state-map (kbd "C-U") 'iedit-downcase-occurrences) +(define-key evil-iedit-state-map (kbd "C-g")'evil-iedit-state/quit-iedit-mode) +(define-key evil-iedit-state-map [tab] 'iedit-toggle-selection) +(define-key evil-iedit-state-map [backspace] 'iedit-blank-occurrences) +(define-key evil-iedit-state-map [escape] 'evil-iedit-state/quit-iedit-mode) + +(define-key evil-iedit-insert-state-map (kbd "C-g") 'evil-iedit-state/quit-iedit-mode) +(define-key evil-iedit-insert-state-map [escape] 'evil-iedit-state) + +;; unbound iedit commands: +;; toggle buffering +;; toggle case sensitive + +(provide 'evil-iedit-state) + +;;; evil-iedit-state.el ends here