├── README.md └── change-inner.el /README.md: -------------------------------------------------------------------------------- 1 | # change-inner.el 2 | 3 | change-inner gives you vim's `ci` command, building on 4 | [expand-region](https://github.com/magnars/expand-region.el). It is most easily 5 | explained by example: 6 | 7 | function test() { 8 | return "semantic kill"; 9 | } 10 | 11 | With point after the word `semantic` 12 | 13 | * `change-inner "` would kill the contents of the string 14 | * `change-outer "` would kill the entire string 15 | * `change-inner {` would kill the return-statement 16 | * `change-outer {` would kill the entire block 17 | 18 | I use `M-i` and `M-o` for this. 19 | 20 | Giving these commands a prefix argument `C-u` means copy instead of kill. 21 | 22 | ## Installation 23 | 24 | Start by installing 25 | [expand-region](https://github.com/magnars/expand-region.el). 26 | 27 | (require 'change-inner) 28 | (global-set-key (kbd "M-i") 'change-inner) 29 | (global-set-key (kbd "M-o") 'change-outer) 30 | 31 | ## It's not working in my favorite mode 32 | 33 | That may just be because expand-region needs some love for your mode. Please 34 | open a ticket there: https://github.com/magnars/expand-region.el 35 | 36 | ## License 37 | 38 | Copyright (C) 2011 Magnar Sveen 39 | 40 | Author: Magnar Sveen 41 | Keywords: convenience, extensions 42 | 43 | This program is free software; you can redistribute it and/or modify 44 | it under the terms of the GNU General Public License as published by 45 | the Free Software Foundation, either version 3 of the License, or 46 | (at your option) any later version. 47 | 48 | This program is distributed in the hope that it will be useful, 49 | but WITHOUT ANY WARRANTY; without even the implied warranty of 50 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 51 | GNU General Public License for more details. 52 | 53 | You should have received a copy of the GNU General Public License 54 | along with this program. If not, see . 55 | -------------------------------------------------------------------------------- /change-inner.el: -------------------------------------------------------------------------------- 1 | ;;; change-inner.el --- Change contents based on semantic units 2 | 3 | ;; Copyright (C) 2012 Magnar Sveen 4 | 5 | ;; Author: Magnar Sveen 6 | ;; Version: 0.2.0 7 | ;; Keywords: convenience, extensions 8 | ;; Package-Requires: ((expand-region "0.7")) 9 | 10 | ;; This program is free software; you can redistribute it and/or 11 | ;; modify it under the terms of the GNU General Public License 12 | ;; as published by the Free Software Foundation; either version 3 13 | ;; of the License, or (at your option) any later version. 14 | 15 | ;; This program is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with this program. If not, see . 22 | 23 | ;;; Commentary: 24 | 25 | ;; # change-inner.el 26 | ;; 27 | ;; change-inner gives you vim's `ci` command, building on 28 | ;; [expand-region](https://github.com/magnars/expand-region.el). It is most easily 29 | ;; explained by example: 30 | ;; 31 | ;; function test() { 32 | ;; return "semantic kill"; 33 | ;; } 34 | ;; 35 | ;; With point after the word `semantic` 36 | ;; 37 | ;; * `change-inner "` would kill the contents of the string 38 | ;; * `change-outer "` would kill the entire string 39 | ;; * `change-inner {` would kill the return-statement 40 | ;; * `change-outer {` would kill the entire block 41 | ;; 42 | ;; I use `M-i` and `M-o` for this. 43 | ;; 44 | ;; Giving these commands a prefix argument `C-u` means copy instead of kill. 45 | ;; 46 | ;; ## Installation 47 | ;; 48 | ;; Start by installing 49 | ;; [expand-region](https://github.com/magnars/expand-region.el). 50 | ;; 51 | ;; (require 'change-inner) 52 | ;; (global-set-key (kbd "M-i") 'change-inner) 53 | ;; (global-set-key (kbd "M-o") 'change-outer) 54 | ;; 55 | ;; ## It's not working in my favorite mode 56 | ;; 57 | ;; That may just be because expand-region needs some love for your mode. Please 58 | ;; open a ticket there: https://github.com/magnars/expand-region.el 59 | 60 | ;;; Code: 61 | 62 | (require 'expand-region) 63 | 64 | (eval-when-compile (require 'cl-lib)) 65 | 66 | (defun ci--flash-region (start end) 67 | "Temporarily highlight region from START to END." 68 | (let ((overlay (make-overlay start end))) 69 | (overlay-put overlay 'face 'secondary-selection) 70 | (overlay-put overlay 'priority 100) 71 | (run-with-timer 0.2 nil 'delete-overlay overlay))) 72 | 73 | (defun change-inner* (yank? search-forward-char) 74 | "Works like vim's ci command. Takes a char, like ( or \" and 75 | kills the innards of the first ancestor semantic unit starting with that char." 76 | (let* ((expand-region-fast-keys-enabled nil) 77 | (expand-region-smart-cursor nil) 78 | (char (or search-forward-char 79 | (char-to-string 80 | (read-char 81 | (if yank? 82 | "Yank inner, starting with:" 83 | "Change inner, starting with:"))))) 84 | (q-char (regexp-quote char)) 85 | (starting-point (point))) 86 | (when search-forward-char 87 | (search-forward char (line-end-position))) 88 | (cl-flet ((message (&rest args) nil)) 89 | (er--expand-region-1) 90 | (er--expand-region-1) 91 | (while (and (not (= (point) (point-min))) 92 | (not (looking-at q-char))) 93 | (er--expand-region-1)) 94 | (if (not (looking-at q-char)) 95 | (if search-forward-char 96 | (error "Couldn't find any expansion starting with %S" char) 97 | (goto-char starting-point) 98 | (setq mark-active nil) 99 | (change-inner* yank? char)) 100 | (er/contract-region 1) 101 | (if yank? 102 | (progn 103 | (copy-region-as-kill (region-beginning) (region-end)) 104 | (ci--flash-region (region-beginning) (region-end)) 105 | (goto-char starting-point)) 106 | (kill-region (region-beginning) (region-end))))))) 107 | 108 | ;;;###autoload 109 | (defun change-inner (arg) 110 | (interactive "P") 111 | (change-inner* arg nil)) 112 | 113 | ;;;###autoload 114 | (defun copy-inner () 115 | (interactive) 116 | (change-inner* t nil)) 117 | 118 | (defun change-outer* (yank? search-forward-char) 119 | "Works like vim's ci command. Takes a char, like ( or \" and 120 | kills the first ancestor semantic unit starting with that char." 121 | (let* ((expand-region-fast-keys-enabled nil) 122 | (expand-region-smart-cursor nil) 123 | (char (or search-forward-char 124 | (char-to-string 125 | (read-char 126 | (if yank? 127 | "Yank outer, starting with:" 128 | "Change outer, starting with:"))))) 129 | (q-char (regexp-quote char)) 130 | (starting-point (point))) 131 | (when search-forward-char 132 | (search-forward char (line-end-position))) 133 | (cl-flet ((message (&rest args) nil)) 134 | (when (looking-at q-char) 135 | (er/expand-region 1)) 136 | (while (and (not (= (point) (point-min))) 137 | (not (looking-at q-char))) 138 | (er/expand-region 1)) 139 | (if (not (looking-at q-char)) 140 | (if search-forward-char 141 | (error "Couldn't find any expansion starting with %S" char) 142 | (goto-char starting-point) 143 | (setq mark-active nil) 144 | (change-outer* yank? char)) 145 | (if yank? 146 | (progn 147 | (copy-region-as-kill (region-beginning) (region-end)) 148 | (ci--flash-region (region-beginning) (region-end)) 149 | (goto-char starting-point)) 150 | (kill-region (region-beginning) (region-end))))))) 151 | 152 | ;;;###autoload 153 | (defun change-outer (arg) 154 | (interactive "P") 155 | (change-outer* arg nil)) 156 | 157 | ;;;###autoload 158 | (defun copy-outer () 159 | (interactive) 160 | (change-outer* t nil)) 161 | 162 | (provide 'change-inner) 163 | ;;; change-inner.el ends here 164 | --------------------------------------------------------------------------------