diff --git a/.travis.yml b/.travis.yml index 95d1be429b8..21be7cbf49f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,14 +3,18 @@ notifications: sudo: false +addons: + apt: + packages: + - xclip + language: node_js node_js: - "4.2" - env: - GITHUB_TOKEN=1b17d62d38a4846efa7ea4de4b773b581787b0f1 - + before_install: - if [ $TRAVIS_OS_NAME == "linux" ]; then export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; diff --git a/src/mode/modeNormal.ts b/src/mode/modeNormal.ts index 7416b4aa10a..76adbb07268 100644 --- a/src/mode/modeNormal.ts +++ b/src/mode/modeNormal.ts @@ -9,6 +9,7 @@ import {Motion, MotionMode} from './../motion/motion'; import {ModeHandler} from './modeHandler'; import {DeleteOperator} from './../operator/delete'; import {ChangeOperator} from './../operator/change'; +import {PutOperator} from './../operator/put'; import {TextEditor} from './../textEditor'; export class NormalMode extends Mode { @@ -136,6 +137,10 @@ export class NormalMode extends Mode { return {}; }, "X" : async (m) => { return vscode.commands.executeCommand("deleteLeft"); }, + "p" : async (m) => { + await new PutOperator(this._modeHandler).run(m.position, null); + return {}; + }, "esc": async () => { return vscode.commands.executeCommand("workbench.action.closeMessages"); } }; diff --git a/src/mode/modeVisual.ts b/src/mode/modeVisual.ts index c38d9ae07f1..6a8632d10c4 100644 --- a/src/mode/modeVisual.ts +++ b/src/mode/modeVisual.ts @@ -35,7 +35,7 @@ export class VisualMode extends Mode { // simply allow the operators to say what mode they transition into. 'd': new DeleteOperator(modeHandler), 'x': new DeleteOperator(modeHandler), - 'c': new ChangeOperator(modeHandler) + 'c': new ChangeOperator(modeHandler), }; } diff --git a/src/operator/put.ts b/src/operator/put.ts new file mode 100644 index 00000000000..7775a3bd5ca --- /dev/null +++ b/src/operator/put.ts @@ -0,0 +1,33 @@ +"use strict"; + +import { paste } from 'copy-paste'; +import { Position } from './../motion/position'; +import { Operator } from './operator'; +import { ModeHandler } from './../mode/modeHandler.ts'; +import { TextEditor } from './../textEditor'; + +export class PutOperator extends Operator { + + constructor(modeHandler: ModeHandler) { + super(modeHandler); + } + + public key(): string { return "p"; } + + /** + * Run this operator on a range. + */ + public async run(start: Position, end: Position): Promise { + return new Promise((resolve, reject) => { + paste(async (err, data) => { + if (err) { + reject(); + } else { + await TextEditor.insertAt(data, start.getRight()); + this.modeHandler.currentMode.motion.moveTo(start.line, start.getRight().character); + resolve(); + } + }); + }); + } +} \ No newline at end of file diff --git a/src/textEditor.ts b/src/textEditor.ts index 9fc9209cec7..163c4af64e7 100644 --- a/src/textEditor.ts +++ b/src/textEditor.ts @@ -1,6 +1,7 @@ "use strict"; import * as vscode from "vscode"; +import {copy} from "copy-paste"; export class TextEditor { static async insert(text: string): Promise { @@ -16,6 +17,7 @@ export class TextEditor { } static async delete(range: vscode.Range): Promise { + copy(vscode.window.activeTextEditor.document.getText(range)); return vscode.window.activeTextEditor.edit(editBuilder => { editBuilder.delete(range); }); diff --git a/test/operator/put.test.ts b/test/operator/put.test.ts new file mode 100644 index 00000000000..65cdeb97a1b --- /dev/null +++ b/test/operator/put.test.ts @@ -0,0 +1,61 @@ +"use strict"; + +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { copy } from "copy-paste"; +import { ModeHandler } from "../../src/mode/modeHandler"; +import { PutOperator } from "../../src/operator/put"; +import { TextEditor } from '../../src/textEditor'; +import { Position, PositionOptions } from "../../src/motion/position"; +import { setupWorkspace, cleanUpWorkspace } from '../testUtils'; + +suite("put operator", () => { + suiteSetup(setupWorkspace); + + suiteTeardown(cleanUpWorkspace); + + test("put 'the dog' into empty file", async () => { + const expectedText = "the dog"; + const position = new Position(0, 0, PositionOptions.CharacterWiseExclusive); + const mode = new ModeHandler(); + const put = new PutOperator(mode); + copy(expectedText); + + await put.run(position, position); + + const actualText = TextEditor.readLineAt(0); + const cursorPosition = vscode.window.activeTextEditor.selection.active; + assert.equal(actualText, expectedText, + "did not paste expected content"); + assert.equal(cursorPosition.line, position.getRight().line, + "cursor should be on the same line"); + assert.equal(cursorPosition.character, position.getRight().character, + "cursor should be on start of put content"); + }); + + test("put ' brown' into 'the dog'", async () => { + const phrase = "brown "; + const expectedText = `the ${phrase}dog`; + const position = new Position(0, 3, PositionOptions.CharacterWiseExclusive); + const mode = new ModeHandler(); + const put = new PutOperator(mode); + copy(phrase); + + // using ^ to show the cusor position + // before : the dog + // ^ + // after : the brown dog + // ^ + + await put.run(position, position); + + const actualText = TextEditor.readLineAt(0); + const cursorPosition = vscode.window.activeTextEditor.selection.active; + assert.equal(actualText, expectedText, + "did not paste expected content"); + assert.equal(cursorPosition.line, position.getRight().line, + "cursor should be on the same line"); + assert.equal(cursorPosition.character, position.getRight().character, + "cursor should be on start of put content"); + }); +}); \ No newline at end of file diff --git a/test/textEditor.test.ts b/test/textEditor.test.ts index 6cab76fa09f..714b1bae45c 100644 --- a/test/textEditor.test.ts +++ b/test/textEditor.test.ts @@ -2,6 +2,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; +import {paste} from "copy-paste"; import {TextEditor} from './../src/textEditor'; import {setupWorkspace, cleanUpWorkspace} from './testUtils'; @@ -59,4 +60,15 @@ suite("text editor", () => { assert.throws(() => TextEditor.readLineAt(1), RangeError); assert.throws(() => TextEditor.readLineAt(2), RangeError); }); + + test("delete should copy to clipboard", async () => { + const expectedText = "Hello World"; + await TextEditor.insert(expectedText); + + const range = vscode.window.activeTextEditor.document.lineAt(0).range; + + await TextEditor.delete(range); + const actualText = paste(); + assert.equal(actualText, expectedText); + }); }); diff --git a/typings.json b/typings.json index c7b32686740..822376c0afd 100644 --- a/typings.json +++ b/typings.json @@ -1,6 +1,7 @@ { "name": "vim", "ambientDependencies": { + "copy-paste": "registry:dt/copy-paste#1.1.3+20160117130525", "lodash": "github:DefinitelyTyped/DefinitelyTyped/lodash/lodash.d.ts#7b7aa2027a8fb6219a8bcf1b6bb12bcd0ff9539d" } }