diff --git a/features/multiple-cursors-core.feature b/features/multiple-cursors-core.feature index 0a6e8b9..3bd4103 100644 --- a/features/multiple-cursors-core.feature +++ b/features/multiple-cursors-core.feature @@ -65,6 +65,14 @@ Feature: Multiple cursors core And I type "!" Then I should see "This text! contains the word text! twice" + Scenario: Undo mode + Given I have cursors at "text" in "This text contains the word text twice" + When I press "C-g" + And I press "M-f" + And I press "C-_" + And I type "!" + Then I should see "This !text contains the word !text twice" + Scenario: Setting and popping mark Given I have cursors at "text" in "This text contains the word text twice" And I press "C-SPC" diff --git a/multiple-cursors-core.el b/multiple-cursors-core.el index d2b2c42..a0d10ef 100644 --- a/multiple-cursors-core.el +++ b/multiple-cursors-core.el @@ -52,6 +52,14 @@ (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/get-all-fake-cursors-state () + "Return list of all fake cursor states +like this: ((CURSOR-POS MARK-POSITION (list of cursor specific variables)) ...)" + (mapcar (lambda (cursor) + (mc/get-state-from-overlay cursor)) + (mc/all-fake-cursors))) + (defun mc/all-fake-cursors (&optional start end) (remove-if-not 'mc/fake-cursor-p (overlays-in (or start (point-min)) @@ -152,6 +160,23 @@ highlights the entire width of the window." (dolist (var mc/cursor-specific-vars) (when (boundp var) (set var (overlay-get o var))))) +(defun mc/get-state-from-overlay (o) + "Return list describing state of cursor overlay" + (list + (marker-position (overlay-get o 'point)) + (marker-position (overlay-get o 'mark)) + (mapcar (lambda (var) + (when (boundp var) (cons var (overlay-get o var)))) + mc/cursor-specific-vars))) + +(defun mc/create-overlay-from-state (point mark cursor-vars) + "Creates cursor overlay according to cursor-info" + (goto-char point) + (push-mark mark t) + (loop for (var . value) in cursor-vars + do (setq var value)) + (mc/create-fake-cursor-at-point)) + (defun mc/remove-fake-cursor (o) "Delete overlay with state, including dependent overlays and markers." (set-marker (overlay-get o 'point) nil) @@ -459,6 +484,20 @@ They are temporarily disabled when multiple-cursors are active.") :group 'multiple-cursors) (put 'mc/mode-line 'risky-local-variable t) +(defun mc/restore-mode (real-cursor real-mark fake-cursors) + "Restore state of mc mode after undo" + (save-excursion + ;; remove all existing fake cursors + (when multiple-cursors-mode + (mc/remove-fake-cursors)) + ;; and create set a new one + (mapc #'(lambda (cursor) + (apply 'mc/create-overlay-from-state cursor)) + fake-cursors)) + (goto-char real-cursor) + (push-mark real-mark t) + (multiple-cursors-mode t)) + ;;;###autoload (define-minor-mode multiple-cursors-mode "Mode while multiple cursors are active." @@ -471,6 +510,7 @@ They are temporarily disabled when multiple-cursors are active.") (run-hooks 'multiple-cursors-mode-enabled-hook)) (remove-hook 'post-command-hook 'mc/execute-this-command-for-all-cursors t) (remove-hook 'pre-command-hook 'mc/make-a-note-of-the-command-being-run t) + (push `(apply mc/restore-mode . ,(list (point) (mark) (mc/get-all-fake-cursors-state))) buffer-undo-list) (setq mc--this-command nil) (mc--maybe-set-killed-rectangle) (mc/remove-fake-cursors)