Skip to content

Commit 8a15cdb

Browse files
committed
Make getAndUpdateModeHandler return undefined when there is no active editor
This means `vimState.editor` should never be undefined - now we should start using that instead of `vscode.window.activeTextEditor` to avoid a whole bunch of pesky race conditions. Refs #5663
1 parent 1564e64 commit 8a15cdb

29 files changed

+104
-81
lines changed

extensionBase.ts

Lines changed: 76 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,22 @@ interface ICodeKeybinding {
2828
commands?: { command: string; args: any[] }[];
2929
}
3030

31-
export async function getAndUpdateModeHandler(forceSyncAndUpdate = false): Promise<ModeHandler> {
31+
export async function getAndUpdateModeHandler(
32+
forceSyncAndUpdate = false
33+
): Promise<ModeHandler | undefined> {
3234
const activeTextEditor = vscode.window.activeTextEditor;
35+
if (activeTextEditor === undefined) {
36+
return undefined;
37+
}
38+
3339
const activeEditorId = EditorIdentity.fromEditor(activeTextEditor);
3440

3541
let [curHandler, isNew] = await ModeHandlerMap.getOrCreate(activeEditorId);
3642
if (isNew) {
3743
extensionContext.subscriptions.push(curHandler);
3844
}
3945

40-
curHandler.vimState.editor = activeTextEditor!;
46+
curHandler.vimState.editor = activeTextEditor;
4147

4248
if (
4349
forceSyncAndUpdate ||
@@ -250,9 +256,8 @@ export async function activate(
250256
Register.putByKey(filepathComponents[filepathComponents.length - 1], '%', undefined, true);
251257

252258
taskQueue.enqueueTask(async () => {
253-
if (vscode.window.activeTextEditor !== undefined) {
254-
const mh: ModeHandler = await getAndUpdateModeHandler(true);
255-
259+
const mh = await getAndUpdateModeHandler(true);
260+
if (mh) {
256261
globalState.jumpTracker.handleFileJump(
257262
lastClosedModeHandler ? Jump.fromStateNow(lastClosedModeHandler.vimState) : null,
258263
Jump.fromStateNow(mh.vimState)
@@ -272,12 +277,15 @@ export async function activate(
272277
vscode.window.activeTextEditor === undefined ||
273278
e.textEditor.document !== vscode.window.activeTextEditor.document
274279
) {
275-
// we don't care if there is no active editor
276-
// or user selection changed in a paneled window (e.g debug console/terminal)
280+
// We don't care if user selection changed in a paneled window (e.g debug console/terminal)
277281
return;
278282
}
279283

280284
const mh = await getAndUpdateModeHandler();
285+
if (mh === undefined) {
286+
// We don't care if there is no active editor
287+
return;
288+
}
281289

282290
if (e.kind !== vscode.TextEditorSelectionChangeKind.Mouse) {
283291
const selectionsHash = e.selections.reduce(
@@ -341,32 +349,34 @@ export async function activate(
341349
overrideCommand(context, 'type', async (args) => {
342350
taskQueue.enqueueTask(async () => {
343351
const mh = await getAndUpdateModeHandler();
344-
345-
if (compositionState.isInComposition) {
346-
compositionState.composingText += args.text;
347-
} else {
348-
await mh.handleKeyEvent(args.text);
352+
if (mh) {
353+
if (compositionState.isInComposition) {
354+
compositionState.composingText += args.text;
355+
} else {
356+
await mh.handleKeyEvent(args.text);
357+
}
349358
}
350359
});
351360
});
352361

353362
overrideCommand(context, 'replacePreviousChar', async (args) => {
354363
taskQueue.enqueueTask(async () => {
355364
const mh = await getAndUpdateModeHandler();
356-
357-
if (compositionState.isInComposition) {
358-
compositionState.composingText =
359-
compositionState.composingText.substr(
360-
0,
361-
compositionState.composingText.length - args.replaceCharCnt
362-
) + args.text;
363-
} else {
364-
await vscode.commands.executeCommand('default:replacePreviousChar', {
365-
text: args.text,
366-
replaceCharCnt: args.replaceCharCnt,
367-
});
368-
mh.vimState.cursorStopPosition = mh.vimState.editor.selection.start;
369-
mh.vimState.cursorStartPosition = mh.vimState.editor.selection.start;
365+
if (mh) {
366+
if (compositionState.isInComposition) {
367+
compositionState.composingText =
368+
compositionState.composingText.substr(
369+
0,
370+
compositionState.composingText.length - args.replaceCharCnt
371+
) + args.text;
372+
} else {
373+
await vscode.commands.executeCommand('default:replacePreviousChar', {
374+
text: args.text,
375+
replaceCharCnt: args.replaceCharCnt,
376+
});
377+
mh.vimState.cursorStopPosition = mh.vimState.editor.selection.start;
378+
mh.vimState.cursorStartPosition = mh.vimState.editor.selection.start;
379+
}
370380
}
371381
});
372382
});
@@ -380,22 +390,30 @@ export async function activate(
380390
overrideCommand(context, 'compositionEnd', async () => {
381391
taskQueue.enqueueTask(async () => {
382392
const mh = await getAndUpdateModeHandler();
383-
let text = compositionState.composingText;
384-
compositionState.reset();
385-
mh.handleMultipleKeyEvents(text.split(''));
393+
if (mh) {
394+
const text = compositionState.composingText;
395+
compositionState.reset();
396+
mh.handleMultipleKeyEvents(text.split(''));
397+
}
386398
});
387399
});
388400

389401
// Register extension commands
390402
registerCommand(context, 'vim.showQuickpickCmdLine', async () => {
391403
const mh = await getAndUpdateModeHandler();
392-
await commandLine.PromptAndRun('', mh.vimState);
393-
mh.updateView();
404+
if (mh) {
405+
await commandLine.PromptAndRun('', mh.vimState);
406+
mh.updateView();
407+
}
394408
});
395409

396410
registerCommand(context, 'vim.remap', async (args: ICodeKeybinding) => {
397411
taskQueue.enqueueTask(async () => {
398412
const mh = await getAndUpdateModeHandler();
413+
if (mh === undefined) {
414+
return;
415+
}
416+
399417
if (args.after) {
400418
for (const key of args.after) {
401419
await mh.handleKeyEvent(Notation.NormalizeKey(key, configuration.leader));
@@ -443,11 +461,13 @@ export async function activate(
443461
});
444462
}
445463

446-
// Initialize mode handler for current active Text Editor at startup.
447-
if (vscode.window.activeTextEditor) {
448-
let mh = await getAndUpdateModeHandler();
449-
// This is called last because getAndUpdateModeHandler() will change cursor
450-
mh.updateView({ drawSelection: false, revealRange: false });
464+
{
465+
// Initialize mode handler for current active Text Editor at startup.
466+
const modeHandler = await getAndUpdateModeHandler();
467+
if (modeHandler) {
468+
// This is called last because getAndUpdateModeHandler() will change cursor
469+
modeHandler.updateView({ drawSelection: false, revealRange: false });
470+
}
451471
}
452472

453473
// Disable automatic keyboard navigation in lists, so it doesn't interfere
@@ -472,13 +492,15 @@ async function toggleExtension(isDisabled: boolean, compositionState: Compositio
472492
// If activate was called and no editor window is open, we can't properly initialize.
473493
return;
474494
}
475-
let mh = await getAndUpdateModeHandler();
476-
if (isDisabled) {
477-
await mh.handleKeyEvent(SpecialKeys.ExtensionDisable);
478-
compositionState.reset();
479-
ModeHandlerMap.clear();
480-
} else {
481-
await mh.handleKeyEvent(SpecialKeys.ExtensionEnable);
495+
const mh = await getAndUpdateModeHandler();
496+
if (mh) {
497+
if (isDisabled) {
498+
await mh.handleKeyEvent(SpecialKeys.ExtensionDisable);
499+
compositionState.reset();
500+
ModeHandlerMap.clear();
501+
} else {
502+
await mh.handleKeyEvent(SpecialKeys.ExtensionEnable);
503+
}
482504
}
483505
}
484506

@@ -547,18 +569,21 @@ function registerEventListener<T>(
547569

548570
async function handleKeyEvent(key: string): Promise<void> {
549571
const mh = await getAndUpdateModeHandler();
550-
551-
taskQueue.enqueueTask(async () => {
552-
await mh.handleKeyEvent(key);
553-
});
572+
if (mh) {
573+
taskQueue.enqueueTask(async () => {
574+
await mh.handleKeyEvent(key);
575+
});
576+
}
554577
}
555578

556579
async function checkIfRecursiveRemapping(key: string): Promise<void> {
557580
const mh = await getAndUpdateModeHandler();
558-
if (mh.vimState.isCurrentlyPerformingRecursiveRemapping) {
559-
mh.vimState.forceStopRecursiveRemapping = true;
560-
} else {
561-
handleKeyEvent(key);
581+
if (mh) {
582+
if (mh.vimState.isCurrentlyPerformingRecursiveRemapping) {
583+
mh.vimState.forceStopRecursiveRemapping = true;
584+
} else {
585+
handleKeyEvent(key);
586+
}
562587
}
563588
}
564589

extensionWeb.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import './src/configuration/validators/vimrcValidator';
1515

1616
import * as vscode from 'vscode';
1717
import { activate as activateFunc } from './extensionBase';
18-
export { getAndUpdateModeHandler } from './extensionBase';
1918

2019
export async function activate(context: vscode.ExtensionContext) {
2120
activateFunc(context, false);

test/cmd_line/bang.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ suite('bang (!) cmd_line', () => {
77

88
setup(async () => {
99
await setupWorkspace();
10-
modeHandler = await getAndUpdateModeHandler();
10+
modeHandler = (await getAndUpdateModeHandler())!;
1111
});
1212

1313
teardown(cleanUpWorkspace);

test/cmd_line/command.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ suite('cmd_line/search command', () => {
77

88
setup(async () => {
99
await setupWorkspace();
10-
modeHandler = await getAndUpdateModeHandler();
10+
modeHandler = (await getAndUpdateModeHandler())!;
1111
});
1212

1313
teardown(cleanUpWorkspace);

test/cmd_line/cursorLocation.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ suite('cursor location', () => {
1010

1111
suiteSetup(async () => {
1212
await setupWorkspace();
13-
modeHandler = await getAndUpdateModeHandler();
13+
modeHandler = (await getAndUpdateModeHandler())!;
1414
});
1515

1616
suiteTeardown(cleanUpWorkspace);

test/cmd_line/only.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ suite(':only command', () => {
3737

3838
setup(async () => {
3939
await setupWorkspace();
40-
modeHandler = await getAndUpdateModeHandler();
40+
modeHandler = (await getAndUpdateModeHandler())!;
4141
});
4242

4343
teardown(cleanUpWorkspace);

test/cmd_line/put.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ suite('put cmd_line', () => {
99

1010
setup(async () => {
1111
await setupWorkspace();
12-
modeHandler = await getAndUpdateModeHandler();
12+
modeHandler = (await getAndUpdateModeHandler())!;
1313
});
1414

1515
teardown(cleanUpWorkspace);

test/cmd_line/smile.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ suite('Smile command', () => {
1717

1818
setup(async () => {
1919
await setupWorkspace();
20-
modeHandler = await getAndUpdateModeHandler();
20+
modeHandler = (await getAndUpdateModeHandler())!;
2121
});
2222

2323
teardown(cleanUpWorkspace);

test/cmd_line/sort.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ suite('Basic sort', () => {
1010

1111
setup(async () => {
1212
await setupWorkspace();
13-
modeHandler = await getAndUpdateModeHandler();
13+
modeHandler = (await getAndUpdateModeHandler())!;
1414
vimState = modeHandler.vimState;
1515
});
1616

test/cmd_line/split.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ suite('Horizontal split', () => {
1111

1212
setup(async () => {
1313
await setupWorkspace();
14-
modeHandler = await getAndUpdateModeHandler();
14+
modeHandler = (await getAndUpdateModeHandler())!;
1515
});
1616

1717
teardown(cleanUpWorkspace);

0 commit comments

Comments
 (0)