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:
Anthony Dresser
2019-12-04 19:28:22 -08:00
committed by GitHub
parent a8818ab0df
commit f5ce7fb2a5
1507 changed files with 42813 additions and 27370 deletions

View File

@@ -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 }));
});
}

View File

@@ -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)) {

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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);
});
});