Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support "{char} registers and clipboard access via "* register. #543

Merged
merged 9 commits into from
Aug 2, 2016
3 changes: 2 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 28 additions & 3 deletions src/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Copy link
Member

@johnfn johnfn Aug 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is excellent.

(Yay, someone understands my abstractions... other than me... hehe)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've done a great job designing project source code, well done @johnfn 👍

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];
Expand Down Expand Up @@ -892,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;

Expand Down Expand Up @@ -960,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);

Expand Down Expand Up @@ -1079,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) {
Expand Down
7 changes: 5 additions & 2 deletions src/mode/modeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ export class VimState {
}
}

public registerName = '"';

/**
* This is for oddball commands that don't manipulate text in any way.
*/
Expand Down Expand Up @@ -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();

Expand Down
51 changes: 40 additions & 11 deletions src/register/register.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -18,25 +18,39 @@ export interface IRegisterContent {
}

export class Register {
private static validRegisters = [
'"'
];

/**
* 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 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 ".
*/
public static put(content: string, vimState: VimState): void {
const register = vimState.registerName;
const register = vimState.recordedState.registerName;

if (Register.validRegisters.indexOf(register) === -1) {
if (!Register.isValidRegister(register)) {
throw new Error(`Invalid register ${register}`);
}

if (register === '*') {
clipboard.copy(content);
}

Register.registers[register] = {
text : content,
registerMode: vimState.effectiveRegisterMode(),
Expand All @@ -47,13 +61,28 @@ export class Register {
* Gets content from a register. If none is specified, uses the default
* register ".
*/
public static get(vimState: VimState): IRegisterContent {
const register = vimState.registerName;
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) =>
clipboard.paste((err, text) => {
if (err) { reject(err); }
else { resolve(text); }
})
);
Register.registers[register].text = text;
}

return Register.registers[register];
}
}
57 changes: 57 additions & 0 deletions test/register/register.test.ts
Original file line number Diff line number Diff line change
@@ -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 () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer these to be done in the style of newTest - I find that to be much easier to read. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, feel free to comment on things I've missed ;-)

I will fix them tomorrow (3am here)

Get Outlook for Android

On Mon, Aug 1, 2016 at 2:57 AM +0430, "Grant Mathews" notifications@github.com wrote:

@@ -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 () => {

I would prefer these to be done in the style of newTest - I find that to be much easier to read. :)


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub:
https://github.com/VSCodeVim/Vim/pull/543/files/f3ecd4056c99d2f37571bb3bcc19c613e7e715ce#r72912330

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnfn Where can i find an example of "style of newTest" you mentioned ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modeNormal.test.ts is a good place to look. :)

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"]);
});

});
3 changes: 3 additions & 0 deletions typings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
43 changes: 43 additions & 0 deletions typings/globals/copy-paste/index.d.ts
Original file line number Diff line number Diff line change
@@ -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;
}
8 changes: 8 additions & 0 deletions typings/globals/copy-paste/typings.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
1 change: 1 addition & 0 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
@@ -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" />
Expand Down