Normalize the vim sample

This commit is contained in:
Alex Dima
2018-11-08 10:13:29 +01:00
parent cbd57bff5f
commit 0460228f31
15 changed files with 586 additions and 388 deletions

View File

@ -90,7 +90,13 @@ const samples = [{
apis: ['window.createTreeView', 'window.registerTreeDataProvider', 'TreeView', 'TreeDataProvider'],
contributions: ['views', 'viewsContainers']
},
// { description: 'vim-sample', path: 'vim-sample', guide: null, apis: [], contributions: [] },
{
description: 'vim-sample',
path: 'vim-sample',
guide: null,
apis: [`commands`, `StatusBarItem`, `window.createStatusBarItem`, `TextEditorCursorStyle`, `window.activeTextEditor`, `Position`, `Range`, `Selection`, `TextEditor`, `TextEditorRevealType`, `TextDocument`],
contributions: []
},
// { description: 'webpack-sample', path: 'webpack-sample', guide: null, apis: [], contributions: [] },
];

7
vim-sample/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"eg2.tslint"
]
}

View File

@ -1,27 +1,14 @@
// A launch configuration that compiles the extension and then opens it inside a new window
{
"version": "0.1.0",
"version": "0.2.0",
"configurations": [
{
"name": "Launch Extension",
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/out/src/**/*.js"],
"preLaunchTask": "npm: watch"
},
{
"name": "Launch Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/out/test/**/*.js"],
"args": ["--extensionDevelopmentPath=${workspaceRoot}"],
"outFiles": ["${workspaceFolder}/out/**/*.js"],
"preLaunchTask": "npm: watch"
}
]

View File

@ -1,14 +1,3 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary task as npm scripts
"typescript.tsc.autoDetect": "off",
"typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version
"editor.insertSpaces": false,
}

View File

@ -1,38 +1,32 @@
# VSCode vim sample
This is an extension sample showing how vim emulation could be implemented in a VS Code extension, starting with VS Code version 0.10.12.
This is an extension sample showing how vim emulation could be implemented in a VS Code extension.
![Screenshot](example.gif)
## Why create this sample and not contribute to an existing vim extension?
The reason for creating this sample was to figure out the APIs missing in VS Code in order to be able to create a good vim emulation experience.
There are already multiple [vim extensions for VS Code](https://marketplace.visualstudio.com/search?term=vim&target=VSCode&sortBy=Relevance) and I don't want to send mixed signals by picking one to contribute to.
And it's fun to write an extension, even if it's just a sample! :)
## Approach (what is new in 0.10.12)
## Approach
### Typing
* When a key is pressed, VS Code gets a `keydown` event. The `keydown` event contains information about modifier keys (`ctrl`, `alt`, etc.) and about the key code. The `keydown` event does not contain information about what character would get produced and not all `keydown` events produce characters.
* The `keydown` event runs through the keybinding rules and the first rule that matches gets executed. This allows to bind commands to key combinations that would usually not produce visual characters.
* An extension should generally not add rules for key combinations that might produce characters (e.g. don't add rules for `"a"`, `"shift+a"` or `"ctrl+alt+o"`, as these key combinations will not necessarily produce the same characters under different keyboard layouts).
* When a `keydown` is not matched by any keybinding rule, it might produce a character. Since 0.10.12 this is dispatched to the `type` command.
* When a `keydown` is not matched by any keybinding rule, it might produce a character. This is dispatched to the `type` command.
* It is therefore possible for an extension to **overwrite** the `type` command and handle characters instead of the VS Code editor.
* There is a `default:type` command that maps to the VS Code editor's type handler in case an extension wishes to delegate back typing to VS Code.
* There is another command, `replacePreviousChar` that you should be aware of. On the Mac if I remember correctly, it is possible to generate certain accented characters by long pressing a key. When this happens, a `replacePreviousChar` command is invoked containing the new characters and the number of characters that should be replaced before.
* There is another command, `replacePreviousChar` that you should be aware of. On the Mac it is possible to generate certain accented characters by long pressing a key. When this happens, a `replacePreviousChar` command is invoked containing the new characters and the number of characters that should be replaced before.
* At this time, `type`, `replacePreviousChar`, `paste` and `cut` are dispatched through the keybinding rules. There are [other internal VS Code editor commands](https://github.com/Microsoft/vscode/blob/master/src/vs/editor/browser/view/viewController.ts) (such as those coming in from mouse operations) that are not dispatched and cannot be at this time overwritten from an extension. From writing this sample, I haven't found the need to overwrite them, but if you have a use-case where that would make sense, please open an issue on the vscode repo.
### Cursor Style
* The `TextEditorOptions` now also has a `cursorStyle` property that allows to change the cursor style programmatically.
* The `TextEditorOptions` has a `cursorStyle` property that allows to change the cursor style programmatically.
### Keybinding contexts
* There is a new command, `setContext`, that can be invoked with two arguments, a key and a value. It allows to add custom properties to the keybinding conditions.
## Take it for a spin
## Running the Sample
> Note: You need to run from VS Code source, as the 0.10.12 version is not yet released.

File diff suppressed because it is too large Load Diff

View File

@ -70,8 +70,9 @@
"postinstall": "node ./node_modules/vscode/bin/install"
},
"devDependencies": {
"typescript": "^2.1.4",
"vscode": "^1.1.17",
"@types/node": "*"
"@types/node": "8.x.x",
"tslint": "^5.11.0",
"typescript": "^2.9.2",
"vscode": "^1.1.17"
}
}

