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

Adding an overload to TextEditor.edit for insertion of a SnippetString #17628

Merged
merged 13 commits into from
Jan 20, 2017
Merged
38 changes: 37 additions & 1 deletion extensions/vscode-api-tests/src/editor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
'use strict';

import * as assert from 'assert';
import { workspace, window, Position, Range, commands, TextEditor, TextDocument, TextEditorCursorStyle, TextEditorLineNumbersStyle } from 'vscode';
import { workspace, window, Position, Range, commands, TextEditor, TextDocument, TextEditorCursorStyle, TextEditorLineNumbersStyle, SnippetString, Selection } from 'vscode';
import { createRandomFile, deleteFile, cleanUp } from './utils';

suite('editor tests', () => {
Expand All @@ -33,6 +33,42 @@ suite('editor tests', () => {
});
}

test('insert snippet', () => {
const snippetString = new SnippetString()
.appendText('This is a ')
.appendTabstop()
.appendPlaceholder('placeholder')
.appendText(' snippet');

return withRandomFileEditor('', (editor, doc) => {
editor.edit(snippetString);

return editor.edit(() => {}).then(() => {
assert.equal(doc.getText(), 'This is a placeholder snippet');
assert.ok(doc.isDirty);
});
});
});

test('insert snippet with replacement', () => {
const snippetString = new SnippetString()
.appendText('has been');

return withRandomFileEditor('This will be replaced', (editor, doc) => {
editor.selection = new Selection(
new Position(0, 5),
new Position(0, 12)
);

editor.edit(snippetString);

return editor.edit(() => {}).then(() => {
assert.equal(doc.getText(), 'This has been replaced');
assert.ok(doc.isDirty);
});
});
});

test('make edit', () => {
return withRandomFileEditor('', (editor, doc) => {
return editor.edit((builder) => {
Expand Down
1 change: 1 addition & 0 deletions extensions/vscode-api-tests/src/typings/ref.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
/// <reference path='../../../../src/typings/mocha.d.ts'/>
/// <reference path='../../../../extensions/declares.d.ts'/>
/// <reference path='../../../../extensions/node.d.ts'/>
Expand Down
8 changes: 8 additions & 0 deletions src/vs/vscode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,14 @@ declare module 'vscode' {
*/
edit(callback: (editBuilder: TextEditorEdit) => void, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable<boolean>;

/**
* Enters snippet mode in the editor with the specified snippet.
*
* @param snippet The snippet to insert in this edit.
* @param options The undo/redo behaviour around this edit. By default, undo stops will be created before and after this edit.
*/
edit(snippet: SnippetString, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): void;

/**
* Adds a set of decorations to the text editor. If a set of decorations already exists with
* the given [decoration type](#TextEditorDecorationType), they will be replaced.
Expand Down
11 changes: 11 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ declare module 'vscode' {
getClickCommand?(node: T): string;
}

export interface TextEditor {
/**
* Enters snippet mode in the editor with the specified snippet.
*
* @param snippet The snippet to insert in this edit.
* @param options The undo/redo behaviour around this edit. By default, undo stops will be created before and after this edit.
*/
edit(snippet: SnippetString, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): void;

}
Copy link
Member

Choose a reason for hiding this comment

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

It is enough to have it vscode.d.ts only. Duplication not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should editor-api-tests be changed to point to the local vscode.d.ts instead of pulling it from https://raw.githubusercontent.com/Microsoft/vscode/master/src/vs/vscode.d.ts at build time?


export interface SCMResourceThemableDecorations {
readonly iconPath?: string | Uri;
}
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { IWorkspaceConfigurationValues } from 'vs/workbench/services/configurati
import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkspaceSymbol } from 'vs/workbench/parts/search/common/search';
import { IApplyEditsOptions, TextEditorRevealType, ITextEditorConfigurationUpdate, IResolvedTextEditorConfiguration, ISelectionChangeEvent } from './mainThreadEditorsTracker';
import { IApplyEditsOptions, IInsertSnippetOptions, TextEditorRevealType, ITextEditorConfigurationUpdate, IResolvedTextEditorConfiguration, ISelectionChangeEvent } from './mainThreadEditorsTracker';

import { InternalTreeExplorerNodeContent } from 'vs/workbench/parts/explorers/common/treeExplorerViewModel';

Expand Down Expand Up @@ -137,6 +137,7 @@ export abstract class MainThreadEditorsShape {
$tryRevealRange(id: string, range: editorCommon.IRange, revealType: TextEditorRevealType): TPromise<any> { throw ni(); }
$trySetSelections(id: string, selections: editorCommon.ISelection[]): TPromise<any> { throw ni(); }
$tryApplyEdits(id: string, modelVersionId: number, edits: editorCommon.ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean> { throw ni(); }
$tryInsertSnippet(id: string, template: string, opts: IInsertSnippetOptions): TPromise<any> { throw ni(); }
}

export abstract class MainThreadTreeExplorersShape {
Expand Down
17 changes: 12 additions & 5 deletions src/vs/workbench/api/node/extHostEditors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Event, { Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
import { ExtHostDocuments, ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocuments';
import { Selection, Range, Position, EndOfLine, TextEditorRevealType, TextEditorSelectionChangeKind, TextEditorLineNumbersStyle } from './extHostTypes';
import { Selection, Range, Position, EndOfLine, TextEditorRevealType, TextEditorSelectionChangeKind, TextEditorLineNumbersStyle, SnippetString } from './extHostTypes';
import { ISingleEditOperation, TextEditorCursorStyle } from 'vs/editor/common/editorCommon';
import { IResolvedTextEditorConfiguration, ISelectionChangeEvent, ITextEditorConfigurationUpdate } from 'vs/workbench/api/node/mainThreadEditorsTracker';
import * as TypeConverters from './extHostTypeConverters';
Expand Down Expand Up @@ -595,10 +595,17 @@ class ExtHostTextEditor implements vscode.TextEditor {

// ---- editing

edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Thenable<boolean> {
let edit = new TextEditorEdit(this._documentData.document, options);
callback(edit);
return this._applyEdit(edit);
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable<boolean>;
edit(snippet: SnippetString, options: { undoStopBefore: boolean; undoStopAfter: boolean; }): void;

edit(callbackOrSnippet: ((edit: TextEditorEdit) => void) | SnippetString, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Thenable<boolean> | void {
if (SnippetString.isSnippetString(callbackOrSnippet)) {
this._proxy.$tryInsertSnippet(this._id, callbackOrSnippet.value, options);
} else {
let edit = new TextEditorEdit(this._documentData.document, options);
callbackOrSnippet(edit);
return this._applyEdit(edit);
}
}

_applyEdit(editBuilder: TextEditorEdit): TPromise<boolean> {
Expand Down
10 changes: 10 additions & 0 deletions src/vs/workbench/api/node/extHostTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,16 @@ export class WorkspaceEdit {

export class SnippetString {

static isSnippetString(thing: any): thing is SnippetString {
if (thing instanceof SnippetString) {
return true;
}
if (!thing) {
return false;
}
return typeof (<SnippetString>thing).value === 'string';
}

private static _escape(value: string): string {
return value.replace(/\$|}|\\/g, '\\$&');
}
Expand Down
10 changes: 9 additions & 1 deletion src/vs/workbench/api/node/mainThreadEditors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { IModelService } from 'vs/editor/common/services/modelService';
import { MainThreadEditorsTracker, TextEditorRevealType, MainThreadTextEditor, IApplyEditsOptions, ITextEditorConfigurationUpdate } from 'vs/workbench/api/node/mainThreadEditorsTracker';
import { MainThreadEditorsTracker, TextEditorRevealType, MainThreadTextEditor, IApplyEditsOptions, IInsertSnippetOptions, ITextEditorConfigurationUpdate } from 'vs/workbench/api/node/mainThreadEditorsTracker';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { equals as arrayEquals } from 'vs/base/common/arrays';
import { equals as objectEquals } from 'vs/base/common/objects';
Expand Down Expand Up @@ -293,6 +293,14 @@ export class MainThreadEditors extends MainThreadEditorsShape {
return TPromise.as(this._textEditorsMap[id].applyEdits(modelVersionId, edits, opts));
}

$tryInsertSnippet(id: string, template: string, opts: IInsertSnippetOptions): TPromise<any> {
if (!this._textEditorsMap[id]) {
return TPromise.wrapError('TextEditor disposed');
}
this._textEditorsMap[id].insertSnippet(template, opts);
return TPromise.as(null);
}

$registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {
this._editorTracker.registerTextEditorDecorationType(key, options);
}
Expand Down
26 changes: 25 additions & 1 deletion src/vs/workbench/api/node/mainThreadEditorsTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { SnippetController } from 'vs/editor/contrib/snippet/common/snippetController';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';

export interface ITextEditorConfigurationUpdate {
Expand Down Expand Up @@ -58,12 +59,19 @@ export enum TextEditorRevealType {
InCenterIfOutsideViewport = 2
}

export interface IApplyEditsOptions {
export interface IUndoStopOptions {
undoStopBefore: boolean;
undoStopAfter: boolean;
}

export interface IApplyEditsOptions extends IUndoStopOptions {
setEndOfLine: EndOfLine;
}

export interface IInsertSnippetOptions extends IUndoStopOptions {

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ha. Noted.

}

/**
* Text Editor that is permanently bound to the same model.
* It can be bound or not to a CodeEditor.
Expand Down Expand Up @@ -383,6 +391,22 @@ export class MainThreadTextEditor {
console.warn('applyEdits on invisible editor');
return false;
}

insertSnippet(template: string, opts: IInsertSnippetOptions) {
const snippetController = SnippetController.get(this._codeEditor);

this._codeEditor.focus();

if (opts.undoStopBefore) {
this._codeEditor.pushUndoStop();
}

snippetController.insertSnippet(template, 0, 0);

if (opts.undoStopAfter) {
this._codeEditor.pushUndoStop();
}
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/test/node/api/extHostEditors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ suite('ExtHostTextEditorOptions', () => {
$tryRevealRange: undefined,
$trySetSelections: undefined,
$tryApplyEdits: undefined,
$tryInsertSnippet: undefined
};
opts = new ExtHostTextEditorOptions(mockProxy, '1', {
tabSize: 4,
Expand Down