mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 01:00:29 -04:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { join, basename, extname } from 'vs/base/common/path';
|
||||
import { extname } from 'vs/base/common/path';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -19,7 +19,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { isValidBasename } from 'vs/base/common/extpath';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { joinPath, basename } from 'vs/base/common/resources';
|
||||
|
||||
const id = 'workbench.action.openSnippets';
|
||||
|
||||
@@ -69,7 +69,7 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir
|
||||
}
|
||||
|
||||
existing.push({
|
||||
label: basename(file.location.fsPath),
|
||||
label: basename(file.location),
|
||||
filepath: file.location,
|
||||
description: names.size === 0
|
||||
? nls.localize('global.scope', "(global)")
|
||||
@@ -78,9 +78,9 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir
|
||||
|
||||
} else {
|
||||
// language snippet
|
||||
const mode = basename(file.location.fsPath).replace(/\.json$/, '');
|
||||
const mode = basename(file.location).replace(/\.json$/, '');
|
||||
existing.push({
|
||||
label: basename(file.location.fsPath),
|
||||
label: basename(file.location),
|
||||
description: `(${modeService.getLanguageName(mode)})`,
|
||||
filepath: file.location
|
||||
});
|
||||
@@ -88,14 +88,14 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir
|
||||
}
|
||||
}
|
||||
|
||||
const dir = join(envService.appSettingsHome, 'snippets');
|
||||
const dir = joinPath(envService.userRoamingDataHome, 'snippets');
|
||||
for (const mode of modeService.getRegisteredModes()) {
|
||||
const label = modeService.getLanguageName(mode);
|
||||
if (label && !seen.has(mode)) {
|
||||
future.push({
|
||||
label: mode,
|
||||
description: `(${label})`,
|
||||
filepath: URI.file(join(dir, `${mode}.json`)),
|
||||
filepath: joinPath(dir, `${mode}.json`),
|
||||
hint: true
|
||||
});
|
||||
}
|
||||
@@ -220,7 +220,7 @@ CommandsRegistry.registerCommand(id, async (accessor): Promise<any> => {
|
||||
const globalSnippetPicks: SnippetPick[] = [{
|
||||
scope: nls.localize('new.global_scope', 'global'),
|
||||
label: nls.localize('new.global', "New Global Snippets file..."),
|
||||
uri: URI.file(join(envService.appSettingsHome, 'snippets'))
|
||||
uri: joinPath(envService.userRoamingDataHome, 'snippets')
|
||||
}];
|
||||
|
||||
const workspaceSnippetPicks: SnippetPick[] = [];
|
||||
@@ -259,8 +259,8 @@ CommandsRegistry.registerCommand(id, async (accessor): Promise<any> => {
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id,
|
||||
title: { value: nls.localize('openSnippet.label', "Configure User Snippets"), original: 'Preferences: Configure User Snippets' },
|
||||
category: nls.localize('preferences', "Preferences")
|
||||
title: { value: nls.localize('openSnippet.label', "Configure User Snippets"), original: 'Configure User Snippets' },
|
||||
category: { value: nls.localize('preferences', "Preferences"), original: 'Preferences' }
|
||||
}
|
||||
});
|
||||
|
||||
@@ -273,4 +273,13 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
// },
|
||||
// order: 1
|
||||
// });
|
||||
|
||||
// MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
// group: '3_snippets',
|
||||
// command: {
|
||||
// id,
|
||||
// title: nls.localize('userSnippets', "User Snippets")
|
||||
// },
|
||||
// order: 1
|
||||
// });
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
|
||||
@@ -14,6 +14,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile';
|
||||
import { IQuickPickItem, IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
|
||||
interface ISnippetPick extends IQuickPickItem {
|
||||
snippet: Snippet;
|
||||
@@ -87,6 +88,7 @@ class InsertSnippetAction extends EditorAction {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const clipboardService = accessor.get(IClipboardService);
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const { lineNumber, column } = editor.getPosition();
|
||||
let { snippet, name, langId } = Args.fromUser(arg);
|
||||
@@ -165,9 +167,13 @@ class InsertSnippetAction extends EditorAction {
|
||||
}
|
||||
return quickInputService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject);
|
||||
}
|
||||
}).then(snippet => {
|
||||
}).then(async snippet => {
|
||||
let clipboardText: string | undefined;
|
||||
if (snippet.needsClipboard) {
|
||||
clipboardText = await clipboardService.readText();
|
||||
}
|
||||
if (snippet) {
|
||||
SnippetController2.get(editor).insert(snippet.codeSnippet, 0, 0);
|
||||
SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets.contribution';
|
||||
import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile';
|
||||
import { isPatternInWord } from 'vs/base/common/filters';
|
||||
|
||||
export class SnippetCompletion implements CompletionItem {
|
||||
|
||||
@@ -32,7 +33,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 +42,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;
|
||||
}
|
||||
|
||||
@@ -50,25 +50,15 @@ export class SnippetCompletion implements CompletionItem {
|
||||
}
|
||||
}
|
||||
|
||||
export function matches(pattern: string, patternStart: number, word: string, wordStart: number): boolean {
|
||||
while (patternStart < pattern.length && wordStart < word.length) {
|
||||
if (pattern[patternStart] === word[wordStart]) {
|
||||
patternStart += 1;
|
||||
}
|
||||
wordStart += 1;
|
||||
}
|
||||
return patternStart === pattern.length;
|
||||
}
|
||||
|
||||
export class SnippetCompletionProvider implements CompletionItemProvider {
|
||||
|
||||
private static readonly _maxPrefix = 10000;
|
||||
|
||||
readonly _debugDisplayName = 'snippetCompletions';
|
||||
|
||||
constructor(
|
||||
@IModeService
|
||||
private readonly _modeService: IModeService,
|
||||
@ISnippetsService
|
||||
private readonly _snippets: ISnippetsService
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@ISnippetsService private readonly _snippets: ISnippetsService
|
||||
) {
|
||||
//
|
||||
}
|
||||
@@ -119,7 +109,7 @@ export class SnippetCompletionProvider implements CompletionItemProvider {
|
||||
suggestions = [];
|
||||
for (let start of lineOffsets) {
|
||||
availableSnippets.forEach(snippet => {
|
||||
if (matches(linePrefixLow, start, snippet.prefixLow, 0)) {
|
||||
if (isPatternInWord(linePrefixLow, start, linePrefixLow.length, snippet.prefixLow, 0, snippet.prefixLow.length)) {
|
||||
suggestions.push(new SnippetCompletion(snippet, Range.fromPositions(position.delta(0, -(linePrefixLow.length - start)), position)));
|
||||
availableSnippets.delete(snippet);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ const languageScopeSchema: IJSONSchema = {
|
||||
type: ['string', 'array']
|
||||
},
|
||||
body: {
|
||||
description: nls.localize('snippetSchema.json.body', 'The snippet content. Use \'$1\', \'${1:defaultText}\' to define cursor positions, use \'$0\' for the final cursor position. Insert variable values with \'${varName}\' and \'${varName:defaultText}\', e.g \'This is file: $TM_FILENAME\'.'),
|
||||
description: nls.localize('snippetSchema.json.body', 'The snippet content. Use \'$1\', \'${1:defaultText}\' to define cursor positions, use \'$0\' for the final cursor position. Insert variable values with \'${varName}\' and \'${varName:defaultText}\', e.g. \'This is file: $TM_FILENAME\'.'),
|
||||
type: ['string', 'array'],
|
||||
items: {
|
||||
type: 'string'
|
||||
@@ -78,11 +78,11 @@ const globalSchema: IJSONSchema = {
|
||||
type: ['string', 'array']
|
||||
},
|
||||
scope: {
|
||||
description: nls.localize('snippetSchema.json.scope', "A list of language names to which this snippet applies, e.g 'typescript,javascript'."),
|
||||
description: nls.localize('snippetSchema.json.scope', "A list of language names to which this snippet applies, e.g. 'typescript,javascript'."),
|
||||
type: 'string'
|
||||
},
|
||||
body: {
|
||||
description: nls.localize('snippetSchema.json.body', 'The snippet content. Use \'$1\', \'${1:defaultText}\' to define cursor positions, use \'$0\' for the final cursor position. Insert variable values with \'${varName}\' and \'${varName:defaultText}\', e.g \'This is file: $TM_FILENAME\'.'),
|
||||
description: nls.localize('snippetSchema.json.body', 'The snippet content. Use \'$1\', \'${1:defaultText}\' to define cursor positions, use \'$0\' for the final cursor position. Insert variable values with \'${varName}\' and \'${varName:defaultText}\', e.g. \'This is file: $TM_FILENAME\'.'),
|
||||
type: ['string', 'array'],
|
||||
items: {
|
||||
type: 'string'
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { combinedDisposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { combinedDisposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { endsWith, isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
@@ -114,7 +113,7 @@ namespace snippetExt {
|
||||
}
|
||||
|
||||
function watch(service: IFileService, resource: URI, callback: (type: FileChangeType, resource: URI) => any): IDisposable {
|
||||
return combinedDisposable([
|
||||
return combinedDisposable(
|
||||
service.watch(resource),
|
||||
service.onFileChanges(e => {
|
||||
for (const change of e.changes) {
|
||||
@@ -123,14 +122,14 @@ function watch(service: IFileService, resource: URI, callback: (type: FileChange
|
||||
}
|
||||
}
|
||||
})
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
class SnippetsService implements ISnippetsService {
|
||||
|
||||
readonly _serviceBrand: any;
|
||||
|
||||
private readonly _disposables: IDisposable[] = [];
|
||||
private readonly _disposables = new DisposableStore();
|
||||
private readonly _pendingWork: Promise<any>[] = [];
|
||||
private readonly _files = new Map<string, SnippetFile>();
|
||||
|
||||
@@ -152,7 +151,7 @@ class SnippetsService implements ISnippetsService {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._disposables);
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
private _joinSnippets(): Promise<any> {
|
||||
@@ -257,20 +256,18 @@ class SnippetsService implements ISnippetsService {
|
||||
|
||||
private _initWorkspaceSnippets(): void {
|
||||
// workspace stuff
|
||||
let disposables: IDisposable[] = [];
|
||||
let disposables = new DisposableStore();
|
||||
let updateWorkspaceSnippets = () => {
|
||||
disposables = dispose(disposables);
|
||||
disposables.clear();
|
||||
this._pendingWork.push(this._initWorkspaceFolderSnippets(this._contextService.getWorkspace(), disposables));
|
||||
};
|
||||
this._disposables.push({
|
||||
dispose() { dispose(disposables); }
|
||||
});
|
||||
this._disposables.push(this._contextService.onDidChangeWorkspaceFolders(updateWorkspaceSnippets));
|
||||
this._disposables.push(this._contextService.onDidChangeWorkbenchState(updateWorkspaceSnippets));
|
||||
this._disposables.add(disposables);
|
||||
this._disposables.add(this._contextService.onDidChangeWorkspaceFolders(updateWorkspaceSnippets));
|
||||
this._disposables.add(this._contextService.onDidChangeWorkbenchState(updateWorkspaceSnippets));
|
||||
updateWorkspaceSnippets();
|
||||
}
|
||||
|
||||
private _initWorkspaceFolderSnippets(workspace: IWorkspace, bucket: IDisposable[]): Promise<any> {
|
||||
private _initWorkspaceFolderSnippets(workspace: IWorkspace, bucket: DisposableStore): Promise<any> {
|
||||
let promises = workspace.folders.map(folder => {
|
||||
const snippetFolder = folder.toResource('.vscode');
|
||||
return this._fileService.exists(snippetFolder).then(value => {
|
||||
@@ -278,8 +275,8 @@ class SnippetsService implements ISnippetsService {
|
||||
this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket);
|
||||
} else {
|
||||
// watch
|
||||
bucket.push(watch(this._fileService, snippetFolder, (type) => {
|
||||
if (type === FileChangeType.ADDED) {
|
||||
bucket.add(this._fileService.onFileChanges(e => {
|
||||
if (e.contains(snippetFolder, FileChangeType.ADDED)) {
|
||||
this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket);
|
||||
}
|
||||
}));
|
||||
@@ -290,28 +287,29 @@ class SnippetsService implements ISnippetsService {
|
||||
}
|
||||
|
||||
private _initUserSnippets(): Promise<any> {
|
||||
const userSnippetsFolder = URI.file(join(this._environmentService.appSettingsHome, 'snippets'));
|
||||
const userSnippetsFolder = resources.joinPath(this._environmentService.userRoamingDataHome, 'snippets');
|
||||
return this._fileService.createFolder(userSnippetsFolder).then(() => this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables));
|
||||
}
|
||||
|
||||
private _initFolderSnippets(source: SnippetSource, folder: URI, bucket: IDisposable[]): Promise<any> {
|
||||
let disposables: IDisposable[] = [];
|
||||
let addFolderSnippets = (type?: FileChangeType) => {
|
||||
disposables = dispose(disposables);
|
||||
private _initFolderSnippets(source: SnippetSource, folder: URI, bucket: DisposableStore): Promise<any> {
|
||||
const disposables = new DisposableStore();
|
||||
const addFolderSnippets = (type?: FileChangeType) => {
|
||||
disposables.clear();
|
||||
|
||||
if (type === FileChangeType.DELETED) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._fileService.resolve(folder).then(stat => {
|
||||
for (const entry of stat.children || []) {
|
||||
disposables.push(this._addSnippetFile(entry.resource, source));
|
||||
disposables.add(this._addSnippetFile(entry.resource, source));
|
||||
}
|
||||
}, err => {
|
||||
this._logService.error(`Failed snippets from folder '${folder.toString()}'`, err);
|
||||
});
|
||||
};
|
||||
|
||||
bucket.push(watch(this._fileService, folder, addFolderSnippets));
|
||||
bucket.push(combinedDisposable(disposables));
|
||||
bucket.add(watch(this._fileService, folder, addFolderSnippets));
|
||||
bucket.add(disposables);
|
||||
return addFolderSnippets();
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export class TabCompletionController implements editorCommon.IEditorContribution
|
||||
private _activeSnippets: Snippet[] = [];
|
||||
private _enabled: boolean;
|
||||
private _selectionListener: IDisposable;
|
||||
private _configListener: IDisposable;
|
||||
private readonly _configListener: IDisposable;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@@ -129,7 +129,7 @@ export class TabCompletionController implements editorCommon.IEditorContribution
|
||||
if (this._activeSnippets.length === 1) {
|
||||
// one -> just insert
|
||||
const [snippet] = this._activeSnippets;
|
||||
SnippetController2.get(this._editor).insert(snippet.codeSnippet, snippet.prefix.length, 0);
|
||||
SnippetController2.get(this._editor).insert(snippet.codeSnippet, { overwriteBefore: snippet.prefix.length, overwriteAfter: 0 });
|
||||
|
||||
} else if (this._activeSnippets.length > 1) {
|
||||
// two or more -> show IntelliSense box
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
Reference in New Issue
Block a user