From 435d503317d2d9fbb9fdc5407ec7740c89c4cf6f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 25 Jul 2016 16:59:01 +0200 Subject: [PATCH] Implement move active tab --- vim-sample/src/common.ts | 9 +++- vim-sample/src/controller.ts | 54 ++++++++++++++------- vim-sample/src/extension.ts | 25 ++++++---- vim-sample/src/mappings.ts | 32 ++++++++++--- vim-sample/src/motions.ts | 92 +++++++++++++++++++++++------------- 5 files changed, 142 insertions(+), 70 deletions(-) diff --git a/vim-sample/src/common.ts b/vim-sample/src/common.ts index 9d05e6ed..f19665f8 100644 --- a/vim-sample/src/common.ts +++ b/vim-sample/src/common.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import * as vscode from 'vscode'; import {MotionState, Motion} from './motions'; export enum Mode { @@ -34,7 +35,13 @@ export interface IController { getDeleteRegister(): DeleteRegister; } +export abstract class AbstractCommandDescriptor { + + public abstract createCommand(args?: any): Command; + +} + export interface Command { commandId: string, - args?: any + args?: any[] } \ No newline at end of file diff --git a/vim-sample/src/controller.ts b/vim-sample/src/controller.ts index 2ef81cd2..5dd41bf2 100644 --- a/vim-sample/src/controller.ts +++ b/vim-sample/src/controller.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import * as vscode from 'vscode'; import { TextEditorCursorStyle, Position, @@ -135,12 +136,12 @@ export class Controller implements IController { return `VIM:> ${label}` + (this._currentInput ? ` >${this._currentInput}` : ``); } - public type(editor: TextEditor, text: string): ITypeResult { + public type(editor: TextEditor, text: string): Thenable { if (this._currentMode !== Mode.NORMAL && this._currentMode !== Mode.REPLACE) { - return { + return Promise.resolve({ hasConsumedInput: false, executeEditorCommand: null - }; + }); } if (this._currentMode === Mode.REPLACE) { let pos = editor.selection.active; @@ -150,10 +151,10 @@ export class Controller implements IController { setPositionAndReveal(editor, pos.line, pos.character + 1); }); - return { + return Promise.resolve({ hasConsumedInput: true, executeEditorCommand: null - }; + }); } this._currentInput += text; return this._interpretNormalModeInput(editor); @@ -175,8 +176,31 @@ export class Controller implements IController { return true; } - private _interpretNormalModeInput(editor: TextEditor): ITypeResult { - let command = Mappings.findCommand(this._currentInput); + private _interpretNormalModeInput(editor: TextEditor): Thenable { + if (this._currentInput.startsWith(':')) { + return vscode.window.showInputBox({value: 'tabm'}).then((value) => { + let motionCommand = value ? Mappings.findMotionCommand(value) : null; + this._currentInput = ''; + return { + hasConsumedInput: true, + executeEditorCommand: motionCommand + }; + }); + } + let result = this._findMapping(this._currentInput, editor); + if (!result) { + // INVALID INPUT - beep!! + this._currentInput = ''; + result = { + hasConsumedInput: true, + executeEditorCommand: null + }; + } + return Promise.resolve(result); + } + + private _findMapping(input: string, editor: TextEditor): ITypeResult { + let command = Mappings.findCommand(input); if (command) { this._currentInput = ''; return { @@ -185,7 +209,7 @@ export class Controller implements IController { }; } - let operator = Mappings.findOperator(this._currentInput); + let operator = Mappings.findOperator(input); if (operator) { if (this._isVisual) { if (operator.runVisual(this, editor)) { @@ -203,7 +227,7 @@ export class Controller implements IController { }; } - let motionCommand = Mappings.findMotionCommand(this._currentInput, this._isVisual); + let motionCommand = Mappings.findMotionCommand(input, this._isVisual); if (motionCommand) { this._currentInput = ''; return { @@ -212,7 +236,7 @@ export class Controller implements IController { }; } - let motion = Mappings.findMotion(this._currentInput); + let motion = Mappings.findMotion(input); if (motion) { let newPos = motion.run(editor.document, editor.selection.active, this._motionState); if (this._isVisual) { @@ -229,20 +253,14 @@ export class Controller implements IController { } // is it motion building - if (this.isMotionPrefix(this._currentInput)) { + if (this.isMotionPrefix(input)) { return { hasConsumedInput: true, executeEditorCommand: null }; } - // INVALID INPUT - beep!! - this._currentInput = ''; - - return { - hasConsumedInput: true, - executeEditorCommand: null - }; + return null; } } diff --git a/vim-sample/src/extension.ts b/vim-sample/src/extension.ts index 86bd1e9f..f623eaa8 100644 --- a/vim-sample/src/extension.ts +++ b/vim-sample/src/extension.ts @@ -127,17 +127,22 @@ class VimExt { } public type(text: string): void { - let r = this._controller.type(vscode.window.activeTextEditor, text); - if (r.hasConsumedInput) { - this._ensureState(); - if (r.executeEditorCommand) { - vscode.commands.executeCommand(r.executeEditorCommand.commandId, r.executeEditorCommand.args); + let r = this._controller.type(vscode.window.activeTextEditor, text).then( + (r) => { + if (r.hasConsumedInput) { + this._ensureState(); + if (r.executeEditorCommand) { + let args = [r.executeEditorCommand.commandId]; + args = args.concat(r.executeEditorCommand.args); + vscode.commands.executeCommand.apply(this, args); + } + return; + } + vscode.commands.executeCommand('default:type', { + text: text + }); } - return; - } - vscode.commands.executeCommand('default:type', { - text: text - }); + ); } public replacePrevChar(text: string, replaceCharCnt: number): void { diff --git a/vim-sample/src/mappings.ts b/vim-sample/src/mappings.ts index 70bb7012..c8bcacf7 100644 --- a/vim-sample/src/mappings.ts +++ b/vim-sample/src/mappings.ts @@ -5,9 +5,9 @@ 'use strict'; import {TextEditor} from 'vscode'; -import {Motion, Motions, MotionCommand} from './motions'; +import {Motion, Motions} from './motions'; import {Operator, Operators} from './operators'; -import {IController, Command} from './common'; +import {IController, Command, AbstractCommandDescriptor} from './common'; const CHAR_TO_MOTION: { [char: string]: Motion; } = {}; @@ -15,8 +15,8 @@ function defineMotion(char: string, motion: Motion): void { CHAR_TO_MOTION[char] = motion; }; -const CHAR_TO_MOTION_COMMAND: { [char: string]: MotionCommand; } = {}; -function defineMotionCommand(char: string, motionCommand: MotionCommand): void { +const CHAR_TO_MOTION_COMMAND: { [char: string]: AbstractCommandDescriptor; } = {}; +function defineMotionCommand(char: string, motionCommand: AbstractCommandDescriptor): void { CHAR_TO_MOTION_COMMAND[char] = motionCommand; }; @@ -52,6 +52,13 @@ defineMotionCommand('L', Motions.ViewPortBottom); defineMotion('w', Motions.NextWordStart); defineMotion('e', Motions.NextWordEnd); +// Window motions +defineMotionCommand('tabm', Motions.MoveActiveEditor); +defineMotionCommand('tabm<', Motions.MoveActiveEditorLeft); +defineMotionCommand('tabm>', Motions.MoveActiveEditorRight); +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 { @@ -106,7 +113,10 @@ export class Mappings { if (!motionCommand) { motionCommand = CHAR_TO_MOTION_COMMAND[parsed.input.substr(1, 3)]; } - return motionCommand ? motionCommand.command({ isVisual: isVisual, repeat: parsed.repeatCount }) : null; + if (!motionCommand) { + motionCommand = CHAR_TO_MOTION_COMMAND[parsed.input]; + } + return motionCommand ? motionCommand.createCommand({ isVisual: isVisual, repeat: parsed.hasRepeatCount ? parsed.repeatCount : undefined}) : null; } public static findOperator(input: string): IFoundOperator { @@ -117,10 +127,10 @@ export class Mappings { } let operatorArgs = parsed.input.substr(1); return { - runNormal: (controller: IController, editor:TextEditor) => { + runNormal: (controller: IController, editor: TextEditor) => { return operator.runNormalMode(controller, editor, parsed.repeatCount, operatorArgs); }, - runVisual: (controller: IController, editor:TextEditor) => { + runVisual: (controller: IController, editor: TextEditor) => { return operator.runVisualMode(controller, editor, operatorArgs); } }; @@ -150,6 +160,14 @@ function _parseNumberAndString(input: string): INumberAndString { input: input.substr(repeatCountMatch[0].length) }; } + repeatCountMatch = input.match(/(\d+)$/); + if (repeatCountMatch) { + return { + hasRepeatCount: true, + repeatCount: parseInt(repeatCountMatch[1], 10), + input: input.substr(0, input.length - repeatCountMatch[1].length) + }; + } return { hasRepeatCount: false, repeatCount: 1, diff --git a/vim-sample/src/motions.ts b/vim-sample/src/motions.ts index 69409b6c..450f8459 100644 --- a/vim-sample/src/motions.ts +++ b/vim-sample/src/motions.ts @@ -6,7 +6,7 @@ import {Position, TextDocument} from 'vscode'; import {Words, WordCharacters} from './words'; -import {Command} from './common'; +import {Command, AbstractCommandDescriptor} from './common'; export class MotionState { @@ -22,12 +22,6 @@ export class MotionState { } -export abstract class MotionCommand { - - public abstract command(args: any): Command; - -} - export abstract class Motion { public abstract run(doc: TextDocument, pos: Position, state: MotionState): Position; @@ -250,37 +244,60 @@ class GoToLineDefinedMotion extends GoToLineMotion { } } -class CursorMoveCommand extends MotionCommand { +class CursorMoveCommand extends AbstractCommandDescriptor { constructor(private to: string, private by?: string) { super(); } - public command(count?: number): Command { - let cursorMoveArgs= { to: this.to } + public createCommand(args?: any): Command { + let cursorMoveArgs: any = { + to: this.to, + by: this.by, + value: args.repeat || 1, + select: !!args.isVisual + } return { commandId: 'cursorMove', - args: this.addArgs(cursorMoveArgs, count) + args: cursorMoveArgs }; } - - protected addArgs(cursorMoveArgs: any, args: any): any { - cursorMoveArgs.select = !!args.isVisual; - if (this.by) { - cursorMoveArgs.by = this.by; - } - return cursorMoveArgs; - } - } -class RepeatCursorMoveCommand extends CursorMoveCommand { +class MoveActiveEditorCommandByPosition extends AbstractCommandDescriptor { - protected addArgs(cursorMoveArgs: any, args: any): any { - cursorMoveArgs.amount = args.repeat || 1; - return super.addArgs(cursorMoveArgs, args); + constructor() { + super(); } + public createCommand(args?: any): Command { + let moveActiveEditorArgs: any = { + to: args.repeat === void 0 ? 'last' : 'position', + value: args.repeat !== void 0 ? args.repeat + 1 : undefined + } + return { + commandId: 'moveActiveEditor', + args: moveActiveEditorArgs + }; + } +} + +class MoveActiveEditorCommand extends AbstractCommandDescriptor { + + constructor(private to: string) { + super(); + } + + public createCommand(args?: any): Command { + let moveActiveEditorArgs: any = { + to: this.to, + value: args.repeat ? args.repeat : 1 + } + return { + commandId: 'moveActiveEditor', + args: moveActiveEditorArgs + }; + } } export const Motions = { @@ -288,8 +305,8 @@ export const Motions = { NextCharacter: new NextCharacterMotion(), - Left: new RepeatCursorMoveCommand('left'), - Right: new RepeatCursorMoveCommand('right'), + Left: new CursorMoveCommand('left'), + Right: new CursorMoveCommand('right'), Down: new DownMotion(), Up: new UpMotion(), @@ -301,20 +318,27 @@ export const Motions = { GoToFirstLine: new GoToFirstLineMotion(), GoToLastLine: new GoToLastLineMotion(), - ScrollLeft: new RepeatCursorMoveCommand('left'), - ScrollRight: new RepeatCursorMoveCommand('right'), - ScrollLeftByHalfLine: new RepeatCursorMoveCommand('left', 'halfLine'), - ScrollRightByHalfLine: new RepeatCursorMoveCommand('right', 'halfLine'), + ScrollLeft: new CursorMoveCommand('left'), + ScrollRight: new CursorMoveCommand('right'), + ScrollLeftByHalfLine: new CursorMoveCommand('left', 'halfLine'), + ScrollRightByHalfLine: new CursorMoveCommand('right', 'halfLine'), - WrappedLineUp: new RepeatCursorMoveCommand('up', 'wrappedLine'), - WrappedLineDown: new RepeatCursorMoveCommand('down', 'wrappedLine'), + WrappedLineUp: new CursorMoveCommand('up', 'wrappedLine'), + WrappedLineDown: new CursorMoveCommand('down', 'wrappedLine'), WrappedLineStart: new CursorMoveCommand('wrappedLineStart'), WrappedLineFirstNonWhiteSpaceCharacter: new CursorMoveCommand('wrappedLineFirstNonWhitespaceCharacter'), WrappedLineColumnCenter: new CursorMoveCommand('wrappedLineColumnCenter'), WrappedLineEnd: new CursorMoveCommand('wrappedLineEnd'), - ViewPortTop: new RepeatCursorMoveCommand('viewPortTop'), - ViewPortBottom: new RepeatCursorMoveCommand('viewPortBottom'), + ViewPortTop: new CursorMoveCommand('viewPortTop'), + ViewPortBottom: new CursorMoveCommand('viewPortBottom'), ViewPortCenter: new CursorMoveCommand('viewPortCenter'), + + MoveActiveEditor: new MoveActiveEditorCommandByPosition(), + MoveActiveEditorLeft: new MoveActiveEditorCommand('left'), + MoveActiveEditorRight: new MoveActiveEditorCommand('right'), + MoveActiveEditorFirst: new MoveActiveEditorCommand('first'), + MoveActiveEditorLast: new MoveActiveEditorCommand('last'), + MoveActiveEditorCenter: new MoveActiveEditorCommand('center') };