mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-04-27 16:55:44 +08:00
Support scroll commands
This commit is contained in:
@ -28,6 +28,30 @@
|
||||
"key": "ctrl+r",
|
||||
"mac": "cmd+r",
|
||||
"when": "editorTextFocus"
|
||||
},{
|
||||
"command": "e",
|
||||
"key": "ctrl+e",
|
||||
"when": "editorTextFocus && vim.inNormalMode"
|
||||
},{
|
||||
"command": "d",
|
||||
"key": "ctrl+d",
|
||||
"when": "editorTextFocus && vim.inNormalMode"
|
||||
},{
|
||||
"command": "f",
|
||||
"key": "ctrl+f",
|
||||
"when": "editorTextFocus && vim.inNormalMode"
|
||||
},{
|
||||
"command": "y",
|
||||
"key": "ctrl+y",
|
||||
"when": "editorTextFocus && vim.inNormalMode"
|
||||
},{
|
||||
"command": "u",
|
||||
"key": "ctrl+u",
|
||||
"when": "editorTextFocus && vim.inNormalMode"
|
||||
},{
|
||||
"command": "b",
|
||||
"key": "ctrl+b",
|
||||
"when": "editorTextFocus && vim.inNormalMode"
|
||||
}]
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@ -13,6 +13,12 @@ export enum Mode {
|
||||
REPLACE
|
||||
}
|
||||
|
||||
export interface ModifierKeys {
|
||||
ctrl?: boolean,
|
||||
alt?: boolean,
|
||||
shifit?: boolean
|
||||
}
|
||||
|
||||
export class DeleteRegister {
|
||||
public isWholeLine:boolean;
|
||||
public content:string;
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
|
||||
import {Words} from './words';
|
||||
import {MotionState, Motion} from './motions';
|
||||
import {Mode, IController, DeleteRegister, Command} from './common';
|
||||
import {Mode, IController, DeleteRegister, Command, ModifierKeys} from './common';
|
||||
import {Mappings} from './mappings';
|
||||
|
||||
export interface ITypeResult {
|
||||
@ -139,7 +139,7 @@ export class Controller implements IController {
|
||||
return `VIM:> ${label}` + (this._currentInput ? ` >${this._currentInput}` : ``);
|
||||
}
|
||||
|
||||
public type(editor: TextEditor, text: string): Thenable<ITypeResult> {
|
||||
public type(editor: TextEditor, text: string, modifierKeys: ModifierKeys): Thenable<ITypeResult> {
|
||||
if (this._currentMode !== Mode.NORMAL && this._currentMode !== Mode.REPLACE) {
|
||||
return Promise.resolve({
|
||||
hasConsumedInput: false,
|
||||
@ -160,7 +160,7 @@ export class Controller implements IController {
|
||||
});
|
||||
}
|
||||
this._currentInput += text;
|
||||
return this._interpretNormalModeInput(editor);
|
||||
return this._interpretNormalModeInput(editor, modifierKeys);
|
||||
}
|
||||
|
||||
public replacePrevChar(editor: TextEditor, text: string, replaceCharCnt: number): boolean {
|
||||
@ -179,19 +179,19 @@ export class Controller implements IController {
|
||||
return true;
|
||||
}
|
||||
|
||||
private _interpretNormalModeInput(editor: TextEditor): Thenable<ITypeResult> {
|
||||
private _interpretNormalModeInput(editor: TextEditor, modifierKeys: ModifierKeys): Thenable<ITypeResult> {
|
||||
if (this._currentInput.startsWith(':')) {
|
||||
return vscode.window.showInputBox({value: 'tabm'}).then((value) => {
|
||||
let result = this._findMapping(value || '', editor);
|
||||
let result = this._findMapping(value || '', editor, modifierKeys);
|
||||
return Promise.resolve(result);
|
||||
});
|
||||
}
|
||||
let result = this._findMapping(this._currentInput, editor);
|
||||
let result = this._findMapping(this._currentInput, editor, modifierKeys);
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private _findMapping(input: string, editor: TextEditor): ITypeResult {
|
||||
let command = Mappings.findCommand(input);
|
||||
private _findMapping(input: string, editor: TextEditor, modifierKeys: ModifierKeys): ITypeResult {
|
||||
let command = Mappings.findCommand(input, modifierKeys);
|
||||
if (command) {
|
||||
this._currentInput = '';
|
||||
return {
|
||||
@ -200,7 +200,7 @@ export class Controller implements IController {
|
||||
};
|
||||
}
|
||||
|
||||
let operator = Mappings.findOperator(input);
|
||||
let operator = Mappings.findOperator(input, modifierKeys);
|
||||
if (operator) {
|
||||
if (this._isVisual) {
|
||||
if (operator.runVisual(this, editor)) {
|
||||
@ -218,7 +218,7 @@ export class Controller implements IController {
|
||||
};
|
||||
}
|
||||
|
||||
let motionCommand = Mappings.findMotionCommand(input, this._isVisual);
|
||||
let motionCommand = Mappings.findMotionCommand(input, this._isVisual, modifierKeys);
|
||||
if (motionCommand) {
|
||||
this._currentInput = '';
|
||||
return {
|
||||
|
||||
@ -9,33 +9,42 @@ import * as vscode from 'vscode';
|
||||
import {Words} from './words';
|
||||
import {MotionState, Motion, Motions} from './motions';
|
||||
import {Operator, Operators} from './operators';
|
||||
import {Mode, IController} from './common';
|
||||
import {Mode, IController, ModifierKeys} from './common';
|
||||
import {Mappings} from './mappings';
|
||||
import {Controller} from './controller';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
function registerCommandNice(commandId:string, run:(...args:any[])=>void): void {
|
||||
function registerCommandNice(commandId: string, run: (...args: any[]) => void): void {
|
||||
context.subscriptions.push(vscode.commands.registerCommand(commandId, run));
|
||||
}
|
||||
function registerCtrlKeyBinding(key: string): void {
|
||||
registerCommandNice(key, function (args) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
vimExt.type(key, {ctrl: true});
|
||||
});
|
||||
}
|
||||
|
||||
let vimExt = new VimExt();
|
||||
|
||||
registerCommandNice('type', function(args) {
|
||||
registerCommandNice('type', function (args) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
vimExt.type(args.text);
|
||||
});
|
||||
registerCommandNice('replacePreviousChar', function(args) {
|
||||
registerCommandNice('replacePreviousChar', function (args) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
vimExt.replacePrevChar(args.text, args.replaceCharCnt);
|
||||
});
|
||||
registerCommandNice('vim.goToNormalMode', function(args) {
|
||||
registerCommandNice('vim.goToNormalMode', function (args) {
|
||||
vimExt.goToNormalMode();
|
||||
});
|
||||
registerCommandNice('vim.clearInput', function(args) {
|
||||
registerCommandNice('vim.clearInput', function (args) {
|
||||
console.log(args);
|
||||
vimExt.clearInput();
|
||||
});
|
||||
// registerCommandNice('paste', function(args) {
|
||||
@ -44,6 +53,13 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
// registerCommandNice('cut', function(args) {
|
||||
// console.log('cut (no args)');
|
||||
// });
|
||||
|
||||
registerCtrlKeyBinding('e');
|
||||
registerCtrlKeyBinding('d');
|
||||
registerCtrlKeyBinding('f');
|
||||
registerCtrlKeyBinding('y');
|
||||
registerCtrlKeyBinding('u');
|
||||
registerCtrlKeyBinding('b');
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
@ -126,8 +142,8 @@ class VimExt {
|
||||
this._ensureState();
|
||||
}
|
||||
|
||||
public type(text: string): void {
|
||||
let r = this._controller.type(vscode.window.activeTextEditor, text).then(
|
||||
public type(text: string, modifierKeys: ModifierKeys = {ctrl: false, shifit: false, alt: false}): void {
|
||||
let r = this._controller.type(vscode.window.activeTextEditor, text, modifierKeys).then(
|
||||
(r) => {
|
||||
if (r.hasConsumedInput) {
|
||||
this._ensureState();
|
||||
@ -197,11 +213,11 @@ class ContextKey {
|
||||
private _name: string;
|
||||
private _lastValue: boolean;
|
||||
|
||||
constructor(name:string) {
|
||||
constructor(name: string) {
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
public set(value:boolean): void {
|
||||
public set(value: boolean): void {
|
||||
if (this._lastValue === value) {
|
||||
return;
|
||||
}
|
||||
@ -219,7 +235,7 @@ class StatusBar {
|
||||
this._actual.show();
|
||||
}
|
||||
|
||||
public setText(text:string): void {
|
||||
public setText(text: string): void {
|
||||
if (this._lastText === text) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7,18 +7,61 @@
|
||||
import {TextEditor} from 'vscode';
|
||||
import {Motion, Motions} from './motions';
|
||||
import {Operator, Operators} from './operators';
|
||||
import {IController, Command, AbstractCommandDescriptor} from './common';
|
||||
import {IController, Command, AbstractCommandDescriptor, ModifierKeys} from './common';
|
||||
|
||||
|
||||
const CHAR_TO_MOTION: { [char: string]: Motion; } = {};
|
||||
function defineMotion(char: string, motion: Motion): void {
|
||||
CHAR_TO_MOTION[char] = motion;
|
||||
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];
|
||||
};
|
||||
|
||||
const CHAR_TO_MOTION_COMMAND: { [char: string]: AbstractCommandDescriptor; } = {};
|
||||
function defineMotionCommand(char: string, motionCommand: AbstractCommandDescriptor): void {
|
||||
CHAR_TO_MOTION_COMMAND[char] = motionCommand;
|
||||
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);
|
||||
defineOperator('i', Operators.Insert);
|
||||
defineOperator('a', Operators.Append);
|
||||
defineOperator('A', Operators.AppendEndOfLine);
|
||||
defineOperator('d', Operators.DeleteTo);
|
||||
defineOperator('p', Operators.Put);
|
||||
defineOperator('r', Operators.Replace);
|
||||
defineOperator('R', Operators.ReplaceMode);
|
||||
defineOperator('c', Operators.Change);
|
||||
defineOperator('v', Operators.Visual);
|
||||
|
||||
// Commands
|
||||
defineCommand('u', 'undo');
|
||||
defineCommand('U', 'undo');
|
||||
|
||||
// Left-right motions
|
||||
defineMotionCommand('h', Motions.Left);
|
||||
@ -31,11 +74,11 @@ defineMotionCommand('gm', Motions.WrappedLineColumnCenter);
|
||||
defineMotionCommand('g$', Motions.WrappedLineEnd);
|
||||
defineMotionCommand('g_', Motions.WrappedLineLastNonWhiteSpaceCharacter);
|
||||
|
||||
// Scroll motions
|
||||
defineMotionCommand('zh', Motions.ScrollLeft);
|
||||
defineMotionCommand('zl', Motions.ScrollRight);
|
||||
defineMotionCommand('zH', Motions.ScrollLeftByHalfLine);
|
||||
defineMotionCommand('zL', Motions.ScrollRightByHalfLine);
|
||||
// Cursor scroll motions
|
||||
defineMotionCommand('zh', Motions.CursorScrollLeft);
|
||||
defineMotionCommand('zl', Motions.CursorScrollRight);
|
||||
defineMotionCommand('zH', Motions.CursorScrollLeftByHalfLine);
|
||||
defineMotionCommand('zL', Motions.CursorScrollRightByHalfLine);
|
||||
|
||||
// Up-down motions
|
||||
defineMotionCommand('j', Motions.Down);
|
||||
@ -61,27 +104,13 @@ defineMotionCommand('tabm<<', Motions.MoveActiveEditorFirst);
|
||||
defineMotionCommand('tabm>>', Motions.MoveActiveEditorLast);
|
||||
defineMotionCommand('tabm.', Motions.MoveActiveEditorCenter);
|
||||
|
||||
const CHAR_TO_OPERATOR: { [char: string]: Operator; } = {};
|
||||
function defineOperator(char: string, operator: Operator): void {
|
||||
CHAR_TO_OPERATOR[char] = operator;
|
||||
};
|
||||
defineOperator('x', Operators.DeleteCharUnderCursor);
|
||||
defineOperator('i', Operators.Insert);
|
||||
defineOperator('a', Operators.Append);
|
||||
defineOperator('A', Operators.AppendEndOfLine);
|
||||
defineOperator('d', Operators.DeleteTo);
|
||||
defineOperator('p', Operators.Put);
|
||||
defineOperator('r', Operators.Replace);
|
||||
defineOperator('R', Operators.ReplaceMode);
|
||||
defineOperator('c', Operators.Change);
|
||||
defineOperator('v', Operators.Visual);
|
||||
|
||||
const CHAR_TO_COMMAND: { [char: string]: Command; } = {};
|
||||
function defineCommand(char: string, commandId: string, args?: any): void {
|
||||
CHAR_TO_COMMAND[char] = {commandId : commandId, args: args};
|
||||
};
|
||||
defineCommand('u', 'undo');
|
||||
defineCommand('U', 'undo');
|
||||
// Scroll motions
|
||||
defineMotionCommand('e', Motions.ScrollDownByLine, {ctrl: true});
|
||||
defineMotionCommand('d', Motions.ScrollDownByHalfPage, {ctrl: true});
|
||||
defineMotionCommand('f', Motions.ScrollDownByPage, {ctrl: true});
|
||||
defineMotionCommand('y', Motions.ScrollUpByLine, {ctrl: true});
|
||||
defineMotionCommand('u', Motions.ScrollUpByHalfPage, {ctrl: true});
|
||||
defineMotionCommand('b', Motions.ScrollUpByPage, {ctrl: true});
|
||||
|
||||
export interface IFoundOperator {
|
||||
runNormal(controller: IController, editor:TextEditor): boolean;
|
||||
@ -92,9 +121,9 @@ export class Mappings {
|
||||
|
||||
public static findMotion(input: string): Motion {
|
||||
let parsed = _parseNumberAndString(input);
|
||||
let motion = CHAR_TO_MOTION[parsed.input.substr(0, 1)];
|
||||
let motion = getMotion(parsed.input.substr(0, 1));
|
||||
if (!motion) {
|
||||
motion = CHAR_TO_MOTION[parsed.input.substr(0, 2)];
|
||||
motion = getMotion(parsed.input.substr(0, 2));
|
||||
if (!motion) {
|
||||
return null;
|
||||
}
|
||||
@ -102,36 +131,36 @@ export class Mappings {
|
||||
return motion.repeat(parsed.hasRepeatCount, parsed.repeatCount);
|
||||
}
|
||||
|
||||
public static findMotionCommand(input: string, isVisual: boolean = false): Command {
|
||||
public static findMotionCommand(input: string, isVisual: boolean, modifierKeys: ModifierKeys): Command {
|
||||
let parsed = _parseNumberAndString(input);
|
||||
let command = Mappings.findMotionCommandFromNumberAndString(parsed, isVisual);
|
||||
let command = Mappings.findMotionCommandFromNumberAndString(parsed, isVisual, modifierKeys);
|
||||
if (!command) {
|
||||
parsed = _parseNumberAndString(input, false);
|
||||
command= Mappings.findMotionCommandFromNumberAndString(parsed, isVisual);
|
||||
command= Mappings.findMotionCommandFromNumberAndString(parsed, isVisual, modifierKeys);
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
private static findMotionCommandFromNumberAndString(numberAndString: INumberAndString, isVisual: boolean): Command {
|
||||
let motionCommand = CHAR_TO_MOTION_COMMAND[numberAndString.input.substr(0, 1)];
|
||||
private static findMotionCommandFromNumberAndString(numberAndString: INumberAndString, isVisual: boolean, modifierKeys: ModifierKeys): Command {
|
||||
let motionCommand = getMotionCommand(numberAndString.input.substr(0, 1), modifierKeys);
|
||||
if (!motionCommand) {
|
||||
motionCommand = CHAR_TO_MOTION_COMMAND[numberAndString.input.substr(0, 2)];
|
||||
motionCommand = getMotionCommand(numberAndString.input.substr(0, 2), modifierKeys);
|
||||
}
|
||||
if (!motionCommand) {
|
||||
motionCommand = CHAR_TO_MOTION_COMMAND[numberAndString.input.substr(1, 2)];
|
||||
motionCommand = getMotionCommand(numberAndString.input.substr(1, 2), modifierKeys);
|
||||
}
|
||||
if (!motionCommand) {
|
||||
motionCommand = CHAR_TO_MOTION_COMMAND[numberAndString.input.substr(1, 3)];
|
||||
motionCommand = getMotionCommand(numberAndString.input.substr(1, 3), modifierKeys);
|
||||
}
|
||||
if (!motionCommand) {
|
||||
motionCommand = CHAR_TO_MOTION_COMMAND[numberAndString.input];
|
||||
motionCommand = getMotionCommand(numberAndString.input, modifierKeys);
|
||||
}
|
||||
return motionCommand ? motionCommand.createCommand({ isVisual: isVisual, repeat: numberAndString.hasRepeatCount ? numberAndString.repeatCount : undefined}) : null;
|
||||
}
|
||||
|
||||
public static findOperator(input: string): IFoundOperator {
|
||||
public static findOperator(input: string, modifierKeys: ModifierKeys): IFoundOperator {
|
||||
let parsed = _parseNumberAndString(input);
|
||||
let operator = CHAR_TO_OPERATOR[parsed.input.substr(0, 1)];
|
||||
let operator = getOperator(parsed.input.substr(0, 1), modifierKeys);
|
||||
if (!operator) {
|
||||
return null;
|
||||
}
|
||||
@ -146,8 +175,8 @@ export class Mappings {
|
||||
};
|
||||
}
|
||||
|
||||
public static findCommand(input: string): Command {
|
||||
return CHAR_TO_COMMAND[input] || null;
|
||||
public static findCommand(input: string, modifierKeys: ModifierKeys): Command {
|
||||
return getCommand(input, modifierKeys) || null;
|
||||
}
|
||||
|
||||
public static isMotionPrefix(input: string): boolean {
|
||||
|
||||
@ -264,6 +264,25 @@ class CursorMoveCommand extends AbstractCommandDescriptor {
|
||||
}
|
||||
}
|
||||
|
||||
class EditorScrollCommand extends AbstractCommandDescriptor {
|
||||
|
||||
constructor(private to: string, private by?: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
public createCommand(args?: any): Command {
|
||||
let editorScrollArgs: any = {
|
||||
to: this.to,
|
||||
by: this.by,
|
||||
value: args.repeat || 1,
|
||||
}
|
||||
return {
|
||||
commandId: 'editorScroll',
|
||||
args: editorScrollArgs
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class MoveActiveEditorCommandByPosition extends AbstractCommandDescriptor {
|
||||
|
||||
constructor() {
|
||||
@ -318,10 +337,10 @@ export const Motions = {
|
||||
GoToFirstLine: new GoToFirstLineMotion(),
|
||||
GoToLastLine: new GoToLastLineMotion(),
|
||||
|
||||
ScrollLeft: new CursorMoveCommand('left'),
|
||||
ScrollRight: new CursorMoveCommand('right'),
|
||||
ScrollLeftByHalfLine: new CursorMoveCommand('left', 'halfLine'),
|
||||
ScrollRightByHalfLine: new CursorMoveCommand('right', 'halfLine'),
|
||||
CursorScrollLeft: new CursorMoveCommand('left'),
|
||||
CursorScrollRight: new CursorMoveCommand('right'),
|
||||
CursorScrollLeftByHalfLine: new CursorMoveCommand('left', 'halfLine'),
|
||||
CursorScrollRightByHalfLine: new CursorMoveCommand('right', 'halfLine'),
|
||||
|
||||
WrappedLineUp: new CursorMoveCommand('up', 'wrappedLine'),
|
||||
WrappedLineDown: new CursorMoveCommand('down', 'wrappedLine'),
|
||||
@ -341,5 +360,12 @@ export const Motions = {
|
||||
MoveActiveEditorRight: new MoveActiveEditorCommand('right'),
|
||||
MoveActiveEditorFirst: new MoveActiveEditorCommand('first'),
|
||||
MoveActiveEditorLast: new MoveActiveEditorCommand('last'),
|
||||
MoveActiveEditorCenter: new MoveActiveEditorCommand('center')
|
||||
MoveActiveEditorCenter: new MoveActiveEditorCommand('center'),
|
||||
|
||||
ScrollDownByLine: new EditorScrollCommand('down', 'line'),
|
||||
ScrollDownByHalfPage: new EditorScrollCommand('down', 'halfPage'),
|
||||
ScrollDownByPage: new EditorScrollCommand('down', 'page'),
|
||||
ScrollUpByLine: new EditorScrollCommand('up', 'line'),
|
||||
ScrollUpByHalfPage: new EditorScrollCommand('up', 'halfPage'),
|
||||
ScrollUpByPage: new EditorScrollCommand('up', 'page')
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user