From 2dfa17c77f336966f50902e78db9051bdd0a8f55 Mon Sep 17 00:00:00 2001 From: amin roosta <amin.roosta@outlook.com> Date: Mon, 1 Aug 2016 01:02:30 +0430 Subject: [PATCH 1/8] add register '*' --- src/actions/actions.ts | 25 +++++++++++++++++++++++++ src/mode/modeHandler.ts | 7 +++++-- src/register/register.ts | 10 +++++++--- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/actions/actions.ts b/src/actions/actions.ts index bead742610e..341f53d97d1 100644 --- a/src/actions/actions.ts +++ b/src/actions/actions.ts @@ -367,6 +367,31 @@ class CommandNumber extends BaseCommand { } } +@RegisterAction +class CommandRegister extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ["\"", "<character>"]; + isCompleteAction = false; + + public async exec(position: Position, vimState: VimState): Promise<VimState> { + const register = this.keysPressed[1]; + vimState.recordedState.registerName = register; + return vimState; + } + + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { + const register = keysPressed[1]; + + return super.doesActionApply(vimState, keysPressed) && true; + } + + public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { + const register = keysPressed[1]; + + return super.couldActionApply(vimState, keysPressed) && true; + } +} + @RegisterAction class CommandEsc extends BaseCommand { modes = [ModeName.Insert, ModeName.Visual, ModeName.VisualLine, ModeName.SearchInProgressMode]; diff --git a/src/mode/modeHandler.ts b/src/mode/modeHandler.ts index b573a23d430..dbc382fdfd5 100644 --- a/src/mode/modeHandler.ts +++ b/src/mode/modeHandler.ts @@ -103,8 +103,6 @@ export class VimState { } } - public registerName = '"'; - /** * This is for oddball commands that don't manipulate text in any way. */ @@ -302,6 +300,11 @@ export class RecordedState { */ public count: number = 0; + /** + * The register name for this action. + */ + public registerName: string = '"'; + public clone(): RecordedState { const res = new RecordedState(); diff --git a/src/register/register.ts b/src/register/register.ts index 0bc0947143c..582cdf14814 100644 --- a/src/register/register.ts +++ b/src/register/register.ts @@ -18,8 +18,12 @@ export interface IRegisterContent { } export class Register { + /** + * The '*' is the special register for stroing into system clipboard. + */ private static validRegisters = [ - '"' + '"', + '*' ]; private static registers: { [key: string]: IRegisterContent } = { @@ -31,7 +35,7 @@ export class Register { * register ". */ public static put(content: string, vimState: VimState): void { - const register = vimState.registerName; + const register = vimState.recordedState.registerName; if (Register.validRegisters.indexOf(register) === -1) { throw new Error(`Invalid register ${register}`); @@ -48,7 +52,7 @@ export class Register { * register ". */ public static get(vimState: VimState): IRegisterContent { - const register = vimState.registerName; + const register = vimState.recordedState.registerName; if (Register.validRegisters.indexOf(register) === -1) { throw new Error(`Invalid register ${register}`); From 321de6979f85144acbb23ea6aa8ba3ca4cddff84 Mon Sep 17 00:00:00 2001 From: amin roosta <amin.roosta@outlook.com> Date: Mon, 1 Aug 2016 02:03:31 +0430 Subject: [PATCH 2/8] add copy-paste to access system clipboard --- typings.json | 3 ++ typings/globals/copy-paste/index.d.ts | 43 +++++++++++++++++++++++++ typings/globals/copy-paste/typings.json | 8 +++++ typings/index.d.ts | 1 + 4 files changed, 55 insertions(+) create mode 100644 typings/globals/copy-paste/index.d.ts create mode 100644 typings/globals/copy-paste/typings.json diff --git a/typings.json b/typings.json index 2ac87d04b11..c1da071f9cd 100644 --- a/typings.json +++ b/typings.json @@ -3,5 +3,8 @@ "dependencies": { "diff": "registry:npm/diff#2.0.0+20160211003958", "lodash": "registry:npm/lodash#4.0.0+20160416211519" + }, + "globalDependencies": { + "copy-paste": "registry:dt/copy-paste#1.1.3+20160117130525" } } diff --git a/typings/globals/copy-paste/index.d.ts b/typings/globals/copy-paste/index.d.ts new file mode 100644 index 00000000000..349e868fc30 --- /dev/null +++ b/typings/globals/copy-paste/index.d.ts @@ -0,0 +1,43 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/5421783adfaf9b99e9274f4488cfc0ee73f17a56/copy-paste/copy-paste.d.ts +declare module 'copy-paste' { + + export type CopyCallback = (err: Error) => void; + export type PasteCallback = (err: Error, content: string) => void; + + /** + * Asynchronously replaces the current contents of the clip board with text. + * + * @param {T} content Takes either a string, array, object, or readable stream. + * @return {T} Returns the same value passed in. + */ + export function copy<T>(content: T): T; + + /** + * Asynchronously replaces the current contents of the clip board with text. + * + * @param {T} content Takes either a string, array, object, or readable stream. + * @param {CopyCallback} callback will fire when the copy operation is complete. + * @return {T} Returns the same value passed in. + */ + export function copy<T>(content: T, callback: CopyCallback): T; + + + /** + * Synchronously returns the current contents of the system clip board. + * + * Note: The synchronous version of paste is not always availabled. + * An error message is shown if the synchronous version of paste is used on an unsupported platform. + * The asynchronous version of paste is always available. + * + * @return {string} Returns the current contents of the system clip board. + */ + export function paste(): string; + + /** + * Asynchronously returns the current contents of the system clip board. + * + * @param {PasteCallback} callback The contents of the system clip board are passed to the callback as the second parameter. + */ + export function paste(callback: PasteCallback): void; +} diff --git a/typings/globals/copy-paste/typings.json b/typings/globals/copy-paste/typings.json new file mode 100644 index 00000000000..5cdb3500fa7 --- /dev/null +++ b/typings/globals/copy-paste/typings.json @@ -0,0 +1,8 @@ +{ + "resolution": "main", + "tree": { + "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/5421783adfaf9b99e9274f4488cfc0ee73f17a56/copy-paste/copy-paste.d.ts", + "raw": "registry:dt/copy-paste#1.1.3+20160117130525", + "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/5421783adfaf9b99e9274f4488cfc0ee73f17a56/copy-paste/copy-paste.d.ts" + } +} diff --git a/typings/index.d.ts b/typings/index.d.ts index 95cf3b0a29a..6cb68642ea0 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1,3 +1,4 @@ +/// <reference path="globals/copy-paste/index.d.ts" /> /// <reference path="modules/diff/index.d.ts" /> /// <reference path="modules/lodash/index.d.ts" /> /// <reference path="vscode/index.d.ts" /> From 323a62b093844ad233729d180d56f38530b244f3 Mon Sep 17 00:00:00 2001 From: amin roosta <amin.roosta@outlook.com> Date: Mon, 1 Aug 2016 02:04:17 +0430 Subject: [PATCH 3/8] read and write to clipboard when register * is accessed --- src/actions/actions.ts | 6 +++--- src/register/register.ts | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/actions/actions.ts b/src/actions/actions.ts index 341f53d97d1..d217a356019 100644 --- a/src/actions/actions.ts +++ b/src/actions/actions.ts @@ -917,7 +917,7 @@ export class PutCommand extends BaseCommand { canBeRepeatedWithDot = true; public async exec(position: Position, vimState: VimState, before: boolean = false, adjustIndent: boolean = false): Promise<VimState> { - const register = Register.get(vimState); + const register = await Register.get(vimState); const dest = before ? position : position.getRight(); let text = register.text; @@ -985,7 +985,7 @@ export class GPutCommand extends BaseCommand { } public async execCount(position: Position, vimState: VimState): Promise<VimState> { - const register = Register.get(vimState); + const register = await Register.get(vimState); const addedLinesCount = register.text.split('\n').length; const result = await super.execCount(position, vimState); @@ -1104,7 +1104,7 @@ export class GPutBeforeCommand extends BaseCommand { public async exec(position: Position, vimState: VimState): Promise<VimState> { const result = await new PutCommand().exec(position, vimState, true); - const register = Register.get(vimState); + const register = await Register.get(vimState); const addedLinesCount = register.text.split('\n').length; if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { diff --git a/src/register/register.ts b/src/register/register.ts index 582cdf14814..093feb66027 100644 --- a/src/register/register.ts +++ b/src/register/register.ts @@ -1,5 +1,5 @@ import { VimState } from './../mode/modeHandler'; - +import * as clipboard from 'copy-paste'; /** * There are two different modes of copy/paste in Vim - copy by character * and copy by line. Copy by line typically happens in Visual Line mode, but @@ -41,6 +41,10 @@ export class Register { throw new Error(`Invalid register ${register}`); } + if (register === '*') { + clipboard.copy(content); + } + Register.registers[register] = { text : content, registerMode: vimState.effectiveRegisterMode(), @@ -51,13 +55,24 @@ export class Register { * Gets content from a register. If none is specified, uses the default * register ". */ - public static get(vimState: VimState): IRegisterContent { + public static async get(vimState: VimState): Promise<IRegisterContent> { const register = vimState.recordedState.registerName; if (Register.validRegisters.indexOf(register) === -1) { throw new Error(`Invalid register ${register}`); } + /* Read from system clipboard */ + if (register === '*') { + const text = await new Promise<string>((resolve, reject) => + clipboard.paste((err, text) => { + if (err) { reject(err); } + else { resolve(text); } + }) + ); + Register.registers[register].text = text; + } + return Register.registers[register]; } } From 60e2ed22869fa365cd5f9cbf3baf2c2908fedceb Mon Sep 17 00:00:00 2001 From: amin roosta <amin.roosta@outlook.com> Date: Mon, 1 Aug 2016 02:25:33 +0430 Subject: [PATCH 4/8] unit tests for registers --- test/register/register.test.ts | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/register/register.test.ts diff --git a/test/register/register.test.ts b/test/register/register.test.ts new file mode 100644 index 00000000000..d905e2c083a --- /dev/null +++ b/test/register/register.test.ts @@ -0,0 +1,57 @@ +"use strict"; + +import { ModeHandler } from "../../src/mode/modeHandler"; +import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from '../testUtils'; + +suite("register", () => { + + let modeHandler: ModeHandler; + + setup(async () => { + await setupWorkspace(); + + modeHandler = new ModeHandler(); + }); + + suiteTeardown(cleanUpWorkspace); + + test("basic register put test", async () => { + await modeHandler.handleMultipleKeyEvents( + 'iblah blah'.split('') + ); + + await modeHandler.handleMultipleKeyEvents([ + '<esc>', + '^', '"', '"', 'D', '"', '"', 'p', '"', '"', 'p' + ]); + + await assertEqualLines(["blah blahblah blah"]); + }); + + test("test yy and '*' register", async () => { + await modeHandler.handleMultipleKeyEvents( + 'iblah blah\nblah'.split('') + ); + + await modeHandler.handleMultipleKeyEvents([ + '<esc>', + '^', '"', '*', 'y', 'y', '"', '*', 'p' + ]); + + await assertEqualLines(["blah blah", "blah", "blah"]); + }); + + test("test two seperate registers", async () => { + await modeHandler.handleMultipleKeyEvents( + 'iblah blah\nblah'.split('') + ); + /* Register '"' is the default register */ + await modeHandler.handleMultipleKeyEvents([ + '<esc>', + 'g', 'g', '"', '*', 'y', 'y', 'j', 'y', 'y', '"', '*', 'p', 'p', + ]); + + await assertEqualLines(["blah blah", "blah", "blah blah", "blah"]); + }); + +}); \ No newline at end of file From f3ecd4056c99d2f37571bb3bcc19c613e7e715ce Mon Sep 17 00:00:00 2001 From: amin roosta <amin.roosta@outlook.com> Date: Mon, 1 Aug 2016 02:45:36 +0430 Subject: [PATCH 5/8] support alphanameric registers & update roadmap --- ROADMAP.md | 3 ++- src/register/register.ts | 26 ++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 5b0c9928fbd..affe8c4ef8c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -183,7 +183,8 @@ Status | Command | Description Status | Command | Description ---|--------|------------------------------ - | "{char} | use register {char} for the next delete, yank, or put +:warning: | "{char} | use register {char} for the next delete, yank, or put +:white_check_mark: | "* | use register `*` to access system clipboard | :reg | show the contents of all registers | :reg {arg} | show the contents of registers mentioned in {arg} :white_check_mark: | :1234: y{motion} | yank the text moved over with {motion} into a register diff --git a/src/register/register.ts b/src/register/register.ts index 093feb66027..65d749ec314 100644 --- a/src/register/register.ts +++ b/src/register/register.ts @@ -19,17 +19,23 @@ export interface IRegisterContent { export class Register { /** + * The '"' is the unnamed register. * The '*' is the special register for stroing into system clipboard. + * TODO: Read-Only registers + * '.' register has the last inserted text. + * '%' register has the current file path. + * ':' is the most recently executed command. + * '#' is the name of last edited file. (low priority) */ - private static validRegisters = [ - '"', - '*' - ]; - private static registers: { [key: string]: IRegisterContent } = { - '"': { text: "", registerMode: RegisterMode.CharacterWise } + '"': { text: "", registerMode: RegisterMode.CharacterWise }, + '*': { text: "", registerMode: RegisterMode.CharacterWise } }; + private static isValidRegister(register: string): boolean { + return register in Register.registers || /^[a-z0-9]+$/i.test(register); + } + /** * Puts content in a register. If none is specified, uses the default * register ". @@ -37,7 +43,7 @@ export class Register { public static put(content: string, vimState: VimState): void { const register = vimState.recordedState.registerName; - if (Register.validRegisters.indexOf(register) === -1) { + if (!Register.isValidRegister(register)) { throw new Error(`Invalid register ${register}`); } @@ -58,10 +64,14 @@ export class Register { public static async get(vimState: VimState): Promise<IRegisterContent> { const register = vimState.recordedState.registerName; - if (Register.validRegisters.indexOf(register) === -1) { + if (!Register.isValidRegister(register)) { throw new Error(`Invalid register ${register}`); } + if (!Register.registers[register]) { + Register.registers[register] = { text: "", registerMode: RegisterMode.CharacterWise }; + } + /* Read from system clipboard */ if (register === '*') { const text = await new Promise<string>((resolve, reject) => From 3809e766e6a409badeb5bcf01be21801c92b2358 Mon Sep 17 00:00:00 2001 From: amin roosta <amin.roosta@outlook.com> Date: Mon, 1 Aug 2016 16:17:28 +0430 Subject: [PATCH 6/8] trying to fix ci build --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7bf884db2b7..ec3834f0cb5 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,7 @@ } }, "scripts": { - "vscode:prepublish": "node ./node_modules/vscode/bin/compile", + "vscode:prepublish": "node ./node_modules/vscode/bin/compile -p ./", "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", "test": "node ./node_modules/vscode/bin/test", "postinstall": "node ./node_modules/vscode/bin/install && gulp init" @@ -188,4 +188,4 @@ "typings": "^1.0.4", "vscode": "^0.11.13" } -} \ No newline at end of file +} From b7d21dea0f05c32637354784b40a456097636b7c Mon Sep 17 00:00:00 2001 From: amin roosta <amin.roosta@outlook.com> Date: Mon, 1 Aug 2016 16:27:00 +0430 Subject: [PATCH 7/8] Fix gulp:tslint errors --- src/actions/actions.ts | 4 ++-- src/register/register.ts | 9 ++++++--- test/mode/modeNormal.test.ts | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/actions/actions.ts b/src/actions/actions.ts index d217a356019..a6b174325dc 100644 --- a/src/actions/actions.ts +++ b/src/actions/actions.ts @@ -382,13 +382,13 @@ class CommandRegister extends BaseCommand { public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { const register = keysPressed[1]; - return super.doesActionApply(vimState, keysPressed) && true; + return super.doesActionApply(vimState, keysPressed) && Register.isValidRegister(register); } public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { const register = keysPressed[1]; - return super.couldActionApply(vimState, keysPressed) && true; + return super.couldActionApply(vimState, keysPressed) && Register.isValidRegister(register); } } diff --git a/src/register/register.ts b/src/register/register.ts index 65d749ec314..62fcd85f6a3 100644 --- a/src/register/register.ts +++ b/src/register/register.ts @@ -32,7 +32,7 @@ export class Register { '*': { text: "", registerMode: RegisterMode.CharacterWise } }; - private static isValidRegister(register: string): boolean { + public static isValidRegister(register: string): boolean { return register in Register.registers || /^[a-z0-9]+$/i.test(register); } @@ -76,8 +76,11 @@ export class Register { if (register === '*') { const text = await new Promise<string>((resolve, reject) => clipboard.paste((err, text) => { - if (err) { reject(err); } - else { resolve(text); } + if (err) { + reject(err); + } else { + resolve(text); + } }) ); Register.registers[register].text = text; diff --git a/test/mode/modeNormal.test.ts b/test/mode/modeNormal.test.ts index d4b5b16785b..47787dad65a 100644 --- a/test/mode/modeNormal.test.ts +++ b/test/mode/modeNormal.test.ts @@ -1025,5 +1025,5 @@ suite("Mode Normal", () => { start: ["|blah blah"], keysPressed: "Yp", end: ["blah blah", "|blah blah"] - }) + }); }); \ No newline at end of file From bf4f2a76bf3afbc14b3e11c832f34db4de809f55 Mon Sep 17 00:00:00 2001 From: amin roosta <amin.roosta@outlook.com> Date: Tue, 2 Aug 2016 13:30:23 +0430 Subject: [PATCH 8/8] use new test style in register.test.ts --- package.json | 1 + test/register/register.test.ts | 64 +++++++++++++++------------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index ec3834f0cb5..7c06a49dd38 100644 --- a/package.json +++ b/package.json @@ -166,6 +166,7 @@ "postinstall": "node ./node_modules/vscode/bin/install && gulp init" }, "dependencies": { + "copy-paste": "^1.3.0", "diff": "^2.2.3", "lodash": "^4.12.0" }, diff --git a/test/register/register.test.ts b/test/register/register.test.ts index d905e2c083a..3e1ada972ec 100644 --- a/test/register/register.test.ts +++ b/test/register/register.test.ts @@ -2,56 +2,50 @@ import { ModeHandler } from "../../src/mode/modeHandler"; import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from '../testUtils'; +import { getTestingFunctions } from '../testSimplifier'; +import * as clipboard from 'copy-paste'; suite("register", () => { + let modeHandler: ModeHandler = new ModeHandler(); - let modeHandler: ModeHandler; + let { + newTest, + newTestOnly, + } = getTestingFunctions(modeHandler); setup(async () => { await setupWorkspace(); - - modeHandler = new ModeHandler(); }); suiteTeardown(cleanUpWorkspace); - test("basic register put test", async () => { - await modeHandler.handleMultipleKeyEvents( - 'iblah blah'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '<esc>', - '^', '"', '"', 'D', '"', '"', 'p', '"', '"', 'p' - ]); - - await assertEqualLines(["blah blahblah blah"]); + newTest({ + title: "Can copy to a register", + start: ['|one', 'two'], + keysPressed: '"add"ap', + end: ["two", "|one"], }); - test("test yy and '*' register", async () => { - await modeHandler.handleMultipleKeyEvents( - 'iblah blah\nblah'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '<esc>', - '^', '"', '*', 'y', 'y', '"', '*', 'p' - ]); + newTest({ + title: "Can copy to a register", + start: ['|one', 'two'], + keysPressed: '"add"ap', + end: ["two", "|one"], + }); - await assertEqualLines(["blah blah", "blah", "blah"]); + clipboard.copy("12345"); + newTest({ + title: "Can access '*' (clipboard) register", + start: ['|one'], + keysPressed: '"*P', + end: ["1234|5one"], }); - test("test two seperate registers", async () => { - await modeHandler.handleMultipleKeyEvents( - 'iblah blah\nblah'.split('') - ); - /* Register '"' is the default register */ - await modeHandler.handleMultipleKeyEvents([ - '<esc>', - 'g', 'g', '"', '*', 'y', 'y', 'j', 'y', 'y', '"', '*', 'p', 'p', - ]); - - await assertEqualLines(["blah blah", "blah", "blah blah", "blah"]); + newTest({ + title: "Can use two registers together", + start: ['|one', "two"], + keysPressed: '"*yyjyy"*pp', + end: ["one", "two", "one", "|two"], }); }); \ No newline at end of file