├── .gitignore ├── mark-multiple-pkg.el ├── mm-pabbrev-integration.el ├── rename-sgml-tag.el ├── inline-string-rectangle.el ├── README.md ├── mark-more-like-this.el └── mark-multiple.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | -------------------------------------------------------------------------------- /mark-multiple-pkg.el: -------------------------------------------------------------------------------- 1 | (define-package "mark-multiple" "1.0.0" 2 | "Sorta lets you mark several regions at once.") 3 | -------------------------------------------------------------------------------- /mm-pabbrev-integration.el: -------------------------------------------------------------------------------- 1 | ;;; mm-pabbrev-integration.el --- make mark-multiple be friends with pabbrev 2 | 3 | ;; Copyright (C) 2012 Sveen 4 | 5 | ;; Author: Magnar Sveen 6 | ;; Keywords: abbrev 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; For now, disable pabbrev when marking multiple. A better solution is most 24 | ;; welcome. 25 | 26 | ;;; Code: 27 | 28 | (add-hook 'mark-multiple-enabled-hook 'pabbrev-mode-off) 29 | (add-hook 'mark-multiple-disabled-hook 'pabbrev-mode-on) 30 | 31 | (provide 'mm-pabbrev-integration) 32 | ;;; mm-pabbrev-integration.el ends here 33 | -------------------------------------------------------------------------------- /rename-sgml-tag.el: -------------------------------------------------------------------------------- 1 | ;;; rename-sgml-tag.el --- Rename tag (including closing tag) 2 | ;; 3 | ;; Copyright (C) 2011 Magnar Sveen 4 | ;; 5 | ;; Author: Magnar Sveen 6 | ;; Keywords: html tags editing 7 | ;; 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | ;; 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | ;; 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | ;; 21 | ;;; Commentary: 22 | ;; 23 | ;; Rename the current tag (closest from point nesting-wise). 24 | ;; 25 | ;; (require 'rename-sgml-tag) 26 | ;; (define-key sgml-mode-map (kbd "C-c C-r") 'rename-sgml-tag) 27 | ;; 28 | ;; This extension is dependent on the mark-multiple library. 29 | ;; https://github.com/magnars/mark-multiple.el 30 | 31 | ;;; Code: 32 | 33 | (require 'mark-multiple) 34 | 35 | (defun rst--inside-tag-p () 36 | (save-excursion 37 | (not (null (sgml-get-context))))) 38 | 39 | ;;;###autoload 40 | (defun rename-sgml-tag () 41 | (interactive) 42 | (if (not (rst--inside-tag-p)) 43 | (error "Place point inside tag to rename.")) 44 | (let ((context (car (last (sgml-get-context))))) 45 | (if (looking-at " 6 | ;; Keywords: rectangle editing 7 | ;; 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | ;; 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | ;; 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | ;; 21 | ;;; Commentary: 22 | ;; 23 | ;; Replace rectangle contents with visual feedback as you type. 24 | ;; 25 | ;; This is meant as a drop-in replacement for string-rectangle in rect.el. 26 | ;; Instead of typing out the replacement string in the mini-buffer, you get 27 | ;; to watch your changes inline as you type. 28 | ;; 29 | ;; (require 'inline-string-rectangle) 30 | ;; (global-set-key (kbd "C-x r t") 'inline-string-rectangle) 31 | ;; 32 | ;; This extension is dependent on the mark-multiple library. 33 | ;; https://github.com/magnars/mark-multiple.el 34 | 35 | ;;; Code: 36 | 37 | (require 'mark-multiple) 38 | 39 | ;;;###autoload 40 | (defun inline-string-rectangle () 41 | (interactive) 42 | (mm/clear-all) 43 | (let* ((point-column (current-column)) 44 | (point-line (line-number-at-pos)) 45 | (mark-column (save-excursion (exchange-point-and-mark) (current-column))) 46 | (mark-line (save-excursion (exchange-point-and-mark) (line-number-at-pos))) 47 | (left-column (if (< point-column mark-column) point-column mark-column)) 48 | (right-column (if (> point-column mark-column) point-column mark-column)) 49 | (num-mirrors (abs (- point-line mark-line))) 50 | (num-chars (- right-column left-column)) 51 | (navigation-func (if (< point-line mark-line) 'next-line 'previous-line))) 52 | (save-excursion 53 | (move-to-column left-column t) 54 | (mm/create-master (point) (+ (point) num-chars)) 55 | (dotimes (i num-mirrors) 56 | (funcall navigation-func) 57 | (move-to-column right-column t) 58 | (move-to-column left-column t) 59 | (mm/add-mirror (point) (+ (point) num-chars)))) 60 | (move-to-column mark-column) 61 | (set-mark (point)) 62 | (move-to-column point-column)) 63 | (when (not delete-selection-mode) 64 | (kill-region (region-beginning) (region-end)) 65 | (save-excursion 66 | (dolist (mirror mm/mirrors) 67 | (mm/replace-mirror-substring mirror ""))))) 68 | 69 | (provide 'inline-string-rectangle) 70 | 71 | ;;; inline-string-rectangle.el ends here 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > > ----- 2 | > > ----- 3 | > > ----- 4 | > > # Please note! mark-multiple has been superseded by multiple-cursors 5 | > > It has all the functionality of mark-multiple, but with a more robust implementation 6 | > > and more features. 7 | > > 8 | > > To get the features from mark-multiple, use: 9 | > > 10 | > > - `mc/mark-more-like-this` in place of `mark-more-like-this` 11 | > > - `set-rectangular-region-anchor` as a more convenient replacement for `inline-string-rectangle` 12 | > > - or `mc/edit-lines` for a more familiar replacement for `inline-string-rectangle` 13 | > > - `mc/mark-sgml-tag-pair` in place of `rename-sgml-tag` 14 | > > 15 | > > Read more about multiple-cursors on [its own page](https://github.com/magnars/multiple-cursors.el). 16 | > > 17 | ----- 18 | ----- 19 | ----- 20 | 21 | mark-multiple.el 22 | ================ 23 | 24 | An emacs extension that sorta lets you mark several regions at once. 25 | 26 | More precisely, it allows for one master region, with several mirror 27 | regions. The mirrors are updated inline while you type. This allows for some 28 | awesome functionality. Or at least, some more visually pleasing insert and 29 | replace operations. 30 | 31 | Video 32 | ----- 33 | You can [watch an intro to mark-multiple at Emacs Rocks](http://emacsrocks.com/e08.html). 34 | 35 | Done 36 | ---- 37 | * A general library for managing master and mirrors 38 | * `mark-more-like-this` which selects next/previous substring in the buffer that 39 | matches the current region. 40 | * `inline-string-rectangle` which works like `string-rectangle` but lets you 41 | write inline - making it less error prone. 42 | * `rename-sgml-tag` which updates the matching tag while typing. 43 | 44 | Note: `js2-rename-var` has been moved to [js2-refactor.el](https://github.com/magnars/js2-refactor.el). 45 | 46 | Installation 47 | ------------ 48 | 49 | git submodule add https://github.com/magnars/mark-multiple.el.git site-lisp/mark-multiple 50 | 51 | Then add the modules you want to your init-file: 52 | 53 | (require 'inline-string-rectangle) 54 | (global-set-key (kbd "C-x r t") 'inline-string-rectangle) 55 | 56 | (require 'mark-more-like-this) 57 | (global-set-key (kbd "C-<") 'mark-previous-like-this) 58 | (global-set-key (kbd "C->") 'mark-next-like-this) 59 | (global-set-key (kbd "C-M-m") 'mark-more-like-this) ; like the other two, but takes an argument (negative is previous) 60 | (global-set-key (kbd "C-*") 'mark-all-like-this) 61 | 62 | (add-hook 'sgml-mode-hook 63 | (lambda () 64 | (require 'rename-sgml-tag) 65 | (define-key sgml-mode-map (kbd "C-c C-r") 'rename-sgml-tag))) 66 | 67 | Feel free to come up with your own keybindings. 68 | 69 | Bugs and gotchas 70 | ---------------- 71 | * Adding a master and mirrors does not remove the active region. This might feel 72 | strange, but turns out to be practical. 73 | 74 | * The current mark-multiple general library lets you do stupid shit, like adding 75 | overlapping mirrors. That's only a problem for people who want to write their 76 | own functions using `mm/create-master` and `mm/add-mirror`. 77 | 78 | * Seems like there is some conflict with undo-tree.el, which sometimes clobbers 79 | the undo history. I might be doing something particularly stupid. Looking into it. 80 | 81 | * Reverting a buffer with active marks makes them unremovable. 82 | 83 | A wild idea 84 | ----------- 85 | 86 | Is this a subset of a crazy multiple-point module? Would that even work? 87 | 88 | **Edit**: Yes, indeed it is. And yes, it does. This module has been pretty much 89 | eclipsed by [multiple-cursors.el](https://github.com/magnars/multiple-cursors.el), which 90 | goes quite a bit farther and with a saner implementation to boot. 91 | 92 | Contribute 93 | ---------- 94 | 95 | If you make some nice commands with mark-multiple, it would be 96 | great if you opened a pull-request. The repo is at: 97 | 98 | https://github.com/magnars/mark-multiple.el 99 | 100 | Contributors 101 | ------------ 102 | 103 | * [Pao-Chin Wu](https://github.com/abaw) fixed `mark-next-like-this` if no region is active. 104 | * [Syohei YOSHIDA](https://github.com/syohex) improved the error messages for `mark-next-like-this` 105 | * [Mounier Florian](https://github.com/paradoxxxzero) added `mark-all-like-this` and `mark-all-like-this-in-region` 106 | 107 | Thanks! 108 | 109 | License 110 | ------- 111 | 112 | Copyright (C) 2011 Magnar Sveen 113 | 114 | Author: Magnar Sveen 115 | Keywords: marking library 116 | 117 | This program is free software; you can redistribute it and/or modify 118 | it under the terms of the GNU General Public License as published by 119 | the Free Software Foundation, either version 3 of the License, or 120 | (at your option) any later version. 121 | 122 | This program is distributed in the hope that it will be useful, 123 | but WITHOUT ANY WARRANTY; without even the implied warranty of 124 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 125 | GNU General Public License for more details. 126 | -------------------------------------------------------------------------------- /mark-more-like-this.el: -------------------------------------------------------------------------------- 1 | ;;; mark-more-like-this.el --- Mark additional regions in buffer matching current region. 2 | ;; 3 | ;; Copyright (C) 2011 Magnar Sveen 4 | ;; 5 | ;; Author: Magnar Sveen 6 | ;; Keywords: marking replace 7 | ;; 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | ;; 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | ;; 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | ;; 21 | ;;; Commentary: 22 | ;; 23 | ;; These commands will look for strings in the buffer that matches your currently 24 | ;; active region and make them mirrors. The mirrors are updated inline as you type. 25 | ;; 26 | ;; (require 'mark-more-like-this) 27 | ;; (global-set-key (kbd "C-<") 'mark-previous-like-this) 28 | ;; (global-set-key (kbd "C->") 'mark-next-like-this) 29 | ;; (global-set-key (kbd "C-M-m") 'mark-more-like-this) 30 | ;; 31 | ;; You should feel free to make your own keybindings. 32 | ;; 33 | ;; 'mark-more-like-this marks the ARG next matches (previous if negative) 34 | ;; 35 | ;; 'mark-next-like-this marks the next occurance. 36 | ;; - with a negative ARG, removes the last occurance. 37 | ;; - with a zero ARG, skips the last occurance and marks the next. 38 | ;; 39 | ;; 'mark-previous-like-this works like -next- but in the other direction. 40 | ;; 41 | ;; This extension is dependent on the mark-multiple library. 42 | ;; https://github.com/magnars/mark-multiple.el 43 | 44 | ;;; Code: 45 | 46 | (require 'mark-multiple) 47 | 48 | ;;;###autoload 49 | (defun mark-next-like-this (arg) 50 | "Find and mark the next part of the buffer matching the currently active region 51 | With negative ARG, delete the last one instead. 52 | With zero ARG, skip the last one and mark next." 53 | (interactive "p") 54 | (unless (or (region-active-p) 55 | mm/master) 56 | (error "Mark a region to match first.")) 57 | (if (< arg 0) 58 | (mm/remove-mirror (mm/furthest-mirror-after-master))) 59 | (if (>= arg 0) 60 | (progn 61 | (when (null mm/master) 62 | (mm/create-master (region-beginning) (region-end))) 63 | 64 | (save-excursion 65 | (goto-char (mm/last-overlay-end)) 66 | (if (= arg 0) 67 | (mm/remove-mirror (mm/furthest-mirror-after-master))) 68 | (let ((case-fold-search nil) 69 | (master-str (mm/master-substring))) 70 | (if (search-forward master-str nil t) 71 | (mm/add-mirror (- (point) (length master-str)) (point)) 72 | (error "no more found \"%s\" forward" 73 | (substring-no-properties master-str)))))))) 74 | 75 | ;;;###autoload 76 | (defun mark-previous-like-this (arg) 77 | "Find and mark the previous part of the buffer matching the currently active region 78 | With negative ARG, delete the last one instead. 79 | With zero ARG, skip the last one and mark previous." 80 | (interactive "p") 81 | (unless (or (region-active-p) 82 | mm/master) 83 | (error "Mark a region to match first.")) 84 | (if (< arg 0) 85 | (mm/remove-mirror (mm/furthest-mirror-before-master))) 86 | (if (>= arg 0) 87 | (progn 88 | (when (null mm/master) 89 | (mm/create-master (region-beginning) (region-end))) 90 | 91 | (save-excursion 92 | (goto-char (mm/first-overlay-start)) 93 | (if (= arg 0) 94 | (mm/remove-mirror (mm/furthest-mirror-before-master))) 95 | (let ((case-fold-search nil) 96 | (master-str (mm/master-substring))) 97 | (if (search-backward master-str nil t) 98 | (mm/add-mirror (point) (+ (point) (length master-str))) 99 | (error "no more found \"%s\" backward" 100 | (substring-no-properties master-str)))))))) 101 | 102 | ;;;###autoload 103 | (defun mark-all-like-this () 104 | "Find and mark all the parts of the buffer matching the currently active region" 105 | (interactive) 106 | (unless (or (region-active-p) mm/master) (error "Mark a region to match first.")) 107 | (if (not mm/master) 108 | (mm/create-master (region-beginning) (region-end))) 109 | (dolist (mirror mm/mirrors) 110 | (delete-overlay mirror)) 111 | (setq mm/mirrors ()) 112 | (save-excursion 113 | (goto-char 0) 114 | (let ((case-fold-search nil) 115 | (master-str (mm/master-substring))) 116 | (while (search-forward master-str nil t) 117 | (let ((start (- (point) (length master-str))) 118 | (end (point))) 119 | (if (/= (overlay-start mm/master) start) 120 | (mm/add-mirror start end))))))) 121 | 122 | ;;;###autoload 123 | (defun mark-all-like-this-in-region (reg-start reg-end) 124 | "Find and mark all the parts in the region matching the given search" 125 | (interactive "r") 126 | (let ((search (read-from-minibuffer "Mark all in region: ")) 127 | (case-fold-search nil)) 128 | (deactivate-mark) 129 | (save-excursion 130 | (goto-char reg-start) 131 | (while (search-forward search reg-end t) 132 | (mm/create-master-or-mirror (- (point) (length search)) (point)))) 133 | (unless mm/master 134 | (error "Search failed for %S" search)) 135 | (goto-char (mm/master-start)))) 136 | 137 | ;;;###autoload 138 | (defun mark-more-like-this (arg) 139 | "Marks next part of buffer that matches the currently active region ARG times. 140 | Given a negative ARG it searches backwards instead." 141 | (interactive "p") 142 | (unless (or (region-active-p) 143 | mm/master) 144 | (error "Mark a region to match first.")) 145 | (if (> arg 0) 146 | (dotimes (i arg) (mark-next-like-this 1)) 147 | (dotimes (i (- arg)) (mark-previous-like-this 1)))) 148 | 149 | ;;;###autoload 150 | (defun mark-more-like-this-extended () 151 | "Like mark-more-like-this, but then lets you adjust with arrows key. 152 | The actual adjustment made depends on the final component of the 153 | key-binding used to invoke the command, with all modifiers removed: 154 | 155 | Mark previous like this 156 | Mark next like this 157 | If last was previous, skip it 158 | If last was next, remove it 159 | If last was next, skip it 160 | If last was previous, remove it 161 | 162 | Then, continue to read input events and further add or move marks 163 | as long as the input event read (with all modifiers removed) 164 | is one of the above." 165 | (interactive) 166 | (let ((first t) 167 | (ev last-command-event) 168 | (cmd 'mark-next-like-this) 169 | (arg 1) 170 | last echo-keystrokes) 171 | (while cmd 172 | (let ((base (event-basic-type ev))) 173 | (cond ((eq base 'left) 174 | (if (eq last 'mark-previous-like-this) 175 | (setq cmd last arg 0) 176 | (setq cmd 'mark-next-like-this arg -1))) 177 | ((eq base 'up) 178 | (setq cmd 'mark-previous-like-this arg 1)) 179 | ((eq base 'right) 180 | (if (eq last 'mark-next-like-this) 181 | (setq cmd last arg 0) 182 | (setq cmd 'mark-previous-like-this arg -1))) 183 | ((eq base 'down) 184 | (setq cmd 'mark-next-like-this arg 1)) 185 | (first 186 | (setq cmd 'mark-next-like-this arg 1)) 187 | (t 188 | (setq cmd nil)))) 189 | (when cmd 190 | (ignore-errors 191 | (funcall cmd arg)) 192 | (setq first nil last cmd) 193 | (setq ev (read-event "Use arrow keys for more marks: ")))) 194 | (push ev unread-command-events))) 195 | 196 | (provide 'mark-more-like-this) 197 | 198 | ;;; mark-more-like-this.el ends here 199 | -------------------------------------------------------------------------------- /mark-multiple.el: -------------------------------------------------------------------------------- 1 | ;;; mark-multiple.el --- A library that sorta lets you mark several regions at once 2 | 3 | ;; Copyright (C) 2011 Magnar Sveen 4 | 5 | ;; Author: Magnar Sveen 6 | ;; Keywords: marking library 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; -------------------------------------------------------------------------------- 24 | ;; -------------------------------------------------------------------------------- 25 | ;; -------------------------------------------------------------------------------- 26 | 27 | ;; # Please note! mark-multiple has been superseded by multiple-cursors 28 | ;; 29 | ;; It has all the functionality of mark-multiple, but with a more robust implementation 30 | ;; and more features. 31 | ;; 32 | ;; To get the features from mark-multiple, use: 33 | ;; 34 | ;; - `mc/mark-more-like-this` in place of `mark-more-like-this` 35 | ;; - `set-rectangular-region-anchor` as a more convenient replacement for `inline-string-rectangle` 36 | ;; - or `mc/edit-lines` for a more familiar replacement for `inline-string-rectangle` 37 | ;; - `mc/mark-sgml-tag-pair` in place of `rename-sgml-tag` 38 | ;; 39 | ;; Read more about multiple-cursors on [its own page](https://github.com/magnars/multiple-cursors.el). 40 | 41 | ;; -------------------------------------------------------------------------------- 42 | ;; -------------------------------------------------------------------------------- 43 | ;; -------------------------------------------------------------------------------- 44 | 45 | ;; An emacs extension that sorta lets you mark several regions at once. 46 | ;; 47 | ;; More precisely, it allows for one master region, with several mirror 48 | ;; regions. The mirrors are updated inline while you type. This allows for some 49 | ;; awesome functionality. Or at least, some more visually pleasing insert and 50 | ;; replace operations. 51 | ;; 52 | ;; Video 53 | ;; ----- 54 | ;; You can [watch an intro to mark-multiple at Emacs Rocks](http://emacsrocks.com/e08.html). 55 | ;; 56 | ;; Done 57 | ;; ---- 58 | ;; * A general library for managing master and mirrors 59 | ;; * `mark-more-like-this` which selects next/previous substring in the buffer that 60 | ;; matches the current region. 61 | ;; * `inline-string-rectangle` which works like `string-rectangle` but lets you 62 | ;; write inline - making it less error prone. 63 | ;; * `rename-sgml-tag` which updates the matching tag while typing. 64 | ;; * `js2-rename-var` which renames the variable on point and all occurrences 65 | ;; in its lexical scope. 66 | ;; 67 | ;; Installation 68 | ;; ------------ 69 | ;; 70 | ;; git submodule add https://github.com/magnars/mark-multiple.el.git site-lisp/mark-multiple 71 | ;; 72 | ;; Then add the modules you want to your init-file: 73 | ;; 74 | ;; (require 'inline-string-rectangle) 75 | ;; (global-set-key (kbd "C-x r t") 'inline-string-rectangle) 76 | ;; 77 | ;; (require 'mark-more-like-this) 78 | ;; (global-set-key (kbd "C-<") 'mark-previous-like-this) 79 | ;; (global-set-key (kbd "C->") 'mark-next-like-this) 80 | ;; (global-set-key (kbd "C-M-m") 'mark-more-like-this) ; like the other two, but takes an argument (negative is previous) 81 | ;; 82 | ;; (add-hook 'sgml-mode-hook 83 | ;; (lambda () 84 | ;; (require 'rename-sgml-tag) 85 | ;; (define-key sgml-mode-map (kbd "C-c C-r") 'rename-sgml-tag))) 86 | ;; 87 | ;; Feel free to come up with your own keybindings. 88 | ;; 89 | ;; Ideas for more 90 | ;; -------------- 91 | ;; * `js-rename-local-var` which renames the variable at point in the local file. 92 | ;; 93 | ;; Bugs and gotchas 94 | ;; ---------------- 95 | ;; * Adding a master and mirrors does not remove the active region. This might feel 96 | ;; strange, but turns out to be practical. 97 | ;; 98 | ;; * The current mark-multiple general library lets you do stupid shit, like adding 99 | ;; overlapping mirrors. That's only a problem for people who want to write their 100 | ;; own functions using `mm/create-master` and `mm/add-mirror`. 101 | ;; 102 | ;; * Seems like there is some conflict with undo-tree.el, which sometimes clobbers 103 | ;; the undo history. I might be doing something particularly stupid. Looking into it. 104 | ;; 105 | ;; * Reverting the buffer with active marks makes them unremovable. 106 | ;; 107 | ;; 108 | 109 | ;;; Code: 110 | 111 | (defvar mm/master nil 112 | "The master overlay has the point. Moving point out of master clears all.") 113 | 114 | (defvar mm/mirrors nil 115 | "A list of overlays that mirrors master after each change.") 116 | 117 | (make-variable-buffer-local 'mm/master) 118 | (make-variable-buffer-local 'mm/mirrors) 119 | 120 | (defvar mm/keymap (make-sparse-keymap)) 121 | (define-key mm/keymap (kbd "C-g") 'mm/deactivate-region-or-clear-all) 122 | (define-key mm/keymap (kbd "C-m") 'mm/deactivate-region-and-clear-all) 123 | (define-key mm/keymap (kbd "") 'mm/deactivate-region-and-clear-all) 124 | 125 | (defface mm/master-face 126 | '((((class color) (background light)) (:background "DarkSeaGreen1")) 127 | (t (:background "DimGrey"))) 128 | "The face used to highlight master" 129 | :group 'mark-multiple) 130 | 131 | (defface mm/mirror-face 132 | '((((class color) (background light)) (:background "DarkSeaGreen1")) 133 | (t (:background "DimGrey"))) 134 | "The face used to highlight mirror" 135 | :group 'mark-multiple) 136 | 137 | (defun mm/create-master (start end) 138 | "Start a new multiple mark selection by defining the master region from START to END. 139 | Point must be within the region." 140 | (if (or (< (point) start) 141 | (> (point) end)) 142 | (error "Point must be inside master region")) 143 | (mm/clear-all) 144 | (setq mm/master 145 | (make-overlay start end nil nil t)) 146 | (overlay-put mm/master 'priority 100) 147 | (overlay-put mm/master 'face 'mm/master-face) 148 | (overlay-put mm/master 'keymap mm/keymap) 149 | (overlay-put mm/master 'modification-hooks '(mm/on-master-modification)) 150 | (overlay-put mm/master 'insert-in-front-hooks '(mm/on-master-modification)) 151 | (overlay-put mm/master 'insert-behind-hooks '(mm/on-master-modification)) 152 | (setq mm/mirrors ()) 153 | (add-hook 'post-command-hook 'mm/post-command-handler nil t) 154 | (run-hooks 'mark-multiple-enabled-hook)) 155 | 156 | 157 | (defun mm/add-mirror (start end) 158 | "Add a region START to END that will mirror the current master." 159 | (if (null mm/master) 160 | (error "No master defined to mirror. Start with mm/create-master.")) 161 | (let ((mirror (make-overlay start end nil nil t))) 162 | (setq mm/mirrors (cons mirror mm/mirrors)) 163 | (overlay-put mirror 'priority 100) 164 | (overlay-put mirror 'face 'mm/mirror-face))) 165 | 166 | ;;;###autoload 167 | (defun mm/deactivate-region-or-clear-all () 168 | "Deactivate mark if active, otherwise clear all." 169 | (interactive) 170 | (if (use-region-p) 171 | (deactivate-mark) 172 | (mm/clear-all))) 173 | 174 | ;;;###autoload 175 | (defun mm/deactivate-region-and-clear-all () 176 | "Deactivate mark and clear all." 177 | (interactive) 178 | (deactivate-mark) 179 | (mm/clear-all)) 180 | 181 | ;;;###autoload 182 | (defun mm/clear-all () 183 | "Remove all marks" 184 | (interactive) 185 | (when (overlayp mm/master) 186 | (delete-overlay mm/master) 187 | (dolist (mirror mm/mirrors) 188 | (delete-overlay mirror)) 189 | (setq mm/master nil) 190 | (setq mm/mirrors ()) 191 | (remove-hook 'post-command-hook 'mm/post-command-handler) 192 | (run-hooks 'mark-multiple-disabled-hook))) 193 | 194 | (defun mm/master-start () 195 | (overlay-start mm/master)) 196 | 197 | (defun mm/master-end () 198 | (overlay-end mm/master)) 199 | 200 | (defun mm/point-is-outside-of-master () 201 | "Is point outside of master?" 202 | (or (null mm/master) 203 | (< (point) (mm/master-start)) 204 | (> (point) (mm/master-end)))) 205 | 206 | (defun mm/active-region-is-outside-of-master () 207 | "Is region active and mark outside master?" 208 | (and (region-active-p) 209 | (or (< (mark) (mm/master-start)) 210 | (> (mark) (mm/master-end))))) 211 | 212 | (defun mm/post-command-handler () 213 | "Clear all marks if point or region is outside of master" 214 | (if (or (mm/point-is-outside-of-master) 215 | (mm/active-region-is-outside-of-master)) 216 | (mm/clear-all))) 217 | 218 | (defun mm/master-substring () 219 | "Get the buffer substring that is in master" 220 | (buffer-substring (mm/master-start) (mm/master-end))) 221 | 222 | (defun mm/on-master-modification (overlay after? beg end &optional length) 223 | "Update all mirrors after a change" 224 | (save-excursion 225 | (dolist (mirror mm/mirrors) 226 | (mm/replace-mirror-substring mirror (mm/master-substring))))) 227 | 228 | (defun mm/replace-mirror-substring (mirror substring) 229 | "Replace the contents of MIRROR with SUBSTRING" 230 | (goto-char (overlay-start mirror)) 231 | (delete-char (- (overlay-end mirror) (overlay-start mirror))) 232 | (insert substring)) 233 | 234 | ;; Define some utility functions for users of mark-multiple: 235 | 236 | (defun mm/create-master-or-mirror (start end) 237 | "Create master from START to END if there is none, otherwise add mirror." 238 | (if (null mm/master) 239 | (mm/create-master start end) 240 | (mm/add-mirror start end))) 241 | 242 | (defun mm/remove-mirror (mirror) 243 | "Removes all traces of MIRROR" 244 | (setq mm/mirrors (remove mirror mm/mirrors)) 245 | (delete-overlay mirror)) 246 | 247 | (defun mm/furthest-mirror-before-master () 248 | "Find the mirror with the lowest start position before master" 249 | (if (null mm/mirrors) 250 | (error "No mirrors to be found, sir.")) 251 | (let ((first nil) 252 | (start (mm/master-start))) 253 | (dolist (mirror mm/mirrors) 254 | (when (< (overlay-start mirror) start) 255 | (setq first mirror) 256 | (setq start (overlay-start mirror)))) 257 | first)) 258 | 259 | (defun mm/furthest-mirror-after-master () 260 | "Find the mirror with the highest end position after master" 261 | (if (null mm/mirrors) 262 | (error "No mirrors to be found, sir.")) 263 | (let ((last nil) 264 | (end (mm/master-end))) 265 | (dolist (mirror mm/mirrors) 266 | (when (> (overlay-end mirror) end) 267 | (setq last mirror) 268 | (setq end (overlay-end mirror)))) 269 | last)) 270 | 271 | (defun mm/first-overlay-start () 272 | "Find first buffer position covered by master and mirrors" 273 | (let ((start (mm/master-start))) 274 | (dolist (mirror mm/mirrors) 275 | (if (< (overlay-start mirror) start) 276 | (setq start (overlay-start mirror)))) 277 | start)) 278 | 279 | (defun mm/last-overlay-end () 280 | "Find last buffer position covered by master and mirrors" 281 | (let ((end (mm/master-end))) 282 | (dolist (mirror mm/mirrors) 283 | (if (> (overlay-end mirror) end) 284 | (setq end (overlay-end mirror)))) 285 | end)) 286 | 287 | (eval-after-load "pabbrev" '(require 'mm-pabbrev-integration)) 288 | 289 | (provide 'mark-multiple) 290 | ;;; mark-multiple.el ends here 291 | --------------------------------------------------------------------------------