mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-21 12:20:29 -04:00
Merge VS Code 1.23.1 (#1520)
This commit is contained in:
@@ -20,9 +20,9 @@ export interface ICompletionItem extends ISuggestionItem {
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"ICompletionStats" : {
|
||||
"suggestionCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"snippetCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"textCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
"suggestionCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"snippetCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"textCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
}
|
||||
*/
|
||||
// __GDPR__TODO__: This is a dynamically extensible structure which can not be declared statically.
|
||||
@@ -174,6 +174,7 @@ export class CompletionModel {
|
||||
// use a score of `-100` because that is out of the
|
||||
// bound of values `fuzzyScore` will return
|
||||
item.score = -100;
|
||||
item.matches = undefined;
|
||||
|
||||
} else if (typeof suggestion.filterText === 'string') {
|
||||
// when there is a `filterText` it must match the `word`.
|
||||
|
||||
@@ -210,13 +210,20 @@ registerDefaultLanguageCommand('_executeCompletionItemProvider', (model, positio
|
||||
suggestions: []
|
||||
};
|
||||
|
||||
let resolving: Thenable<any>[] = [];
|
||||
let maxItemsToResolve = args['maxItemsToResolve'] || 0;
|
||||
|
||||
return provideSuggestionItems(model, position).then(items => {
|
||||
|
||||
for (const { container, suggestion } of items) {
|
||||
result.incomplete = result.incomplete || container.incomplete;
|
||||
result.suggestions.push(suggestion);
|
||||
for (const item of items) {
|
||||
if (resolving.length < maxItemsToResolve) {
|
||||
resolving.push(item.resolve());
|
||||
}
|
||||
result.incomplete = result.incomplete || item.container.incomplete;
|
||||
result.suggestions.push(item.suggestion);
|
||||
}
|
||||
|
||||
}).then(() => {
|
||||
return TPromise.join(resolving);
|
||||
}).then(() => {
|
||||
return result;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -222,7 +222,7 @@ export class SuggestController implements IEditorContribution {
|
||||
|
||||
} else if (suggestion.command.id === TriggerSuggestAction.id) {
|
||||
// retigger
|
||||
this._model.trigger({ auto: this._model.state === State.Auto }, true);
|
||||
this._model.trigger({ auto: true }, true);
|
||||
|
||||
} else {
|
||||
// exec command, done
|
||||
@@ -318,7 +318,7 @@ export class TriggerSuggestAction extends EditorAction {
|
||||
alias: 'Trigger Suggest',
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCompletionItemProvider),
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Space,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyCode.Space }
|
||||
}
|
||||
@@ -350,7 +350,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: x => x.acceptSelectedSuggestion(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyCode.Tab
|
||||
}
|
||||
}));
|
||||
@@ -361,7 +361,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: x => x.acceptSelectedSuggestion(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit),
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit),
|
||||
primary: KeyCode.Enter
|
||||
}
|
||||
}));
|
||||
@@ -372,7 +372,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: x => x.cancelSuggestWidget(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyCode.Escape,
|
||||
secondary: [KeyMod.Shift | KeyCode.Escape]
|
||||
}
|
||||
@@ -384,7 +384,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: c => c.selectNextSuggestion(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyCode.DownArrow,
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow],
|
||||
mac: { primary: KeyCode.DownArrow, secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow, KeyMod.WinCtrl | KeyCode.KEY_N] }
|
||||
@@ -397,7 +397,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: c => c.selectNextPageSuggestion(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyCode.PageDown,
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.PageDown]
|
||||
}
|
||||
@@ -415,7 +415,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: c => c.selectPrevSuggestion(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyCode.UpArrow,
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow],
|
||||
mac: { primary: KeyCode.UpArrow, secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow, KeyMod.WinCtrl | KeyCode.KEY_P] }
|
||||
@@ -428,7 +428,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: c => c.selectPrevPageSuggestion(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyCode.PageUp,
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.PageUp]
|
||||
}
|
||||
@@ -446,7 +446,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: x => x.toggleSuggestionDetails(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Space,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyCode.Space }
|
||||
}
|
||||
@@ -458,7 +458,7 @@ registerEditorCommand(new SuggestCommand({
|
||||
handler: x => x.toggleSuggestionFocus(),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Space,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Space }
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export class LRUMemory extends Memory {
|
||||
this._cache.set(key, {
|
||||
touch: this._seq++,
|
||||
type: item.suggestion.type,
|
||||
insertText: undefined
|
||||
insertText: item.suggestion.insertText
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,18 +66,24 @@ export class LRUMemory extends Memory {
|
||||
// in order of completions, select the first
|
||||
// that has been used in the past
|
||||
let { word } = model.getWordUntilPosition(pos);
|
||||
if (word.length !== 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let lineSuffix = model.getLineContent(pos.lineNumber).substr(pos.column - 10, pos.column - 1);
|
||||
if (/\s$/.test(lineSuffix)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let res = 0;
|
||||
let seq = -1;
|
||||
if (word.length === 0) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const { suggestion } = items[i];
|
||||
const key = `${model.getLanguageIdentifier().language}/${suggestion.label}`;
|
||||
const item = this._cache.get(key);
|
||||
if (item && item.touch > seq && item.type === suggestion.type) {
|
||||
seq = item.touch;
|
||||
res = i;
|
||||
}
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const { suggestion } = items[i];
|
||||
const key = `${model.getLanguageIdentifier().language}/${suggestion.label}`;
|
||||
const item = this._cache.get(key);
|
||||
if (item && item.touch > seq && item.type === suggestion.type && item.insertText === suggestion.insertText) {
|
||||
seq = item.touch;
|
||||
res = i;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@@ -192,7 +198,9 @@ export class SuggestMemories {
|
||||
|
||||
try {
|
||||
const raw = this._storageService.get(`${this._storagePrefix}/${this._mode}`, StorageScope.WORKSPACE);
|
||||
this._strategy.fromJSON(JSON.parse(raw));
|
||||
if (raw) {
|
||||
this._strategy.fromJSON(JSON.parse(raw));
|
||||
}
|
||||
} catch (e) {
|
||||
// things can go wrong with JSON...
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITextModel, IWordAtPosition } from 'vs/editor/common/model';
|
||||
import { ISuggestSupport, SuggestRegistry, StandardTokenType, SuggestTriggerKind, SuggestContext } from 'vs/editor/common/modes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest';
|
||||
import { CompletionModel } from './completionModel';
|
||||
import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
@@ -46,6 +47,7 @@ export class LineContext {
|
||||
}
|
||||
const pos = editor.getPosition();
|
||||
model.tokenizeIfCheap(pos.lineNumber);
|
||||
|
||||
const word = model.getWordAtPosition(pos);
|
||||
if (!word) {
|
||||
return false;
|
||||
@@ -92,12 +94,12 @@ export class SuggestModel implements IDisposable {
|
||||
|
||||
private _requestPromise: TPromise<void>;
|
||||
private _context: LineContext;
|
||||
private _currentPosition: Position;
|
||||
private _currentSelection: Selection;
|
||||
|
||||
private _completionModel: CompletionModel;
|
||||
private _onDidCancel: Emitter<ICancelEvent> = new Emitter<ICancelEvent>();
|
||||
private _onDidTrigger: Emitter<ITriggerEvent> = new Emitter<ITriggerEvent>();
|
||||
private _onDidSuggest: Emitter<ISuggestEvent> = new Emitter<ISuggestEvent>();
|
||||
private readonly _onDidCancel: Emitter<ICancelEvent> = new Emitter<ICancelEvent>();
|
||||
private readonly _onDidTrigger: Emitter<ITriggerEvent> = new Emitter<ITriggerEvent>();
|
||||
private readonly _onDidSuggest: Emitter<ISuggestEvent> = new Emitter<ISuggestEvent>();
|
||||
|
||||
readonly onDidCancel: Event<ICancelEvent> = this._onDidCancel.event;
|
||||
readonly onDidTrigger: Event<ITriggerEvent> = this._onDidTrigger.event;
|
||||
@@ -110,7 +112,7 @@ export class SuggestModel implements IDisposable {
|
||||
this._requestPromise = null;
|
||||
this._completionModel = null;
|
||||
this._context = null;
|
||||
this._currentPosition = this._editor.getPosition() || new Position(1, 1);
|
||||
this._currentSelection = this._editor.getSelection() || new Selection(1, 1, 1, 1);
|
||||
|
||||
// wire up various listeners
|
||||
this._toDispose.push(this._editor.onDidChangeModel(() => {
|
||||
@@ -243,8 +245,8 @@ export class SuggestModel implements IDisposable {
|
||||
|
||||
private _onCursorChange(e: ICursorSelectionChangedEvent): void {
|
||||
|
||||
const prevPosition = this._currentPosition;
|
||||
this._currentPosition = this._editor.getPosition();
|
||||
const prevSelection = this._currentSelection;
|
||||
this._currentSelection = this._editor.getSelection();
|
||||
|
||||
if (!e.selection.isEmpty()
|
||||
|| e.reason !== CursorChangeReason.NotSet
|
||||
@@ -272,9 +274,9 @@ export class SuggestModel implements IDisposable {
|
||||
// trigger 24x7 IntelliSense when idle, enabled, when cursor
|
||||
// moved RIGHT, and when at a good position
|
||||
if (this._editor.getConfiguration().contribInfo.quickSuggestions !== false
|
||||
&& prevPosition.isBefore(this._currentPosition)
|
||||
&& (prevSelection.containsRange(this._currentSelection)
|
||||
|| prevSelection.getEndPosition().isBeforeOrEqual(this._currentSelection.getPosition()))
|
||||
) {
|
||||
|
||||
this.cancel();
|
||||
|
||||
this._triggerAutoSuggestPromise = TPromise.timeout(this._quickSuggestDelay);
|
||||
@@ -404,6 +406,12 @@ export class SuggestModel implements IDisposable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.leadingWord.startColumn < this._context.leadingWord.startColumn) {
|
||||
// happens when the current word gets outdented
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.column < this._context.column) {
|
||||
// typed -> moved cursor LEFT -> retrigger if still on a word
|
||||
if (ctx.leadingWord.word) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'vs/css!./media/suggest';
|
||||
import * as nls from 'vs/nls';
|
||||
import { createMatches } from 'vs/base/common/filters';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import Event, { Emitter, chain } from 'vs/base/common/event';
|
||||
import { Event, Emitter, chain } from 'vs/base/common/event';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -207,6 +207,7 @@ class SuggestionDetails {
|
||||
private docs: HTMLElement;
|
||||
private ariaLabel: string;
|
||||
private disposables: IDisposable[];
|
||||
private renderDisposeable: IDisposable;
|
||||
private borderWidth: number = 1;
|
||||
|
||||
constructor(
|
||||
@@ -247,6 +248,8 @@ class SuggestionDetails {
|
||||
}
|
||||
|
||||
render(item: ICompletionItem): void {
|
||||
this.renderDisposeable = dispose(this.renderDisposeable);
|
||||
|
||||
if (!item || !canExpandCompletionItem(item)) {
|
||||
this.type.textContent = '';
|
||||
this.docs.textContent = '';
|
||||
@@ -261,7 +264,9 @@ class SuggestionDetails {
|
||||
} else {
|
||||
addClass(this.docs, 'markdown-docs');
|
||||
this.docs.innerHTML = '';
|
||||
this.docs.appendChild(this.markdownRenderer.render(item.suggestion.documentation));
|
||||
const renderedContents = this.markdownRenderer.render(item.suggestion.documentation);
|
||||
this.renderDisposeable = renderedContents;
|
||||
this.docs.appendChild(renderedContents.element);
|
||||
}
|
||||
|
||||
if (item.suggestion.detail) {
|
||||
@@ -338,6 +343,7 @@ class SuggestionDetails {
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
this.renderDisposeable = dispose(this.renderDisposeable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +367,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
private isAuto: boolean;
|
||||
private loadingTimeout: number;
|
||||
private currentSuggestionDetails: TPromise<void>;
|
||||
private focusedItemIndex: number;
|
||||
private focusedItem: ICompletionItem;
|
||||
private ignoreFocusEvents = false;
|
||||
private completionModel: CompletionModel;
|
||||
@@ -440,7 +445,8 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.list = new List(this.listElement, this, [renderer], {
|
||||
useShadows: false,
|
||||
selectOnMouseDown: true,
|
||||
focusOnMouseDown: false
|
||||
focusOnMouseDown: false,
|
||||
openController: { shouldOpen: () => false }
|
||||
});
|
||||
|
||||
this.toDispose = [
|
||||
@@ -592,27 +598,17 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
|
||||
this.suggestionSupportsAutoAccept.set(!item.suggestion.noAutoAccept);
|
||||
|
||||
const oldFocus = this.focusedItem;
|
||||
const oldFocusIndex = this.focusedItemIndex;
|
||||
this.focusedItemIndex = index;
|
||||
this.focusedItem = item;
|
||||
|
||||
if (oldFocus) {
|
||||
this.ignoreFocusEvents = true;
|
||||
this.list.splice(oldFocusIndex, 1, [oldFocus]);
|
||||
this.ignoreFocusEvents = false;
|
||||
}
|
||||
|
||||
this.list.reveal(index);
|
||||
|
||||
this.currentSuggestionDetails = item.resolve()
|
||||
.then(() => {
|
||||
// item can have extra information, so re-render
|
||||
this.ignoreFocusEvents = true;
|
||||
this.list.splice(index, 1, [item]);
|
||||
this.ignoreFocusEvents = false;
|
||||
|
||||
this.list.setFocus([index]);
|
||||
this.list.reveal(index);
|
||||
this.ignoreFocusEvents = false;
|
||||
|
||||
if (this.expandDocsSettingFromStorage()) {
|
||||
this.showDetails();
|
||||
@@ -641,6 +637,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
case State.Hidden:
|
||||
hide(this.messageElement, this.details.element, this.listElement);
|
||||
this.hide();
|
||||
this.listHeight = 0;
|
||||
if (stateChanged) {
|
||||
this.list.splice(0, this.list.length);
|
||||
}
|
||||
@@ -725,7 +722,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
stats['wasAutomaticallyTriggered'] = !!isAuto;
|
||||
/* __GDPR__
|
||||
"suggestWidget" : {
|
||||
"wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"${include}": [
|
||||
"${ICompletionStats}",
|
||||
"${EditorTelemetryData}"
|
||||
@@ -735,7 +732,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() });
|
||||
|
||||
this.focusedItem = null;
|
||||
this.focusedItemIndex = null;
|
||||
this.list.splice(0, this.list.length, this.completionModel.items);
|
||||
|
||||
if (isFrozen) {
|
||||
|
||||
@@ -21,7 +21,7 @@ suite('SuggestMemories', function () {
|
||||
|
||||
setup(function () {
|
||||
pos = { lineNumber: 1, column: 1 };
|
||||
buffer = TextModel.createFromString('This is some text');
|
||||
buffer = TextModel.createFromString('This is some text.\nthis.\nfoo: ,');
|
||||
items = [
|
||||
createSuggestItem('foo', 0),
|
||||
createSuggestItem('bar', 0)
|
||||
@@ -39,7 +39,9 @@ suite('SuggestMemories', function () {
|
||||
mem.memorize(buffer, pos, null);
|
||||
});
|
||||
|
||||
test('ShyMemories', function () {
|
||||
test('LRUMemory', function () {
|
||||
|
||||
pos = { lineNumber: 2, column: 6 };
|
||||
|
||||
const mem = new LRUMemory();
|
||||
mem.memorize(buffer, pos, items[1]);
|
||||
@@ -59,7 +61,19 @@ suite('SuggestMemories', function () {
|
||||
createSuggestItem('new1', 0),
|
||||
createSuggestItem('new2', 0)
|
||||
]), 0);
|
||||
});
|
||||
|
||||
test('intellisense is not showing top options first #43429', function () {
|
||||
// ensure we don't memorize for whitespace prefixes
|
||||
|
||||
pos = { lineNumber: 2, column: 6 };
|
||||
const mem = new LRUMemory();
|
||||
|
||||
mem.memorize(buffer, pos, items[1]);
|
||||
assert.equal(mem.select(buffer, pos, items), 1);
|
||||
|
||||
assert.equal(mem.select(buffer, { lineNumber: 3, column: 5 }, items), 0); // foo: |,
|
||||
assert.equal(mem.select(buffer, { lineNumber: 3, column: 6 }, items), 1); // foo: ,|
|
||||
});
|
||||
|
||||
test('PrefixMemory', function () {
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { Handler } from 'vs/editor/common/editorCommon';
|
||||
import { ISuggestSupport, ISuggestResult, SuggestRegistry, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
import { ISuggestSupport, ISuggestResult, SuggestRegistry, SuggestTriggerKind, LanguageIdentifier, TokenizationRegistry, IState, MetadataConsts } from 'vs/editor/common/modes';
|
||||
import { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/suggestModel';
|
||||
import { TestCodeEditor, MockScopeLocation } from 'vs/editor/test/browser/testCodeEditor';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
@@ -21,54 +21,121 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
|
||||
import { IStorageService, NullStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { ISelectedSuggestion } from 'vs/editor/contrib/suggest/suggestWidget';
|
||||
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { TokenizationResult2 } from 'vs/editor/common/core/token';
|
||||
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
function createMockEditor(model: TextModel): TestCodeEditor {
|
||||
const contextKeyService = new MockContextKeyService();
|
||||
const telemetryService = NullTelemetryService;
|
||||
const notificationService = new TestNotificationService();
|
||||
const instantiationService = new InstantiationService(new ServiceCollection(
|
||||
[IContextKeyService, contextKeyService],
|
||||
[ITelemetryService, telemetryService],
|
||||
[IStorageService, NullStorageService]
|
||||
[IStorageService, NullStorageService],
|
||||
[INotificationService, TestNotificationService]
|
||||
));
|
||||
|
||||
const editor = new TestCodeEditor(new MockScopeLocation(), {}, instantiationService, contextKeyService);
|
||||
const editor = new TestCodeEditor(new MockScopeLocation(), {}, false, instantiationService, contextKeyService, notificationService);
|
||||
editor.setModel(model);
|
||||
return editor;
|
||||
}
|
||||
|
||||
suite('SuggestModel - Context', function () {
|
||||
const OUTER_LANGUAGE_ID = new LanguageIdentifier('outerMode', 3);
|
||||
const INNER_LANGUAGE_ID = new LanguageIdentifier('innerMode', 4);
|
||||
|
||||
let model: TextModel;
|
||||
class OuterMode extends MockMode {
|
||||
constructor() {
|
||||
super(OUTER_LANGUAGE_ID);
|
||||
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {}));
|
||||
|
||||
setup(function () {
|
||||
model = TextModel.createFromString('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?');
|
||||
this._register(TokenizationRegistry.register(this.getLanguageIdentifier().language, {
|
||||
getInitialState: (): IState => NULL_STATE,
|
||||
tokenize: undefined,
|
||||
tokenize2: (line: string, state: IState): TokenizationResult2 => {
|
||||
const tokensArr: number[] = [];
|
||||
let prevLanguageId: LanguageIdentifier = undefined;
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const languageId = (line.charAt(i) === 'x' ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID);
|
||||
if (prevLanguageId !== languageId) {
|
||||
tokensArr.push(i);
|
||||
tokensArr.push((languageId.id << MetadataConsts.LANGUAGEID_OFFSET));
|
||||
}
|
||||
prevLanguageId = languageId;
|
||||
}
|
||||
|
||||
const tokens = new Uint32Array(tokensArr.length);
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
tokens[i] = tokensArr[i];
|
||||
}
|
||||
return new TokenizationResult2(tokens, state);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
class InnerMode extends MockMode {
|
||||
constructor() {
|
||||
super(INNER_LANGUAGE_ID);
|
||||
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {}));
|
||||
}
|
||||
}
|
||||
|
||||
const assertAutoTrigger = (model: TextModel, offset: number, expected: boolean, message?: string): void => {
|
||||
const pos = model.getPositionAt(offset);
|
||||
const editor = createMockEditor(model);
|
||||
editor.setPosition(pos);
|
||||
assert.equal(LineContext.shouldAutoTrigger(editor), expected, message);
|
||||
editor.dispose();
|
||||
};
|
||||
|
||||
let disposables: Disposable[] = [];
|
||||
|
||||
setup(() => {
|
||||
disposables = [];
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
model.dispose();
|
||||
dispose(disposables);
|
||||
disposables = [];
|
||||
});
|
||||
|
||||
test('Context - shouldAutoTrigger', function () {
|
||||
const model = TextModel.createFromString('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?');
|
||||
disposables.push(model);
|
||||
|
||||
function assertAutoTrigger(offset: number, expected: boolean): void {
|
||||
const pos = model.getPositionAt(offset);
|
||||
const editor = createMockEditor(model);
|
||||
editor.setPosition(pos);
|
||||
assert.equal(LineContext.shouldAutoTrigger(editor), expected);
|
||||
editor.dispose();
|
||||
}
|
||||
|
||||
assertAutoTrigger(3, true); // end of word, Das|
|
||||
assertAutoTrigger(4, false); // no word Das |
|
||||
assertAutoTrigger(1, false); // middle of word D|as
|
||||
assertAutoTrigger(55, false); // number, 1861|
|
||||
assertAutoTrigger(model, 3, true, 'end of word, Das|');
|
||||
assertAutoTrigger(model, 4, false, 'no word Das |');
|
||||
assertAutoTrigger(model, 1, false, 'middle of word D|as');
|
||||
assertAutoTrigger(model, 55, false, 'number, 1861|');
|
||||
});
|
||||
|
||||
test('shouldAutoTrigger at embedded language boundaries', () => {
|
||||
const outerMode = new OuterMode();
|
||||
const innerMode = new InnerMode();
|
||||
disposables.push(outerMode, innerMode);
|
||||
|
||||
const model = TextModel.createFromString('a<xx>a<x>', undefined, outerMode.getLanguageIdentifier());
|
||||
disposables.push(model);
|
||||
|
||||
assertAutoTrigger(model, 1, true, 'a|<x — should trigger at end of word');
|
||||
assertAutoTrigger(model, 2, false, 'a<|x — should NOT trigger at start of word');
|
||||
assertAutoTrigger(model, 3, false, 'a<x|x — should NOT trigger in middle of word');
|
||||
assertAutoTrigger(model, 4, true, 'a<xx|> — should trigger at boundary between languages');
|
||||
assertAutoTrigger(model, 5, false, 'a<xx>|a — should NOT trigger at start of word');
|
||||
assertAutoTrigger(model, 6, true, 'a<xx>a|< — should trigger at end of word');
|
||||
assertAutoTrigger(model, 8, true, 'a<xx>a<x|> — should trigger at end of word at boundary');
|
||||
});
|
||||
});
|
||||
|
||||
suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
@@ -616,4 +683,24 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('Completion unexpectedly triggers on second keypress of an edit group in a snippet #43523', function () {
|
||||
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, alwaysSomethingSupport));
|
||||
|
||||
return withOracle((model, editor) => {
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
editor.setValue('d');
|
||||
editor.setSelection(new Selection(1, 1, 1, 2));
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'e' });
|
||||
|
||||
}, event => {
|
||||
assert.equal(event.auto, true);
|
||||
assert.equal(event.completionModel.items.length, 1);
|
||||
const [first] = event.completionModel.items;
|
||||
|
||||
assert.equal(first.support, alwaysSomethingSupport);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user