Vim/extension.ts

236 lines
7.1 KiB
TypeScript
Raw Normal View History

2016-06-30 19:35:56 +03:00
'use strict';
/**
* Extension.ts is a lightweight wrapper around ModeHandler. It converts key
* events to their string names and passes them on to ModeHandler via
* handleKeyEvent().
*/
2015-11-13 10:38:13 +03:00
import * as vscode from 'vscode';
import * as util from './src/util';
2016-06-30 19:35:56 +03:00
import { showCmdLine } from './src/cmd_line/main';
import { ModeHandler } from './src/mode/modeHandler';
import { TaskQueue } from './src/taskQueue';
2016-07-14 20:54:04 +03:00
import { Position } from './src/motion/position';
import { Globals } from './src/globals';
2015-11-12 22:51:40 +03:00
interface VSCodeKeybinding {
key: string;
command: string;
when: string;
}
const packagejson: {
contributes: {
keybindings: VSCodeKeybinding[];
}
} = require('../package.json'); // out/../package.json
2016-07-21 03:17:50 +03:00
export class EditorIdentity {
private _fileName: string;
private _viewColumn: vscode.ViewColumn;
constructor(textEditor?: vscode.TextEditor) {
this._fileName = textEditor && textEditor.document.fileName || "";
this._viewColumn = textEditor && textEditor.viewColumn || vscode.ViewColumn.One;
}
get fileName() {
return this._fileName;
}
get viewColumn() {
return this._viewColumn;
}
public hasSameBuffer(identity: EditorIdentity): boolean {
return this.fileName === identity.fileName;
}
public isEqual(identity: EditorIdentity): boolean {
return this.fileName === identity.fileName && this.viewColumn === identity.viewColumn;
}
public toString() {
return this.fileName + this.viewColumn;
}
}
2016-06-30 19:35:56 +03:00
let extensionContext: vscode.ExtensionContext;
2016-06-18 10:21:17 +03:00
/**
* Note: We can't initialize modeHandler here, or even inside activate(), because some people
* see a bug where VSC hasn't fully initialized yet, which pretty much breaks VSCodeVim entirely.
*/
2016-07-21 03:17:50 +03:00
let modeHandlerToEditorIdentity: { [key: string]: ModeHandler } = {};
let previousActiveEditorId: EditorIdentity = new EditorIdentity();
2016-06-30 19:35:56 +03:00
let taskQueue = new TaskQueue();
2016-06-30 19:35:56 +03:00
export async function getAndUpdateModeHandler(): Promise<ModeHandler> {
2016-07-21 03:17:50 +03:00
const oldHandler = modeHandlerToEditorIdentity[previousActiveEditorId.toString()];
const activeEditorId = new EditorIdentity(vscode.window.activeTextEditor);
2016-07-21 03:17:50 +03:00
if (!modeHandlerToEditorIdentity[activeEditorId.toString()]) {
const newModeHandler = new ModeHandler(activeEditorId.fileName);
2016-07-21 03:17:50 +03:00
modeHandlerToEditorIdentity[activeEditorId.toString()] = newModeHandler;
extensionContext.subscriptions.push(newModeHandler);
}
2016-07-21 03:17:50 +03:00
const handler = modeHandlerToEditorIdentity[activeEditorId.toString()];
2016-07-21 03:17:50 +03:00
if (previousActiveEditorId.hasSameBuffer(activeEditorId)) {
if (!previousActiveEditorId.isEqual(activeEditorId)) {
// We have opened two editors, working on the same file.
previousActiveEditorId = activeEditorId;
handler.vimState.cursorPosition = Position.FromVSCodePosition(vscode.window.activeTextEditor.selection.end);
handler.vimState.cursorStartPosition = Position.FromVSCodePosition(vscode.window.activeTextEditor.selection.start);
}
} else {
previousActiveEditorId = activeEditorId;
await handler.updateView(handler.vimState);
}
if (oldHandler && oldHandler.vimState.focusChanged) {
oldHandler.vimState.focusChanged = false;
handler.vimState.focusChanged = true;
}
2016-06-30 19:35:56 +03:00
return handler;
2016-06-30 19:35:56 +03:00
}
2015-11-17 09:14:48 +03:00
2016-07-22 01:16:37 +03:00
class CompositionState {
public isInComposition: boolean = false;
public composingText: string = "";
}
export async function activate(context: vscode.ExtensionContext) {
extensionContext = context;
2016-07-22 01:16:37 +03:00
let compositionState = new CompositionState();
vscode.window.onDidChangeActiveTextEditor(handleActiveEditorChange, this);
registerCommand(context, 'type', async (args) => {
if (!vscode.window.activeTextEditor) {
return;
}
taskQueue.enqueueTask({
promise: async () => {
const mh = await getAndUpdateModeHandler();
2016-07-22 01:16:37 +03:00
if (compositionState.isInComposition) {
compositionState.composingText += args.text;
} else {
await mh.handleKeyEvent(args.text);
}
},
isRunning: false
});
});
registerCommand(context, 'replacePreviousChar', async (args) => {
if (!vscode.window.activeTextEditor) {
return;
}
taskQueue.enqueueTask({
promise: async () => {
const mh = await getAndUpdateModeHandler();
2016-07-22 01:16:37 +03:00
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(vscode.window.activeTextEditor.selection.start);
mh.vimState.cursorStartPosition = Position.FromVSCodePosition(vscode.window.activeTextEditor.selection.start);
}
},
isRunning: false
});
});
registerCommand(context, 'compositionStart', async (args) => {
if (!vscode.window.activeTextEditor) {
return;
}
taskQueue.enqueueTask({
promise: async () => {
const mh = await getAndUpdateModeHandler();
compositionState.isInComposition = true;
},
isRunning: false
});
});
registerCommand(context, 'compositionEnd', async (args) => {
if (!vscode.window.activeTextEditor) {
return;
}
taskQueue.enqueueTask({
promise: async () => {
const mh = await getAndUpdateModeHandler();
let text = compositionState.composingText;
compositionState = new CompositionState();
await mh.handleMultipleKeyEvents(text.split(""));
},
isRunning: false
});
});
2016-07-14 20:54:04 +03:00
registerCommand(context, 'extension.showCmdLine', () => {
2016-07-21 03:17:50 +03:00
showCmdLine("", modeHandlerToEditorIdentity[new EditorIdentity(vscode.window.activeTextEditor).toString()]);
});
2015-11-17 23:41:38 +03:00
for (let { key } of packagejson.contributes.keybindings) {
let bracketedKey = util.translateToAngleBracketNotation(key);
registerCommand(context, `extension.vim_${ key }`, () => handleKeyEvent(`${ bracketedKey }`));
}
// Initialize mode handler for current active Text Editor at startup.
if (vscode.window.activeTextEditor) {
let mh = await getAndUpdateModeHandler()
mh.updateView(mh.vimState, false);
}
}
function registerCommand(context: vscode.ExtensionContext, command: string, callback: (...args: any[]) => any) {
let disposable = vscode.commands.registerCommand(command, callback);
context.subscriptions.push(disposable);
2015-11-17 09:14:48 +03:00
}
async function handleKeyEvent(key: string): Promise<void> {
const mh = await getAndUpdateModeHandler();
2016-01-30 22:41:53 +03:00
taskQueue.enqueueTask({
promise : async () => { await mh.handleKeyEvent(key); },
isRunning : false
});
2016-06-30 19:35:56 +03:00
}
async function handleActiveEditorChange(): Promise<void> {
// Don't run this event handler during testing
if (Globals.isTesting) {
return;
}
if (vscode.window.activeTextEditor !== undefined) {
const mh = await getAndUpdateModeHandler();
mh.updateView(mh.vimState, false);
}
}
2016-06-30 19:35:56 +03:00
process.on('unhandledRejection', function(reason: any, p: any) {
console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
2016-07-15 10:59:08 +03:00
});