refactor taskqueue

This commit is contained in:
Jason Poon 2017-10-13 11:54:40 -07:00
parent 347f5ef005
commit 9b7c7333ed
6 changed files with 122 additions and 170 deletions

11
.vscode/tasks.json vendored
View File

@ -16,17 +16,8 @@
"taskName": "build",
"args": [],
"isBuildCommand": true,
"isWatching": false,
"problemMatcher": "$tsc-watch"
},
{
"taskName": "yolo",
"args": ["nothing"],
"isBuildCommand": true,
"isWatching": false,
"isBackground": false,
"problemMatcher": "$tsc-watch"
}
]
}

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015 VSCode-Extension
Copyright (c) 2015 VSCodeVim
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -213,66 +213,54 @@ export async function activate(context: vscode.ExtensionContext) {
});
overrideCommand(context, 'type', async args => {
taskQueue.enqueueTask({
promise: async () => {
const mh = await getAndUpdateModeHandler();
taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler();
if (compositionState.isInComposition) {
compositionState.composingText += args.text;
} else {
await mh.handleKeyEvent(args.text);
}
},
isRunning: false,
if (compositionState.isInComposition) {
compositionState.composingText += args.text;
} else {
await mh.handleKeyEvent(args.text);
}
});
});
overrideCommand(context, 'replacePreviousChar', async args => {
taskQueue.enqueueTask({
promise: async () => {
const mh = await getAndUpdateModeHandler();
taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler();
if (compositionState.isInComposition) {
compositionState.composingText =
compositionState.composingText.substr(
0,
compositionState.composingText.length - args.replaceCharCnt
) + args.text;
} else {
await vscode.commands.executeCommand('default:replacePreviousChar', {
text: args.text,
replaceCharCnt: args.replaceCharCnt,
});
mh.vimState.cursorPosition = Position.FromVSCodePosition(
mh.vimState.editor.selection.start
);
mh.vimState.cursorStartPosition = Position.FromVSCodePosition(
mh.vimState.editor.selection.start
);
}
},
isRunning: false,
if (compositionState.isInComposition) {
compositionState.composingText =
compositionState.composingText.substr(
0,
compositionState.composingText.length - args.replaceCharCnt
) + args.text;
} else {
await vscode.commands.executeCommand('default:replacePreviousChar', {
text: args.text,
replaceCharCnt: args.replaceCharCnt,
});
mh.vimState.cursorPosition = Position.FromVSCodePosition(
mh.vimState.editor.selection.start
);
mh.vimState.cursorStartPosition = Position.FromVSCodePosition(
mh.vimState.editor.selection.start
);
}
});
});
overrideCommand(context, 'compositionStart', async args => {
taskQueue.enqueueTask({
promise: async () => {
compositionState.isInComposition = true;
},
isRunning: false,
taskQueue.enqueueTask(async () => {
compositionState.isInComposition = true;
});
});
overrideCommand(context, 'compositionEnd', async args => {
taskQueue.enqueueTask({
promise: async () => {
const mh = await getAndUpdateModeHandler();
let text = compositionState.composingText;
compositionState = new CompositionState();
await mh.handleMultipleKeyEvents(text.split(''));
},
isRunning: false,
taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler();
let text = compositionState.composingText;
compositionState = new CompositionState();
await mh.handleMultipleKeyEvents(text.split(''));
});
});
@ -284,29 +272,26 @@ export async function activate(context: vscode.ExtensionContext) {
});
registerCommand(context, 'vim.remap', async (args: ICodeKeybinding) => {
taskQueue.enqueueTask({
promise: async () => {
const mh = await getAndUpdateModeHandler();
if (args.after) {
for (const key of args.after) {
await mh.handleKeyEvent(AngleBracketNotation.Normalize(key));
}
return;
taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler();
if (args.after) {
for (const key of args.after) {
await mh.handleKeyEvent(AngleBracketNotation.Normalize(key));
}
return;
}
if (args.commands) {
for (const command of args.commands) {
// Check if this is a vim command by looking for :
if (command.command.slice(0, 1) === ':') {
await runCmdLine(command.command.slice(1, command.command.length), mh);
await mh.updateView(mh.vimState);
} else {
await vscode.commands.executeCommand(command.command, command.args);
}
if (args.commands) {
for (const command of args.commands) {
// Check if this is a vim command by looking for :
if (command.command.slice(0, 1) === ':') {
await runCmdLine(command.command.slice(1, command.command.length), mh);
await mh.updateView(mh.vimState);
} else {
await vscode.commands.executeCommand(command.command, command.args);
}
}
},
isRunning: false,
}
});
});
@ -447,11 +432,8 @@ function registerCommand(
async function handleKeyEvent(key: string): Promise<void> {
const mh = await getAndUpdateModeHandler();
taskQueue.enqueueTask({
promise: async () => {
await mh.handleKeyEvent(key);
},
isRunning: false,
taskQueue.enqueueTask(async () => {
await mh.handleKeyEvent(key);
});
}
@ -474,15 +456,12 @@ async function handleActiveEditorChange(): Promise<void> {
return;
}
taskQueue.enqueueTask({
promise: async () => {
if (vscode.window.activeTextEditor !== undefined) {
const mh = await getAndUpdateModeHandler();
taskQueue.enqueueTask(async () => {
if (vscode.window.activeTextEditor !== undefined) {
const mh = await getAndUpdateModeHandler();
mh.updateView(mh.vimState, { drawSelection: false, revealRange: false });
}
},
isRunning: false,
mh.updateView(mh.vimState, { drawSelection: false, revealRange: false });
}
});
}

View File

@ -361,25 +361,19 @@ function overlapSetting(args: {
set: function(value) {
this['_' + propertyKey] = value;
taskQueue.enqueueTask({
promise: async () => {
if (value === undefined || Globals.isTesting) {
return;
}
taskQueue.enqueueTask(async () => {
if (value === undefined || Globals.isTesting) {
return;
}
let codeValue = value;
let codeValue = value;
if (args.codeValueMapping) {
codeValue = args.codeValueMapping[value];
}
if (args.codeValueMapping) {
codeValue = args.codeValueMapping[value];
}
await vscode.workspace
.getConfiguration('editor')
.update(args.codeName, codeValue, true);
},
isRunning: false,
queue: 'config',
});
await vscode.workspace.getConfiguration('editor').update(args.codeName, codeValue, true);
}, 'config');
},
enumerable: true,
configurable: true,

View File

@ -581,16 +581,15 @@ export class ModeHandler implements vscode.Disposable {
return;
}
taskQueue.enqueueTask({
promise: () => this.handleSelectionChange(e),
isRunning: false,
taskQueue.enqueueTask(
() => this.handleSelectionChange(e),
undefined,
/**
* We don't want these to become backlogged! If they do, we'll update
* the selection to an incorrect value and see a jittering cursor.
*/
highPriority: true,
});
true
);
}
);

View File

@ -1,26 +1,34 @@
import * as _ from 'lodash';
export interface IEnqueuedTask {
interface IEnqueuedTask {
promise: () => Promise<void>;
isRunning: boolean;
queue?: string;
highPriority?: boolean;
queue: string;
isHighPriority: boolean;
}
/**
* TaskQueue
*
* Enqueue promises here. They will be run sequentially.
*/
class TaskQueue {
private _taskQueue: {
[key: string]: {
tasks: IEnqueuedTask[];
highPriorityCount: number;
};
} = {};
private async _runTasks(queueName: string): Promise<void> {
private isRunning(queueName: string): boolean {
return (
this._taskQueue[queueName] &&
_.filter(this._taskQueue[queueName].tasks, x => x.isRunning).length > 0
);
}
private numHighPriority(queueName: string): number {
if (!this._taskQueue[queueName]) {
return 0;
}
return _.filter(this._taskQueue[queueName].tasks, x => x.isHighPriority).length;
}
private async runTasks(queueName: string): Promise<void> {
while (this._taskQueue[queueName].tasks.length > 0) {
let task: IEnqueuedTask = this._taskQueue[queueName].tasks[0];
@ -29,73 +37,54 @@ class TaskQueue {
await task.promise();
task.isRunning = false;
} catch (e) {
console.log(e);
console.log(e.stack);
console.error(e);
} finally {
this.removeTask(task);
if (task.highPriority) {
this._taskQueue[queueName].highPriorityCount--;
}
this.dequeueTask(task);
}
}
}
public get tasks(): number {
let result = 0;
for (const list in this._taskQueue) {
if (this._taskQueue.hasOwnProperty(list)) {
result += this._taskQueue[list].tasks.length;
}
}
return result;
}
/**
* Removes a task from the task queue.
* Dequeues a task from the task queue.
*
* (Keep in mind that if the task is already running, the semantics of
* promises don't allow you to stop it.)
* Note: If the task is already running, the semantics of
* promises don't allow you to stop it.
*/
public removeTask(task: IEnqueuedTask): void {
let queueName = task.queue || 'default';
this._taskQueue[queueName].tasks.splice(
_.findIndex(this._taskQueue[queueName].tasks, t => t === task),
1
);
private dequeueTask(task: IEnqueuedTask): void {
_.remove(this._taskQueue[task.queue].tasks, t => t === task);
}
/**
* Adds a task to the task queue.
*/
public enqueueTask(task: IEnqueuedTask): void {
let queueName = task.queue || 'default';
let otherTaskRunning =
this._taskQueue[queueName] &&
_.filter(this._taskQueue[queueName].tasks, x => x.isRunning).length > 0;
public enqueueTask(
action: () => Promise<void>,
queueName: string = 'default',
isHighPriority: boolean = false
): void {
let task: IEnqueuedTask = {
promise: action,
queue: queueName,
isHighPriority: isHighPriority,
isRunning: false,
};
if (this._taskQueue[queueName]) {
if (task.highPriority) {
// Insert task as the last high priotity task.
const numHighPriority = this._taskQueue[queueName].highPriorityCount;
this._taskQueue[queueName].tasks.splice(numHighPriority, 0, task);
this._taskQueue[queueName].highPriorityCount++;
} else {
this._taskQueue[queueName].tasks.push(task);
}
} else {
if (!this._taskQueue[queueName]) {
this._taskQueue[queueName] = {
tasks: [task],
highPriorityCount: 0,
tasks: [],
};
}
if (!otherTaskRunning) {
this._runTasks(queueName);
if (isHighPriority) {
// Insert task as the last high priotity task.
const numHighPriority = this.numHighPriority(queueName);
this._taskQueue[queueName].tasks.splice(numHighPriority, 0, task);
} else {
this._taskQueue[queueName].tasks.push(task);
}
if (!this.isRunning(queueName)) {
this.runTasks(queueName);
}
}
}