gabesoft / evil-mc

Multiple cursors implementation for evil-mode
MIT License
383 stars 35 forks source link

Evil-Escape compatibility #41

Open CeleritasCelery opened 7 years ago

CeleritasCelery commented 7 years ago

I am trying to fix #27 by making some changes to evil-escape. I have got it mostly working with one minor issue that I hope I could get some help with. When you use evil-escape you have a two-key escape sequence. The problem in #27 was that the first key was being left. I got that cleaned up but when you try and undo it shows the insertion of the first-key as the top of the stack (f by default). This causes problems because only the fake cursors have this item in their undo stack so now the real cursor and the fake cursors are out of sync. Is there a way to change the undo stack for the fake cursors so this doesn't show up in their undo stack?

and just for reference here are the changes I made. I couldn't find anyway to acomplish this by changing evil-mc, I had to change evil-escape. I changed one function called evil-escape-pre-command-hook

(defun evil-escape-pre-command-hook ()
  "evil-escape pre-command hook."
  (with-demoted-errors "evil-escape: Error %S"
    (when (evil-escape-p)
      (let* ((modified (buffer-modified-p))
             (inserted (evil-escape--insert))
             (fkey (elt evil-escape-key-sequence 0))
             (skey (elt evil-escape-key-sequence 1))
             (evt (read-event nil nil evil-escape-delay)))
        (when inserted (evil-escape--delete))
        (set-buffer-modified-p modified)
        (cond
         ((and (characterp evt)
               (or (and (equal (this-command-keys) (evil-escape--first-key))
                        (char-equal evt skey))
                   (and evil-escape-unordered-key-sequence
                        (equal (this-command-keys) (evil-escape--second-key))
                        (char-equal evt fkey))))
          (evil-repeat-stop)
          (when (evil-escape-func)
            (progn
              (run-at-time "0.05 sec" nil
                           (lambda ()
                             (evil-mc-execute-for-all-fake-cursors
                              (lambda (cursor)
                                (let ((undo-inhibit-record-point t))
                                  (delete-char -1)
                                  (unless (or (bolp) (eolp))
                                    (evil-backward-char)))))))
              (setq this-command (evil-escape-func)))))
         ((null evt))
         (t (setq unread-command-events
                  (append unread-command-events (list evt)))))))))

I am sure @syl20bnr could come up with a much better solution. But this seems to working for me. Any help would be appreciated.

gabesoft commented 7 years ago

You could define a function that changes the undo-stack the way you need to. See evil-mc-execute-default-undo and evil-mc-execute-default-redo handlers in evil-mc-command-execute.el for some examples of how to handle the undo-stack. Then you could call that function using evil-mc-execute-for-all-fake-cursors

CeleritasCelery commented 7 years ago

I am using undo-tree. Is that going to make a difference?

gabesoft commented 7 years ago

No, it should work with undo-tree

CeleritasCelery commented 7 years ago

okay. Then maybe I am missing something here. I would assume that calling

(evil-mc-execute-for-all-fake-cursors
   (lambda (cursor)
      (setq undo-stack-pointer (cdr-safe undo-stack-pointer ))))

would remove that top item off of each of the cursors undo stack. But it doesn't seem to do that. Is there some special way to use this? I just want to remove the top most item that has the f's in there.

gabesoft commented 7 years ago

Actually for that to work the context of the current cursor has to be set up before you can modify the undo-stack-pointer otherwise you're just modifying the real cursor one. In the function evil-mc-execute-for-all see line 600 for how to set up the current cursor state-variables. The function evil-mc-execute-for-all-cursors does not do that but your function evil-mc-execute-for-all-fake-cursors will need to do it if you want to modify the undo-stack-pointer for each cursor.

duianto commented 2 years ago

Here's a possible fix:

  (setq evil-mc-custom-known-commands
        '((custom/evil-mc-evil-escape-move-back-fake-cursors
           (:default . evil-mc-execute-default-call))))

  (defun custom/evil-mc-evil-escape-move-back-fake-cursors ()
    "Move the fake cursors to the left once,
unless they already are at the beginning of the line."
    (unless (bolp) (backward-char)))

  (defun custom/evil-mc-evil-escape-fix ()
    "Prevent the first evil-escape-key-sequence key (default: f),
from being typed at all of the fake cursors.
And move back the fake cursors when the real insert state cursor is at the end
of a line."
    (when (evil-mc-has-cursors-p)
      (evil-mc-pause-cursors)
      (run-with-idle-timer
       0 nil '(lambda ()
                (evil-mc-resume-cursors)
                (let ((evil-mc-command '((:name . custom/evil-mc-evil-escape-move-back-fake-cursors))))
                  (evil-mc-execute-for-all))))))

  (advice-add 'evil-escape-func :before 'custom/evil-mc-evil-escape-fix)
  ;; (advice-remove 'evil-escape-func 'custom/evil-mc-evil-escape-fix)