mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 01:00:29 -04:00
Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c (#8525)
* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c * remove files we don't want * fix hygiene * update distro * update distro * fix hygiene * fix strict nulls * distro * distro * fix tests * fix tests * add another edit * fix viewlet icon * fix azure dialog * fix some padding * fix more padding issues
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { compare } from 'vs/base/common/strings';
|
||||
import { compare, startsWith } from 'vs/base/common/strings';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
@@ -22,14 +22,14 @@ export class SnippetCompletion implements CompletionItem {
|
||||
detail: string;
|
||||
insertText: string;
|
||||
documentation?: MarkdownString;
|
||||
range: IRange;
|
||||
range: IRange | { insert: IRange, replace: IRange };
|
||||
sortText: string;
|
||||
kind: CompletionItemKind;
|
||||
insertTextRules: CompletionItemInsertTextRule;
|
||||
|
||||
constructor(
|
||||
readonly snippet: Snippet,
|
||||
range: IRange
|
||||
range: IRange | { insert: IRange, replace: IRange }
|
||||
) {
|
||||
this.label = snippet.prefix;
|
||||
this.detail = localize('detail.snippet', "{0} ({1})", snippet.description || snippet.name, snippet.source);
|
||||
@@ -80,7 +80,8 @@ export class SnippetCompletionProvider implements CompletionItemProvider {
|
||||
let suggestions: SnippetCompletion[];
|
||||
let pos = { lineNumber: position.lineNumber, column: 1 };
|
||||
let lineOffsets: number[] = [];
|
||||
let linePrefixLow = model.getLineContent(position.lineNumber).substr(0, position.column - 1).toLowerCase();
|
||||
const lineContent = model.getLineContent(position.lineNumber);
|
||||
const linePrefixLow = lineContent.substr(0, position.column - 1).toLowerCase();
|
||||
let endsInWhitespace = linePrefixLow.match(/\s$/);
|
||||
|
||||
while (pos.column < position.column) {
|
||||
@@ -104,13 +105,19 @@ export class SnippetCompletionProvider implements CompletionItemProvider {
|
||||
}
|
||||
}
|
||||
|
||||
const lineSuffixLow = lineContent.substr(position.column - 1).toLowerCase();
|
||||
let availableSnippets = new Set<Snippet>();
|
||||
snippets.forEach(availableSnippets.add, availableSnippets);
|
||||
suggestions = [];
|
||||
for (let start of lineOffsets) {
|
||||
availableSnippets.forEach(snippet => {
|
||||
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)));
|
||||
const snippetPrefixSubstr = snippet.prefixLow.substr(linePrefixLow.length - start);
|
||||
const endColumn = startsWith(lineSuffixLow, snippetPrefixSubstr) ? position.column + snippetPrefixSubstr.length : position.column;
|
||||
const replace = Range.fromPositions(position.delta(0, -(linePrefixLow.length - start)), { lineNumber: position.lineNumber, column: endColumn });
|
||||
const insert = replace.setEndPosition(position.lineNumber, position.column);
|
||||
|
||||
suggestions.push(new SnippetCompletion(snippet, { replace, insert }));
|
||||
availableSnippets.delete(snippet);
|
||||
}
|
||||
});
|
||||
@@ -119,7 +126,8 @@ export class SnippetCompletionProvider implements CompletionItemProvider {
|
||||
// add remaing snippets when the current prefix ends in whitespace or when no
|
||||
// interesting positions have been found
|
||||
availableSnippets.forEach(snippet => {
|
||||
suggestions.push(new SnippetCompletion(snippet, Range.fromPositions(position)));
|
||||
const range = Range.fromPositions(position);
|
||||
suggestions.push(new SnippetCompletion(snippet, { replace: range, insert: range }));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { parse as jsonParse } from 'vs/base/common/json';
|
||||
import { parse as jsonParse, getNodeType } from 'vs/base/common/json';
|
||||
import { forEach } from 'vs/base/common/collections';
|
||||
import { localize } from 'vs/nls';
|
||||
import { extname, basename } from 'vs/base/common/path';
|
||||
@@ -14,6 +14,7 @@ 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';
|
||||
import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader';
|
||||
|
||||
class SnippetBodyInsights {
|
||||
|
||||
@@ -153,7 +154,8 @@ export class SnippetFile {
|
||||
readonly location: URI,
|
||||
public defaultScopes: string[] | undefined,
|
||||
private readonly _extension: IExtensionDescription | undefined,
|
||||
private readonly _fileService: IFileService
|
||||
private readonly _fileService: IFileService,
|
||||
private readonly _extensionResourceLoaderService: IExtensionResourceLoaderService
|
||||
) {
|
||||
this.isGlobalSnippets = extname(location.path) === '.code-snippets';
|
||||
this.isUserSnippets = !this._extension;
|
||||
@@ -199,11 +201,20 @@ export class SnippetFile {
|
||||
}
|
||||
}
|
||||
|
||||
private async _load(): Promise<string> {
|
||||
if (this._extension) {
|
||||
return this._extensionResourceLoaderService.readExtensionResource(this.location);
|
||||
} else {
|
||||
const content = await this._fileService.readFile(this.location);
|
||||
return content.value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
load(): Promise<this> {
|
||||
if (!this._loadPromise) {
|
||||
this._loadPromise = Promise.resolve(this._fileService.readFile(this.location)).then(content => {
|
||||
const data = <JsonSerializedSnippets>jsonParse(content.value.toString());
|
||||
if (typeof data === 'object') {
|
||||
this._loadPromise = Promise.resolve(this._load()).then(content => {
|
||||
const data = <JsonSerializedSnippets>jsonParse(content);
|
||||
if (getNodeType(data) === 'object') {
|
||||
forEach(data, entry => {
|
||||
const { key: name, value: scopeOrTemplate } = entry;
|
||||
if (isJsonSerializedSnippet(scopeOrTemplate)) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { Snippet, SnippetFile, SnippetSource } from 'vs/workbench/contrib/snippe
|
||||
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService';
|
||||
import { SnippetCompletionProvider } from './snippetCompletionProvider';
|
||||
import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader';
|
||||
|
||||
namespace snippetExt {
|
||||
|
||||
@@ -139,6 +140,7 @@ class SnippetsService implements ISnippetsService {
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IExtensionResourceLoaderService private readonly _extensionResourceLoaderService: IExtensionResourceLoaderService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
) {
|
||||
this._pendingWork.push(Promise.resolve(lifecycleService.when(LifecyclePhase.Restored).then(() => {
|
||||
@@ -225,7 +227,7 @@ class SnippetsService implements ISnippetsService {
|
||||
file.defaultScopes = [];
|
||||
}
|
||||
} else {
|
||||
const file = new SnippetFile(SnippetSource.Extension, validContribution.location, validContribution.language ? [validContribution.language] : undefined, extension.description, this._fileService);
|
||||
const file = new SnippetFile(SnippetSource.Extension, validContribution.location, validContribution.language ? [validContribution.language] : undefined, extension.description, this._fileService, this._extensionResourceLoaderService);
|
||||
this._files.set(file.location.toString(), file);
|
||||
|
||||
if (this._environmentService.isExtensionDevelopment) {
|
||||
@@ -318,9 +320,9 @@ class SnippetsService implements ISnippetsService {
|
||||
const key = uri.toString();
|
||||
if (source === SnippetSource.User && ext === '.json') {
|
||||
const langName = resources.basename(uri).replace(/\.json/, '');
|
||||
this._files.set(key, new SnippetFile(source, uri, [langName], undefined, this._fileService));
|
||||
this._files.set(key, new SnippetFile(source, uri, [langName], undefined, this._fileService, this._extensionResourceLoaderService));
|
||||
} else if (ext === '.code-snippets') {
|
||||
this._files.set(key, new SnippetFile(source, uri, undefined, undefined, this._fileService));
|
||||
this._files.set(key, new SnippetFile(source, uri, undefined, undefined, this._fileService, this._extensionResourceLoaderService));
|
||||
}
|
||||
return {
|
||||
dispose: () => this._files.delete(key)
|
||||
|
||||
@@ -11,7 +11,7 @@ suite('Snippets', function () {
|
||||
|
||||
class TestSnippetFile extends SnippetFile {
|
||||
constructor(filepath: URI, snippets: Snippet[]) {
|
||||
super(SnippetSource.Extension, filepath, undefined, undefined, undefined!);
|
||||
super(SnippetSource.Extension, filepath, undefined, undefined, undefined!, undefined!);
|
||||
this.data.push(...snippets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ suite('SnippetsService', function () {
|
||||
assert.equal(result.incomplete, undefined);
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.equal(result.suggestions[0].label, 'bar');
|
||||
assert.equal(result.suggestions[0].range.startColumn, 1);
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
assert.equal(result.suggestions[0].insertText, 'barCodeSnippet');
|
||||
});
|
||||
});
|
||||
@@ -117,10 +117,10 @@ suite('SnippetsService', function () {
|
||||
assert.equal(result.suggestions.length, 2);
|
||||
assert.equal(result.suggestions[0].label, 'bar');
|
||||
assert.equal(result.suggestions[0].insertText, 's1');
|
||||
assert.equal(result.suggestions[0].range.startColumn, 1);
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
assert.equal(result.suggestions[1].label, 'bar-bar');
|
||||
assert.equal(result.suggestions[1].insertText, 's2');
|
||||
assert.equal(result.suggestions[1].range.startColumn, 1);
|
||||
assert.equal((result.suggestions[1].range as any).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 5), context)!.then(result => {
|
||||
@@ -128,7 +128,7 @@ suite('SnippetsService', function () {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.equal(result.suggestions[0].label, 'bar-bar');
|
||||
assert.equal(result.suggestions[0].insertText, 's2');
|
||||
assert.equal(result.suggestions[0].range.startColumn, 1);
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 6), context)!.then(result => {
|
||||
@@ -136,10 +136,10 @@ suite('SnippetsService', function () {
|
||||
assert.equal(result.suggestions.length, 2);
|
||||
assert.equal(result.suggestions[0].label, 'bar');
|
||||
assert.equal(result.suggestions[0].insertText, 's1');
|
||||
assert.equal(result.suggestions[0].range.startColumn, 5);
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 5);
|
||||
assert.equal(result.suggestions[1].label, 'bar-bar');
|
||||
assert.equal(result.suggestions[1].insertText, 's2');
|
||||
assert.equal(result.suggestions[1].range.startColumn, 1);
|
||||
assert.equal((result.suggestions[1].range as any).insert.startColumn, 1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -165,14 +165,14 @@ suite('SnippetsService', function () {
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), context)!;
|
||||
}).then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.equal(result.suggestions[0].range.startColumn, 2);
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
model.dispose();
|
||||
|
||||
model = TextModel.createFromString('a<?', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), context)!;
|
||||
}).then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.equal(result.suggestions[0].range.startColumn, 2);
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
model.dispose();
|
||||
});
|
||||
});
|
||||
@@ -400,13 +400,43 @@ suite('SnippetsService', function () {
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
let [first] = result.suggestions;
|
||||
assert.equal(first.range.startColumn, 2);
|
||||
assert.equal((first.range as any).insert.startColumn, 2);
|
||||
|
||||
model = TextModel.createFromString('1', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 2), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
[first] = result.suggestions;
|
||||
assert.equal(first.range.startColumn, 1);
|
||||
assert.equal((first.range as any).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
test('Snippet replace range', async function () {
|
||||
snippetService = new SimpleSnippetService([new Snippet(
|
||||
['fooLang'],
|
||||
'notWordTest',
|
||||
'not word',
|
||||
'',
|
||||
'not word snippet',
|
||||
'',
|
||||
SnippetSource.User
|
||||
)]);
|
||||
|
||||
const provider = new SnippetCompletionProvider(modeService, snippetService);
|
||||
|
||||
let model = TextModel.createFromString('not wordFoo bar', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
let [first] = result.suggestions;
|
||||
assert.equal((first.range as any).insert.endColumn, 3);
|
||||
assert.equal((first.range as any).replace.endColumn, 9);
|
||||
|
||||
model = TextModel.createFromString('not woFoo bar', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
[first] = result.suggestions;
|
||||
assert.equal((first.range as any).insert.endColumn, 3);
|
||||
assert.equal((first.range as any).replace.endColumn, 3);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user