From 2e904a0be21f0755f65e64c89dd506eb54a4604d Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 26 Jun 2015 21:10:15 -0500 Subject: [PATCH 1/3] Add safety ceiling to cursor count at creation The customizable option `mc/max-cursors' now provides a soft maximum for the number of cursors allowable. This is helpful for slower emacsen who may freeze up when adding too many cursors (as in `mark-all' variants). Fix: #206 --- multiple-cursors-core.el | 45 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/multiple-cursors-core.el b/multiple-cursors-core.el index 8971a42..0388631 100644 --- a/multiple-cursors-core.el +++ b/multiple-cursors-core.el @@ -157,7 +157,8 @@ highlights the entire width of the window." (set-marker (overlay-get o 'point) nil) (set-marker (overlay-get o 'mark) nil) (mc/delete-region-overlay o) - (delete-overlay o)) + (delete-overlay o) + (decf mc--active-cursor-count)) (defun mc/pop-state-from-overlay (o) "Restore the state stored in given overlay and then remove the overlay." @@ -176,9 +177,43 @@ highlights the entire width of the window." "Returns a unique cursor id" (incf mc--current-cursor-id)) +(defvar mc--active-cursor-count 1 + "Number of active cursors. +This number is incremented by `mc/create-fake-cursor-at-point' +and decremented by `mc/remove-fake-cursor'.") + +(defvar mc--max-cursors-original nil + "This variable maintains the original maximum number of cursors. +When `mc/create-fake-cursor-at-point' is called and +`mc/max-cursors' is overridden, this value serves as a backup so +that `mc/max-cursors' can take on a new value. When +`mc/remove-fake-cursors' is called, the values are reset.") + +(defcustom mc/max-cursors nil + "Safety ceiling for the number of active cursors. +If your emacs slows down or freezes when using too many cursors, +customize this value appropriately. + +Cursors will be added until this value is reached, at which point +you can either temporarily override the value or abort the +operation entirely. + +If this value is nil, there is no ceiling." + :type '(integer) + :group 'multiple-cursors) + (defun mc/create-fake-cursor-at-point (&optional id) "Add a fake cursor and possibly a fake active region overlay based on point and mark. Saves the current state in the overlay to be restored later." + (unless mc--max-cursors-original + (setq mc--max-cursors-original mc/max-cursors)) + (when mc/max-cursors + (unless (< mc--active-cursor-count mc/max-cursors) + (if (yes-or-no-p (format "%d active cursors. Continue? " mc--active-cursor-count)) + (setq mc/max-cursors (read-number "Enter a new, temporary maximum: ")) + (mc/remove-fake-cursors) + (error "Aborted: too many cursors")))) + (incf mc--active-cursor-count) (let ((overlay (mc/make-cursor-overlay-at-point))) (overlay-put overlay 'mc-id (or id (mc/create-cursor-id))) (overlay-put overlay 'type 'fake-cursor) @@ -382,7 +417,10 @@ the original cursor, to inform about the lack of support." Do not use to conclude editing with multiple cursors. For that you should disable multiple-cursors-mode." (mc/for-each-fake-cursor - (mc/remove-fake-cursor cursor))) + (mc/remove-fake-cursor cursor)) + (when mc--max-cursors-original + (setq mc/max-cursors mc--max-cursors-original)) + (setq mc--max-cursors-original nil)) (defun mc/keyboard-quit () "Deactivate mark if there are any active, otherwise exit multiple-cursors-mode." @@ -424,7 +462,8 @@ The entries are returned in the order they are found in the buffer." (defun mc--maybe-set-killed-rectangle () "Add the latest kill-ring entry for each cursor to killed-rectangle. So you can paste it in later with `yank-rectangle'." - (let ((entries (mc--kill-ring-entries))) + (let ((entries (let ((mc--active-cursor-count -1)) + (mc--kill-ring-entries)))) (unless (mc--all-equal entries) (setq killed-rectangle entries)))) From cd967e720fa125b74b51e3f0217562d6743d12fb Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 26 Jun 2015 21:33:48 -0500 Subject: [PATCH 2/3] Small optimization If we remove the ceiling entirely, we won't even enter the form that checks values, etc. --- multiple-cursors-core.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/multiple-cursors-core.el b/multiple-cursors-core.el index 0388631..cba6da6 100644 --- a/multiple-cursors-core.el +++ b/multiple-cursors-core.el @@ -462,8 +462,7 @@ The entries are returned in the order they are found in the buffer." (defun mc--maybe-set-killed-rectangle () "Add the latest kill-ring entry for each cursor to killed-rectangle. So you can paste it in later with `yank-rectangle'." - (let ((entries (let ((mc--active-cursor-count -1)) - (mc--kill-ring-entries)))) + (let ((entries (let (mc/max-cursors) (mc--kill-ring-entries)))) (unless (mc--all-equal entries) (setq killed-rectangle entries)))) From 3f1611c7cbd4daa52f15cb3a5f3bbcc11d4eb949 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sat, 27 Jun 2015 09:38:02 -0500 Subject: [PATCH 3/3] Factor out a superfluous variable `mc--active-cursor-count' was effectively duplicating the purpose of `mc/num-cursors'. While the latter is necessarily slower (being a function), it is well-tested and stable. --- multiple-cursors-core.el | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/multiple-cursors-core.el b/multiple-cursors-core.el index cba6da6..6602703 100644 --- a/multiple-cursors-core.el +++ b/multiple-cursors-core.el @@ -157,8 +157,7 @@ highlights the entire width of the window." (set-marker (overlay-get o 'point) nil) (set-marker (overlay-get o 'mark) nil) (mc/delete-region-overlay o) - (delete-overlay o) - (decf mc--active-cursor-count)) + (delete-overlay o)) (defun mc/pop-state-from-overlay (o) "Restore the state stored in given overlay and then remove the overlay." @@ -177,11 +176,6 @@ highlights the entire width of the window." "Returns a unique cursor id" (incf mc--current-cursor-id)) -(defvar mc--active-cursor-count 1 - "Number of active cursors. -This number is incremented by `mc/create-fake-cursor-at-point' -and decremented by `mc/remove-fake-cursor'.") - (defvar mc--max-cursors-original nil "This variable maintains the original maximum number of cursors. When `mc/create-fake-cursor-at-point' is called and @@ -208,12 +202,11 @@ Saves the current state in the overlay to be restored later." (unless mc--max-cursors-original (setq mc--max-cursors-original mc/max-cursors)) (when mc/max-cursors - (unless (< mc--active-cursor-count mc/max-cursors) - (if (yes-or-no-p (format "%d active cursors. Continue? " mc--active-cursor-count)) + (unless (< (mc/num-cursors) mc/max-cursors) + (if (yes-or-no-p (format "%d active cursors. Continue? " (mc/num-cursors))) (setq mc/max-cursors (read-number "Enter a new, temporary maximum: ")) (mc/remove-fake-cursors) (error "Aborted: too many cursors")))) - (incf mc--active-cursor-count) (let ((overlay (mc/make-cursor-overlay-at-point))) (overlay-put overlay 'mc-id (or id (mc/create-cursor-id))) (overlay-put overlay 'type 'fake-cursor)