emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/multiple-cursors c3b2d84 164/434: Merge pull request #23 f


From: ELPA Syncer
Subject: [nongnu] elpa/multiple-cursors c3b2d84 164/434: Merge pull request #23 from segv/master
Date: Sat, 7 Aug 2021 09:20:18 -0400 (EDT)

branch: elpa/multiple-cursors
commit c3b2d8483b5ba6e8253a068504b722fd30cd810d
Merge: a0f771f 2818d9e
Author: Magnar Sveen <magnars@gmail.com>
Commit: Magnar Sveen <magnars@gmail.com>

    Merge pull request #23 from segv/master
    
    Improve mc/cycle and mc/mark-next/prev
---
 features/mark-more.feature             |  38 +++++++++++
 features/multiple-cursors-core.feature |  14 ++++
 mc-cycle-cursors.el                    |  63 ++++++++++++++----
 mc-mark-more.el                        | 113 ++++++++++++++++++++-------------
 multiple-cursors-core.el               |  11 ++--
 5 files changed, 177 insertions(+), 62 deletions(-)

diff --git a/features/mark-more.feature b/features/mark-more.feature
index 189b22c..8ca86f7 100644
--- a/features/mark-more.feature
+++ b/features/mark-more.feature
@@ -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
+    """
diff --git a/features/multiple-cursors-core.feature 
b/features/multiple-cursors-core.feature
index 46d14bf..8d8fb65 100644
--- a/features/multiple-cursors-core.feature
+++ b/features/multiple-cursors-core.feature
@@ -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"
+
diff --git a/mc-cycle-cursors.el b/mc-cycle-cursors.el
index f70a96a..46d7426 100644
--- a/mc-cycle-cursors.el
+++ b/mc-cycle-cursors.el
@@ -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))
 
+(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))
+
 (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)
diff --git a/mc-mark-more.el b/mc-mark-more.el
index 883fb1e..7d722b0 100644
--- a/mc-mark-more.el
+++ b/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)
diff --git a/multiple-cursors-core.el b/multiple-cursors-core.el
index 387f7ea..3874231 100644
--- a/multiple-cursors-core.el
+++ b/multiple-cursors-core.el
@@ -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."



reply via email to

[Prev in Thread] Current Thread [Next in Thread]