提交 b7ae720d 编写于 作者: J Johannes Rieken

wire up variable resolver, resolve variables per selection

上级 47c0398d
......@@ -13,6 +13,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { Range } from 'vs/editor/common/core/range';
import { IPosition } from 'vs/editor/common/core/position';
import { dispose } from 'vs/base/common/lifecycle';
import { EditorSnippetVariableResolver } from "vs/editor/contrib/snippet/common/snippetVariables";
class OneSnippet {
......@@ -172,21 +173,26 @@ export class SnippetSession {
return templateLines.join(model.getEOL());
}
static adjustRange(model: IModel, range: Range, overwriteBefore: number, overwriteAfter: number): Range {
static adjustSelection(model: IModel, selection: Selection, overwriteBefore: number, overwriteAfter: number): Selection {
if (overwriteBefore !== 0 || overwriteAfter !== 0) {
let { startLineNumber, startColumn, endLineNumber, endColumn } = range;
let { startLineNumber, startColumn, endLineNumber, endColumn } = selection;
startColumn -= overwriteBefore;
endColumn += overwriteAfter;
range = Range.plusRange(range, {
const range = model.validateRange(Range.plusRange(selection, {
startLineNumber,
startColumn,
endLineNumber,
endColumn,
});
range = model.validateRange(range);
}));
selection = Selection.createWithDirection(
range.startLineNumber, range.startColumn,
range.endLineNumber, range.endColumn,
selection.getDirection()
);
}
return range;
return selection;
}
private readonly _editor: ICommonCodeEditor;
......@@ -222,11 +228,11 @@ export class SnippetSession {
.sort((a, b) => Range.compareRangesUsingStarts(a.selection, b.selection));
for (const { selection, idx } of indexedSelection) {
const range = SnippetSession.adjustRange(model, selection, this._overwriteBefore, this._overwriteAfter);
const range = SnippetSession.adjustSelection(model, selection, this._overwriteBefore, this._overwriteAfter);
const start = range.getStartPosition();
const adjustedTemplate = SnippetSession.normalizeWhitespace(model, start, this._template);
const snippet = SnippetParser.parse(adjustedTemplate);
const snippet = SnippetParser.parse(adjustedTemplate).resolveVariables(new EditorSnippetVariableResolver(model, range));
const offset = model.getOffsetAt(start) + delta;
edits[idx] = EditOperation.replaceMove(range, snippet.text);
......
......@@ -183,9 +183,6 @@ export class Placeholder extends Marker {
get isFinalTabstop() {
return this.index === '0';
}
with(defaultValue: Marker[]): Placeholder {
return new Placeholder(this.index, defaultValue);
}
toString() {
return Marker.toString(this.defaultValue);
}
......@@ -201,6 +198,13 @@ export class Variable extends Marker {
get isDefined(): boolean {
return this.resolvedValue !== undefined;
}
len(): number {
if (this.isDefined) {
return this.resolvedValue.length;
} else {
return super.len();
}
}
toString() {
return this.isDefined ? this.resolvedValue : Marker.toString(this.defaultValue);
}
......@@ -271,7 +275,7 @@ export class TextmateSnippet {
return Marker.toString(this.marker);
}
resolveVariables(resolver: { resolve(name: string): string }): void {
resolveVariables(resolver: { resolve(name: string): string }): this {
walk(this.marker, candidate => {
if (candidate instanceof Variable) {
candidate.resolvedValue = resolver.resolve(candidate.name);
......@@ -282,6 +286,7 @@ export class TextmateSnippet {
}
return true;
});
return this;
}
}
......
......@@ -6,14 +6,61 @@
'use strict';
import { basename, dirname, normalize } from 'vs/base/common/paths';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ICommonCodeEditor, IModel } from 'vs/editor/common/editorCommon';
import { Selection } from 'vs/editor/common/core/selection';
import { ISnippetVariableResolver } from './snippet';
export class EditorSnippetVariableResolver {
constructor(
private readonly _model: IModel,
private readonly _selection: Selection
) {
//
}
resolve(name: string): string {
if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') {
return this._model.getValueInRange(this._selection);
} else if (name === 'TM_CURRENT_LINE') {
return this._model.getLineContent(this._selection.positionLineNumber);
} else if (name === 'TM_CURRENT_WORD') {
const info = this._model.getWordAtPosition({
lineNumber: this._selection.positionLineNumber,
column: this._selection.positionColumn
});
return info ? info.word : '';
} else if (name === 'TM_LINE_INDEX') {
return String(this._selection.positionLineNumber - 1);
} else if (name === 'TM_LINE_NUMBER') {
return String(this._selection.positionLineNumber);
} else if (name === 'TM_FILENAME') {
return basename(this._model.uri.fsPath);
} else if (name === 'TM_DIRECTORY') {
const dir = dirname(this._model.uri.fsPath);
return dir !== '.' ? dir : '';
} else if (name === 'TM_FILEPATH') {
return this._model.uri.fsPath;
} else {
return undefined;
}
}
}
export class SnippetVariablesResolver implements ISnippetVariableResolver {
private _editor: editorCommon.ICommonCodeEditor;
private readonly _editor: ICommonCodeEditor;
constructor(editor: editorCommon.ICommonCodeEditor) {
constructor(editor: ICommonCodeEditor) {
this._editor = editor;
}
......
......@@ -52,15 +52,15 @@ suite('SnippetSession', function () {
assertNormalized(new Position(2, 3), 'foo\r\tbar', 'foo\n bar');
});
test('adjust selection (overwrite[Before|After]', function () {
test('adjust selection (overwrite[Before|After])', function () {
let range = SnippetSession.adjustRange(model, new Range(1, 2, 1, 2), 1, 0);
let range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 1, 0);
assert.ok(range.equalsRange(new Range(1, 1, 1, 2)));
range = SnippetSession.adjustRange(model, new Range(1, 2, 1, 2), 1111, 0);
range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 1111, 0);
assert.ok(range.equalsRange(new Range(1, 1, 1, 2)));
range = SnippetSession.adjustRange(model, new Range(1, 2, 1, 2), 0, 10);
range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 0, 10);
assert.ok(range.equalsRange(new Range(1, 2, 1, 12)));
range = SnippetSession.adjustRange(model, new Range(1, 2, 1, 2), 0, 10111);
range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 0, 10111);
assert.ok(range.equalsRange(new Range(1, 2, 1, 17)));
});
......@@ -377,5 +377,13 @@ suite('SnippetSession', function () {
assertSelections(editor, new Selection(1, 11, 1, 11));
});
test('snippets, snippet with variables', function () {
const session = new SnippetSession(editor, '@line=$TM_LINE_NUMBER$0');
session.insert();
assert.equal(model.getValue(), '@line=1function foo() {\n @line=2console.log(a);\n}');
assertSelections(editor, new Selection(1, 8, 1, 8), new Selection(2, 12, 2, 12));
});
});
......@@ -8,112 +8,97 @@ import * as assert from 'assert';
import { isWindows } from 'vs/base/common/platform';
import URI from 'vs/base/common/uri';
import { Selection } from 'vs/editor/common/core/selection';
import { SnippetVariablesResolver } from 'vs/editor/contrib/snippet/common/snippetVariables';
import { EditorSnippetVariableResolver } from 'vs/editor/contrib/snippet/common/snippetVariables';
import { SnippetParser } from 'vs/editor/contrib/snippet/common/snippetParser';
import { MockCodeEditor, withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { Model } from 'vs/editor/common/model/model';
suite('Snippet Variables Resolver', function () {
const model = Model.createFromString('', undefined, undefined, URI.parse('file:///foo/files/text.txt'));
let model: Model;
let resolver: EditorSnippetVariableResolver;
function variablesTest(callback: (editor: MockCodeEditor, resolver: SnippetVariablesResolver) => any) {
const lines: string[] = [
setup(function () {
model = Model.createFromString([
'this is line one',
'this is line two',
' this is line three'
];
].join('\n'), undefined, undefined, URI.parse('file:///foo/files/text.txt'));
model.setValue(lines.join('\n'));
resolver = new EditorSnippetVariableResolver(model, new Selection(1, 1, 1, 1));
});
withMockCodeEditor(lines, { model }, editor => {
callback(editor, new SnippetVariablesResolver(editor));
});
}
teardown(function () {
model.dispose();
});
test('editor variables, basics', function () {
variablesTest((editor, resolver) => {
assert.equal(resolver.resolve('TM_FILENAME'), 'text.txt');
assert.equal(resolver.resolve('something'), undefined);
editor.setModel(null);
assert.throws(() => resolver.resolve('TM_FILENAME'));
});
assert.equal(resolver.resolve('TM_FILENAME'), 'text.txt');
assert.equal(resolver.resolve('something'), undefined);
});
test('editor variables, file/dir', function () {
variablesTest((editor, resolver) => {
assert.equal(resolver.resolve('TM_FILENAME'), 'text.txt');
if (!isWindows) {
assert.equal(resolver.resolve('TM_DIRECTORY'), '/foo/files');
assert.equal(resolver.resolve('TM_FILEPATH'), '/foo/files/text.txt');
}
editor.setModel(Model.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')));
assert.equal(resolver.resolve('TM_FILENAME'), 'ghi');
if (!isWindows) {
assert.equal(resolver.resolve('TM_DIRECTORY'), '/abc/def');
assert.equal(resolver.resolve('TM_FILEPATH'), '/abc/def/ghi');
}
editor.setModel(Model.createFromString('', undefined, undefined, URI.parse('mem:fff.ts')));
assert.equal(resolver.resolve('TM_DIRECTORY'), '');
assert.equal(resolver.resolve('TM_FILEPATH'), 'fff.ts');
});
assert.equal(resolver.resolve('TM_FILENAME'), 'text.txt');
if (!isWindows) {
assert.equal(resolver.resolve('TM_DIRECTORY'), '/foo/files');
assert.equal(resolver.resolve('TM_FILEPATH'), '/foo/files/text.txt');
}
resolver = new EditorSnippetVariableResolver(
Model.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')),
new Selection(1, 1, 1, 1)
);
assert.equal(resolver.resolve('TM_FILENAME'), 'ghi');
if (!isWindows) {
assert.equal(resolver.resolve('TM_DIRECTORY'), '/abc/def');
assert.equal(resolver.resolve('TM_FILEPATH'), '/abc/def/ghi');
}
resolver = new EditorSnippetVariableResolver(
Model.createFromString('', undefined, undefined, URI.parse('mem:fff.ts')),
new Selection(1, 1, 1, 1)
);
assert.equal(resolver.resolve('TM_DIRECTORY'), '');
assert.equal(resolver.resolve('TM_FILEPATH'), 'fff.ts');
});
test('editor variables, selection', function () {
variablesTest((editor, resolver) => {
resolver = new EditorSnippetVariableResolver(model, new Selection(1, 2, 2, 3));
assert.equal(resolver.resolve('TM_SELECTED_TEXT'), 'his is line one\nth');
assert.equal(resolver.resolve('TM_CURRENT_LINE'), 'this is line two');
assert.equal(resolver.resolve('TM_LINE_INDEX'), '1');
assert.equal(resolver.resolve('TM_LINE_NUMBER'), '2');
editor.setSelection(new Selection(1, 2, 2, 3));
assert.equal(resolver.resolve('TM_SELECTED_TEXT'), 'his is line one\nth');
assert.equal(resolver.resolve('TM_CURRENT_LINE'), 'this is line two');
assert.equal(resolver.resolve('TM_LINE_INDEX'), '1');
assert.equal(resolver.resolve('TM_LINE_NUMBER'), '2');
resolver = new EditorSnippetVariableResolver(model, new Selection(2, 3, 1, 2));
assert.equal(resolver.resolve('TM_SELECTED_TEXT'), 'his is line one\nth');
assert.equal(resolver.resolve('TM_CURRENT_LINE'), 'this is line one');
assert.equal(resolver.resolve('TM_LINE_INDEX'), '0');
assert.equal(resolver.resolve('TM_LINE_NUMBER'), '1');
editor.setSelection(new Selection(2, 3, 1, 2));
assert.equal(resolver.resolve('TM_SELECTED_TEXT'), 'his is line one\nth');
assert.equal(resolver.resolve('TM_CURRENT_LINE'), 'this is line one');
assert.equal(resolver.resolve('TM_LINE_INDEX'), '0');
assert.equal(resolver.resolve('TM_LINE_NUMBER'), '1');
resolver = new EditorSnippetVariableResolver(model, new Selection(1, 2, 1, 2));
assert.equal(resolver.resolve('TM_SELECTED_TEXT'), '');
editor.setSelection(new Selection(1, 2, 1, 2));
assert.equal(resolver.resolve('TM_SELECTED_TEXT'), '');
assert.equal(resolver.resolve('TM_CURRENT_WORD'), 'this');
assert.equal(resolver.resolve('TM_CURRENT_WORD'), 'this');
resolver = new EditorSnippetVariableResolver(model, new Selection(3, 1, 3, 1));
assert.equal(resolver.resolve('TM_CURRENT_WORD'), '');
editor.setSelection(new Selection(3, 1, 3, 1));
assert.equal(resolver.resolve('TM_CURRENT_WORD'), '');
});
});
test('TextmateSnippet, resolve variable', function () {
const snippet = SnippetParser.parse('"$TM_CURRENT_WORD"');
assert.equal(snippet.text, '""');
snippet.resolveVariables(resolver);
assert.equal(snippet.text, '"this"');
const snippet = SnippetParser.parse('"$TM_SELECTED_TEXT"');
variablesTest((editor, resolver) => {
assert.equal(snippet.text, '""');
editor.setSelection(new Selection(1, 1, 1, 5));
snippet.resolveVariables(resolver);
assert.equal(snippet.text, '"this"');
});
});
test('TextmateSnippet, resolve variable with default', function () {
const snippet = SnippetParser.parse('"${TM_SELECTED_TEXT:foo}"');
variablesTest((editor, resolver) => {
assert.equal(snippet.text, '"foo"');
editor.setSelection(new Selection(1, 1, 1, 5));
snippet.resolveVariables(resolver);
assert.equal(snippet.text, '"this"');
});
const snippet = SnippetParser.parse('"${TM_CURRENT_WORD:foo}"');
assert.equal(snippet.text, '"foo"');
snippet.resolveVariables(resolver);
assert.equal(snippet.text, '"this"');
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册