Merge VS Code 1.31.1 (#4283)

This commit is contained in:
Matt Irvine
2019-03-15 13:09:45 -07:00
committed by GitHub
parent 7d31575149
commit 86bac90001
1716 changed files with 53308 additions and 48375 deletions

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { alert } from 'vs/base/browser/ui/aria/aria';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { onUnexpectedError } from 'vs/base/common/errors';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
@@ -12,33 +12,36 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution, ScrollType, Handler } from 'vs/editor/common/editorCommon';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CompletionItemProvider, CompletionItemInsertTextRule } from 'vs/editor/common/modes';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
import { SuggestMemories } from 'vs/editor/contrib/suggest/suggestMemory';
import { ISuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ICompletionItem } from './completionModel';
import { Context as SuggestContext, ISuggestionItem } from './suggest';
import { Context as SuggestContext, CompletionItem } from './suggest';
import { SuggestAlternatives } from './suggestAlternatives';
import { State, SuggestModel } from './suggestModel';
import { ISelectedSuggestion, SuggestWidget } from './suggestWidget';
import { WordContextKey } from 'vs/editor/contrib/suggest/wordContextKey';
import { once, anyEvent } from 'vs/base/common/event';
import { Event } from 'vs/base/common/event';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { IdleValue } from 'vs/base/common/async';
import { CharacterSet } from 'vs/editor/common/core/characterClassifier';
import { isObject } from 'vs/base/common/types';
class AcceptOnCharacterOracle {
private _disposables: IDisposable[] = [];
private _activeAcceptCharacters = new Set<string>();
private _activeItem: ISelectedSuggestion;
private _active?: {
readonly acceptCharacters: CharacterSet;
readonly item: ISelectedSuggestion;
};
constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) {
@@ -47,31 +50,32 @@ class AcceptOnCharacterOracle {
this._disposables.push(widget.onDidHide(this.reset, this));
this._disposables.push(editor.onWillType(text => {
if (this._activeItem) {
const ch = text[text.length - 1];
if (this._activeAcceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) {
accept(this._activeItem);
if (this._active) {
const ch = text.charCodeAt(text.length - 1);
if (this._active.acceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) {
accept(this._active.item);
}
}
}));
}
private _onItem(selected: ISelectedSuggestion): void {
if (!selected || isFalsyOrEmpty(selected.item.suggestion.commitCharacters)) {
private _onItem(selected: ISelectedSuggestion | undefined): void {
if (!selected || !isNonEmptyArray(selected.item.completion.commitCharacters)) {
this.reset();
return;
}
this._activeItem = selected;
this._activeAcceptCharacters.clear();
for (const ch of selected.item.suggestion.commitCharacters) {
const acceptCharacters = new CharacterSet();
for (const ch of selected.item.completion.commitCharacters) {
if (ch.length > 0) {
this._activeAcceptCharacters.add(ch[0]);
acceptCharacters.add(ch.charCodeAt(0));
}
}
this._active = { acceptCharacters, item: selected };
}
reset(): void {
this._activeItem = undefined;
this._active = undefined;
}
dispose() {
@@ -87,9 +91,8 @@ export class SuggestController implements IEditorContribution {
return editor.getContribution<SuggestController>(SuggestController.ID);
}
private _model: SuggestModel;
private _widget: SuggestWidget;
private readonly _memory: IdleValue<SuggestMemories>;
private readonly _model: SuggestModel;
private readonly _widget: IdleValue<SuggestWidget>;
private readonly _alternatives: IdleValue<SuggestAlternatives>;
private _toDispose: IDisposable[] = [];
@@ -98,15 +101,62 @@ export class SuggestController implements IEditorContribution {
constructor(
private _editor: ICodeEditor,
@IEditorWorkerService editorWorker: IEditorWorkerService,
@ISuggestMemoryService private readonly _memoryService: ISuggestMemoryService,
@ICommandService private readonly _commandService: ICommandService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
this._model = new SuggestModel(this._editor, editorWorker);
this._memory = new IdleValue(() => {
let res = _instantiationService.createInstance(SuggestMemories, this._editor);
this._toDispose.push(res);
return res;
this._widget = new IdleValue(() => {
const widget = this._instantiationService.createInstance(SuggestWidget, this._editor);
this._toDispose.push(widget);
this._toDispose.push(widget.onDidSelect(item => this._onDidSelectItem(item, false, true), this));
// Wire up logic to accept a suggestion on certain characters
const autoAcceptOracle = new AcceptOnCharacterOracle(this._editor, widget, item => this._onDidSelectItem(item, false, true));
this._toDispose.push(
autoAcceptOracle,
this._model.onDidSuggest(e => {
if (e.completionModel.items.length === 0) {
autoAcceptOracle.reset();
}
})
);
// Wire up makes text edit context key
let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
this._toDispose.push(widget.onDidFocus(({ item }) => {
const position = this._editor.getPosition()!;
const startColumn = item.completion.range.startColumn;
const endColumn = position.column;
let value = true;
if (
this._editor.getConfiguration().contribInfo.acceptSuggestionOnEnter === 'smart'
&& this._model.state === State.Auto
&& !item.completion.command
&& !item.completion.additionalTextEdits
&& !(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)
&& endColumn - startColumn === item.completion.insertText.length
) {
const oldText = this._editor.getModel()!.getValueInRange({
startLineNumber: position.lineNumber,
startColumn,
endLineNumber: position.lineNumber,
endColumn
});
value = oldText !== item.completion.insertText;
}
makesTextEdit.set(value);
}));
this._toDispose.push({
dispose() { makesTextEdit.reset(); }
});
return widget;
});
this._alternatives = new IdleValue(() => {
@@ -118,20 +168,17 @@ export class SuggestController implements IEditorContribution {
this._toDispose.push(_instantiationService.createInstance(WordContextKey, _editor));
this._toDispose.push(this._model.onDidTrigger(e => {
if (!this._widget) {
this._createSuggestWidget();
}
this._widget.showTriggered(e.auto, e.shy ? 250 : 50);
this._widget.getValue().showTriggered(e.auto, e.shy ? 250 : 50);
}));
this._toDispose.push(this._model.onDidSuggest(e => {
if (!e.shy) {
let index = this._memory.getValue().select(this._editor.getModel(), this._editor.getPosition(), e.completionModel.items);
this._widget.showSuggestions(e.completionModel, index, e.isFrozen, e.auto);
let index = this._memoryService.select(this._editor.getModel()!, this._editor.getPosition()!, e.completionModel.items);
this._widget.getValue().showSuggestions(e.completionModel, index, e.isFrozen, e.auto);
}
}));
this._toDispose.push(this._model.onDidCancel(e => {
if (this._widget && !e.retrigger) {
this._widget.hideWidget();
this._widget.getValue().hideWidget();
}
}));
this._toDispose.push(this._editor.onDidBlurEditorText(() => {
@@ -150,51 +197,6 @@ export class SuggestController implements IEditorContribution {
updateFromConfig();
}
private _createSuggestWidget(): void {
this._widget = this._instantiationService.createInstance(SuggestWidget, this._editor);
this._toDispose.push(this._widget.onDidSelect(item => this._onDidSelectItem(item, false, true), this));
// Wire up logic to accept a suggestion on certain characters
const autoAcceptOracle = new AcceptOnCharacterOracle(this._editor, this._widget, item => this._onDidSelectItem(item, false, true));
this._toDispose.push(
autoAcceptOracle,
this._model.onDidSuggest(e => {
if (e.completionModel.items.length === 0) {
autoAcceptOracle.reset();
}
})
);
let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
this._toDispose.push(this._widget.onDidFocus(({ item }) => {
const position = this._editor.getPosition();
const startColumn = item.suggestion.range.startColumn;
const endColumn = position.column;
let value = true;
if (
this._editor.getConfiguration().contribInfo.acceptSuggestionOnEnter === 'smart'
&& this._model.state === State.Auto
&& !item.suggestion.command
&& !item.suggestion.additionalTextEdits
&& !(item.suggestion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet)
&& endColumn - startColumn === item.suggestion.insertText.length
) {
const oldText = this._editor.getModel().getValueInRange({
startLineNumber: position.lineNumber,
startColumn,
endLineNumber: position.lineNumber,
endColumn
});
value = oldText !== item.suggestion.insertText;
}
makesTextEdit.set(value);
}));
this._toDispose.push({
dispose() { makesTextEdit.reset(); }
});
}
getId(): string {
return SuggestController.ID;
@@ -202,26 +204,25 @@ export class SuggestController implements IEditorContribution {
dispose(): void {
this._toDispose = dispose(this._toDispose);
if (this._widget) {
this._widget.dispose();
this._widget = null;
}
this._widget.dispose();
if (this._model) {
this._model.dispose();
this._model = null;
}
}
protected _onDidSelectItem(event: ISelectedSuggestion, keepAlternativeSuggestions: boolean, undoStops: boolean): void {
protected _onDidSelectItem(event: ISelectedSuggestion | undefined, keepAlternativeSuggestions: boolean, undoStops: boolean): void {
if (!event || !event.item) {
this._alternatives.getValue().reset();
this._model.cancel();
return;
}
if (!this._editor.hasModel()) {
return;
}
const model = this._editor.getModel();
const modelVersionNow = model.getAlternativeVersionId();
const { suggestion, position } = event.item;
const { completion: suggestion, position } = event.item;
const editorColumn = this._editor.getPosition().column;
const columnDelta = editorColumn - position.column;
@@ -236,10 +237,10 @@ export class SuggestController implements IEditorContribution {
}
// keep item in memory
this._memory.getValue().memorize(model, this._editor.getPosition(), event.item);
this._memoryService.memorize(model, this._editor.getPosition(), event.item);
let { insertText } = suggestion;
if (!(suggestion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet)) {
if (!(suggestion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) {
insertText = SnippetParser.escape(insertText);
}
@@ -251,7 +252,7 @@ export class SuggestController implements IEditorContribution {
overwriteBefore + columnDelta,
overwriteAfter,
false, false,
!(suggestion.insertTextRules & CompletionItemInsertTextRule.KeepWhitespace)
!(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace)
);
if (undoStops) {
@@ -264,11 +265,11 @@ export class SuggestController implements IEditorContribution {
} else if (suggestion.command.id === TriggerSuggestAction.id) {
// retigger
this._model.trigger({ auto: true }, true);
this._model.trigger({ auto: true, shy: false }, true);
} else {
// exec command, done
this._commandService.executeCommand(suggestion.command.id, ...suggestion.command.arguments).then(undefined, onUnexpectedError);
this._commandService.executeCommand(suggestion.command.id, ...(suggestion.command.arguments ? [...suggestion.command.arguments] : [])).catch(onUnexpectedError);
this._model.cancel();
}
@@ -290,54 +291,59 @@ export class SuggestController implements IEditorContribution {
this._alertCompletionItem(event.item);
}
private _alertCompletionItem({ suggestion }: ICompletionItem): void {
private _alertCompletionItem({ completion: suggestion }: CompletionItem): void {
let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' did insert the following text: {1}", suggestion.label, suggestion.insertText);
alert(msg);
}
triggerSuggest(onlyFrom?: CompletionItemProvider[]): void {
this._model.trigger({ auto: false }, false, onlyFrom);
this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth);
this._editor.focus();
if (this._editor.hasModel()) {
this._model.trigger({ auto: false, shy: false }, false, onlyFrom);
this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth);
this._editor.focus();
}
}
triggerSuggestAndAcceptBest(defaultTypeText: string): void {
triggerSuggestAndAcceptBest(arg: { fallback: string }): void {
if (!this._editor.hasModel()) {
return;
}
const positionNow = this._editor.getPosition();
const fallback = () => {
if (positionNow.equals(this._editor.getPosition())) {
this._editor.trigger('suggest', Handler.Type, { text: defaultTypeText });
if (positionNow.equals(this._editor.getPosition()!)) {
this._commandService.executeCommand(arg.fallback);
}
};
const makesTextEdit = (item: ISuggestionItem): boolean => {
if (item.suggestion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet || item.suggestion.additionalTextEdits) {
const makesTextEdit = (item: CompletionItem): boolean => {
if (item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet || item.completion.additionalTextEdits) {
// snippet, other editor -> makes edit
return true;
}
const position = this._editor.getPosition();
const startColumn = item.suggestion.range.startColumn;
const position = this._editor.getPosition()!;
const startColumn = item.completion.range.startColumn;
const endColumn = position.column;
if (endColumn - startColumn !== item.suggestion.insertText.length) {
if (endColumn - startColumn !== item.completion.insertText.length) {
// unequal lengths -> makes edit
return true;
}
const textNow = this._editor.getModel().getValueInRange({
const textNow = this._editor.getModel()!.getValueInRange({
startLineNumber: position.lineNumber,
startColumn,
endLineNumber: position.lineNumber,
endColumn
});
// unequal text -> makes edit
return textNow !== item.suggestion.insertText;
return textNow !== item.completion.insertText;
};
once(this._model.onDidTrigger)(_ => {
Event.once(this._model.onDidTrigger)(_ => {
// wait for trigger because only then the cancel-event is trustworthy
let listener: IDisposable[] = [];
anyEvent<any>(this._model.onDidTrigger, this._model.onDidCancel)(() => {
Event.any<any>(this._model.onDidTrigger, this._model.onDidCancel)(() => {
// retrigger or cancel -> try to type default text
dispose(listener);
fallback();
@@ -349,7 +355,7 @@ export class SuggestController implements IEditorContribution {
fallback();
return;
}
const index = this._memory.getValue().select(this._editor.getModel(), this._editor.getPosition(), completionModel.items);
const index = this._memoryService.select(this._editor.getModel()!, this._editor.getPosition()!, completionModel.items);
const item = completionModel.items[index];
if (!makesTextEdit(item)) {
fallback();
@@ -368,8 +374,8 @@ export class SuggestController implements IEditorContribution {
acceptSelectedSuggestion(keepAlternativeSuggestions?: boolean): void {
if (this._widget) {
const item = this._widget.getFocusedItem();
this._onDidSelectItem(item, keepAlternativeSuggestions, true);
const item = this._widget.getValue().getFocusedItem();
this._onDidSelectItem(item, !!keepAlternativeSuggestions, true);
}
}
@@ -384,55 +390,55 @@ export class SuggestController implements IEditorContribution {
cancelSuggestWidget(): void {
if (this._widget) {
this._model.cancel();
this._widget.hideWidget();
this._widget.getValue().hideWidget();
}
}
selectNextSuggestion(): void {
if (this._widget) {
this._widget.selectNext();
this._widget.getValue().selectNext();
}
}
selectNextPageSuggestion(): void {
if (this._widget) {
this._widget.selectNextPage();
this._widget.getValue().selectNextPage();
}
}
selectLastSuggestion(): void {
if (this._widget) {
this._widget.selectLast();
this._widget.getValue().selectLast();
}
}
selectPrevSuggestion(): void {
if (this._widget) {
this._widget.selectPrevious();
this._widget.getValue().selectPrevious();
}
}
selectPrevPageSuggestion(): void {
if (this._widget) {
this._widget.selectPreviousPage();
this._widget.getValue().selectPreviousPage();
}
}
selectFirstSuggestion(): void {
if (this._widget) {
this._widget.selectFirst();
this._widget.getValue().selectFirst();
}
}
toggleSuggestionDetails(): void {
if (this._widget) {
this._widget.toggleDetails();
this._widget.getValue().toggleDetails();
}
}
toggleSuggestionFocus(): void {
if (this._widget) {
this._widget.toggleDetailsFocus();
this._widget.getValue().toggleDetailsFocus();
}
}
}
@@ -606,7 +612,10 @@ registerEditorCommand(new SuggestCommand({
SuggestAlternatives.OtherSuggestions.toNegated(),
SnippetController2.InSnippetMode.toNegated()
),
handler: x => x.triggerSuggestAndAcceptBest('\t'),//todo@joh fallback/default configurable?
handler: (x, arg) => {
x.triggerSuggestAndAcceptBest(isObject(arg) ? { fallback: 'tab', ...arg } : { fallback: 'tab' });
},
kbOpts: {
weight,
primary: KeyCode.Tab