Repository: vscode-neovim/vscode-neovim (TypeScript)

Issue:
[Bug] `ci'`, cursor jumps to the end of line
OS: macOS 14
nvim: 0.9.5
vscode-nvim: 1.5.0

vscode-neovim behavior (cursor jumps to the end of the line):

https://github.com/vscode-neovim/vscode-neovim/assets/28700378/238e82cd-8bfc-4e7b-a25d-1cc9b2758bde

Neovim behavior (cursor stays inside the quotes):

https://github.com/vscode-neovim/vscode-neovim/assets/28700378/b71d0191-ddeb-4c1e-8063-e4bf10b39f83

Code:

```styl
$base = '../rewards'
$bgi = $proxy($base, 'bgi')

@import '../includes/highlight.styl'

.open-task-btn
  $bgi '../static/rewards/open-tasks-btn-double.png'
  $highlight '../static/rewards/open-tasks-btn-double.png'
```


Discussion: #1080 Still can't reproduce
@xiyaowong I tried disabling all extensions except vscode-neovim, but the issue still persists.
@xiyaowong Can you share your environment info, e.g. OS, vscode version, vscode-neovim version, neovim version, etc?
OS: windows 11
All are the latest versions
@xiyaowong Can you try this on a Mac? I don't have a Windows machine at hand right now.
Facing same issue on MacOS 14.2.1
@xiyaowong Confirmed, both #1752 and #1080 are not reproducible on Windows 11 but reproducible on macOS 14, all other related apps are the latest.
FWIW, I've tried to replicate this on Linux, and could not. I guess it's macOS specific?
@ollien Thanks for the experiment, looks like it is.
Please confirm if the issue still exists
@xiyaowong Still exists.



Fix (patch):
diff --git a/src/cursor_manager.ts b/src/cursor_manager.ts
index aadacdaf5..d50e9c8e8 100644
--- a/src/cursor_manager.ts
+++ b/src/cursor_manager.ts
@@ -61,8 +61,17 @@ export class CursorManager implements Disposable {
      * cursor updates while entering insert mode and insert mode commands. Thus, when those events occur,
      * this flag is used to disable ignoring the update. This is set to true when entering insert
      * mode or running insert mode command, and set to false before document updates in insert mode.
+     *
+     * The flag corresponds to each `TextEditor`
      */
-    public wantInsertCursorUpdate = true;
+    private _wantInsertCursorUpdate: WeakMap<TextEditor, boolean> = new WeakMap();
+    public wantInsertCursorUpdate = (editor: TextEditor) => this._wantInsertCursorUpdate.get(editor) ?? false;
+    public setWantInsertCursorUpdate = (editor: TextEditor | undefined, want: boolean) => {
+        if (!editor) return;
+        if (want) this._wantInsertCursorUpdate.set(editor, want);
+        else this._wantInsertCursorUpdate.delete(editor);
+    };
+
     /**
      * Set of grids that needs to undergo cursor update
      */
@@ -138,7 +147,9 @@ export class CursorManager implements Disposable {
                 break;
             }
             case "mode_change": {
-                if (this.main.modeManager.isInsertMode) this.wantInsertCursorUpdate = true;
+                if (this.main.modeManager.isInsertMode) {
+                    this.setWantInsertCursorUpdate(window.activeTextEditor, true);
+                }
                 args.forEach((arg) => this.updateCursorStyle(arg[0]));
                 break;
             }
@@ -222,7 +233,7 @@ export class CursorManager implements Disposable {
 
         if (
             this.main.modeManager.isInsertMode &&
-            !this.wantInsertCursorUpdate &&
+            !this.wantInsertCursorUpdate(editor) &&
             !this.main.modeManager.isRecordingInInsertMode
         ) {
             logger.debug(`Skipping insert cursor update in editor`);
diff --git a/src/document_change_manager.ts b/src/document_change_manager.ts
index 65d43b0b0..4b059839e 100644
--- a/src/document_change_manager.ts
+++ b/src/document_change_manager.ts
@@ -336,6 +336,7 @@ export class DocumentChangeManager implements Disposable {
 
     private onChangeTextDocumentLocked = async (e: TextDocumentChangeEvent, origText: string): Promise<void> => {
         const { document: doc, contentChanges } = e;
+        const editor = window.visibleTextEditors.find((e) => e.document === doc);
 
         logger.log(doc.uri, LogLevel.Debug, `Change text document for uri: ${doc.uri.toString()}`);
         logger.log(
@@ -391,7 +392,7 @@ export class DocumentChangeManager implements Disposable {
         this.bufferSkipTicks.set(bufId, bufTick + changeArgs.length);
 
         logger.log(doc.uri, LogLevel.Debug, `Setting wantInsertCursorUpdate to false`);
-        this.main.cursorManager.wantInsertCursorUpdate = false;
+        if (editor) this.main.cursorManager.setWantInsertCursorUpdate(editor, false);
 
         await actions.lua("handle_changes", bufId, changeArgs);
 
@@ -401,7 +402,6 @@ export class DocumentChangeManager implements Disposable {
         // position/visual range of nvim will change. And the changed result is
         // usually incorrect, so synchronization is forced here.
         if (!this.main.modeManager.isInsertMode) {
-            const editor = window.visibleTextEditors.find((e) => e.document === doc);
             if (editor && editor === activeEditor) {
                 // Don't await here, since it will cause a deadlock
                 this.main.cursorManager.applySelectionChanged(editor);
diff --git a/src/typing_manager.ts b/src/typing_manager.ts
index b58e21f4d..61dd7d8be 100644
--- a/src/typing_manager.ts
+++ b/src/typing_manager.ts
@@ -291,7 +291,7 @@ export class TypingManager implements Disposable {
 
     private onSendCommand = async (key: string): Promise<void> => {
         logger.debug(`Send for: ${key}`);
-        this.main.cursorManager.wantInsertCursorUpdate = true;
+        this.main.cursorManager.setWantInsertCursorUpdate(window.activeTextEditor, true);
         if (this.isInsertMode && !(await this.client.mode).blocking) {
             logger.debug(`Syncing buffers with neovim (${key})`);
             await this.main.changeManager.documentChangeLock.waitForUnlock();