mirror of
https://github.com/magnars/multiple-cursors.el.git
synced 2025-10-13 13:03:03 +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"
|
||||
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
|
||||
"""
|
||||
|
@ -158,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"
|
||||
|
||||
|
@ -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,23 +54,58 @@
|
||||
(setq prev cursor))))
|
||||
prev))
|
||||
|
||||
(defun mc/cycle-forward ()
|
||||
(interactive)
|
||||
(let ((next-cursor (mc/next-cursor-after-point)))
|
||||
(unless next-cursor
|
||||
(error "We're already at the last cursor"))
|
||||
(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)))
|
||||
|
||||
(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)))
|
||||
(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 ()
|
||||
(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)
|
||||
|
113
mc-mark-more.el
113
mc-mark-more.el
@ -79,34 +79,54 @@
|
||||
(mc/cursor-end cursor))))
|
||||
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
|
||||
(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-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 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))
|
||||
|
||||
(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 (arg)
|
||||
|
@ -49,12 +49,15 @@
|
||||
(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."
|
||||
|
Loading…
x
Reference in New Issue
Block a user