提交 4ae7d630 编写于 作者: J Johannes Rieken

know when a snippet needs clipboard access, #76847

上级 6505977f
......@@ -32,7 +32,7 @@ export class SnippetCompletion implements CompletionItem {
) {
this.label = snippet.prefix;
this.detail = localize('detail.snippet', "{0} ({1})", snippet.description || snippet.name, snippet.source);
this.insertText = snippet.body;
this.insertText = snippet.codeSnippet;
this.range = range;
this.sortText = `${snippet.snippetSource === SnippetSource.Extension ? 'z' : 'a'}-${snippet.prefix}`;
this.kind = CompletionItemKind.Snippet;
......@@ -41,7 +41,6 @@ export class SnippetCompletion implements CompletionItem {
resolve(): this {
this.documentation = new MarkdownString().appendCodeblock('', new SnippetParser().text(this.snippet.codeSnippet));
this.insertText = this.snippet.codeSnippet;
return this;
}
......
......@@ -13,11 +13,67 @@ import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IdleValue } from 'vs/base/common/async';
class SnippetBodyInsights {
readonly codeSnippet: string;
readonly isBogous: boolean;
readonly needsClipboard: boolean;
constructor(body: string) {
// init with defaults
this.isBogous = false;
this.needsClipboard = false;
this.codeSnippet = body;
// check snippet...
const textmateSnippet = new SnippetParser().parse(body, false);
let placeholders = new Map<string, number>();
let placeholderMax = 0;
for (const placeholder of textmateSnippet.placeholders) {
placeholderMax = Math.max(placeholderMax, placeholder.index);
}
let stack = [...textmateSnippet.children];
while (stack.length > 0) {
const marker = stack.shift()!;
if (marker instanceof Variable) {
if (marker.children.length === 0 && !KnownSnippetVariableNames[marker.name]) {
// a 'variable' without a default value and not being one of our supported
// variables is automatically turned into a placeholder. This is to restore
// a bug we had before. So `${foo}` becomes `${N:foo}`
const index = placeholders.has(marker.name) ? placeholders.get(marker.name)! : ++placeholderMax;
placeholders.set(marker.name, index);
const synthetic = new Placeholder(index).appendChild(new Text(marker.name));
textmateSnippet.replace(marker, [synthetic]);
this.isBogous = true;
}
if (marker.name === 'CLIPBOARD') {
this.needsClipboard = true;
}
} else {
// recurse
stack.push(...marker.children);
}
}
if (this.isBogous) {
this.codeSnippet = textmateSnippet.toTextmateString();
}
}
}
export class Snippet {
private _codeSnippet: string;
private _isBogous: boolean;
private readonly _bodyInsights: IdleValue<SnippetBodyInsights>;
readonly prefixLow: string;
......@@ -32,29 +88,19 @@ export class Snippet {
) {
//
this.prefixLow = prefix ? prefix.toLowerCase() : prefix;
this._bodyInsights = new IdleValue(() => new SnippetBodyInsights(this.body));
}
get codeSnippet(): string {
this._ensureCodeSnippet();
return this._codeSnippet;
return this._bodyInsights.getValue().codeSnippet;
}
get isBogous(): boolean {
this._ensureCodeSnippet();
return this._isBogous;
return this._bodyInsights.getValue().isBogous;
}
private _ensureCodeSnippet() {
if (!this._codeSnippet) {
const rewrite = Snippet._rewriteBogousVariables(this.body);
if (typeof rewrite === 'string') {
this._codeSnippet = rewrite;
this._isBogous = true;
} else {
this._codeSnippet = this.body;
this._isBogous = false;
}
}
get needsClipboard(): boolean {
return this._bodyInsights.getValue().needsClipboard;
}
static compare(a: Snippet, b: Snippet): number {
......@@ -70,49 +116,6 @@ export class Snippet {
return 0;
}
}
static _rewriteBogousVariables(template: string): false | string {
const textmateSnippet = new SnippetParser().parse(template, false);
let placeholders = new Map<string, number>();
let placeholderMax = 0;
for (const placeholder of textmateSnippet.placeholders) {
placeholderMax = Math.max(placeholderMax, placeholder.index);
}
let didChange = false;
let stack = [...textmateSnippet.children];
while (stack.length > 0) {
const marker = stack.shift()!;
if (
marker instanceof Variable
&& marker.children.length === 0
&& !KnownSnippetVariableNames[marker.name]
) {
// a 'variable' without a default value and not being one of our supported
// variables is automatically turned into a placeholder. This is to restore
// a bug we had before. So `${foo}` becomes `${N:foo}`
const index = placeholders.has(marker.name) ? placeholders.get(marker.name)! : ++placeholderMax;
placeholders.set(marker.name, index);
const synthetic = new Placeholder(index).appendChild(new Text(marker.name));
textmateSnippet.replace(marker, [synthetic]);
didChange = true;
} else {
// recurse
stack.push(...marker.children);
}
}
if (!didChange) {
return false;
} else {
return textmateSnippet.toTextmateString();
}
}
}
......
......@@ -65,4 +65,19 @@ suite('Snippets', function () {
});
test('Snippet#needsClipboard', function () {
function assertNeedsClipboard(body: string, expected: boolean): void {
let snippet = new Snippet(['foo'], 'FooSnippet1', 'foo', '', body, 'test', SnippetSource.User);
assert.equal(snippet.needsClipboard, expected);
}
assertNeedsClipboard('foo$CLIPBOARD', true);
assertNeedsClipboard('${CLIPBOARD}', true);
assertNeedsClipboard('foo${CLIPBOARD}bar', true);
assertNeedsClipboard('foo$clipboard', false);
assertNeedsClipboard('foo${clipboard}', false);
assertNeedsClipboard('baba', false);
});
});
......@@ -9,8 +9,12 @@ import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/sn
suite('SnippetRewrite', function () {
function assertRewrite(input: string, expected: string | boolean): void {
const actual = Snippet._rewriteBogousVariables(input);
assert.equal(actual, expected);
const actual = new Snippet(['foo'], 'foo', 'foo', 'foo', input, 'foo', SnippetSource.User);
if (typeof expected === 'boolean') {
assert.equal(actual.codeSnippet, input);
} else {
assert.equal(actual.codeSnippet, expected);
}
}
test('bogous variable rewrite', function () {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册