View File

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import { MotionState, Motion } from './motions';
export enum Mode {
@ -14,9 +13,9 @@ export enum Mode {
}
export interface ModifierKeys {
ctrl?: boolean,
alt?: boolean,
shifit?: boolean
ctrl?: boolean;
alt?: boolean;
shifit?: boolean;
}
export class DeleteRegister {
@ -48,6 +47,6 @@ export abstract class AbstractCommandDescriptor {
}
export interface Command {
commandId: string,
args?: any[]
commandId: string;
args?: any[];
}

View File

@ -4,15 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {
TextEditorCursorStyle,
Position,
Range,
Selection,
TextEditor,
TextEditorRevealType,
window
} from 'vscode';
import { TextEditorCursorStyle, Position, Range, Selection, TextEditor, TextEditorRevealType, window } from 'vscode';
import { Words } from './words';
import { MotionState, Motion } from './motions';

View File

@ -6,11 +6,7 @@
import * as vscode from 'vscode';
import { Words } from './words';
import { MotionState, Motion, Motions } from './motions';
import { Operator, Operators } from './operators';
import { Mode, IController, ModifierKeys } from './common';
import { Mappings } from './mappings';
import { Mode, ModifierKeys } from './common';
import { Controller } from './controller';
export function activate(context: vscode.ExtensionContext) {
@ -95,7 +91,7 @@ class VimExt {
this._hasInput = new ContextKey('vim.hasInput');
this._statusBar = new StatusBar();
this._controller = new Controller()
this._controller = new Controller();
vscode.window.onDidChangeActiveTextEditor((textEditor) => {
if (!textEditor) {

View File

@ -13,39 +13,39 @@ const CHAR_TO_BINDING: { [char: string]: any; } = {};
function defineBinding(char: string, value: any, modifierKeys: ModifierKeys): void {
let key = modifierKeys.ctrl ? 'CTRL + ' + char : char;
CHAR_TO_BINDING[key] = value;
};
}
function getBinding(char: string, modifierKeys: ModifierKeys): any {
let key = modifierKeys.ctrl ? 'CTRL + ' + char : char;
return CHAR_TO_BINDING[key];
};
}
function defineOperator(char: string, operator: Operator, modifierKeys: ModifierKeys = {}): void {
defineBinding(char + '__operator__', operator, modifierKeys);
};
}
function getOperator(char: string, modifierKeys: ModifierKeys = {}): Operator {
return getBinding(char + '__operator__', modifierKeys);
};
}
function defineCommand(char: string, commandId: string, modifierKeys: ModifierKeys = {}): void {
defineBinding(char + '__command__', { commandId: commandId }, modifierKeys);
};
}
function getCommand(char: string, modifierKeys: ModifierKeys = {}): Command {
return getBinding(char + '__command__', modifierKeys);
};
}
function defineMotion(char: string, motion: Motion, modifierKeys: ModifierKeys = {}): void {
defineBinding(char + '__motion__', motion, modifierKeys);
};
}
function getMotion(char: string, modifierKeys: ModifierKeys = {}): Motion {
return getBinding(char + '__motion__', modifierKeys);
};
}
function defineMotionCommand(char: string, motionCommand: AbstractCommandDescriptor, modifierKeys: ModifierKeys = {}): void {
defineBinding(char + '__motioncommand__', motionCommand, modifierKeys);
};
}
function getMotionCommand(char: string, modifierKeys: ModifierKeys = {}): AbstractCommandDescriptor {
return getBinding(char + '__motioncommand__', modifierKeys);
};
}
// Operators
defineOperator('x', Operators.DeleteCharUnderCursor);
@ -223,7 +223,7 @@ function _parseNumberAndString(input: string, numberAtBeginning: boolean = true)
hasRepeatCount: false,
repeatCount: 1,
input: input
}
};
}
interface INumberAndString {

View File

@ -256,7 +256,7 @@ class CursorMoveCommand extends AbstractCommandDescriptor {
by: this.by,
value: args.repeat || 1,
select: !!args.isVisual
}
};
return {
commandId: 'cursorMove',
args: cursorMoveArgs
@ -276,7 +276,7 @@ class EditorScrollCommand extends AbstractCommandDescriptor {
by: this.by,
value: args.repeat || 1,
revealCursor: true
}
};
return {
commandId: 'editorScroll',
args: editorScrollArgs
@ -313,7 +313,7 @@ class MoveActiveEditorCommandByPosition extends AbstractCommandDescriptor {
let moveActiveEditorArgs: any = {
to: args.repeat === void 0 ? 'last' : 'position',
value: args.repeat !== void 0 ? args.repeat + 1 : undefined
}
};
return {
commandId: 'moveActiveEditor',
args: moveActiveEditorArgs
@ -331,7 +331,7 @@ class MoveActiveEditorCommand extends AbstractCommandDescriptor {
let moveActiveEditorArgs: any = {
to: this.to,
value: args.repeat ? args.repeat : 1
}
};
return {
commandId: 'moveActiveEditor',
args: moveActiveEditorArgs
@ -348,7 +348,7 @@ class FoldCommand extends AbstractCommandDescriptor {
let foldEditorArgs: any = {
levels: args.repeat ? args.repeat : 1,
direction: 'up'
}
};
return {
commandId: 'editor.fold',
args: foldEditorArgs
@ -366,7 +366,7 @@ class UnfoldCommand extends AbstractCommandDescriptor {
let foldEditorArgs: any = {
levels: args.repeat ? args.repeat : 1,
direction: 'up'
}
};
return {
commandId: 'editor.unfold',
args: foldEditorArgs

View File

@ -5,7 +5,7 @@
'use strict';
import { Position, Selection, Range, TextDocument, TextEditor, TextEditorRevealType } from 'vscode';
import { MotionState, Motion, Motions } from './motions';
import { Motion, Motions } from './motions';
import { Mode, IController, DeleteRegister } from './common';
export abstract class Operator {

View File

@ -1,11 +1,11 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"target": "es6",
"outDir": "out",
"lib": ["es6"],
"sourceMap": true,
"rootDir": "src"
},
"exclude": ["node_modules"]
}
"exclude": ["node_modules", ".vscode-test"]
}

6
vim-sample/tslint.json Normal file
View File

@ -0,0 +1,6 @@
{
"rules": {
"indent": [true, "tabs"],
"semicolon": [true, "always"]
}
}