mirror of
https://github.com/magnars/multiple-cursors.el.git
synced 2025-10-14 05:13:05 +00:00
Merge pull request #23 from segv/master
Improve mc/cycle and mc/mark-next/prev
This commit is contained in:
commit
c3b2d8483b
@ -94,3 +94,41 @@ Feature: Marking multiple parts of the buffer
|
|||||||
And I type "more"
|
And I type "more"
|
||||||
Then I should have 2 cursors
|
Then I should have 2 cursors
|
||||||
And I should see "Here's more, more and text"
|
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
|
||||||
|
"""
|
||||||
|
@ -158,3 +158,17 @@ Feature: Multiple cursors core
|
|||||||
contains
|
contains
|
||||||
twice
|
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"
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
(eval-when-compile (require 'cl))
|
(eval-when-compile (require 'cl))
|
||||||
|
|
||||||
(defun mc/next-cursor-after-point ()
|
(defun mc/next-fake-cursor-after-point ()
|
||||||
(let ((pos (point))
|
(let ((pos (point))
|
||||||
(next-pos (point-max))
|
(next-pos (point-max))
|
||||||
next)
|
next)
|
||||||
@ -42,7 +42,7 @@
|
|||||||
(setq next cursor))))
|
(setq next cursor))))
|
||||||
next))
|
next))
|
||||||
|
|
||||||
(defun mc/prev-cursor-before-point ()
|
(defun mc/prev-fake-cursor-before-point ()
|
||||||
(let ((pos (point))
|
(let ((pos (point))
|
||||||
(prev-pos (point-min))
|
(prev-pos (point-min))
|
||||||
prev)
|
prev)
|
||||||
@ -54,23 +54,58 @@
|
|||||||
(setq prev cursor))))
|
(setq prev cursor))))
|
||||||
prev))
|
prev))
|
||||||
|
|
||||||
(defun mc/cycle-forward ()
|
(defcustom mc/cycle-looping-behaviour 'continue
|
||||||
(interactive)
|
"What to do if asked to cycle beyond the last cursor or before the first cursor."
|
||||||
(let ((next-cursor (mc/next-cursor-after-point)))
|
:type '(radio (const :tag "Loop around to beginning/end of document." continue)
|
||||||
(unless next-cursor
|
(const :tag "Warn and then loop around." warn)
|
||||||
(error "We're already at the last cursor"))
|
(const :tag "Signal an error." error)
|
||||||
|
(const :tag "Don't loop." stop)))
|
||||||
|
|
||||||
|
(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/create-fake-cursor-at-point)
|
||||||
(mc/pop-state-from-overlay next-cursor)
|
(mc/pop-state-from-overlay next-cursor)
|
||||||
(recenter)))
|
(recenter))
|
||||||
|
|
||||||
|
(defun mc/cycle-forward ()
|
||||||
|
(interactive)
|
||||||
|
(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 ()
|
(defun mc/cycle-backward ()
|
||||||
(interactive)
|
(interactive)
|
||||||
(let ((prev-cursor (mc/prev-cursor-before-point)))
|
(mc/cycle (mc/prev-fake-cursor-before-point)
|
||||||
(unless prev-cursor
|
(mc/last-fake-cursor-before (point-max))
|
||||||
(error "We're already at the first cursor"))
|
"We're already at the last cursor"))
|
||||||
(mc/create-fake-cursor-at-point)
|
|
||||||
(mc/pop-state-from-overlay prev-cursor)
|
|
||||||
(recenter)))
|
|
||||||
|
|
||||||
(define-key mc/keymap (kbd "C-v") 'mc/cycle-forward)
|
(define-key mc/keymap (kbd "C-v") 'mc/cycle-forward)
|
||||||
(define-key mc/keymap (kbd "M-v") 'mc/cycle-backward)
|
(define-key mc/keymap (kbd "M-v") 'mc/cycle-backward)
|
||||||
|
113
mc-mark-more.el
113
mc-mark-more.el
@ -79,34 +79,54 @@
|
|||||||
(mc/cursor-end cursor))))
|
(mc/cursor-end cursor))))
|
||||||
strings))
|
strings))
|
||||||
|
|
||||||
|
(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)))
|
||||||
|
|
||||||
|
(defun mc/mark-more-like-this (skip-last direction)
|
||||||
|
(let ((case-fold-search nil)
|
||||||
|
(re (regexp-opt (mc/region-strings)))
|
||||||
|
(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
|
;;;###autoload
|
||||||
(defun mc/mark-next-like-this (arg)
|
(defun mc/mark-next-like-this (arg)
|
||||||
"Find and mark the next part of the buffer matching the currently active region
|
"Find and mark the next part of the buffer matching the currently active region
|
||||||
With negative ARG, delete the last one instead.
|
With negative ARG, delete the last one instead.
|
||||||
With zero ARG, skip the last one and mark next."
|
With zero ARG, skip the last one and mark next."
|
||||||
(interactive "p")
|
(interactive "p")
|
||||||
(unless (region-active-p)
|
(if (region-active-p)
|
||||||
(error "Mark a region to match first."))
|
(if (< arg 0)
|
||||||
(when (< arg 0)
|
(mc/remove-fake-cursor (mc/furthest-cursor-after-point))
|
||||||
(mc/remove-fake-cursor (mc/furthest-cursor-after-point)))
|
(mc/mark-more-like-this (= arg 0) 'forwards))
|
||||||
(when (>= arg 0)
|
(mc/mark-lines arg 'forwards))
|
||||||
(let ((case-fold-search nil)
|
(mc/maybe-multiple-cursors-mode))
|
||||||
(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)))
|
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun mc/mark-previous-like-this (arg)
|
(defun mc/mark-previous-like-this (arg)
|
||||||
@ -114,28 +134,33 @@ With zero ARG, skip the last one and mark next."
|
|||||||
With negative ARG, delete the last one instead.
|
With negative ARG, delete the last one instead.
|
||||||
With zero ARG, skip the last one and mark next."
|
With zero ARG, skip the last one and mark next."
|
||||||
(interactive "p")
|
(interactive "p")
|
||||||
(unless (region-active-p)
|
(if (region-active-p)
|
||||||
(error "Mark a region to match first."))
|
(if (< arg 0)
|
||||||
(when (< arg 0)
|
(mc/remove-fake-cursor (mc/furthest-cursor-before-point))
|
||||||
(mc/remove-fake-cursor (mc/furthest-cursor-before-point)))
|
(mc/mark-more-like-this (= arg 0) 'backwards))
|
||||||
(when (>= arg 0)
|
(mc/mark-lines arg 'backwards))
|
||||||
(let ((case-fold-search nil)
|
(mc/maybe-multiple-cursors-mode))
|
||||||
(point-first (< (point) (mark)))
|
|
||||||
(re (regexp-opt (mc/region-strings)))
|
(defun mc/mark-lines (num-lines direction)
|
||||||
(furthest-cursor (mc/furthest-cursor-before-point)))
|
(dotimes (i num-lines)
|
||||||
(mc/save-excursion
|
(mc/create-fake-cursor-at-point)
|
||||||
(goto-char (mc/first-region-start))
|
(ecase direction
|
||||||
(when (= arg 0)
|
(forwards (loop do (next-line 1 nil)
|
||||||
(mc/remove-fake-cursor furthest-cursor))
|
while (mc/all-fake-cursors (point) (1+ (point)))))
|
||||||
(if (search-backward-regexp re nil t)
|
(backwards (loop do (previous-line 1 nil)
|
||||||
(progn
|
while (mc/all-fake-cursors (point) (1+ (point))))))))
|
||||||
(push-mark (match-end 0))
|
|
||||||
(unless point-first (exchange-point-and-mark))
|
;;;###autoload
|
||||||
(mc/create-fake-cursor-at-point))
|
(defun mc/mark-next-lines (arg)
|
||||||
(error "no more found backward")))))
|
(interactive "p")
|
||||||
(if (> (mc/num-cursors) 1)
|
(mc/mark-lines arg 'forwards)
|
||||||
(multiple-cursors-mode 1)
|
(mc/maybe-multiple-cursors-mode))
|
||||||
(multiple-cursors-mode 0)))
|
|
||||||
|
;;;###autoload
|
||||||
|
(defun mc/mark-previous-lines (arg)
|
||||||
|
(interactive "p")
|
||||||
|
(mc/mark-lines arg 'backwards)
|
||||||
|
(mc/maybe-multiple-cursors-mode))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun mc/unmark-next-like-this (arg)
|
(defun mc/unmark-next-like-this (arg)
|
||||||
|
@ -49,12 +49,15 @@
|
|||||||
(setq buffer-undo-list ;; otherwise add a function to activate this cursor
|
(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)))))
|
(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)
|
(defmacro mc/for-each-fake-cursor (&rest forms)
|
||||||
"Runs the body for each fake cursor, bound to the name cursor"
|
"Runs the body for each fake cursor, bound to the name cursor"
|
||||||
`(mapc #'(lambda (cursor)
|
`(mapc #'(lambda (cursor) ,@forms)
|
||||||
(when (mc/fake-cursor-p cursor)
|
(mc/all-fake-cursors)))
|
||||||
,@forms))
|
|
||||||
(overlays-in (point-min) (point-max))))
|
|
||||||
|
|
||||||
(defmacro mc/save-excursion (&rest forms)
|
(defmacro mc/save-excursion (&rest forms)
|
||||||
"Saves and restores all the state that multiple-cursors cares about."
|
"Saves and restores all the state that multiple-cursors cares about."
|
||||||
|
Loading…
x
Reference in New Issue
Block a user