Merge VS Code 1.23.1 (#1520)

This commit is contained in:
Matt Irvine
2018-06-05 11:24:51 -07:00
committed by GitHub
parent e3baf5c443
commit 0c58f09e59
3651 changed files with 74249 additions and 48599 deletions

View File

@@ -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`.

View File

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

View File

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

View File

@@ -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...
}

View File

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

View File

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

View File

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

View File

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