gabesoft / evil-mc

Multiple cursors implementation for evil-mode
MIT License
387 stars 36 forks source link

escape insert-state has a trouble with evil-escape #27

Open honmaple opened 8 years ago

honmaple commented 8 years ago

I use evil-escape to quit insert-state,but I find a trouble with this.

evil
evil
evil

I edit (insert)

helloedit
helloedit
helloedit

when I press escape,it quit insert-state well.But I use 'jj' to quit insert-state.The content would look like this(normal-state)

hellojedit
hellojedit
helloedit

How can I resolve this problem?
and I suggest use 'escape' instead of 'g r u' for evil-mc-undo-all-cursors. Sorry abot my English.Thank you.

gabesoft commented 8 years ago

Yea, I haven't used evil-escape. So, I haven't run into this problem before. I'll take a look when I get a chance. Thanks for reporting.

gabesoft commented 8 years ago

For a quick fix, just to get you going, you could add evil-escape to the evil-mc-incompatible-minor-modes so that it gets disabled when there are multiple cursors. That way you can just use the regular escape key when there are multiple cursors and jj otherwise.

honmaple commented 8 years ago

I just add

(push 'evil-escape-mode evil-mc-incompatible-minor-modes)

to disable 'jj'. Though it's not resolve,use the regular escape key is also a way.Thank you

aldanor commented 8 years ago

Confirming this, when using evil-escape from insert-mode, the first letter of the escape sequence is erroneously added to all cursors except the currently active one.

Dickby commented 8 years ago

I binded evil-escape to a "jk" key-chord, and the fake cursors are not stepping back one character if I use it with active evil-mc.

CeleritasCelery commented 7 years ago

@gabesoft is there something like a before-cursor-deletion-hook that would allow us to call evil-escape--delete-func at each cursor before they are removed?

gabesoft commented 7 years ago

There is a hook that gets run when cursors are deleted. See evil-mc-after-cursors-deleted but it's called only once after all cursors are deleted.

duianto commented 3 years ago

This might be a solution to exit from insert state with evil-escape.

  (defun custom/evil-mc-evil-escape-fix ()
    (when (evil-mc-has-cursors-p)
      (evil-mc-pause-cursors)
      (run-with-idle-timer 0 nil '(lambda () (evil-mc-resume-cursors)))))

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

It pauses the cursors before exiting insert state, therefore nothing is written to the fake cursors.

An issue remains, about how to align the cursors.

The real cursor moves backwards one character (default vim behavior), but the paused cursors remains at their pre evil-escape positions.

[] = fake cursors | = real cursor, in insert state

Before:

ab[c]
ab[c]
ab|c

After, pressing fd quickly.

ab[c]
ab[c]
a[b]c

I don't know how to move the fake cursors in elisp. (evil-forward-char) or (evil-backward-char) only moves the real cursor, when the cursors are paused or resumed.

The real cursor can be moved forward once to align them, but the expected behavior is that all cursors are moved backwards when exiting insert state, that's how it works when Esc is pressed instead of fd quickly.

duianto commented 3 years ago

This moves back the fake cursors, to keep them aligned.

  (defun custom/evil-mc-evil-escape-fix ()
    (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 . evil-backward-char))))
                  (evil-mc-execute-for-all))))))

But there are two cases where they become unaligned:

Issue 1 of 2

If insert state is entered on the lines last character, by pressing a (evil-append).

abc|
def abc[]
abc[]

fd results in:

ab[c]
def a[b]c
a[b]c

It seems like the fake cursors only looked like they were after the last character.

Issue 2 of 2

Update: This issue isn't limited to this "solution". And it's unrelated to evil-escape. Because it also happens when pressing Esc.

An issue has been opened about this: esc unaligns the cursors, when real cursor on first column https://github.com/gabesoft/evil-mc/issues/124 End of Update

When the real cursor is on the first column.

|abc
def [a]bc
[a]bc

fd moves back the fake cursors that are not on the first column.

[a]bc
def[ ]abc
[a]bc

Notes

The docstring for: evil-mc-execute-for-all says:

Execute the current command, stored at ‘evil-mc-command’, for all fake cursors.

When there are multiple evil-mc cursors. and h (evil-backward-char) is pressed. Then evil-mc-execute-for-all is called in the post-command-hook.

And evil-mc-command contains:

((nil) (:keys . [104]) (:keys-count) (:undo-list-pointer-post nil (nil rear-nonsticky nil 8 . 9) (
 . -12) (8 . 13) 5 nil (nil rear-nonsticky nil 4 . 5) (
 . -8) (4 . 9) 3 nil (1 . 4) (t . 0)) (:keys-post-raw . [104]) (:keys-post . [104]) (:last-input . 104) (:evil-state-end . normal) (:undo-list-pointer-pre nil (nil rear-nonsticky nil 8 . 9) (
 . -12) (8 . 13) 5 nil (nil rear-nonsticky nil 4 . 5) (
 . -8) (4 . 9) 3 nil (1 . 4) (t . 0)) (:evil-state-begin . normal) (:keys-pre-with-count nil evil-backward-char [104] nil) (:keys-pre . [104]) (:name . evil-backward-char))

And it works to move the back the fake cursors when evaluating: (newlines have been added to show each list on it's own line, for readability)

(let ((evil-mc-command '((nil)
                         (:keys . [104])
                         (:keys-count)
                         (:undo-list-pointer-post nil (nil rear-nonsticky nil 8 . 9) ( . -12) (8 . 13) 5 nil (nil rear-nonsticky nil 4 . 5) ( . -8) (4 . 9) 3 nil (1 . 4) (t . 0))
                         (:keys-post-raw . [104])
                         (:keys-post . [104])
                         (:last-input . 104)
                         (:evil-state-end . normal)
                         (:undo-list-pointer-pre nil (nil rear-nonsticky nil 8 . 9) ( . -12) (8 . 13) 5 nil (nil rear-nonsticky nil 4 . 5) ( . -8) (4 . 9) 3 nil (1 . 4) (t . 0))
                         (:evil-state-begin . normal)
                         (:keys-pre-with-count nil evil-backward-char [104] nil)
                         (:keys-pre . [104])
                         (:name . evil-backward-char))))
  (evil-mc-execute-for-all))

But it seems to also work to reduce evil-mc-command to:

(let ((evil-mc-command '((:name . evil-backward-char))))
  (evil-mc-execute-for-all))

Are the other commands important?

duianto commented 3 years ago

This seems to work:

  (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)

It handles issue 1 of 2 above: https://github.com/gabesoft/evil-mc/issues/27#issuecomment-859996030 Pressing fd (evil-escape) with the real insert state cursor at the end of a line. Moves back the fake cursors so that all cursors are aligned.

Issue 2 of 2 might not have an easy/desired fix. The expected vim behavior when exiting insert state to normal state is that the cursor moves back. This causes the evil-mc cursors to become unaligned, when the real or a fake cursor is at the beginning of a line.