mirror of
https://github.com/magnars/multiple-cursors.el.git
synced 2026-05-10 09:28:18 +00:00
Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 47e03d54f4 | |||
| 39f17258b8 | |||
| 66ceb0b0d4 | |||
| 6cff0c2ebd | |||
| 4c293c46fd | |||
| 754de4e63b | |||
| 1cb894d119 | |||
| cc45842384 | |||
| 94af07453d | |||
| cb848b2a39 | |||
| 474838666a | |||
| 53db250b43 | |||
| 75df47fc4a | |||
| 4535033952 | |||
| 69c99618f9 | |||
| 7a236d174e | |||
| 374244b66e | |||
| 8b91e4788c | |||
| bf0177205e | |||
| 6e159238b2 | |||
| 9dac934bce | |||
| ccb42b5d70 | |||
| 8a53db8a39 | |||
| 72ba43a08e | |||
| 16589a5af2 | |||
| 980a8808dd | |||
| ed18fa49c1 | |||
| 1cdd73037f | |||
| aa9a1ece7b | |||
| 25dd14d350 | |||
| 373dcbe002 | |||
| d24ddc53ea | |||
| 5c60757080 | |||
| a6e0ccb22f | |||
| 0ee76bfad1 | |||
| e7a5fe61c4 | |||
| 641eb680ca | |||
| 16add89d29 | |||
| cb7a145153 | |||
| 82a1fe3746 | |||
| 56839dfdca | |||
| 3f3cdc6c92 | |||
| 5190e0cdc6 | |||
| a4f6ea179a | |||
| 9582c7220b | |||
| a6984a1141 | |||
| 6a5969e14a | |||
| 9a376a6fa2 | |||
| bfb293f200 | |||
| 3ce6e4a670 | |||
| 424e0667a4 | |||
| f1a5a39b48 | |||
| 84ab4ee26a | |||
| 07b88080b1 | |||
| e7605bbd7c | |||
| dc0aa99459 | |||
| 6339657440 | |||
| 94164f706a | |||
| 52300ff852 | |||
| 129ea778cc | |||
| 0b9aae12ca | |||
| fa753aa587 | |||
| bc8af45bfe | |||
| b218a13cf7 | |||
| 0a081a7c5f | |||
| 839c6ef077 | |||
| 7d79b983ea | |||
| 717c97e32b | |||
| c010afbbef | |||
| f721308591 | |||
| 1074c88c99 | |||
| a37e02b907 | |||
| fe211c018c | |||
| 1afbb9317c | |||
| dce6c00fab | |||
| 5f7c92bb74 | |||
| 6b09b9c73f | |||
| b3ca408f52 | |||
| 7382b462b2 | |||
| be4067da45 | |||
| 65d4f873fe | |||
| dce196ce9f | |||
| bb546b11d8 | |||
| 796fb640e1 | |||
| c3b2d8483b | |||
| 2818d9e7ef | |||
| 1ec78e195f | |||
| 038c9a7f03 | |||
| 19b1a83925 | |||
| a8b632386d | |||
| e4adefc04e | |||
| 324d9354b5 | |||
| 80ebdbb35b | |||
| 9ac7675c78 | |||
| 3f20fc15a7 | |||
| 876937bfa3 | |||
| fcbb7a4df9 | |||
| a0f771f3e4 | |||
| 879ddb0e97 | |||
| 6e9ea1ae52 | |||
| 8a12e97b7d | |||
| f4d5aea860 | |||
| 00f905549e | |||
| 7a6eb0df90 | |||
| 6d4979db46 | |||
| f040a33e3c | |||
| 64ffd81491 | |||
| 97da9778fd | |||
| ae0033fe3d | |||
| 5fcc69cc54 |
@@ -0,0 +1 @@
|
||||
elpa
|
||||
@@ -1,6 +0,0 @@
|
||||
[submodule "util/ecukes"]
|
||||
path = util/ecukes
|
||||
url = git://github.com/rejeep/ecukes.git
|
||||
[submodule "util/espuds"]
|
||||
path = util/espuds
|
||||
url = git://github.com/rejeep/espuds.git
|
||||
+11
-2
@@ -1,12 +1,21 @@
|
||||
language: emacs-lisp
|
||||
before_install:
|
||||
- if [ "$ECUKES_EMACS" = 'emacs-snapshot' ]; then
|
||||
- if [ "$EMACS" = 'emacs-snapshot' ]; then
|
||||
sudo add-apt-repository -y ppa:cassou/emacs &&
|
||||
sudo apt-get update -qq &&
|
||||
sudo apt-get install -qq
|
||||
emacs-snapshot-el emacs-snapshot-gtk emacs-snapshot;
|
||||
fi
|
||||
- if [ "$EMACS" = 'emacs24' ]; then
|
||||
sudo add-apt-repository -y ppa:cassou/emacs &&
|
||||
sudo apt-get update -qq &&
|
||||
sudo apt-get install -qq
|
||||
emacs24 emacs24-el emacs24-common-non-dfsg;
|
||||
fi
|
||||
- curl -fsSkL https://raw.github.com/rejeep/carton/master/go | sh
|
||||
- export PATH="/home/travis/.carton/bin:$PATH"
|
||||
- carton
|
||||
env:
|
||||
- ECUKES_EMACS=emacs
|
||||
- EMACS=emacs24 TAGS=""
|
||||
script:
|
||||
./run-travis-ci.sh
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
(source "melpa" "http://melpa.milkbox.net/packages/")
|
||||
|
||||
(package-file "multiple-cursors.el")
|
||||
|
||||
(development
|
||||
(depends-on "ecukes")
|
||||
(depends-on "espuds")
|
||||
(depends-on "wrap-region"))
|
||||
@@ -35,42 +35,90 @@ insert a newline in multiple-cursors-mode, use `C-j`.
|
||||
|
||||
You can [watch an intro to multiple-cursors at Emacs Rocks](http://emacsrocks.com/e13.html).
|
||||
|
||||
## More commands to play around with
|
||||
## Command overview
|
||||
|
||||
I've set up my key-bindings like so:
|
||||
### Mark one more occurrence
|
||||
|
||||
;; From active region to multiple cursors:
|
||||
(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
|
||||
(global-set-key (kbd "C-S-c C-e") 'mc/edit-ends-of-lines)
|
||||
(global-set-key (kbd "C-S-c C-a") 'mc/edit-beginnings-of-lines)
|
||||
- `mc/mark-next-like-this`: Adds a cursor and region at the next part of the buffer forwards that matches the current region.
|
||||
- `mc/mark-next-word-like-this`: Like `mc/mark-next-like-this` but only for whole words.
|
||||
- `mc/mark-next-symbol-like-this`: Like `mc/mark-next-like-this` but only for whole symbols.
|
||||
- `mc/mark-previous-like-this`: Adds a cursor and region at the next part of the buffer backwards that matches the current region.
|
||||
- `mc/mark-previous-word-like-this`: Like `mc/mark-previous-like-this` but only for whole words.
|
||||
- `mc/mark-previous-symbol-like-this`: Like `mc/mark-previous-like-this` but only for whole symbols.
|
||||
- `mc/mark-more-like-this-extended`: Use arrow keys to quickly mark/skip next/previous occurances.
|
||||
- `mc/add-cursor-on-click`: Bind to a mouse event to add cursors by clicking. See tips-section.
|
||||
- `mc/pop-mark`: Set a cursor at the current point and move to the next (different) position on the mark stack. This allows for fine grained control over the placement of cursors.
|
||||
|
||||
When you have an active region that spans multiple lines, the preceeding three
|
||||
commands will add one cursor to each line.
|
||||
### Juggle around with the current cursors
|
||||
|
||||
;; Rectangular region mode
|
||||
(global-set-key (kbd "H-SPC") 'set-rectangular-region-anchor)
|
||||
- `mc/unmark-next-like-this`: Remove the cursor furthest down in the buffer.
|
||||
- `mc/unmark-previous-like-this`: Remove the cursor furthest up in the buffer.
|
||||
- `mc/skip-to-next-like-this`: Remove the cursor furthest down, marking the next occurance down.
|
||||
- `mc/skip-to-previous-like-this`: Remove the cursor furthest up, marking the next occurance up.
|
||||
- `mc/mark-next-like-this-extended`: Temporarily bind the arrow keys to mark/unmark/skip cursors.
|
||||
|
||||
Think of this one as `set-mark` except you're marking a rectangular region. It is
|
||||
an exceedingly quick way of adding multiple cursors to multiple lines.
|
||||
### Mark many occurrences
|
||||
|
||||
;; Mark more like this
|
||||
(global-set-key (kbd "M-æ") 'mc/mark-all-like-this)
|
||||
(global-set-key (kbd "C-å") 'mc/mark-previous-like-this)
|
||||
(global-set-key (kbd "C-æ") 'mc/mark-next-like-this)
|
||||
(global-set-key (kbd "C-Æ") 'mc/mark-more-like-this-extended)
|
||||
(global-set-key (kbd "M-å") 'mc/mark-all-in-region)
|
||||
- `mc/edit-lines`: Adds one cursor to each line in the current region.
|
||||
- `mc/edit-beginnings-of-lines`: Adds a cursor at the start of each line in the current region.
|
||||
- `mc/edit-ends-of-lines`: Adds a cursor at the end of each line in the current region.
|
||||
- `mc/mark-all-like-this`: Marks all parts of the buffer that matches the current region.
|
||||
- `mc/mark-all-words-like-this`: Like `mc/mark-all-like-this` but only for whole words.
|
||||
- `mc/mark-all-symbols-like-this`: Like `mc/mark-all-like-this` but only for whole symbols.
|
||||
- `mc/mark-all-in-region`: Prompts for a string to match in the region, adding cursors to all of them.
|
||||
- `mc/mark-all-like-this-in-defun`: Marks all parts of the current defun that matches the current region.
|
||||
- `mc/mark-all-words-like-this-in-defun`: Like `mc/mark-all-like-this-in-defun` but only for whole words.
|
||||
- `mc/mark-all-symbols-like-this-in-defun`: Like `mc/mark-all-like-this-in-defun` but only for whole symbols.
|
||||
- `mc/mark-all-like-this-dwim`: Tries to be smart about marking everything you want. Can be pressed multiple times.
|
||||
|
||||
Okay, yes, I have a crazy norwegian keyboard. Regardless, these will look at
|
||||
whatever you've got selected at the moment, and mark more places like that in
|
||||
the buffer.
|
||||
### Special
|
||||
|
||||
- `set-rectangular-region-anchor`: Think of this one as `set-mark` except you're marking a rectangular region.
|
||||
- `mc/mark-sgml-tag-pair`: Mark the current opening and closing tag.
|
||||
- `mc/insert-numbers`: Insert increasing numbers for each cursor, top to bottom.
|
||||
- `mc/sort-regions`: Sort the marked regions alphabetically.
|
||||
- `mc/reverse-regions`: Reverse the order of the marked regions.
|
||||
|
||||
## Tips and tricks
|
||||
|
||||
- To get out of multiple-cursors-mode, press `<return>` or `C-g`. The latter will
|
||||
first disable multiple regions before disabling multiple cursors. If you want to
|
||||
insert a newline in multiple-cursors-mode, use `C-j`.
|
||||
|
||||
- Sometimes you end up with cursors outside of your view. You can
|
||||
scroll the screen to center on each cursor with `C-v` and `M-v`.
|
||||
|
||||
- Try pressing `mc/mark-next-like-this` with no region selected. It
|
||||
will just add a cursor on the next line.
|
||||
|
||||
- Try pressing `mc/mark-all-like-this-dwim` on a tagname in html-mode.
|
||||
|
||||
- Notice that the number of cursors active can be seen in the modeline.
|
||||
|
||||
- If you get out of multiple-cursors-mode and yank - it will yank only
|
||||
from the kill-ring of main cursor. To yank from the kill-rings of
|
||||
every cursor use yank-rectangle, normally found at C-x r y.
|
||||
|
||||
- You can use `mc/reverse-regions` with nothing selected and just one cursor.
|
||||
It will then flip the sexp at point and the one below it.
|
||||
|
||||
- If you would like to keep the global bindings clean, and get custom keybindings
|
||||
when the region is active, you can try [region-bindings-mode](https://github.com/fgallina/region-bindings-mode).
|
||||
|
||||
BTW, I highly recommend adding `mc/mark-next-like-this` to a key binding that's
|
||||
right next to the key for `er/expand-region`.
|
||||
|
||||
## Scrolling
|
||||
### Binding mouse events
|
||||
|
||||
Sometimes you end up with cursors outside of your view. You can scroll the
|
||||
screen to center on each cursor with `C-v` and `M-v`.
|
||||
To override a mouse event, you will likely have to also unbind the
|
||||
`down-mouse` part of the event. Like this:
|
||||
|
||||
(global-unset-key (kbd "M-<down-mouse-1>"))
|
||||
(global-set-key (kbd "M-<mouse-1>") 'mc/add-cursor-on-click)
|
||||
|
||||
Or you can do like me and find an unused, but less convenient, binding:
|
||||
|
||||
(global-set-key (kbd "C-S-<mouse-1>") 'mc/add-cursor-on-click)
|
||||
|
||||
## Unknown commands
|
||||
|
||||
@@ -104,15 +152,26 @@ You'll find the repo at:
|
||||
|
||||
https://github.com/magnars/multiple-cursors.el
|
||||
|
||||
To fetch the test dependencies:
|
||||
To fetch the test dependencies, install
|
||||
[carton](https://github.com/rejeep/carton) if you haven't already,
|
||||
then:
|
||||
|
||||
$ cd /path/to/multiple-cursors
|
||||
$ git submodule update --init
|
||||
$ carton
|
||||
|
||||
Run the tests with:
|
||||
|
||||
$ ./util/ecukes/ecukes --graphical
|
||||
$ ./run-tests.sh
|
||||
|
||||
## Contributors
|
||||
|
||||
* [Takafumi Arakaki](https://github.com/tkf) has contributed several small improvements
|
||||
* [Marco Baringer](https://github.com/segv) contributed looping to mc/cycle and adding cursors without region for mark-more.
|
||||
* [Ivan Andrus](https://github.com/gvol) added showing number of cursors in mode-line
|
||||
* [Fuco](https://github.com/Fuco1) added the first version of `mc/mark-all-like-this-dwim`
|
||||
* [Zach Kost-Smith](https://github.com/smithzvk) added `mc/mark-pop`
|
||||
|
||||
Thanks!
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -42,3 +42,16 @@ Feature: Switching from a multiline region to multiple cursors
|
||||
And I go to the front of the word "long"
|
||||
And I press "C-S-c C-S-c"
|
||||
Then I should have 2 cursors
|
||||
|
||||
Scenario: Edit without using transient mark mode
|
||||
Given I turn off transient-mark-mode
|
||||
And I insert:
|
||||
"""
|
||||
hello
|
||||
there
|
||||
"""
|
||||
And I go to the front of the word "hello"
|
||||
And I set the mark
|
||||
And I go to the front of the word "there"
|
||||
And I press "C-S-c C-S-c"
|
||||
Then I should have 2 cursors
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Feature: Insert increasing numbers
|
||||
|
||||
Scenario: Three cursors, 0-1-2
|
||||
Given I have cursors at "text" in "This text contains the word text thrice (text)"
|
||||
When I press "H-0"
|
||||
And I press "SPC"
|
||||
Then I should see "This 0 text contains the word 1 text thrice (2 text)"
|
||||
|
||||
Scenario: Three cursors, 9-10-11
|
||||
Given I have cursors at "text" in "This text contains the word text thrice (text)"
|
||||
When I press "C-9 H-0"
|
||||
And I press "SPC"
|
||||
Then I should see "This 9 text contains the word 10 text thrice (11 text)"
|
||||
@@ -0,0 +1,94 @@
|
||||
Feature: Mark all do-what-I-mean (html)
|
||||
|
||||
Background:
|
||||
Given I turn on html-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
<body>
|
||||
<div class="abc"> def </div>
|
||||
<div class="ghi"> jkl </div>
|
||||
</body>
|
||||
"""
|
||||
|
||||
Scenario: Marks tags in html-mode, from front
|
||||
When I go to the front of the word "abc"
|
||||
And I press "M-b"
|
||||
And I press "M-b"
|
||||
And I press "M-$"
|
||||
And I type "h1"
|
||||
Then I should see:
|
||||
"""
|
||||
<body>
|
||||
<h1 class="abc"> def </h1>
|
||||
<div class="ghi"> jkl </div>
|
||||
</body>
|
||||
"""
|
||||
|
||||
Scenario: Marks tags in html-mode, from back
|
||||
When I go to the end of the word "jkl"
|
||||
And I press "M-f"
|
||||
And I press "M-$"
|
||||
And I type "h1"
|
||||
Then I should see:
|
||||
"""
|
||||
<body>
|
||||
<div class="abc"> def </div>
|
||||
<h1 class="ghi"> jkl </h1>
|
||||
</body>
|
||||
"""
|
||||
|
||||
Scenario: Marks tags in html-mode, from outside front
|
||||
When I go to the front of the word "abc"
|
||||
And I press "M-b"
|
||||
And I press "M-b"
|
||||
And I press "C-b"
|
||||
And I press "M-$"
|
||||
And I type "h1"
|
||||
Then I should see:
|
||||
"""
|
||||
<body>
|
||||
<h1 class="abc"> def </h1>
|
||||
<div class="ghi"> jkl </div>
|
||||
</body>
|
||||
"""
|
||||
|
||||
Scenario: Marks tags in html-mode, from outside back
|
||||
When I go to the end of the word "jkl"
|
||||
And I press "M-f"
|
||||
And I press "C-f"
|
||||
And I press "M-$"
|
||||
And I type "h1"
|
||||
Then I should see:
|
||||
"""
|
||||
<body>
|
||||
<div class="abc"> def </div>
|
||||
<h1 class="ghi"> jkl </h1>
|
||||
</body>
|
||||
"""
|
||||
|
||||
Scenario: Marks words in html-mode
|
||||
When I go to the front of the word "abc"
|
||||
And I press "M-$"
|
||||
And I type "def"
|
||||
Then I should see:
|
||||
"""
|
||||
<body>
|
||||
<div class="def"> def </div>
|
||||
<div class="ghi"> jkl </div>
|
||||
</body>
|
||||
"""
|
||||
|
||||
Scenario: Marks words in html-mode
|
||||
When I go to the front of the word "abc"
|
||||
And I press "M-$"
|
||||
And I type "def"
|
||||
And I press "M-$"
|
||||
And I type "hah"
|
||||
Then I should see:
|
||||
"""
|
||||
<body>
|
||||
<div class="hah"> hah </div>
|
||||
<div class="ghi"> jkl </div>
|
||||
</body>
|
||||
"""
|
||||
@@ -0,0 +1,38 @@
|
||||
Feature: Mark all do-what-I-mean
|
||||
|
||||
Scenario: Mark symbols in defun
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I go to the end of the word "abc"
|
||||
And I press "M-f"
|
||||
And I press "M-$"
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
|
||||
Scenario: Mark all symbols by pressing twice
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message ghi))
|
||||
"""
|
||||
When I go to the end of the word "abc"
|
||||
And I press "M-f"
|
||||
And I press "M-$"
|
||||
And I press "M-$"
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (hmm) (message hmm))
|
||||
"""
|
||||
@@ -94,3 +94,51 @@ Feature: Marking multiple parts of the buffer
|
||||
And I type "more"
|
||||
Then I should have 2 cursors
|
||||
And I should see "Here's more, more and text"
|
||||
|
||||
Scenario: Marking without an active region
|
||||
When I insert:
|
||||
"""
|
||||
aaa
|
||||
bbb
|
||||
ccc
|
||||
"""
|
||||
And I go to the front of the word "bbb"
|
||||
And I press "C->"
|
||||
And I type "_"
|
||||
Then I should have 2 cursors
|
||||
And I should see:
|
||||
"""
|
||||
aaa
|
||||
_bbb
|
||||
_ccc
|
||||
"""
|
||||
|
||||
Scenario: Increasing number of cursors without an active region
|
||||
When I insert:
|
||||
"""
|
||||
aaa
|
||||
bbb
|
||||
ccc
|
||||
"""
|
||||
And I go to the front of the word "bbb"
|
||||
And I press "C->"
|
||||
And I press "C-<"
|
||||
And i press "C-f"
|
||||
And I type "_"
|
||||
Then I should have 3 cursors
|
||||
And I should see:
|
||||
"""
|
||||
a_aa
|
||||
b_bb
|
||||
c_cc
|
||||
"""
|
||||
|
||||
Scenario: Multiple cursor with shift selection
|
||||
When I insert "This text contains the word text twice"
|
||||
And I go to the front of the word "text"
|
||||
And I press "M-S-f"
|
||||
And I press "C->"
|
||||
And I press "C-f"
|
||||
And I press "<deletechar>"
|
||||
Then I should see "This text ontains the word text wice"
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
Feature: Popping cursors off of the mark stack
|
||||
|
||||
Scenario: Single pop
|
||||
Given I turn off transient-mark-mode
|
||||
And I insert:
|
||||
"""
|
||||
hello
|
||||
there
|
||||
"""
|
||||
And I go to the front of the word "hello"
|
||||
And I set the mark
|
||||
And I go to the front of the word "there"
|
||||
And I press "M-x mc/mark-pop"
|
||||
Then I should have 2 cursors
|
||||
|
||||
Scenario: Multiple pops
|
||||
Given I turn off transient-mark-mode
|
||||
And I insert:
|
||||
"""
|
||||
hello
|
||||
there, my friend
|
||||
"""
|
||||
And I go to the front of the word "hello"
|
||||
And I set the mark
|
||||
And I go to the front of the word "my"
|
||||
And I set the mark
|
||||
And I go to the front of the word "friend"
|
||||
And I press "M-x mc/mark-pop"
|
||||
And I press "M-x mc/mark-pop"
|
||||
Then I should have 3 cursors
|
||||
|
||||
Scenario: Discard identical mark and point
|
||||
Given I turn off transient-mark-mode
|
||||
And I insert:
|
||||
"""
|
||||
hello
|
||||
there, my friend
|
||||
"""
|
||||
And I go to the front of the word "hello"
|
||||
And I set the mark
|
||||
And I go to the front of the word "my"
|
||||
And I set the mark
|
||||
And I go to the front of the word "friend"
|
||||
And I set the mark
|
||||
And I press "M-x mc/mark-pop"
|
||||
And I press "M-x mc/mark-pop"
|
||||
Then I should have 3 cursors
|
||||
|
||||
Scenario: Changing the text
|
||||
Given I turn off transient-mark-mode
|
||||
And I insert:
|
||||
"""
|
||||
hello
|
||||
there, my friend
|
||||
"""
|
||||
And I go to the front of the word "hello"
|
||||
And I set the mark
|
||||
And I go to the front of the word "my"
|
||||
And I set the mark
|
||||
And I go to the front of the word "friend"
|
||||
And I press "M-x mc/mark-pop"
|
||||
And I press "M-x mc/mark-pop"
|
||||
And I type "!"
|
||||
Then I should see:
|
||||
"""
|
||||
!hello
|
||||
there, !my !friend
|
||||
"""
|
||||
|
||||
Scenario: With transient-mark-mode
|
||||
And I insert:
|
||||
"""
|
||||
hello
|
||||
there, my friend
|
||||
"""
|
||||
And I go to the front of the word "hello"
|
||||
And I press "C-@ C-@"
|
||||
And I go to the front of the word "my"
|
||||
And I press "C-@ C-@"
|
||||
And I go to the front of the word "friend"
|
||||
And I press "M-x mc/mark-pop"
|
||||
And I press "M-x mc/mark-pop"
|
||||
And I type "!"
|
||||
Then I should see:
|
||||
"""
|
||||
!hello
|
||||
there, !my !friend
|
||||
"""
|
||||
@@ -0,0 +1,137 @@
|
||||
Feature: Mark things
|
||||
|
||||
Scenario: Mark all symbols like this with select
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I select "ghi"
|
||||
And I mark all symbols like this
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (hmm) (message some-other-ghi))
|
||||
"""
|
||||
|
||||
Scenario: Mark all words like this with select
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I select "ghi"
|
||||
And I mark all words like this
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (hmm) (message some-other-hmm))
|
||||
"""
|
||||
|
||||
Scenario: Mark all symbols like this in defun with select
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I select "ghi"
|
||||
And I mark all symbols like this in defun
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
|
||||
Scenario: Mark all words like this in defun with select
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I select "ghi"
|
||||
And I mark all words like this in defun
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
|
||||
Scenario: Mark all symbols like this with no select
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I go to word "ghi"
|
||||
And I mark all symbols like this
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (hmm) (message some-other-ghi))
|
||||
"""
|
||||
|
||||
Scenario: Mark all words like this with no select
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I go to word "ghi"
|
||||
And I mark all words like this
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (hmm) (message some-other-hmm))
|
||||
"""
|
||||
|
||||
Scenario: Mark all symbols like this in defun with no select
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I go to word "ghi"
|
||||
And I mark all symbols like this in defun
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
|
||||
Scenario: Mark all words like this in defun with no select
|
||||
Given I turn on emacs-lisp-mode
|
||||
And I turn on delete-selection-mode
|
||||
And I insert:
|
||||
"""
|
||||
(defun abc (ghi) (message ghi))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
When I go to word "ghi"
|
||||
And I mark all words like this in defun
|
||||
And I type "hmm"
|
||||
Then I should see:
|
||||
"""
|
||||
(defun abc (hmm) (message hmm))
|
||||
(defun def (ghi) (message some-other-ghi))
|
||||
"""
|
||||
@@ -115,10 +115,27 @@ Feature: Multiple cursors core
|
||||
And I go to the front of the word "text"
|
||||
And I press "C-SPC"
|
||||
And I press "M-f"
|
||||
And I press "C->"
|
||||
And I mark next like this
|
||||
And I type "!"
|
||||
Then I should see "This ! contains the word ! twice"
|
||||
|
||||
Scenario: wrap-region (function turns to keyboard macros)
|
||||
Given I turn on wrap-region-mode
|
||||
And I insert "This text contains the word text twice"
|
||||
And I go to the front of the word "text"
|
||||
And I press "C-M-SPC"
|
||||
And I mark next like this
|
||||
And I press "C-g"
|
||||
And I type "("
|
||||
Then I should see "This (text contains the word (text twice"
|
||||
|
||||
Scenario: Bound keyboard macros
|
||||
Given I have bound C-! to a keyboard macro that insert "_"
|
||||
And I have cursors at "text" in "This text contains the word text twice"
|
||||
When I press "C-!"
|
||||
When I press "C-!"
|
||||
Then I should see "This __text contains the word __text twice"
|
||||
|
||||
Scenario: Interprogram paste
|
||||
Given I have cursors at "text" in "This text contains the word text twice"
|
||||
When I copy "external" in another program
|
||||
@@ -141,3 +158,17 @@ Feature: Multiple cursors core
|
||||
contains
|
||||
twice
|
||||
"""
|
||||
|
||||
Scenario: Looping forwards around cursors
|
||||
Given I have cursors at "_" in "1_34567_9"
|
||||
And I press "C-v"
|
||||
And I press "C-v"
|
||||
And I press "C-v"
|
||||
Then the cursor should be at point "8"
|
||||
|
||||
Scenario: Looping backwards around cursors
|
||||
Given I have cursors at "_" in "1_34567_9"
|
||||
And I press "M-v"
|
||||
And I press "M-v"
|
||||
Then the cursor should be at point "2"
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Feature: Sorting and reversing cursor regions
|
||||
|
||||
Scenario: Reversing regions
|
||||
Given I have cursors at "text" in "This text contains the word text thrice (text here)"
|
||||
When I press "M-f"
|
||||
And I press "C-f"
|
||||
And I press "C-SPC"
|
||||
And I press "M-f"
|
||||
And I press "H-1"
|
||||
Then I should see "This text here the word text thrice (text contains)"
|
||||
|
||||
Scenario: Sorting regions
|
||||
Given I have cursors at "text" in "This text contains the word text thrice (text here)"
|
||||
When I press "M-f"
|
||||
And I press "C-f"
|
||||
And I press "C-SPC"
|
||||
And I press "M-f"
|
||||
And I press "H-2"
|
||||
Then I should see "This text contains the word text here (text thrice)"
|
||||
@@ -1,7 +1,41 @@
|
||||
(When "^I mark next like this$"
|
||||
(lambda () (call-interactively 'mc/mark-next-like-this)))
|
||||
|
||||
(When "^I mark previous like this$"
|
||||
(lambda () (call-interactively 'mc/mark-previous-like-this)))
|
||||
|
||||
(When "^I mark all like this$"
|
||||
(lambda () (call-interactively 'mc/mark-all-like-this)))
|
||||
|
||||
(When "^I mark all like this dwim$"
|
||||
(lambda () (call-interactively 'mc/mark-all-like-this-dwim)))
|
||||
|
||||
(When "^I mark all in region$"
|
||||
(lambda () (call-interactively 'mc/mark-all-in-region)))
|
||||
|
||||
(When "^I insert numbers$"
|
||||
(lambda () (call-interactively 'mc/insert-numbers)))
|
||||
|
||||
(When "^I reverse regions$"
|
||||
(lambda () (call-interactively 'mc/reverse-regions)))
|
||||
|
||||
(When "^I sort regions$"
|
||||
(lambda () (call-interactively 'mc/sort-regions)))
|
||||
|
||||
(When "^I edit lines$"
|
||||
(lambda () (call-interactively 'mc/edit-lines)))
|
||||
|
||||
(When "^I set rectangular region anchor$"
|
||||
(lambda () (call-interactively 'set-rectangular-region-anchor)))
|
||||
|
||||
(And "^delete-selection-mode is active$"
|
||||
(lambda ()
|
||||
(delete-selection-mode 1)))
|
||||
|
||||
(Given "^I turn off transient-mark-mode$"
|
||||
(lambda ()
|
||||
(transient-mark-mode -1)))
|
||||
|
||||
(Then "^I should have \\([0-9]+\\) cursors$"
|
||||
(lambda (num)
|
||||
(let ((actual (mc/num-cursors)))
|
||||
@@ -68,6 +102,11 @@
|
||||
(defun mc-test-temp-command-2 () (interactive) (insert ins))
|
||||
(global-set-key (kbd "C-!") 'mc-test-temp-command-2))))
|
||||
|
||||
(Given "^I have bound C-! to a keyboard macro that insert \"_\"$"
|
||||
(lambda ()
|
||||
(fset 'mc-test-temp-kmacro "\C-q_")
|
||||
(global-set-key (kbd "C-!") 'mc-test-temp-kmacro)))
|
||||
|
||||
(When "^I go to character \"\\(.+\\)\"$"
|
||||
(lambda (char)
|
||||
(goto-char (point-min))
|
||||
@@ -90,3 +129,11 @@
|
||||
(assert search nil "The text '%s' was not found in the current buffer." text))
|
||||
(set-mark (point))
|
||||
(re-search-forward text)))
|
||||
|
||||
(When "^I mark all \\(.+\\)$"
|
||||
(lambda (rest)
|
||||
(let ((func (intern (mapconcat 'identity
|
||||
(cons "mc/mark-all"
|
||||
(split-string rest))
|
||||
"-"))))
|
||||
(call-interactively func))))
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
(add-to-list 'load-path multiple-cursors-root-path)
|
||||
(add-to-list 'load-path multiple-cursors-util-path)
|
||||
(add-to-list 'load-path (expand-file-name "espuds" multiple-cursors-util-path))
|
||||
(add-to-list 'load-path (expand-file-name "vendor" multiple-cursors-util-path))
|
||||
|
||||
(require 'multiple-cursors)
|
||||
(require 'espuds)
|
||||
(require 'ert)
|
||||
(require 'wrap-region)
|
||||
|
||||
(defun mc/save-lists ()) ;; redefine to do nothing when running tests
|
||||
|
||||
@@ -21,7 +23,11 @@
|
||||
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
|
||||
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
|
||||
(global-set-key (kbd "M-!") 'mc/mark-all-like-this)
|
||||
(global-set-key (kbd "M-$") 'mc/mark-all-like-this-dwim)
|
||||
(global-set-key (kbd "M-#") 'mc/mark-all-in-region)
|
||||
(global-set-key (kbd "H-0") 'mc/insert-numbers)
|
||||
(global-set-key (kbd "H-1") 'mc/reverse-regions)
|
||||
(global-set-key (kbd "H-2") 'mc/sort-regions)
|
||||
(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
|
||||
(global-set-key (kbd "H-SPC") 'set-rectangular-region-anchor)
|
||||
(switch-to-buffer
|
||||
@@ -31,6 +37,7 @@
|
||||
(cua-mode 0)
|
||||
(delete-selection-mode 0)
|
||||
(subword-mode 0)
|
||||
(wrap-region-mode 0)
|
||||
(setq set-mark-default-inactive nil)
|
||||
(deactivate-mark))
|
||||
|
||||
|
||||
+56
-14
@@ -30,7 +30,7 @@
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defun mc/next-cursor-after-point ()
|
||||
(defun mc/next-fake-cursor-after-point ()
|
||||
(let ((pos (point))
|
||||
(next-pos (point-max))
|
||||
next)
|
||||
@@ -42,7 +42,7 @@
|
||||
(setq next cursor))))
|
||||
next))
|
||||
|
||||
(defun mc/prev-cursor-before-point ()
|
||||
(defun mc/prev-fake-cursor-before-point ()
|
||||
(let ((pos (point))
|
||||
(prev-pos (point-min))
|
||||
prev)
|
||||
@@ -54,27 +54,69 @@
|
||||
(setq prev cursor))))
|
||||
prev))
|
||||
|
||||
(defcustom mc/cycle-looping-behaviour 'continue
|
||||
"What to do if asked to cycle beyond the last cursor or before the first cursor."
|
||||
:type '(radio (const :tag "Loop around to beginning/end of document." continue)
|
||||
(const :tag "Warn and then loop around." warn)
|
||||
(const :tag "Signal an error." error)
|
||||
(const :tag "Don't loop." stop))
|
||||
:group 'multiple-cursors)
|
||||
|
||||
(defun mc/handle-loop-condition (error-message)
|
||||
(ecase mc/cycle-looping-behaviour
|
||||
(error (error error-message))
|
||||
(warn (message error-message))
|
||||
(continue 'continue)
|
||||
(stop 'stop)))
|
||||
|
||||
(defun mc/first-fake-cursor-after (point)
|
||||
"Very similar to mc/furthest-cursor-before-point, but ignores (mark) and (point)."
|
||||
(let* ((cursors (mc/all-fake-cursors))
|
||||
(cursors-after-point (remove-if (lambda (cursor)
|
||||
(< (mc/cursor-beg cursor) point))
|
||||
cursors))
|
||||
(cursors-in-order (sort* cursors-after-point '< :key 'mc/cursor-beg)))
|
||||
(first cursors-in-order)))
|
||||
|
||||
(defun mc/last-fake-cursor-before (point)
|
||||
"Very similar to mc/furthest-cursor-before-point, but ignores (mark) and (point)."
|
||||
(let* ((cursors (mc/all-fake-cursors))
|
||||
(cursors-before-point (remove-if (lambda (cursor)
|
||||
(> (mc/cursor-end cursor) point))
|
||||
cursors))
|
||||
(cursors-in-order (sort* cursors-before-point '> :key 'mc/cursor-end)))
|
||||
(first cursors-in-order)))
|
||||
|
||||
(defun* mc/cycle (next-cursor fallback-cursor loop-message)
|
||||
(when (null next-cursor)
|
||||
(when (eql 'stop (mc/handle-loop-condition loop-message))
|
||||
(return-from mc/cycle nil))
|
||||
(setf next-cursor fallback-cursor))
|
||||
(mc/create-fake-cursor-at-point)
|
||||
(mc/pop-state-from-overlay next-cursor)
|
||||
(recenter))
|
||||
|
||||
(defun mc/cycle-forward ()
|
||||
(interactive)
|
||||
(let ((next-cursor (mc/next-cursor-after-point)))
|
||||
(unless next-cursor
|
||||
(error "We're already at the last cursor"))
|
||||
(mc/create-fake-cursor-at-point)
|
||||
(mc/pop-state-from-overlay next-cursor)
|
||||
(recenter)))
|
||||
(mc/cycle (mc/next-fake-cursor-after-point)
|
||||
(mc/first-fake-cursor-after (point-min))
|
||||
"We're already at the last cursor."))
|
||||
|
||||
(defun mc/cycle-backward ()
|
||||
(interactive)
|
||||
(let ((prev-cursor (mc/prev-cursor-before-point)))
|
||||
(unless prev-cursor
|
||||
(error "We're already at the first cursor"))
|
||||
(mc/create-fake-cursor-at-point)
|
||||
(mc/pop-state-from-overlay prev-cursor)
|
||||
(recenter)))
|
||||
(mc/cycle (mc/prev-fake-cursor-before-point)
|
||||
(mc/last-fake-cursor-before (point-max))
|
||||
"We're already at the last cursor"))
|
||||
|
||||
(define-key mc/keymap (kbd "C-v") 'mc/cycle-forward)
|
||||
(define-key mc/keymap (kbd "M-v") 'mc/cycle-backward)
|
||||
|
||||
(provide 'mc-cycle-cursors)
|
||||
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; byte-compile-warnings: (not cl-functions)
|
||||
;; End:
|
||||
|
||||
;;; mc-cycle-cursors.el ends here
|
||||
|
||||
+3
-5
@@ -35,7 +35,7 @@
|
||||
Starts from mark and moves in straight down or up towards the
|
||||
line point is on."
|
||||
(interactive)
|
||||
(when (not (use-region-p))
|
||||
(when (not (and mark-active (/= (point) (mark))))
|
||||
(error "Mark a set of lines first."))
|
||||
(mc/remove-fake-cursors)
|
||||
(let* ((col (current-column))
|
||||
@@ -57,16 +57,14 @@ line point is on."
|
||||
"Add one cursor to the end of each line in the active region."
|
||||
(interactive)
|
||||
(mc/edit-lines)
|
||||
(mc/execute-command-for-all-fake-cursors 'end-of-line)
|
||||
(end-of-line))
|
||||
(mc/execute-command-for-all-cursors 'end-of-line))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/edit-beginnings-of-lines ()
|
||||
"Add one cursor to the beginning of each line in the active region."
|
||||
(interactive)
|
||||
(mc/edit-lines)
|
||||
(mc/execute-command-for-all-fake-cursors 'beginning-of-line)
|
||||
(beginning-of-line))
|
||||
(mc/execute-command-for-all-cursors 'beginning-of-line))
|
||||
|
||||
(provide 'mc-edit-lines)
|
||||
|
||||
|
||||
+382
-85
@@ -28,6 +28,7 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'multiple-cursors-core)
|
||||
(require 'thingatpt)
|
||||
|
||||
(defun mc/cursor-end (cursor)
|
||||
(if (overlay-get cursor 'mark-active)
|
||||
@@ -79,34 +80,69 @@
|
||||
(mc/cursor-end cursor))))
|
||||
strings))
|
||||
|
||||
(defvar mc/enclose-search-term nil
|
||||
"How should mc/mark-more-* search for more matches?
|
||||
|
||||
Match everything: nil
|
||||
Match only whole words: 'words
|
||||
Match only whole symbols: 'symbols
|
||||
|
||||
Use like case-fold-search, don't recommend setting it globally.")
|
||||
|
||||
(defun mc/mark-more-like-this (skip-last direction)
|
||||
(let ((case-fold-search nil)
|
||||
(re (regexp-opt (mc/region-strings) mc/enclose-search-term))
|
||||
(point-out-of-order (ecase direction
|
||||
(forwards (< (point) (mark)))
|
||||
(backwards (not (< (point) (mark))))))
|
||||
(furthest-cursor (ecase direction
|
||||
(forwards (mc/furthest-cursor-after-point))
|
||||
(backwards (mc/furthest-cursor-before-point))))
|
||||
(start-char (ecase direction
|
||||
(forwards (mc/furthest-region-end))
|
||||
(backwards (mc/first-region-start))))
|
||||
(search-function (ecase direction
|
||||
(forwards 'search-forward-regexp)
|
||||
(backwards 'search-backward-regexp)))
|
||||
(match-point-getter (ecase direction
|
||||
(forwards 'match-beginning)
|
||||
(backwards 'match-end))))
|
||||
(mc/save-excursion
|
||||
(goto-char start-char)
|
||||
(when skip-last
|
||||
(mc/remove-fake-cursor furthest-cursor))
|
||||
(if (funcall search-function re nil t)
|
||||
(progn
|
||||
(push-mark (funcall match-point-getter 0))
|
||||
(when point-out-of-order
|
||||
(exchange-point-and-mark))
|
||||
(mc/create-fake-cursor-at-point))
|
||||
(error "no more matches found.")))))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-next-like-this (arg)
|
||||
"Find and mark the next part of the buffer matching the currently active region
|
||||
With negative ARG, delete the last one instead.
|
||||
With zero ARG, skip the last one and mark next."
|
||||
(interactive "p")
|
||||
(unless (region-active-p)
|
||||
(error "Mark a region to match first."))
|
||||
(when (< arg 0)
|
||||
(mc/remove-fake-cursor (mc/furthest-cursor-after-point)))
|
||||
(when (>= arg 0)
|
||||
(let ((case-fold-search nil)
|
||||
(point-first (< (point) (mark)))
|
||||
(re (regexp-opt (mc/region-strings)))
|
||||
(furthest-cursor (mc/furthest-cursor-after-point)))
|
||||
(mc/save-excursion
|
||||
(goto-char (mc/furthest-region-end))
|
||||
(when (= arg 0)
|
||||
(mc/remove-fake-cursor furthest-cursor))
|
||||
(if (search-forward-regexp re nil t)
|
||||
(progn
|
||||
(push-mark (match-beginning 0))
|
||||
(when point-first (exchange-point-and-mark))
|
||||
(mc/create-fake-cursor-at-point))
|
||||
(error "no more found forward")))))
|
||||
(if (> (mc/num-cursors) 1)
|
||||
(multiple-cursors-mode 1)
|
||||
(multiple-cursors-mode 0)))
|
||||
(if (region-active-p)
|
||||
(if (< arg 0)
|
||||
(mc/remove-fake-cursor (mc/furthest-cursor-after-point))
|
||||
(mc/mark-more-like-this (= arg 0) 'forwards))
|
||||
(mc/mark-lines arg 'forwards))
|
||||
(mc/maybe-multiple-cursors-mode))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-next-word-like-this (arg)
|
||||
(interactive "p")
|
||||
(let ((mc/enclose-search-term 'words))
|
||||
(mc/mark-next-like-this arg)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-next-symbol-like-this (arg)
|
||||
(interactive "p")
|
||||
(let ((mc/enclose-search-term 'symbols))
|
||||
(mc/mark-next-like-this arg)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-previous-like-this (arg)
|
||||
@@ -114,28 +150,69 @@ With zero ARG, skip the last one and mark next."
|
||||
With negative ARG, delete the last one instead.
|
||||
With zero ARG, skip the last one and mark next."
|
||||
(interactive "p")
|
||||
(unless (region-active-p)
|
||||
(error "Mark a region to match first."))
|
||||
(when (< arg 0)
|
||||
(mc/remove-fake-cursor (mc/furthest-cursor-before-point)))
|
||||
(when (>= arg 0)
|
||||
(let ((case-fold-search nil)
|
||||
(point-first (< (point) (mark)))
|
||||
(re (regexp-opt (mc/region-strings)))
|
||||
(furthest-cursor (mc/furthest-cursor-before-point)))
|
||||
(mc/save-excursion
|
||||
(goto-char (mc/first-region-start))
|
||||
(when (= arg 0)
|
||||
(mc/remove-fake-cursor furthest-cursor))
|
||||
(if (search-backward-regexp re nil t)
|
||||
(progn
|
||||
(push-mark (match-end 0))
|
||||
(unless point-first (exchange-point-and-mark))
|
||||
(mc/create-fake-cursor-at-point))
|
||||
(error "no more found backward")))))
|
||||
(if (> (mc/num-cursors) 1)
|
||||
(multiple-cursors-mode 1)
|
||||
(multiple-cursors-mode 0)))
|
||||
(if (region-active-p)
|
||||
(if (< arg 0)
|
||||
(mc/remove-fake-cursor (mc/furthest-cursor-before-point))
|
||||
(mc/mark-more-like-this (= arg 0) 'backwards))
|
||||
(mc/mark-lines arg 'backwards))
|
||||
(mc/maybe-multiple-cursors-mode))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-previous-word-like-this (arg)
|
||||
(interactive "p")
|
||||
(let ((mc/enclose-search-term 'words))
|
||||
(mc/mark-previous-like-this arg)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-previous-symbol-like-this (arg)
|
||||
(interactive "p")
|
||||
(let ((mc/enclose-search-term 'symbols))
|
||||
(mc/mark-previous-like-this arg)))
|
||||
|
||||
(defun mc/mark-lines (num-lines direction)
|
||||
(dotimes (i num-lines)
|
||||
(mc/create-fake-cursor-at-point)
|
||||
(ecase direction
|
||||
(forwards (loop do (next-line 1 nil)
|
||||
while (mc/all-fake-cursors (point) (1+ (point)))))
|
||||
(backwards (loop do (previous-line 1 nil)
|
||||
while (mc/all-fake-cursors (point) (1+ (point))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-next-lines (arg)
|
||||
(interactive "p")
|
||||
(mc/mark-lines arg 'forwards)
|
||||
(mc/maybe-multiple-cursors-mode))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-previous-lines (arg)
|
||||
(interactive "p")
|
||||
(mc/mark-lines arg 'backwards)
|
||||
(mc/maybe-multiple-cursors-mode))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/unmark-next-like-this ()
|
||||
"Deselect next part of the buffer matching the currently active region."
|
||||
(interactive)
|
||||
(mc/mark-next-like-this -1))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/unmark-previous-like-this ()
|
||||
"Deselect prev part of the buffer matching the currently active region."
|
||||
(interactive)
|
||||
(mc/mark-previous-like-this -1))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/skip-to-next-like-this ()
|
||||
"Skip the current one and select the next part of the buffer matching the currently active region."
|
||||
(interactive)
|
||||
(mc/mark-next-like-this 0))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/skip-to-previous-like-this ()
|
||||
"Skip the current one and select the prev part of the buffer matching the currently active region."
|
||||
(interactive)
|
||||
(mc/mark-previous-like-this 0))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-all-like-this ()
|
||||
@@ -147,7 +224,7 @@ With zero ARG, skip the last one and mark next."
|
||||
(let ((master (point))
|
||||
(case-fold-search nil)
|
||||
(point-first (< (point) (mark)))
|
||||
(re (regexp-opt (mc/region-strings))))
|
||||
(re (regexp-opt (mc/region-strings) mc/enclose-search-term)))
|
||||
(mc/save-excursion
|
||||
(goto-char 0)
|
||||
(while (search-forward-regexp re nil t)
|
||||
@@ -160,6 +237,31 @@ With zero ARG, skip the last one and mark next."
|
||||
(multiple-cursors-mode 1)
|
||||
(multiple-cursors-mode 0)))
|
||||
|
||||
(defun mc--select-thing-at-point (thing)
|
||||
(let ((bound (bounds-of-thing-at-point thing)))
|
||||
(when bound
|
||||
(set-mark (car bound))
|
||||
(goto-char (cdr bound))
|
||||
bound)))
|
||||
|
||||
(defun mc--select-thing-at-point-or-bark (thing)
|
||||
(unless (or (region-active-p) (mc--select-thing-at-point thing))
|
||||
(error "Mark a region or set cursor on a %s." thing)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-all-words-like-this ()
|
||||
(interactive)
|
||||
(mc--select-thing-at-point-or-bark 'word)
|
||||
(let ((mc/enclose-search-term 'words))
|
||||
(mc/mark-all-like-this)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-all-symbols-like-this ()
|
||||
(interactive)
|
||||
(mc--select-thing-at-point-or-bark 'symbol)
|
||||
(let ((mc/enclose-search-term 'symbols))
|
||||
(mc/mark-all-like-this)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-all-in-region (beg end)
|
||||
"Find and mark all the parts in the region matching the given search"
|
||||
@@ -179,52 +281,247 @@ With zero ARG, skip the last one and mark next."
|
||||
(multiple-cursors-mode 1)
|
||||
(multiple-cursors-mode 0)))
|
||||
|
||||
(when (not (fboundp 'set-temporary-overlay-map))
|
||||
;; Backport this function from newer emacs versions
|
||||
(defun set-temporary-overlay-map (map &optional keep-pred)
|
||||
"Set a new keymap that will only exist for a short period of time.
|
||||
The new keymap to use must be given in the MAP variable. When to
|
||||
remove the keymap depends on user input and KEEP-PRED:
|
||||
|
||||
- if KEEP-PRED is nil (the default), the keymap disappears as
|
||||
soon as any key is pressed, whether or not the key is in MAP;
|
||||
|
||||
- if KEEP-PRED is t, the keymap disappears as soon as a key *not*
|
||||
in MAP is pressed;
|
||||
|
||||
- otherwise, KEEP-PRED must be a 0-arguments predicate that will
|
||||
decide if the keymap should be removed (if predicate returns
|
||||
nil) or kept (otherwise). The predicate will be called after
|
||||
each key sequence."
|
||||
|
||||
(let* ((clearfunsym (make-symbol "clear-temporary-overlay-map"))
|
||||
(overlaysym (make-symbol "t"))
|
||||
(alist (list (cons overlaysym map)))
|
||||
(clearfun
|
||||
`(lambda ()
|
||||
(unless ,(cond ((null keep-pred) nil)
|
||||
((eq t keep-pred)
|
||||
`(eq this-command
|
||||
(lookup-key ',map
|
||||
(this-command-keys-vector))))
|
||||
(t `(funcall ',keep-pred)))
|
||||
(remove-hook 'pre-command-hook ',clearfunsym)
|
||||
(setq emulation-mode-map-alists
|
||||
(delq ',alist emulation-mode-map-alists))))))
|
||||
(set overlaysym overlaysym)
|
||||
(fset clearfunsym clearfun)
|
||||
(add-hook 'pre-command-hook clearfunsym)
|
||||
|
||||
(push alist emulation-mode-map-alists))))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-more-like-this-extended ()
|
||||
"Like mark-more-like-this, but then lets you adjust with arrows key.
|
||||
The actual adjustment made depends on the final component of the
|
||||
key-binding used to invoke the command, with all modifiers removed:
|
||||
The adjustments work like this:
|
||||
|
||||
<up> Mark previous like this
|
||||
<down> Mark next like this
|
||||
<left> If last was previous, skip it
|
||||
If last was next, remove it
|
||||
<right> If last was next, skip it
|
||||
If last was previous, remove it
|
||||
<up> Mark previous like this and set direction to 'up
|
||||
<down> Mark next like this and set direction to 'down
|
||||
|
||||
Then, continue to read input events and further add or move marks
|
||||
as long as the input event read (with all modifiers removed)
|
||||
is one of the above."
|
||||
If direction is 'up:
|
||||
|
||||
<left> Skip past the cursor furthest up
|
||||
<right> Remove the cursor furthest up
|
||||
|
||||
If direction is 'down:
|
||||
|
||||
<left> Remove the cursor furthest down
|
||||
<right> Skip past the cursor furthest down
|
||||
|
||||
The bindings for these commands can be changed. See `mc/mark-more-like-this-extended-keymap'."
|
||||
(interactive)
|
||||
(let ((first t)
|
||||
(ev last-command-event)
|
||||
(cmd 'mc/mark-next-like-this)
|
||||
(arg 1)
|
||||
last echo-keystrokes)
|
||||
(while cmd
|
||||
(let ((base (event-basic-type ev)))
|
||||
(cond ((eq base 'left)
|
||||
(if (eq last 'mc/mark-previous-like-this)
|
||||
(setq cmd last arg 0)
|
||||
(setq cmd 'mc/mark-next-like-this arg -1)))
|
||||
((eq base 'up)
|
||||
(setq cmd 'mc/mark-previous-like-this arg 1))
|
||||
((eq base 'right)
|
||||
(if (eq last 'mc/mark-next-like-this)
|
||||
(setq cmd last arg 0)
|
||||
(setq cmd 'mc/mark-previous-like-this arg -1)))
|
||||
((eq base 'down)
|
||||
(setq cmd 'mc/mark-next-like-this arg 1))
|
||||
(first
|
||||
(setq cmd 'mc/mark-next-like-this arg 1))
|
||||
(t
|
||||
(setq cmd nil))))
|
||||
(when cmd
|
||||
(ignore-errors
|
||||
(funcall cmd arg))
|
||||
(setq first nil last cmd)
|
||||
(setq ev (read-event "Use arrow keys for more marks: "))))
|
||||
(push ev unread-command-events)))
|
||||
(mc/mmlte--down)
|
||||
(set-temporary-overlay-map mc/mark-more-like-this-extended-keymap t))
|
||||
|
||||
(defvar mc/mark-more-like-this-extended-direction nil
|
||||
"When using mc/mark-more-like-this-extended are we working on the next or previous cursors?")
|
||||
|
||||
(make-variable-buffer-local 'mc/mark-more-like-this-extended)
|
||||
|
||||
(defun mc/mmlte--message ()
|
||||
(if (eq mc/mark-more-like-this-extended-direction 'up)
|
||||
(message "<up> to mark previous, <left> to skip, <right> to remove, <down> to mark next")
|
||||
(message "<down> to mark next, <right> to skip, <left> to remove, <up> to mark previous")))
|
||||
|
||||
(defun mc/mmlte--up ()
|
||||
(interactive)
|
||||
(mc/mark-previous-like-this 1)
|
||||
(setq mc/mark-more-like-this-extended-direction 'up)
|
||||
(mc/mmlte--message))
|
||||
|
||||
(defun mc/mmlte--down ()
|
||||
(interactive)
|
||||
(mc/mark-next-like-this 1)
|
||||
(setq mc/mark-more-like-this-extended-direction 'down)
|
||||
(mc/mmlte--message))
|
||||
|
||||
(defun mc/mmlte--left ()
|
||||
(interactive)
|
||||
(if (eq mc/mark-more-like-this-extended-direction 'down)
|
||||
(mc/unmark-next-like-this)
|
||||
(mc/skip-to-previous-like-this))
|
||||
(mc/mmlte--message))
|
||||
|
||||
(defun mc/mmlte--right ()
|
||||
(interactive)
|
||||
(if (eq mc/mark-more-like-this-extended-direction 'up)
|
||||
(mc/unmark-previous-like-this)
|
||||
(mc/skip-to-next-like-this))
|
||||
(mc/mmlte--message))
|
||||
|
||||
(defvar mc/mark-more-like-this-extended-keymap (make-sparse-keymap))
|
||||
|
||||
(define-key mc/mark-more-like-this-extended-keymap (kbd "<up>") 'mc/mmlte--up)
|
||||
(define-key mc/mark-more-like-this-extended-keymap (kbd "<down>") 'mc/mmlte--down)
|
||||
(define-key mc/mark-more-like-this-extended-keymap (kbd "<left>") 'mc/mmlte--left)
|
||||
(define-key mc/mark-more-like-this-extended-keymap (kbd "<right>") 'mc/mmlte--right)
|
||||
|
||||
(defvar mc--restrict-mark-all-to-symbols nil)
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-all-like-this-dwim (arg)
|
||||
"Tries to guess what you want to mark all of.
|
||||
Can be pressed multiple times to increase selection.
|
||||
|
||||
With prefix, it behaves the same as original `mc/mark-all-like-this'"
|
||||
(interactive "P")
|
||||
(if arg
|
||||
(mc/mark-all-like-this)
|
||||
(if (and (not (use-region-p))
|
||||
(derived-mode-p 'sgml-mode)
|
||||
(mc--on-tag-name-p))
|
||||
(mc/mark-sgml-tag-pair)
|
||||
(let ((before (mc/num-cursors)))
|
||||
(unless (eq last-command 'mc/mark-all-like-this-dwim)
|
||||
(setq mc--restrict-mark-all-to-symbols nil))
|
||||
(unless (use-region-p)
|
||||
(mc--mark-symbol-at-point)
|
||||
(setq mc--restrict-mark-all-to-symbols t))
|
||||
(if mc--restrict-mark-all-to-symbols
|
||||
(mc/mark-all-symbols-like-this-in-defun)
|
||||
(mc/mark-all-like-this-in-defun))
|
||||
(when (<= (mc/num-cursors) before)
|
||||
(if mc--restrict-mark-all-to-symbols
|
||||
(mc/mark-all-symbols-like-this)
|
||||
(mc/mark-all-like-this)))
|
||||
(when (<= (mc/num-cursors) before)
|
||||
(mc/mark-all-like-this))))))
|
||||
|
||||
(defun mc--in-defun ()
|
||||
(bounds-of-thing-at-point 'defun))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-all-like-this-in-defun ()
|
||||
"Mark all like this in defun."
|
||||
(interactive)
|
||||
(if (mc--in-defun)
|
||||
(save-restriction
|
||||
(widen)
|
||||
(narrow-to-defun)
|
||||
(mc/mark-all-like-this))
|
||||
(mc/mark-all-like-this)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-all-words-like-this-in-defun ()
|
||||
"Mark all words like this in defun."
|
||||
(interactive)
|
||||
(mc--select-thing-at-point-or-bark 'word)
|
||||
(if (mc--in-defun)
|
||||
(save-restriction
|
||||
(widen)
|
||||
(narrow-to-defun)
|
||||
(mc/mark-all-words-like-this))
|
||||
(mc/mark-all-words-like-this)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-all-symbols-like-this-in-defun ()
|
||||
"Mark all symbols like this in defun."
|
||||
(interactive)
|
||||
(mc--select-thing-at-point-or-bark 'symbol)
|
||||
(if (mc--in-defun)
|
||||
(save-restriction
|
||||
(widen)
|
||||
(narrow-to-defun)
|
||||
(mc/mark-all-symbols-like-this))
|
||||
(mc/mark-all-symbols-like-this)))
|
||||
|
||||
(defun mc--mark-symbol-at-point ()
|
||||
"Select the symbol under cursor"
|
||||
(interactive)
|
||||
(when (not (use-region-p))
|
||||
(let ((b (bounds-of-thing-at-point 'symbol)))
|
||||
(goto-char (car b))
|
||||
(set-mark (cdr b)))))
|
||||
|
||||
(defun mc--get-nice-sgml-context ()
|
||||
(car
|
||||
(last
|
||||
(progn
|
||||
(when (looking-at "<") (forward-char 1))
|
||||
(when (looking-back ">") (forward-char -1))
|
||||
(sgml-get-context)))))
|
||||
|
||||
(defun mc--on-tag-name-p ()
|
||||
(let* ((context (save-excursion (mc--get-nice-sgml-context)))
|
||||
(tag-name-len (length (aref context 4)))
|
||||
(beg (aref context 2))
|
||||
(end (+ beg tag-name-len (if (eq 'open (aref context 1)) 1 3))))
|
||||
(and context
|
||||
(>= (point) beg)
|
||||
(<= (point) end))))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/add-cursor-on-click (event)
|
||||
"Add a cursor where you click."
|
||||
(interactive "e")
|
||||
(mouse-minibuffer-check event)
|
||||
;; Use event-end in case called from mouse-drag-region.
|
||||
;; If EVENT is a click, event-end and event-start give same value.
|
||||
(let ((position (event-end event)))
|
||||
(if (not (windowp (posn-window position)))
|
||||
(error "Position not in text area of window"))
|
||||
(select-window (posn-window position))
|
||||
(if (numberp (posn-point position))
|
||||
(save-excursion
|
||||
(goto-char (posn-point position))
|
||||
(mc/create-fake-cursor-at-point)))
|
||||
(mc/maybe-multiple-cursors-mode)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-sgml-tag-pair ()
|
||||
"Mark the tag we're in and its pair for renaming."
|
||||
(interactive)
|
||||
(when (not (mc--inside-tag-p))
|
||||
(error "Place point inside tag to rename."))
|
||||
(let ((context (mc--get-nice-sgml-context)))
|
||||
(if (looking-at "</")
|
||||
(setq context (car (last (sgml-get-context)))))
|
||||
(goto-char (aref context 2))
|
||||
(let* ((tag-name (aref context 4))
|
||||
(num-chars (length tag-name))
|
||||
(master-start (1+ (point)))
|
||||
(mirror-end (save-excursion
|
||||
(sgml-skip-tag-forward 1)
|
||||
(1- (point)))))
|
||||
(goto-char (- mirror-end num-chars))
|
||||
(set-mark mirror-end)
|
||||
(mc/create-fake-cursor-at-point)
|
||||
(goto-char master-start)
|
||||
(set-mark (+ (point) num-chars))))
|
||||
(mc/maybe-multiple-cursors-mode))
|
||||
|
||||
(defun mc--inside-tag-p ()
|
||||
(save-excursion
|
||||
(not (null (sgml-get-context)))))
|
||||
|
||||
(provide 'mc-mark-more)
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
;;; mc-mark-pop.el --- Pop cursors off of the mark stack
|
||||
|
||||
(require 'multiple-cursors-core)
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/mark-pop ()
|
||||
"Add one cursor to each line of the active region.
|
||||
Starts from mark and moves in straight down or up towards the
|
||||
line point is on."
|
||||
(interactive)
|
||||
;; If the mark happens to be at the current point, just pop that one off.
|
||||
(while (eql (mark) (point))
|
||||
(pop-mark))
|
||||
(mc/create-fake-cursor-at-point)
|
||||
(exchange-point-and-mark)
|
||||
(pop-mark)
|
||||
(mc/maybe-multiple-cursors-mode))
|
||||
|
||||
;; A good key binding for this feature is perhaps "C-S-p" ('p' for pop).
|
||||
|
||||
(provide 'mc-mark-pop)
|
||||
|
||||
;;; mc-mark-pop.el ends here
|
||||
@@ -0,0 +1,90 @@
|
||||
;;; mc-separate-operations.el - functions that work differently on each cursor
|
||||
|
||||
;; Copyright (C) 2012 Magnar Sveen
|
||||
|
||||
;; Author: Magnar Sveen <magnars@gmail.com>
|
||||
;; Keywords: editing cursors
|
||||
|
||||
;; 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:
|
||||
|
||||
;; This file contains functions that work differently on each cursor,
|
||||
;; instead of treating all of them the same.
|
||||
|
||||
;; Please see multiple-cursors.el for more commentary.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'multiple-cursors-core)
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/insert-numbers (arg)
|
||||
"Insert increasing numbers for each cursor, starting at 0 or ARG."
|
||||
(interactive "P")
|
||||
(setq mc--insert-numbers-number (or arg 0))
|
||||
(mc/for-each-cursor-ordered
|
||||
(mc/execute-command-for-fake-cursor 'mc--insert-number-and-increase cursor)))
|
||||
|
||||
(defvar mc--insert-numbers-number 0)
|
||||
|
||||
(defun mc--insert-number-and-increase ()
|
||||
(interactive)
|
||||
(insert (number-to-string mc--insert-numbers-number))
|
||||
(setq mc--insert-numbers-number (1+ mc--insert-numbers-number)))
|
||||
|
||||
(defun mc--ordered-region-strings ()
|
||||
(let (strings)
|
||||
(save-excursion
|
||||
(mc/for-each-cursor-ordered
|
||||
(setq strings (cons (buffer-substring-no-properties
|
||||
(mc/cursor-beg cursor)
|
||||
(mc/cursor-end cursor)) strings))))
|
||||
(nreverse strings)))
|
||||
|
||||
(defvar mc--strings-to-replace nil)
|
||||
|
||||
(defun mc--replace-region-strings-1 ()
|
||||
(interactive)
|
||||
(delete-region (region-beginning) (region-end))
|
||||
(save-excursion (insert (car mc--strings-to-replace)))
|
||||
(setq mc--strings-to-replace (cdr mc--strings-to-replace)))
|
||||
|
||||
(defun mc--replace-region-strings ()
|
||||
(mc/for-each-cursor-ordered
|
||||
(mc/execute-command-for-fake-cursor 'mc--replace-region-strings-1 cursor)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/reverse-regions ()
|
||||
(interactive)
|
||||
(if (not multiple-cursors-mode)
|
||||
(progn
|
||||
(mc/mark-next-lines 1)
|
||||
(mc/reverse-regions)
|
||||
(multiple-cursors-mode 0))
|
||||
(unless (use-region-p)
|
||||
(mc/execute-command-for-all-cursors 'mark-sexp))
|
||||
(setq mc--strings-to-replace (nreverse (mc--ordered-region-strings)))
|
||||
(mc--replace-region-strings)))
|
||||
|
||||
;;;###autoload
|
||||
(defun mc/sort-regions ()
|
||||
(interactive)
|
||||
(unless (use-region-p)
|
||||
(mc/execute-command-for-all-cursors 'mark-sexp))
|
||||
(setq mc--strings-to-replace (sort (mc--ordered-region-strings) 'string<))
|
||||
(mc--replace-region-strings))
|
||||
|
||||
(provide 'mc-separate-operations)
|
||||
;;; mc-separate-operations.el ends here
|
||||
+236
-105
@@ -25,7 +25,7 @@
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
(require 'cl)
|
||||
|
||||
(require 'rect)
|
||||
|
||||
@@ -41,52 +41,61 @@
|
||||
|
||||
(defmacro mc/add-fake-cursor-to-undo-list (&rest forms)
|
||||
"Make sure point is in the right place when undoing"
|
||||
`(let ((undo-cleaner (cons 'apply (cons 'deactivate-cursor-after-undo (list id)))))
|
||||
(setq buffer-undo-list (cons undo-cleaner buffer-undo-list))
|
||||
,@forms
|
||||
(if (eq undo-cleaner (car buffer-undo-list)) ;; if nothing has been added to the undo-list
|
||||
(setq buffer-undo-list (cdr buffer-undo-list)) ;; then pop the cleaner right off again
|
||||
(setq buffer-undo-list ;; otherwise add a function to activate this cursor
|
||||
(cons (cons 'apply (cons 'activate-cursor-for-undo (list id))) buffer-undo-list)))))
|
||||
(let ((uc (make-symbol "undo-cleaner")))
|
||||
`(let ((,uc (cons 'apply (cons 'deactivate-cursor-after-undo (list id)))))
|
||||
(setq buffer-undo-list (cons ,uc buffer-undo-list))
|
||||
,@forms
|
||||
(if (eq ,uc (car buffer-undo-list)) ;; if nothing has been added to the undo-list
|
||||
(setq buffer-undo-list (cdr buffer-undo-list)) ;; then pop the cleaner right off again
|
||||
(setq buffer-undo-list ;; otherwise add a function to activate this cursor
|
||||
(cons (cons 'apply (cons 'activate-cursor-for-undo (list id))) buffer-undo-list))))))
|
||||
|
||||
(defun mc/all-fake-cursors (&optional start end)
|
||||
(remove-if-not 'mc/fake-cursor-p
|
||||
(overlays-in (or start (point-min))
|
||||
(or end (point-max)))))
|
||||
|
||||
(defmacro mc/for-each-fake-cursor (&rest forms)
|
||||
"Runs the body for each fake cursor, bound to the name cursor"
|
||||
`(mapc #'(lambda (cursor)
|
||||
(when (mc/fake-cursor-p cursor)
|
||||
,@forms))
|
||||
(overlays-in (point-min) (point-max))))
|
||||
`(mapc #'(lambda (cursor) ,@forms)
|
||||
(mc/all-fake-cursors)))
|
||||
|
||||
(defmacro mc/save-excursion (&rest forms)
|
||||
"Saves and restores all the state that multiple-cursors cares about."
|
||||
`(let ((current-state (mc/store-current-state-in-overlay
|
||||
(make-overlay (point) (point) nil nil t))))
|
||||
(overlay-put current-state 'type 'original-cursor)
|
||||
(save-excursion ,@forms)
|
||||
(mc/pop-state-from-overlay current-state)))
|
||||
(let ((cs (make-symbol "current-state")))
|
||||
`(let ((,cs (mc/store-current-state-in-overlay
|
||||
(make-overlay (point) (point) nil nil t))))
|
||||
(overlay-put ,cs 'type 'original-cursor)
|
||||
(save-excursion ,@forms)
|
||||
(mc/pop-state-from-overlay ,cs))))
|
||||
|
||||
(defun mc--compare-by-overlay-start (o1 o2)
|
||||
(< (overlay-start o1) (overlay-start o2)))
|
||||
|
||||
(defmacro mc/for-each-cursor-ordered (&rest forms)
|
||||
"Runs the body for each cursor, fake and real, bound to the name cursor"
|
||||
`(let ((real-cursor (mc/create-fake-cursor-at-point)))
|
||||
(mapc #'(lambda (cursor)
|
||||
(when (mc/fake-cursor-p cursor)
|
||||
,@forms))
|
||||
(sort (overlays-in (point-min) (point-max)) 'mc--compare-by-overlay-start))
|
||||
(mc/pop-state-from-overlay real-cursor)))
|
||||
(let ((rci (make-symbol "real-cursor-id")))
|
||||
`(let ((,rci (overlay-get (mc/create-fake-cursor-at-point) 'mc-id)))
|
||||
(mapc #'(lambda (cursor)
|
||||
(when (mc/fake-cursor-p cursor)
|
||||
,@forms))
|
||||
(sort (overlays-in (point-min) (point-max)) 'mc--compare-by-overlay-start))
|
||||
(mc/pop-state-from-overlay (mc/cursor-with-id ,rci)))))
|
||||
|
||||
(defmacro mc/save-window-scroll (&rest forms)
|
||||
"Saves and restores the window scroll position"
|
||||
`(let ((p (set-marker (make-marker) (point)))
|
||||
(start (set-marker (make-marker) (window-start)))
|
||||
(hscroll (window-hscroll)))
|
||||
,@forms
|
||||
(goto-char p)
|
||||
(set-window-start nil start)
|
||||
(set-window-hscroll nil hscroll)
|
||||
(set-marker p nil)
|
||||
(set-marker start nil)))
|
||||
(let ((p (make-symbol "p"))
|
||||
(s (make-symbol "start"))
|
||||
(h (make-symbol "hscroll")))
|
||||
`(let ((,p (set-marker (make-marker) (point)))
|
||||
(,s (set-marker (make-marker) (window-start)))
|
||||
(,h (window-hscroll)))
|
||||
,@forms
|
||||
(goto-char ,p)
|
||||
(set-window-start nil ,s t)
|
||||
(set-window-hscroll nil ,h)
|
||||
(set-marker ,p nil)
|
||||
(set-marker ,s nil))))
|
||||
|
||||
(defun mc/make-cursor-overlay-at-eol (pos)
|
||||
"Create overlay to look like cursor at end of line."
|
||||
@@ -115,30 +124,32 @@ highlights the entire width of the window."
|
||||
(overlay-put overlay 'type 'additional-region)
|
||||
overlay))
|
||||
|
||||
(defvar mc/cursor-specific-vars '(transient-mark-mode
|
||||
kill-ring
|
||||
kill-ring-yank-pointer
|
||||
mark-ring
|
||||
mark-active
|
||||
yank-undo-function
|
||||
kill-ring-yank-pointer
|
||||
autopair-action
|
||||
autopair-wrap-action
|
||||
er/history)
|
||||
"A list of vars that need to be tracked on a per-cursor basis.")
|
||||
|
||||
(defun mc/store-current-state-in-overlay (o)
|
||||
"Store relevant info about point and mark in the given overlay."
|
||||
(overlay-put o 'point (set-marker (make-marker) (point)))
|
||||
(overlay-put o 'kill-ring kill-ring)
|
||||
(overlay-put o 'kill-ring-yank-pointer kill-ring-yank-pointer)
|
||||
(overlay-put o 'mark (set-marker (make-marker) (mark)))
|
||||
(overlay-put o 'mark-ring mark-ring)
|
||||
(overlay-put o 'mark-active mark-active)
|
||||
(overlay-put o 'yank-undo-function yank-undo-function)
|
||||
(overlay-put o 'kill-ring-yank-pointer kill-ring-yank-pointer)
|
||||
(when (boundp 'er/history) (overlay-put o 'er/history er/history))
|
||||
(dolist (var mc/cursor-specific-vars)
|
||||
(when (boundp var) (overlay-put o var (symbol-value var))))
|
||||
o)
|
||||
|
||||
(defun mc/restore-state-from-overlay (o)
|
||||
"Restore point and mark from stored info in the given overlay."
|
||||
(goto-char (overlay-get o 'point))
|
||||
(setq kill-ring (overlay-get o 'kill-ring))
|
||||
(setq kill-ring-yank-pointer (overlay-get o 'kill-ring-yank-pointer))
|
||||
(set-marker (mark-marker) (overlay-get o 'mark))
|
||||
(setq mark-ring (overlay-get o 'mark-ring))
|
||||
(setq mark-active (overlay-get o 'mark-active))
|
||||
(setq yank-undo-function (overlay-get o 'yank-undo-function))
|
||||
(setq kill-ring-yank-pointer (overlay-get o 'kill-ring-yank-pointer))
|
||||
(when (boundp 'er/history) (setq er/history (overlay-get o 'er/history))))
|
||||
(dolist (var mc/cursor-specific-vars)
|
||||
(when (boundp var) (set var (overlay-get o var)))))
|
||||
|
||||
(defun mc/remove-fake-cursor (o)
|
||||
"Delete overlay with state, including dependent overlays and markers."
|
||||
@@ -183,10 +194,22 @@ Saves the current state in the overlay to be restored later."
|
||||
(run-hooks 'pre-command-hook)
|
||||
(unless (eq this-command 'ignore)
|
||||
(call-interactively cmd))
|
||||
(run-hooks 'post-command-hook)
|
||||
(when deactivate-mark (deactivate-mark)))
|
||||
|
||||
(defvar mc--executing-command-for-fake-cursor nil)
|
||||
|
||||
(defun mc/execute-command-for-fake-cursor (cmd cursor)
|
||||
(let ((mc--executing-command-for-fake-cursor t)
|
||||
(id (overlay-get cursor 'mc-id))
|
||||
(annoying-arrows-mode nil)
|
||||
(smooth-scroll-margin 0))
|
||||
(mc/add-fake-cursor-to-undo-list
|
||||
(mc/pop-state-from-overlay cursor)
|
||||
(ignore-errors
|
||||
(mc/execute-command cmd)
|
||||
(mc/create-fake-cursor-at-point id)))))
|
||||
|
||||
(defun mc/execute-command-for-all-fake-cursors (cmd)
|
||||
"Calls CMD interactively for each cursor.
|
||||
It works by moving point to the fake cursor, setting
|
||||
@@ -197,15 +220,36 @@ cursor with updated info."
|
||||
(mc/save-window-scroll
|
||||
(mc/for-each-fake-cursor
|
||||
(save-excursion
|
||||
(let ((mc--executing-command-for-fake-cursor t)
|
||||
(id (overlay-get cursor 'mc-id))
|
||||
(annoying-arrows-mode nil)
|
||||
(smooth-scroll-margin 0))
|
||||
(mc/add-fake-cursor-to-undo-list
|
||||
(mc/pop-state-from-overlay cursor)
|
||||
(ignore-errors
|
||||
(mc/execute-command cmd)
|
||||
(mc/create-fake-cursor-at-point id)))))))))
|
||||
(mc/execute-command-for-fake-cursor cmd cursor)))))
|
||||
(mc--reset-read-prompts))
|
||||
|
||||
(defun mc/execute-command-for-all-cursors (cmd)
|
||||
"Calls CMD interactively for the real cursor and all fakes."
|
||||
(call-interactively cmd)
|
||||
(mc/execute-command-for-all-fake-cursors cmd))
|
||||
|
||||
;; Intercept some reading commands so you won't have to
|
||||
;; answer them for every single cursor
|
||||
|
||||
(defadvice read-char (around mc-support activate)
|
||||
(if (not multiple-cursors-mode)
|
||||
ad-do-it
|
||||
(unless mc--read-char
|
||||
(setq mc--read-char ad-do-it))
|
||||
(setq ad-return-value mc--read-char)))
|
||||
|
||||
(defadvice read-quoted-char (around mc-support activate)
|
||||
(if (not multiple-cursors-mode)
|
||||
ad-do-it
|
||||
(unless mc--read-quoted-char
|
||||
(setq mc--read-quoted-char ad-do-it))
|
||||
(setq ad-return-value mc--read-quoted-char)))
|
||||
|
||||
(defun mc--reset-read-prompts ()
|
||||
(setq mc--read-char nil)
|
||||
(setq mc--read-quoted-char nil))
|
||||
|
||||
(mc--reset-read-prompts)
|
||||
|
||||
(defun mc/fake-cursor-p (o)
|
||||
"Predicate to check if an overlay is a fake cursor"
|
||||
@@ -262,10 +306,27 @@ been remapped. And certain modes (cua comes to mind) will change their
|
||||
remapping based on state. So a command that changes the state will afterwards
|
||||
not be recognized through the command-remapping lookup."
|
||||
(unless mc--executing-command-for-fake-cursor
|
||||
(setq mc--this-command (or (command-remapping this-original-command)
|
||||
this-original-command))))
|
||||
(let (cmd (or (command-remapping this-original-command)
|
||||
this-original-command))
|
||||
(setq mc--this-command (and (not (eq cmd 'god-mode-self-insert))
|
||||
cmd)))))
|
||||
|
||||
(defun mc/execute-this-command-for-all-cursors ()
|
||||
"Wrap around `mc/execute-this-command-for-all-cursors-1' to protect hook."
|
||||
(condition-case error
|
||||
(mc/execute-this-command-for-all-cursors-1)
|
||||
(error
|
||||
(message "[mc] problem in `mc/execute-this-command-for-all-cursors': %s"
|
||||
(error-message-string error)))))
|
||||
|
||||
;; execute-kbd-macro should never be run for fake cursors. The real cursor will
|
||||
;; execute the keyboard macro, resulting in new commands in the command loop,
|
||||
;; and the fake cursors can pick up on those instead.
|
||||
(defadvice execute-kbd-macro (around skip-fake-cursors activate)
|
||||
(unless mc--executing-command-for-fake-cursor
|
||||
ad-do-it))
|
||||
|
||||
(defun mc/execute-this-command-for-all-cursors-1 ()
|
||||
"Used with post-command-hook to execute supported commands for all cursors.
|
||||
|
||||
It uses two lists of commands to know what to do: the run-once
|
||||
@@ -274,31 +335,43 @@ it will prompt for the proper action and then save that preference.
|
||||
|
||||
Some commands are so unsupported that they are even prevented for
|
||||
the original cursor, to inform about the lack of support."
|
||||
(if (eq 1 (mc/num-cursors)) ;; no fake cursors? disable mc-mode
|
||||
(multiple-cursors-mode 0)
|
||||
(unless mc--executing-command-for-fake-cursor
|
||||
|
||||
(when this-original-command
|
||||
(let ((original-command (or mc--this-command
|
||||
(command-remapping this-original-command)
|
||||
this-original-command)))
|
||||
(if (eq 1 (mc/num-cursors)) ;; no fake cursors? disable mc-mode
|
||||
(multiple-cursors-mode 0)
|
||||
(when this-original-command
|
||||
(let ((original-command (or mc--this-command
|
||||
(command-remapping this-original-command)
|
||||
this-original-command)))
|
||||
|
||||
;; if it's a lambda, we can't know if it's supported or not
|
||||
;; - so go ahead and assume it's ok, because we're just optimistic like that
|
||||
(if (not (symbolp original-command))
|
||||
(mc/execute-command-for-all-fake-cursors original-command)
|
||||
;; skip keyboard macros, since they will generate actual commands that are
|
||||
;; also run in the command loop - we'll handle those later instead.
|
||||
(when (functionp original-command)
|
||||
|
||||
;; otherwise it's a symbol, and we can be more thorough
|
||||
(if (get original-command 'mc--unsupported)
|
||||
(message "%S is not supported with multiple cursors%s"
|
||||
original-command
|
||||
(get original-command 'mc--unsupported))
|
||||
(when (and original-command
|
||||
(not (memq original-command mc--default-cmds-to-run-once))
|
||||
(not (memq original-command mc/cmds-to-run-once))
|
||||
(or (memq original-command mc--default-cmds-to-run-for-all)
|
||||
(memq original-command mc/cmds-to-run-for-all)
|
||||
(mc/prompt-for-inclusion-in-whitelist original-command)))
|
||||
(mc/execute-command-for-all-fake-cursors original-command))))))))
|
||||
;; if it's a lambda, we can't know if it's supported or not
|
||||
;; - so go ahead and assume it's ok, because we're just optimistic like that
|
||||
(if (or (not (symbolp original-command))
|
||||
;; lambda registered by smartrep
|
||||
(string-prefix-p "(" (symbol-name original-command)))
|
||||
(mc/execute-command-for-all-fake-cursors original-command)
|
||||
|
||||
;; smartrep `intern's commands into own obarray to help
|
||||
;; `describe-bindings'. So, let's re-`intern' here to
|
||||
;; make the command comparable by `eq'.
|
||||
(setq original-command (intern (symbol-name original-command)))
|
||||
|
||||
;; otherwise it's a symbol, and we can be more thorough
|
||||
(if (get original-command 'mc--unsupported)
|
||||
(message "%S is not supported with multiple cursors%s"
|
||||
original-command
|
||||
(get original-command 'mc--unsupported))
|
||||
(when (and original-command
|
||||
(not (memq original-command mc--default-cmds-to-run-once))
|
||||
(not (memq original-command mc/cmds-to-run-once))
|
||||
(or (memq original-command mc--default-cmds-to-run-for-all)
|
||||
(memq original-command mc/cmds-to-run-for-all)
|
||||
(mc/prompt-for-inclusion-in-whitelist original-command)))
|
||||
(mc/execute-command-for-all-fake-cursors original-command))))))))))
|
||||
|
||||
(defun mc/remove-fake-cursors ()
|
||||
"Remove all fake cursors.
|
||||
@@ -318,19 +391,22 @@ you should disable multiple-cursors-mode."
|
||||
"Keymap while multiple cursors are active.
|
||||
Main goal of the keymap is to rebind C-g and <return> to conclude
|
||||
multiple cursors editing.")
|
||||
(if mc/keymap
|
||||
nil
|
||||
(unless mc/keymap
|
||||
(setq mc/keymap (make-sparse-keymap))
|
||||
(define-key mc/keymap (kbd "C-g") 'mc/keyboard-quit)
|
||||
(define-key mc/keymap (kbd "<return>") 'multiple-cursors-mode))
|
||||
(define-key mc/keymap (kbd "<return>") 'multiple-cursors-mode)
|
||||
(when (fboundp 'phi-search)
|
||||
(define-key mc/keymap (kbd "C-s") 'phi-search))
|
||||
(when (fboundp 'phi-search-backward)
|
||||
(define-key mc/keymap (kbd "C-r") 'phi-search-backward)))
|
||||
|
||||
(defun mc--all-equal (entries)
|
||||
"Are all these entries equal?"
|
||||
(let ((first (car entries))
|
||||
(defun mc--all-equal (list)
|
||||
"Are all the items in LIST equal?"
|
||||
(let ((first (car list))
|
||||
(all-equal t))
|
||||
(while (and all-equal entries)
|
||||
(setq all-equal (equal first (car entries)))
|
||||
(setq entries (cdr entries)))
|
||||
(while (and all-equal list)
|
||||
(setq all-equal (equal first (car list)))
|
||||
(setq list (cdr list)))
|
||||
all-equal))
|
||||
|
||||
(defun mc--kill-ring-entries ()
|
||||
@@ -348,7 +424,7 @@ So you can paste it in later with `yank-rectangle'."
|
||||
(unless (mc--all-equal entries)
|
||||
(setq killed-rectangle entries))))
|
||||
|
||||
(defvar mc/unsupported-minor-modes '(auto-complete-mode)
|
||||
(defvar mc/unsupported-minor-modes '(auto-complete-mode flyspell-mode)
|
||||
"List of minor-modes that does not play well with multiple-cursors.
|
||||
They are temporarily disabled when multiple-cursors are active.")
|
||||
|
||||
@@ -372,9 +448,16 @@ They are temporarily disabled when multiple-cursors are active.")
|
||||
(mapc 'mc/enable-minor-mode mc/temporarily-disabled-minor-modes)
|
||||
(setq mc/temporarily-disabled-minor-modes nil))
|
||||
|
||||
(defcustom mc/mode-line
|
||||
`(" mc:" (:eval (format ,(propertize "%d" 'face 'font-lock-warning-face)
|
||||
(mc/num-cursors))))
|
||||
"What to display in the mode line while multiple-cursors-mode is active."
|
||||
:group 'multiple-cursors)
|
||||
(put 'mc/mode-line 'risky-local-variable t)
|
||||
|
||||
(define-minor-mode multiple-cursors-mode
|
||||
"Mode while multiple cursors are active."
|
||||
nil " mc" mc/keymap
|
||||
nil mc/mode-line mc/keymap
|
||||
(if multiple-cursors-mode
|
||||
(progn
|
||||
(mc/temporarily-disable-unsupported-minor-modes)
|
||||
@@ -391,6 +474,12 @@ They are temporarily disabled when multiple-cursors are active.")
|
||||
|
||||
(add-hook 'after-revert-hook #'(lambda () (multiple-cursors-mode 0)))
|
||||
|
||||
(defun mc/maybe-multiple-cursors-mode ()
|
||||
"Enable multiple-cursors-mode if there is more than one currently active cursor."
|
||||
(if (> (mc/num-cursors) 1)
|
||||
(multiple-cursors-mode 1)
|
||||
(multiple-cursors-mode 0)))
|
||||
|
||||
(defmacro unsupported-cmd (cmd msg)
|
||||
"Adds command to list of unsupported commands and prevents it
|
||||
from being executed if in multiple-cursors-mode."
|
||||
@@ -434,6 +523,20 @@ from being executed if in multiple-cursors-mode."
|
||||
"The position of the file that keeps track of your preferences
|
||||
for running commands with multiple cursors.")
|
||||
|
||||
(defun mc/dump-list (list-symbol)
|
||||
"Insert (setq 'LIST-SYMBOL LIST-VALUE) to current buffer."
|
||||
(symbol-macrolet ((value (symbol-value list-symbol)))
|
||||
(insert "(setq " (symbol-name list-symbol) "\n"
|
||||
" '(")
|
||||
(newline-and-indent)
|
||||
(set list-symbol
|
||||
(sort value (lambda (x y) (string-lessp (symbol-name x)
|
||||
(symbol-name y)))))
|
||||
(mapc #'(lambda (cmd) (insert (format "%S" cmd)) (newline-and-indent))
|
||||
value)
|
||||
(insert "))")
|
||||
(newline)))
|
||||
|
||||
(defun mc/save-lists ()
|
||||
"Saves preferences for running commands with multiple cursors to `mc/list-file'"
|
||||
(with-temp-file mc/list-file
|
||||
@@ -443,21 +546,9 @@ for running commands with multiple cursors.")
|
||||
(insert ";; It keeps track of your preferences for running commands with multiple cursors.")
|
||||
(newline)
|
||||
(newline)
|
||||
(insert "(setq mc/cmds-to-run-for-all '(")
|
||||
(mapc #'(lambda (cmd) (insert (format "%S" cmd)) (newline-and-indent)) mc/cmds-to-run-for-all)
|
||||
(when mc/cmds-to-run-for-all
|
||||
(forward-line -1)
|
||||
(end-of-line))
|
||||
(insert "))")
|
||||
(mc/dump-list 'mc/cmds-to-run-for-all)
|
||||
(newline)
|
||||
(newline)
|
||||
(insert "(setq mc/cmds-to-run-once '(")
|
||||
(mapc #'(lambda (cmd) (insert (format "%S" cmd)) (newline-and-indent)) mc/cmds-to-run-once)
|
||||
(when mc/cmds-to-run-once
|
||||
(forward-line -1)
|
||||
(end-of-line))
|
||||
(insert "))")
|
||||
(newline)))
|
||||
(mc/dump-list 'mc/cmds-to-run-once)))
|
||||
|
||||
(defvar mc/cmds-to-run-once nil
|
||||
"Commands to run only once in multiple-cursors-mode.")
|
||||
@@ -469,16 +560,42 @@ for running commands with multiple cursors.")
|
||||
mc/edit-ends-of-lines
|
||||
mc/edit-beginnings-of-lines
|
||||
mc/mark-next-like-this
|
||||
mc/mark-next-word-like-this
|
||||
mc/mark-next-symbol-like-this
|
||||
mc/mark-previous-like-this
|
||||
mc/mark-more-like-this-extended
|
||||
mc/mark-previous-word-like-this
|
||||
mc/mark-previous-symbol-like-this
|
||||
mc/mark-all-like-this
|
||||
mc/mark-all-words-like-this
|
||||
mc/mark-all-symbols-like-this
|
||||
mc/mark-more-like-this-extended
|
||||
mc/mark-all-like-this-in-defun
|
||||
mc/mark-all-words-like-this-in-defun
|
||||
mc/mark-all-symbols-like-this-in-defun
|
||||
mc/mark-all-like-this-dwim
|
||||
mc/mark-sgml-tag-pair
|
||||
mc/insert-numbers
|
||||
mc/sort-regions
|
||||
mc/reverse-regions
|
||||
mc/cycle-forward
|
||||
mc/cycle-backward
|
||||
mc/add-cursor-on-click
|
||||
mc/mark-pop
|
||||
mc/add-cursors-to-all-matches
|
||||
mc/mmlte--left
|
||||
mc/mmlte--right
|
||||
mc/mmlte--up
|
||||
mc/mmlte--down
|
||||
mc/unmark-next-like-this
|
||||
mc/unmark-previous-like-this
|
||||
mc/skip-to-next-like-this
|
||||
mc/skip-to-previous-like-this
|
||||
rrm/switch-to-multiple-cursors
|
||||
save-buffer
|
||||
ido-exit-minibuffer
|
||||
exit-minibuffer
|
||||
minibuffer-complete-and-exit
|
||||
execute-extended-command
|
||||
undo
|
||||
redo
|
||||
undo-tree-undo
|
||||
@@ -495,6 +612,7 @@ for running commands with multiple cursors.")
|
||||
describe-function
|
||||
describe-bindings
|
||||
describe-prefix-bindings
|
||||
view-echo-area-messages
|
||||
other-window
|
||||
kill-buffer-and-window
|
||||
split-window-right
|
||||
@@ -502,6 +620,8 @@ for running commands with multiple cursors.")
|
||||
delete-other-windows
|
||||
toggle-window-split
|
||||
mwheel-scroll
|
||||
scroll-up-command
|
||||
scroll-down-command
|
||||
mouse-set-point
|
||||
mouse-drag-region
|
||||
quit-window
|
||||
@@ -516,6 +636,7 @@ for running commands with multiple cursors.")
|
||||
|
||||
(setq mc--default-cmds-to-run-for-all '(mc/keyboard-quit
|
||||
self-insert-command
|
||||
quoted-insert
|
||||
previous-line
|
||||
next-line
|
||||
newline
|
||||
@@ -554,6 +675,11 @@ for running commands with multiple cursors.")
|
||||
backward-delete-char-untabify
|
||||
delete-char delete-forward-char
|
||||
delete-backward-char
|
||||
py-electric-backspace
|
||||
c-electric-backspace
|
||||
org-delete-backward-char
|
||||
paredit-backward-delete
|
||||
autopair-backspace
|
||||
just-one-space
|
||||
zap-to-char
|
||||
end-of-line
|
||||
@@ -589,4 +715,9 @@ for running commands with multiple cursors.")
|
||||
|
||||
(provide 'multiple-cursors-core)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; byte-compile-warnings: (not cl-functions)
|
||||
;; End:
|
||||
|
||||
;;; multiple-cursors-core.el ends here
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
(define-package "multiple-cursors" "1.1.2"
|
||||
(define-package "multiple-cursors" "1.2.1"
|
||||
"Multiple cursors for Emacs.")
|
||||
|
||||
+106
-50
@@ -1,8 +1,9 @@
|
||||
;;; multiple-cursors.el --- An experiment in multiple cursors for emacs.
|
||||
;;; multiple-cursors.el --- Multiple cursors for emacs.
|
||||
|
||||
;; Copyright (C) 2012 Magnar Sveen
|
||||
;; Copyright (C) 2012-2013 Magnar Sveen
|
||||
|
||||
;; Author: Magnar Sveen <magnars@gmail.com>
|
||||
;; Version: 1.2.2
|
||||
;; Keywords: editing cursors
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,112 +24,167 @@
|
||||
;; Multiple cursors for Emacs. This is some pretty crazy functionality, so yes,
|
||||
;; there are kinks. Don't be afraid tho, I've been using it since 2011 with
|
||||
;; great success and much merriment.
|
||||
;;
|
||||
|
||||
;; ## Basic usage
|
||||
;;
|
||||
|
||||
;; Start out with:
|
||||
;;
|
||||
|
||||
;; (require 'multiple-cursors)
|
||||
;;
|
||||
|
||||
;; Then you have to set up your keybindings - multiple-cursors doesn't presume to
|
||||
;; know how you'd like them laid out. Here are some examples:
|
||||
|
||||
;; When you have an active region that spans multiple lines, the following will
|
||||
;; add a cursor to each line:
|
||||
;;
|
||||
|
||||
;; (global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
|
||||
;;
|
||||
|
||||
;; When you want to add multiple cursors not based on continuous lines, but based on
|
||||
;; keywords in the buffer, use:
|
||||
;;
|
||||
|
||||
;; (global-set-key (kbd "C->") 'mc/mark-next-like-this)
|
||||
;; (global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
|
||||
;; (global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
|
||||
;;
|
||||
|
||||
;; First mark the word, then add more cursors.
|
||||
;;
|
||||
|
||||
;; To get out of multiple-cursors-mode, press `<return>` or `C-g`. The latter will
|
||||
;; first disable multiple regions before disabling multiple cursors. If you want to
|
||||
;; insert a newline in multiple-cursors-mode, use `C-j`.
|
||||
|
||||
;; ## Video
|
||||
|
||||
;; You can [watch an intro to multiple-cursors at Emacs Rocks](http://emacsrocks.com/e13.html).
|
||||
|
||||
;; ## Command overview
|
||||
|
||||
;; ### Mark one more occurrence
|
||||
|
||||
;; - `mc/mark-next-like-this`: Adds a cursor and region at the next part of the buffer forwards that matches the current region.
|
||||
;; - `mc/mark-next-word-like-this`: Like `mc/mark-next-like-this` but only for whole words.
|
||||
;; - `mc/mark-next-symbol-like-this`: Like `mc/mark-next-like-this` but only for whole symbols.
|
||||
;; - `mc/mark-previous-like-this`: Adds a cursor and region at the next part of the buffer backwards that matches the current region.
|
||||
;; - `mc/mark-previous-word-like-this`: Like `mc/mark-previous-like-this` but only for whole words.
|
||||
;; - `mc/mark-previous-symbol-like-this`: Like `mc/mark-previous-like-this` but only for whole symbols.
|
||||
;; - `mc/mark-more-like-this-extended`: Use arrow keys to quickly mark/skip next/previous occurances.
|
||||
;; - `mc/add-cursor-on-click`: Bind to a mouse event to add cursors by clicking. See tips-section.
|
||||
|
||||
;; ### Mark many occurrences
|
||||
|
||||
;; - `mc/mark-all-like-this`: Marks all parts of the buffer that matches the current region.
|
||||
;; - `mc/mark-all-words-like-this`: Like `mc/mark-all-like-this` but only for whole words.
|
||||
;; - `mc/mark-all-symbols-like-this`: Like `mc/mark-all-like-this` but only for whole symbols.
|
||||
;; - `mc/mark-all-in-region`: Prompts for a string to match in the region, adding cursors to all of them.
|
||||
;; - `mc/mark-all-like-this-in-defun`: Marks all parts of the current defun that matches the current region.
|
||||
;; - `mc/mark-all-words-like-this-in-defun`: Like `mc/mark-all-like-this-in-defun` but only for whole words.
|
||||
;; - `mc/mark-all-symbols-like-this-in-defun`: Like `mc/mark-all-like-this-in-defun` but only for whole symbols.
|
||||
;; - `mc/mark-all-like-this-dwim`: Tries to be smart about marking everything you want. Can be pressed multiple times.
|
||||
|
||||
;; ### Special
|
||||
|
||||
;; - `set-rectangular-region-anchor`: Think of this one as `set-mark` except you're marking a rectangular region.
|
||||
;; - `mc/mark-sgml-tag-pair`: Mark the current opening and closing tag.
|
||||
;; - `mc/insert-numbers`: Insert increasing numbers for each cursor, top to bottom.
|
||||
;; - `mc/sort-regions`: Sort the marked regions alphabetically.
|
||||
;; - `mc/reverse-regions`: Reverse the order of the marked regions.
|
||||
|
||||
;; ## Tips and tricks
|
||||
|
||||
;; - To get out of multiple-cursors-mode, press `<return>` or `C-g`. The latter will
|
||||
;; first disable multiple regions before disabling multiple cursors. If you want to
|
||||
;; insert a newline in multiple-cursors-mode, use `C-j`.
|
||||
;;
|
||||
;; - Sometimes you end up with cursors outside of your view. You can
|
||||
;; scroll the screen to center on each cursor with `C-v` and `M-v`.
|
||||
;;
|
||||
;; ## More commands to play around with
|
||||
;; - Try pressing `mc/mark-next-like-this` with no region selected. It will just add a cursor
|
||||
;; on the next line.
|
||||
;;
|
||||
;; I've set up my key-bindings like so:
|
||||
;; - Try pressing `mc/mark-all-like-this-dwim` on a tagname in html-mode.
|
||||
;;
|
||||
;; ;; From active region to multiple cursors:
|
||||
;; (global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
|
||||
;; (global-set-key (kbd "C-S-c C-e") 'mc/edit-ends-of-lines)
|
||||
;; (global-set-key (kbd "C-S-c C-a") 'mc/edit-beginnings-of-lines)
|
||||
;; - Notice that the number of cursors active can be seen in the modeline.
|
||||
;;
|
||||
;; When you have an active region that spans multiple lines, the preceeding three
|
||||
;; commands will add one cursor to each line.
|
||||
;; - If you get out of multiple-cursors-mode and yank - it will yank only
|
||||
;; from the kill-ring of main cursor. To yank from the kill-rings of
|
||||
;; every cursor use yank-rectangle, normally found at C-x r y.
|
||||
;;
|
||||
;; ;; Rectangular region mode
|
||||
;; (global-set-key (kbd "H-SPC") 'set-rectangular-region-anchor)
|
||||
;; - You can use `mc/reverse-regions` with nothing selected and just one cursor.
|
||||
;; It will then flip the sexp at point and the one below it.
|
||||
;;
|
||||
;; Think of this one as `set-mark` except you're marking a rectangular region. It is
|
||||
;; an exceedingly quick way of adding multiple cursors to multiple lines.
|
||||
;;
|
||||
;; ;; Mark more like this
|
||||
;; (global-set-key (kbd "M-æ") 'mc/mark-all-like-this)
|
||||
;; (global-set-key (kbd "C-å") 'mc/mark-previous-like-this)
|
||||
;; (global-set-key (kbd "C-æ") 'mc/mark-next-like-this)
|
||||
;; (global-set-key (kbd "C-Æ") 'mc/mark-more-like-this-extended)
|
||||
;; (global-set-key (kbd "M-å") 'mc/mark-all-in-region)
|
||||
;;
|
||||
;; Okay, yes, I have a crazy norwegian keyboard. Regardless, these will look at
|
||||
;; whatever you've got selected at the moment, and mark more places like that in
|
||||
;; the buffer.
|
||||
;; - If you would like to keep the global bindings clean, and get custom keybindings
|
||||
;; when the region is active, you can try [region-bindings-mode](https://github.com/fgallina/region-bindings-mode).
|
||||
;;
|
||||
;; BTW, I highly recommend adding `mc/mark-next-like-this` to a key binding that's
|
||||
;; right next to the key for `er/expand-region`.
|
||||
|
||||
;; ### Binding mouse events
|
||||
|
||||
;; To override a mouse event, you will likely have to also unbind the
|
||||
;; `down-mouse` part of the event. Like this:
|
||||
;;
|
||||
;; (global-unset-key (kbd "M-<down-mouse-1>"))
|
||||
;; (global-set-key (kbd "M-<mouse-1>") 'mc/add-cursor-on-click)
|
||||
;;
|
||||
;; Or you can do like me and find an unused, but less convenient, binding:
|
||||
;;
|
||||
;; (global-set-key (kbd "C-S-<mouse-1>") 'mc/add-cursor-on-click)
|
||||
|
||||
;; ## Unknown commands
|
||||
;;
|
||||
|
||||
;; Multiple-cursors uses two lists of commands to know what to do: the run-once list
|
||||
;; and the run-for-all list. It comes with a set of defaults, but it would be beyond silly
|
||||
;; to try and include all the known Emacs commands.
|
||||
;;
|
||||
|
||||
;; So that's why multiple-cursors occasionally asks what to do about a command. It will
|
||||
;; then remember your choice by saving it in `~/.emacs.d/.mc-lists.el`. You can change
|
||||
;; the location with:
|
||||
;;
|
||||
|
||||
;; (setq mc/list-file "/my/preferred/file")
|
||||
;;
|
||||
;;
|
||||
|
||||
;; ## Known limitations
|
||||
;;
|
||||
|
||||
;; * isearch-forward and isearch-backward aren't supported with multiple cursors.
|
||||
;; You should feel free to add a simplified version that can work with it.
|
||||
;; * Commands run with `M-x` won't be repeated for all cursors.
|
||||
;; * All key bindings that refer to lambdas are always run for all cursors. If you
|
||||
;; need to limit it, you will have to give it a name.
|
||||
;; * Redo might screw with your cursors. Undo works very well.
|
||||
;;
|
||||
;;
|
||||
|
||||
;; ## Contribute
|
||||
;;
|
||||
|
||||
;; Yes, please do. There's a suite of tests, so remember to add tests for your
|
||||
;; specific feature, or I might break it later.
|
||||
;;
|
||||
|
||||
;; You'll find the repo at:
|
||||
;;
|
||||
|
||||
;; https://github.com/magnars/multiple-cursors.el
|
||||
;;
|
||||
|
||||
;; To fetch the test dependencies:
|
||||
;;
|
||||
|
||||
;; $ cd /path/to/multiple-cursors
|
||||
;; $ git submodule update --init
|
||||
;;
|
||||
|
||||
;; Run the tests with:
|
||||
;;
|
||||
|
||||
;; $ ./util/ecukes/ecukes --graphical
|
||||
;;
|
||||
|
||||
;; ## Contributors
|
||||
|
||||
;; * [Takafumi Arakaki](https://github.com/tkf) made .mc-lists.el diff friendly
|
||||
;; * [Marco Baringer](https://github.com/segv) contributed looping to mc/cycle and adding cursors without region for mark-more.
|
||||
;; * [Ivan Andrus](https://github.com/gvol) added showing number of cursors in mode-line
|
||||
;; * [Fuco](https://github.com/Fuco1) added the first version of `mc/mark-all-like-this-dwim`
|
||||
|
||||
;; Thanks!
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'mc-edit-lines)
|
||||
(require 'mc-cycle-cursors)
|
||||
(require 'mc-mark-more)
|
||||
(require 'mc-mark-pop)
|
||||
(require 'rectangular-region-mode)
|
||||
(require 'mc-separate-operations)
|
||||
|
||||
(provide 'multiple-cursors)
|
||||
|
||||
|
||||
+24
-21
@@ -67,27 +67,30 @@ an exceedingly quick way of adding multiple cursors to multiple lines."
|
||||
|
||||
(defun rrm/repaint ()
|
||||
"Start from the anchor and draw a rectangle between it and point."
|
||||
(rrm/remove-rectangular-region-overlays)
|
||||
(let* ((annoying-arrows-mode nil)
|
||||
(point-column (current-column))
|
||||
(point-line (line-number-at-pos))
|
||||
(anchor-column (save-excursion (goto-char rrm/anchor) (current-column)))
|
||||
(anchor-line (save-excursion (goto-char rrm/anchor) (line-number-at-pos)))
|
||||
(left-column (if (< point-column anchor-column) point-column anchor-column))
|
||||
(right-column (if (> point-column anchor-column) point-column anchor-column))
|
||||
(navigation-step (if (< point-line anchor-line) 1 -1)))
|
||||
(move-to-column anchor-column)
|
||||
(set-mark (point))
|
||||
(move-to-column point-column)
|
||||
(mc/save-excursion
|
||||
(while (not (= anchor-line (line-number-at-pos)))
|
||||
(forward-line navigation-step)
|
||||
(move-to-column anchor-column)
|
||||
(when (= anchor-column (current-column))
|
||||
(set-mark (point))
|
||||
(move-to-column point-column)
|
||||
(when (= point-column (current-column))
|
||||
(mc/create-fake-cursor-at-point)))))))
|
||||
(if (not rectangular-region-mode)
|
||||
(remove-hook 'post-command-hook 'rrm/repaint t)
|
||||
;; else
|
||||
(rrm/remove-rectangular-region-overlays)
|
||||
(let* ((annoying-arrows-mode nil)
|
||||
(point-column (current-column))
|
||||
(point-line (line-number-at-pos))
|
||||
(anchor-column (save-excursion (goto-char rrm/anchor) (current-column)))
|
||||
(anchor-line (save-excursion (goto-char rrm/anchor) (line-number-at-pos)))
|
||||
(left-column (if (< point-column anchor-column) point-column anchor-column))
|
||||
(right-column (if (> point-column anchor-column) point-column anchor-column))
|
||||
(navigation-step (if (< point-line anchor-line) 1 -1)))
|
||||
(move-to-column anchor-column)
|
||||
(set-mark (point))
|
||||
(move-to-column point-column)
|
||||
(mc/save-excursion
|
||||
(while (not (= anchor-line (line-number-at-pos)))
|
||||
(forward-line navigation-step)
|
||||
(move-to-column anchor-column)
|
||||
(when (= anchor-column (current-column))
|
||||
(set-mark (point))
|
||||
(move-to-column point-column)
|
||||
(when (= point-column (current-column))
|
||||
(mc/create-fake-cursor-at-point))))))))
|
||||
|
||||
(defun rrm/switch-to-multiple-cursors (&rest forms)
|
||||
"Switch from rectangular-region-mode to multiple-cursors-mode."
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh -e
|
||||
ECUKES=$(find elpa/ecukes-*/ecukes | tail -1)
|
||||
carton exec "$ECUKES" "$@"
|
||||
+6
-13
@@ -1,20 +1,13 @@
|
||||
#!/bin/sh
|
||||
#!/bin/sh -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
set_default () {
|
||||
eval "
|
||||
if [ -z \$$1 ]; then
|
||||
$1=$2
|
||||
fi
|
||||
"
|
||||
}
|
||||
|
||||
set_default ECUKES_EMACS "$(which emacs)"
|
||||
ECUKES_EMACS=${EMACS:-$(which emacs)}
|
||||
export ECUKES_EMACS
|
||||
|
||||
echo "*** Emacs version ***"
|
||||
echo "ECUKES_EMACS =" $(which $ECUKES_EMACS)
|
||||
$ECUKES_EMACS --version
|
||||
echo "ECUKES_EMACS = $ECUKES_EMACS"
|
||||
"$ECUKES_EMACS" --version
|
||||
echo
|
||||
|
||||
exec ./util/ecukes/ecukes --graphical
|
||||
exec ./run-tests.sh $TAGS
|
||||
|
||||
-1
Submodule util/ecukes deleted from b2c449cf01
-2544
File diff suppressed because it is too large
Load Diff
-1
Submodule util/espuds deleted from 62ef75cb51
@@ -7,13 +7,13 @@ end
|
||||
|
||||
def run_all_tests
|
||||
system('clear')
|
||||
result = run "./util/ecukes/ecukes --graphical"
|
||||
result = run "./run-tests.sh"
|
||||
puts result
|
||||
end
|
||||
|
||||
def run_test(file)
|
||||
system('clear')
|
||||
result = run "./util/ecukes/ecukes --graphical #{file}"
|
||||
result = run "./run-tests.sh #{file} --verbose"
|
||||
puts result
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user