mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-21 12:20:29 -04:00
Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { fuzzyScore, fuzzyScoreGracefulAggressive } from 'vs/base/common/filters';
|
||||
import { fuzzyScore, fuzzyScoreGracefulAggressive, skipScore } from 'vs/base/common/filters';
|
||||
import { ISuggestSupport, ISuggestResult } from 'vs/editor/common/modes';
|
||||
import { ISuggestionItem, SnippetConfig } from './suggest';
|
||||
import { isDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -185,11 +185,8 @@ export class CompletionModel {
|
||||
continue;
|
||||
}
|
||||
item.score = match[0];
|
||||
item.matches = [];
|
||||
match = scoreFn(word, suggestion.label, suggestion.overwriteBefore);
|
||||
if (match) {
|
||||
item.matches = match[1];
|
||||
}
|
||||
item.matches = skipScore(word, suggestion.label)[1];
|
||||
|
||||
} else {
|
||||
// by default match `word` against the `label`
|
||||
let match = scoreFn(word, suggestion.label, suggestion.overwriteBefore);
|
||||
|
||||
@@ -10,7 +10,8 @@ import { compareIgnoreCase } from 'vs/base/common/strings';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IModel, IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry, SuggestContext, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
@@ -43,7 +44,7 @@ export function setSnippetSuggestSupport(support: ISuggestSupport): ISuggestSupp
|
||||
return old;
|
||||
}
|
||||
|
||||
export function provideSuggestionItems(model: IModel, position: Position, snippetConfig: SnippetConfig = 'bottom', onlyFrom?: ISuggestSupport[], context?: SuggestContext): TPromise<ISuggestionItem[]> {
|
||||
export function provideSuggestionItems(model: ITextModel, position: Position, snippetConfig: SnippetConfig = 'bottom', onlyFrom?: ISuggestSupport[], context?: SuggestContext): TPromise<ISuggestionItem[]> {
|
||||
|
||||
const allSuggestions: ISuggestionItem[] = [];
|
||||
const acceptSuggestion = createSuggesionFilter(snippetConfig);
|
||||
@@ -127,7 +128,7 @@ function fixOverwriteBeforeAfter(suggestion: ISuggestion, container: ISuggestRes
|
||||
}
|
||||
}
|
||||
|
||||
function createSuggestionResolver(provider: ISuggestSupport, suggestion: ISuggestion, model: IModel, position: Position): () => TPromise<void> {
|
||||
function createSuggestionResolver(provider: ISuggestSupport, suggestion: ISuggestion, model: ITextModel, position: Position): () => TPromise<void> {
|
||||
return () => {
|
||||
if (typeof provider.resolveCompletionItem === 'function') {
|
||||
return asWinJsPromise(token => provider.resolveCompletionItem(model, position, suggestion, token))
|
||||
@@ -233,11 +234,6 @@ let _provider = new class implements ISuggestSupport {
|
||||
|
||||
SuggestRegistry.register('*', _provider);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param editor
|
||||
* @param suggestions
|
||||
*/
|
||||
export function showSimpleSuggestions(editor: ICodeEditor, suggestions: ISuggestion[]) {
|
||||
setTimeout(() => {
|
||||
_suggestions = suggestions;
|
||||
|
||||
@@ -25,7 +25,7 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2
|
||||
import { Context as SuggestContext } from './suggest';
|
||||
import { SuggestModel, State } from './suggestModel';
|
||||
import { ICompletionItem } from './completionModel';
|
||||
import { SuggestWidget } from './suggestWidget';
|
||||
import { SuggestWidget, ISelectedSuggestion } from './suggestWidget';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { SuggestMemories } from 'vs/editor/contrib/suggest/suggestMemory';
|
||||
|
||||
@@ -34,9 +34,9 @@ class AcceptOnCharacterOracle {
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private _activeAcceptCharacters = new Set<string>();
|
||||
private _activeItem: ICompletionItem;
|
||||
private _activeItem: ISelectedSuggestion;
|
||||
|
||||
constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (item: ICompletionItem) => any) {
|
||||
constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) {
|
||||
|
||||
this._disposables.push(widget.onDidShow(() => this._onItem(widget.getFocusedItem())));
|
||||
this._disposables.push(widget.onDidFocus(this._onItem, this));
|
||||
@@ -52,14 +52,14 @@ class AcceptOnCharacterOracle {
|
||||
}));
|
||||
}
|
||||
|
||||
private _onItem(item: ICompletionItem): void {
|
||||
if (!item || isFalsyOrEmpty(item.suggestion.commitCharacters)) {
|
||||
private _onItem(selected: ISelectedSuggestion): void {
|
||||
if (!selected || isFalsyOrEmpty(selected.item.suggestion.commitCharacters)) {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this._activeItem = item;
|
||||
this._activeItem = selected;
|
||||
this._activeAcceptCharacters.clear();
|
||||
for (const ch of item.suggestion.commitCharacters) {
|
||||
for (const ch of selected.item.suggestion.commitCharacters) {
|
||||
if (ch.length > 0) {
|
||||
this._activeAcceptCharacters.add(ch[0]);
|
||||
}
|
||||
@@ -77,7 +77,7 @@ class AcceptOnCharacterOracle {
|
||||
|
||||
export class SuggestController implements IEditorContribution {
|
||||
|
||||
private static ID: string = 'editor.contrib.suggestController';
|
||||
private static readonly ID: string = 'editor.contrib.suggestController';
|
||||
|
||||
public static get(editor: ICodeEditor): SuggestController {
|
||||
return editor.getContribution<SuggestController>(SuggestController.ID);
|
||||
@@ -90,12 +90,12 @@ export class SuggestController implements IEditorContribution {
|
||||
|
||||
constructor(
|
||||
private _editor: ICodeEditor,
|
||||
@ICommandService private _commandService: ICommandService,
|
||||
@IContextKeyService private _contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
this._model = new SuggestModel(this._editor);
|
||||
this._memory = _instantiationService.createInstance(SuggestMemories);
|
||||
this._memory = _instantiationService.createInstance(SuggestMemories, this._editor.getConfiguration().contribInfo.suggestSelection);
|
||||
|
||||
this._toDispose.push(this._model.onDidTrigger(e => {
|
||||
if (!this._widget) {
|
||||
@@ -103,29 +103,22 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
this._widget.showTriggered(e.auto);
|
||||
}));
|
||||
let lastSelectedItem: ICompletionItem;
|
||||
this._toDispose.push(this._model.onDidSuggest(e => {
|
||||
let index = this._memory.select(this._editor.getModel().getLanguageIdentifier(), e.completionModel.items, lastSelectedItem);
|
||||
if (index >= 0) {
|
||||
lastSelectedItem = e.completionModel.items[index];
|
||||
} else {
|
||||
index = 0;
|
||||
lastSelectedItem = undefined;
|
||||
}
|
||||
let index = this._memory.select(this._editor.getModel(), this._editor.getPosition(), e.completionModel.items);
|
||||
this._widget.showSuggestions(e.completionModel, index, e.isFrozen, e.auto);
|
||||
}));
|
||||
this._toDispose.push(this._model.onDidCancel(e => {
|
||||
if (this._widget && !e.retrigger) {
|
||||
this._widget.hideWidget();
|
||||
lastSelectedItem = undefined;
|
||||
}
|
||||
}));
|
||||
|
||||
// Manage the acceptSuggestionsOnEnter context key
|
||||
let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService);
|
||||
let updateFromConfig = () => {
|
||||
const { acceptSuggestionOnEnter } = this._editor.getConfiguration().contribInfo;
|
||||
const { acceptSuggestionOnEnter, suggestSelection } = this._editor.getConfiguration().contribInfo;
|
||||
acceptSuggestionsOnEnter.set(acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart');
|
||||
this._memory.setMode(suggestSelection);
|
||||
};
|
||||
this._toDispose.push(this._editor.onDidChangeConfiguration((e) => updateFromConfig()));
|
||||
updateFromConfig();
|
||||
@@ -148,7 +141,7 @@ export class SuggestController implements IEditorContribution {
|
||||
);
|
||||
|
||||
let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
|
||||
this._toDispose.push(this._widget.onDidFocus(item => {
|
||||
this._toDispose.push(this._widget.onDidFocus(({ item }) => {
|
||||
|
||||
const position = this._editor.getPosition();
|
||||
const startColumn = item.position.column - item.suggestion.overwriteBefore;
|
||||
@@ -193,13 +186,13 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidSelectItem(item: ICompletionItem): void {
|
||||
if (!item) {
|
||||
protected _onDidSelectItem(event: ISelectedSuggestion): void {
|
||||
if (!event || !event.item) {
|
||||
this._model.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
const { suggestion, position } = item;
|
||||
const { suggestion, position } = event.item;
|
||||
const editorColumn = this._editor.getPosition().column;
|
||||
const columnDelta = editorColumn - position.column;
|
||||
|
||||
@@ -209,8 +202,8 @@ export class SuggestController implements IEditorContribution {
|
||||
this._editor.pushUndoStop();
|
||||
}
|
||||
|
||||
// remember this word for future invocations
|
||||
this._memory.remember(this._editor.getModel().getLanguageIdentifier(), item);
|
||||
// keep item in memory
|
||||
this._memory.memorize(this._editor.getModel(), this._editor.getPosition(), event.item);
|
||||
|
||||
let { insertText } = suggestion;
|
||||
if (suggestion.snippetType !== 'textmate') {
|
||||
@@ -237,7 +230,7 @@ export class SuggestController implements IEditorContribution {
|
||||
this._model.cancel();
|
||||
}
|
||||
|
||||
this._alertCompletionItem(item);
|
||||
this._alertCompletionItem(event.item);
|
||||
}
|
||||
|
||||
private _alertCompletionItem({ suggestion }: ICompletionItem): void {
|
||||
|
||||
@@ -5,103 +5,210 @@
|
||||
'use strict';
|
||||
|
||||
import { ICompletionItem } from 'vs/editor/contrib/suggest/completionModel';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { LRUCache, TernarySearchTree } from 'vs/base/common/map';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
export class SuggestMemories {
|
||||
export abstract class Memory {
|
||||
|
||||
private readonly _storagePrefix = 'suggest/memories';
|
||||
private readonly _data = new Map<string, SuggestMemory>();
|
||||
abstract memorize(model: ITextModel, pos: IPosition, item: ICompletionItem): void;
|
||||
|
||||
constructor(
|
||||
@IStorageService private _storageService: IStorageService
|
||||
) {
|
||||
abstract select(model: ITextModel, pos: IPosition, items: ICompletionItem[]): number;
|
||||
|
||||
abstract toJSON(): object;
|
||||
|
||||
abstract fromJSON(data: object): void;
|
||||
}
|
||||
|
||||
export class NoMemory extends Memory {
|
||||
|
||||
memorize(model: ITextModel, pos: IPosition, item: ICompletionItem): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
select(model: ITextModel, pos: IPosition, items: ICompletionItem[]): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
fromJSON() {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
remember({ language }: LanguageIdentifier, item: ICompletionItem): void {
|
||||
let memory = this._data.get(language);
|
||||
if (!memory) {
|
||||
memory = new SuggestMemory();
|
||||
this._data.set(language, memory);
|
||||
}
|
||||
memory.remember(item);
|
||||
this._storageService.store(`${this._storagePrefix}/${language}`, JSON.stringify(memory), StorageScope.WORKSPACE);
|
||||
export interface MemItem {
|
||||
type: string;
|
||||
insertText: string;
|
||||
touch: number;
|
||||
}
|
||||
|
||||
export class LRUMemory extends Memory {
|
||||
|
||||
private _cache = new LRUCache<string, MemItem>(300, .66);
|
||||
private _seq = 0;
|
||||
|
||||
memorize(model: ITextModel, pos: IPosition, item: ICompletionItem): void {
|
||||
const { label } = item.suggestion;
|
||||
const key = `${model.getLanguageIdentifier().language}/${label}`;
|
||||
this._cache.set(key, {
|
||||
touch: this._seq++,
|
||||
type: item.suggestion.type,
|
||||
insertText: undefined
|
||||
});
|
||||
}
|
||||
|
||||
select({ language }: LanguageIdentifier, items: ICompletionItem[], last: ICompletionItem): number {
|
||||
let memory = this._data.get(language);
|
||||
if (!memory) {
|
||||
const key: string = `${this._storagePrefix}/${language}`;
|
||||
const raw = this._storageService.get(key, StorageScope.WORKSPACE);
|
||||
if (raw) {
|
||||
try {
|
||||
const tuples = <[string, MemoryItem][]>JSON.parse(raw);
|
||||
memory = new SuggestMemory(tuples);
|
||||
last = undefined;
|
||||
this._data.set(language, memory);
|
||||
} catch (e) {
|
||||
this._storageService.remove(key, StorageScope.WORKSPACE);
|
||||
select(model: ITextModel, pos: IPosition, items: ICompletionItem[]): number {
|
||||
// in order of completions, select the first
|
||||
// that has been used in the past
|
||||
let { word } = model.getWordUntilPosition(pos);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memory) {
|
||||
return memory.select(items, last);
|
||||
} else {
|
||||
return -1;
|
||||
return res;
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
let data: [string, MemItem][] = [];
|
||||
this._cache.forEach((value, key) => {
|
||||
data.push([key, value]);
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
fromJSON(data: [string, MemItem][]): void {
|
||||
this._cache.clear();
|
||||
let seq = 0;
|
||||
for (const [key, value] of data) {
|
||||
value.touch = seq;
|
||||
this._cache.set(key, value);
|
||||
}
|
||||
this._seq = this._cache.size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface MemoryItem {
|
||||
type: string;
|
||||
insertText: string;
|
||||
}
|
||||
export class PrefixMemory extends Memory {
|
||||
|
||||
export class SuggestMemory {
|
||||
private _trie = TernarySearchTree.forStrings<MemItem>();
|
||||
private _seq = 0;
|
||||
|
||||
private readonly _memory = new LRUCache<string, MemoryItem>(400, 0.75);
|
||||
|
||||
constructor(tuples?: [string, MemoryItem][]) {
|
||||
if (tuples) {
|
||||
for (const [word, item] of tuples) {
|
||||
this._memory.set(word, item);
|
||||
}
|
||||
}
|
||||
memorize(model: ITextModel, pos: IPosition, item: ICompletionItem): void {
|
||||
const { word } = model.getWordUntilPosition(pos);
|
||||
const key = `${model.getLanguageIdentifier().language}/${word}`;
|
||||
this._trie.set(key, {
|
||||
type: item.suggestion.type,
|
||||
insertText: item.suggestion.insertText,
|
||||
touch: this._seq++
|
||||
});
|
||||
}
|
||||
|
||||
remember(item: ICompletionItem): void {
|
||||
if (item.word) {
|
||||
this._memory.set(item.word, { insertText: item.suggestion.insertText, type: item.suggestion.type });
|
||||
select(model: ITextModel, pos: IPosition, items: ICompletionItem[]): number {
|
||||
let { word } = model.getWordUntilPosition(pos);
|
||||
if (!word) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
select(items: ICompletionItem[], last: ICompletionItem): number {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i] === last) {
|
||||
// prefer the last selected item when
|
||||
// there is one
|
||||
return i;
|
||||
}
|
||||
if (items[i].word) {
|
||||
const item = this._memory.get(items[i].word);
|
||||
if (this._matches(item, items[i])) {
|
||||
let key = `${model.getLanguageIdentifier().language}/${word}`;
|
||||
let item = this._trie.get(key);
|
||||
if (!item) {
|
||||
item = this._trie.findSubstr(key);
|
||||
}
|
||||
if (item) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let { type, insertText } = items[i].suggestion;
|
||||
if (type === item.type && insertText === item.insertText) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private _matches(item: MemoryItem, candidate: ICompletionItem): boolean {
|
||||
return item && item.insertText === candidate.suggestion.insertText && item.type === candidate.suggestion.type;
|
||||
toJSON(): object {
|
||||
|
||||
let entries: [string, MemItem][] = [];
|
||||
this._trie.forEach((value, key) => entries.push([key, value]));
|
||||
|
||||
// sort by last recently used (touch), then
|
||||
// take the top 200 item and normalize their
|
||||
// touch
|
||||
entries
|
||||
.sort((a, b) => -(a[1].touch - b[1].touch))
|
||||
.forEach((value, i) => value[1].touch = i);
|
||||
|
||||
return entries.slice(0, 200);
|
||||
}
|
||||
|
||||
toJSON(): [string, MemoryItem][] {
|
||||
const tuples: [string, MemoryItem][] = [];
|
||||
this._memory.forEach((value, key) => tuples.push([key, value]));
|
||||
return tuples;
|
||||
fromJSON(data: [string, MemItem][]): void {
|
||||
this._trie.clear();
|
||||
if (data.length > 0) {
|
||||
this._seq = data[0][1].touch + 1;
|
||||
for (const [key, value] of data) {
|
||||
this._trie.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type MemMode = 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix';
|
||||
|
||||
export class SuggestMemories {
|
||||
|
||||
private readonly _storagePrefix = 'suggest/memories';
|
||||
|
||||
private _mode: MemMode;
|
||||
private _strategy: Memory;
|
||||
private _persistSoon: RunOnceScheduler;
|
||||
|
||||
constructor(
|
||||
mode: MemMode,
|
||||
@IStorageService private readonly _storageService: IStorageService
|
||||
) {
|
||||
this._persistSoon = new RunOnceScheduler(() => this._flush(), 3000);
|
||||
this.setMode(mode);
|
||||
}
|
||||
|
||||
setMode(mode: MemMode): void {
|
||||
if (this._mode === mode) {
|
||||
return;
|
||||
}
|
||||
this._mode = mode;
|
||||
this._strategy = mode === 'recentlyUsedByPrefix' ? new PrefixMemory() : mode === 'recentlyUsed' ? new LRUMemory() : new NoMemory();
|
||||
|
||||
try {
|
||||
const raw = this._storageService.get(`${this._storagePrefix}/${this._mode}`, StorageScope.WORKSPACE);
|
||||
this._strategy.fromJSON(JSON.parse(raw));
|
||||
} catch (e) {
|
||||
// things can go wrong with JSON...
|
||||
}
|
||||
}
|
||||
|
||||
memorize(model: ITextModel, pos: IPosition, item: ICompletionItem): void {
|
||||
this._strategy.memorize(model, pos, item);
|
||||
this._persistSoon.schedule();
|
||||
}
|
||||
|
||||
select(model: ITextModel, pos: IPosition, items: ICompletionItem[]): number {
|
||||
return this._strategy.select(model, pos, items);
|
||||
}
|
||||
|
||||
private _flush() {
|
||||
const raw = JSON.stringify(this._strategy);
|
||||
this._storageService.store(`${this._storagePrefix}/${this._mode}`, raw, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import { TimeoutTimer } from 'vs/base/common/async';
|
||||
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 { IModel, IWordAtPosition } from 'vs/editor/common/editorCommon';
|
||||
import { ISuggestSupport, SuggestRegistry, StandardTokenType, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
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 { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest';
|
||||
import { CompletionModel } from './completionModel';
|
||||
@@ -19,22 +19,22 @@ import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/comm
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
export interface ICancelEvent {
|
||||
retrigger: boolean;
|
||||
readonly retrigger: boolean;
|
||||
}
|
||||
|
||||
export interface ITriggerEvent {
|
||||
auto: boolean;
|
||||
readonly auto: boolean;
|
||||
}
|
||||
|
||||
export interface ISuggestEvent {
|
||||
completionModel: CompletionModel;
|
||||
isFrozen: boolean;
|
||||
auto: boolean;
|
||||
readonly completionModel: CompletionModel;
|
||||
readonly isFrozen: boolean;
|
||||
readonly auto: boolean;
|
||||
}
|
||||
|
||||
export interface SuggestTriggerContext {
|
||||
auto: boolean;
|
||||
triggerCharacter?: string;
|
||||
readonly auto: boolean;
|
||||
readonly triggerCharacter?: string;
|
||||
}
|
||||
|
||||
export class LineContext {
|
||||
@@ -59,25 +59,13 @@ export class LineContext {
|
||||
return true;
|
||||
}
|
||||
|
||||
static isInEditableRange(editor: ICodeEditor): boolean {
|
||||
const model = editor.getModel();
|
||||
const position = editor.getPosition();
|
||||
if (model.hasEditableRange()) {
|
||||
const editableRange = model.getEditableRange();
|
||||
if (!editableRange.containsPosition(position)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
readonly lineNumber: number;
|
||||
readonly column: number;
|
||||
readonly leadingLineContent: string;
|
||||
readonly leadingWord: IWordAtPosition;
|
||||
readonly auto: boolean;
|
||||
|
||||
constructor(model: IModel, position: Position, auto: boolean) {
|
||||
constructor(model: ITextModel, position: Position, auto: boolean) {
|
||||
this.leadingLineContent = model.getLineContent(position.lineNumber).substr(0, position.column - 1);
|
||||
this.leadingWord = model.getWordUntilPosition(position);
|
||||
this.lineNumber = position.lineNumber;
|
||||
@@ -289,9 +277,9 @@ export class SuggestModel implements IDisposable {
|
||||
|
||||
this.cancel();
|
||||
|
||||
if (LineContext.shouldAutoTrigger(this._editor)) {
|
||||
this._triggerAutoSuggestPromise = TPromise.timeout(this._quickSuggestDelay);
|
||||
this._triggerAutoSuggestPromise.then(() => {
|
||||
this._triggerAutoSuggestPromise = TPromise.timeout(this._quickSuggestDelay);
|
||||
this._triggerAutoSuggestPromise.then(() => {
|
||||
if (LineContext.shouldAutoTrigger(this._editor)) {
|
||||
const model = this._editor.getModel();
|
||||
const pos = this._editor.getPosition();
|
||||
|
||||
@@ -307,9 +295,8 @@ export class SuggestModel implements IDisposable {
|
||||
} else {
|
||||
// Check the type of the token that triggered this
|
||||
model.tokenizeIfCheap(pos.lineNumber);
|
||||
const { tokenType } = model
|
||||
.getLineTokens(pos.lineNumber)
|
||||
.findTokenAtOffset(Math.max(pos.column - 1 - 1, 0));
|
||||
const lineTokens = model.getLineTokens(pos.lineNumber);
|
||||
const tokenType = lineTokens.getStandardTokenType(lineTokens.findTokenIndexAtOffset(Math.max(pos.column - 1 - 1, 0)));
|
||||
const inValidScope = quickSuggestions.other && tokenType === StandardTokenType.Other
|
||||
|| quickSuggestions.comments && tokenType === StandardTokenType.Comment
|
||||
|| quickSuggestions.strings && tokenType === StandardTokenType.String;
|
||||
@@ -319,10 +306,10 @@ export class SuggestModel implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
this._triggerAutoSuggestPromise = null;
|
||||
this.trigger({ auto: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
this._triggerAutoSuggestPromise = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,10 +339,6 @@ export class SuggestModel implements IDisposable {
|
||||
const auto = context.auto;
|
||||
const ctx = new LineContext(model, this._editor.getPosition(), auto);
|
||||
|
||||
if (!LineContext.isInEditableRange(this._editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel previous requests, change state & update UI
|
||||
this.cancel(retrigger);
|
||||
this._state = auto ? State.Auto : State.Manual;
|
||||
@@ -364,13 +347,23 @@ export class SuggestModel implements IDisposable {
|
||||
// Capture context when request was sent
|
||||
this._context = ctx;
|
||||
|
||||
// Build context for request
|
||||
let suggestCtx: SuggestContext;
|
||||
if (context.triggerCharacter) {
|
||||
suggestCtx = {
|
||||
triggerKind: SuggestTriggerKind.TriggerCharacter,
|
||||
triggerCharacter: context.triggerCharacter
|
||||
};
|
||||
} else if (onlyFrom && onlyFrom.length) {
|
||||
suggestCtx = { triggerKind: SuggestTriggerKind.TriggerForIncompleteCompletions };
|
||||
} else {
|
||||
suggestCtx = { triggerKind: SuggestTriggerKind.Invoke };
|
||||
}
|
||||
|
||||
this._requestPromise = provideSuggestionItems(model, this._editor.getPosition(),
|
||||
this._editor.getConfiguration().contribInfo.snippetSuggestions,
|
||||
onlyFrom,
|
||||
{
|
||||
triggerCharacter: context.triggerCharacter,
|
||||
triggerKind: context.triggerCharacter ? SuggestTriggerKind.TriggerCharacter : SuggestTriggerKind.Invoke
|
||||
}
|
||||
suggestCtx
|
||||
).then(items => {
|
||||
|
||||
this._requestPromise = null;
|
||||
|
||||
@@ -176,8 +176,14 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ISuggestionTemplateData): void {
|
||||
templateData.highlightedLabel.dispose();
|
||||
templateData.disposables = dispose(templateData.disposables);
|
||||
// {{SQL CARBON EDIT}}
|
||||
if (templateData.highlightedLabel) {
|
||||
templateData.highlightedLabel.dispose();
|
||||
}
|
||||
// {{SQL CARBON EDIT}}
|
||||
if (templateData.disposables) {
|
||||
templateData.disposables = dispose(templateData.disposables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,9 +341,15 @@ class SuggestionDetails {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISelectedSuggestion {
|
||||
item: ICompletionItem;
|
||||
index: number;
|
||||
model: CompletionModel;
|
||||
}
|
||||
|
||||
export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>, IDisposable {
|
||||
|
||||
private static ID: string = 'editor.widget.suggestWidget';
|
||||
private static readonly ID: string = 'editor.widget.suggestWidget';
|
||||
|
||||
static LOADING_MESSAGE: string = nls.localize('suggestWidget.loading', "Loading...");
|
||||
static NO_SUGGESTIONS_MESSAGE: string = nls.localize('suggestWidget.noSuggestions', "No suggestions.");
|
||||
@@ -359,6 +371,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
private listElement: HTMLElement;
|
||||
private details: SuggestionDetails;
|
||||
private list: List<ICompletionItem>;
|
||||
private listHeight: number;
|
||||
|
||||
private suggestWidgetVisible: IContextKey<boolean>;
|
||||
private suggestWidgetMultipleSuggestions: IContextKey<boolean>;
|
||||
@@ -368,14 +381,14 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
private showTimeout: TPromise<void>;
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
private onDidSelectEmitter = new Emitter<ICompletionItem>();
|
||||
private onDidFocusEmitter = new Emitter<ICompletionItem>();
|
||||
private onDidSelectEmitter = new Emitter<ISelectedSuggestion>();
|
||||
private onDidFocusEmitter = new Emitter<ISelectedSuggestion>();
|
||||
private onDidHideEmitter = new Emitter<this>();
|
||||
private onDidShowEmitter = new Emitter<this>();
|
||||
|
||||
|
||||
readonly onDidSelect: Event<ICompletionItem> = this.onDidSelectEmitter.event;
|
||||
readonly onDidFocus: Event<ICompletionItem> = this.onDidFocusEmitter.event;
|
||||
readonly onDidSelect: Event<ISelectedSuggestion> = this.onDidSelectEmitter.event;
|
||||
readonly onDidFocus: Event<ISelectedSuggestion> = this.onDidFocusEmitter.event;
|
||||
readonly onDidHide: Event<this> = this.onDidHideEmitter.event;
|
||||
readonly onDidShow: Event<this> = this.onDidShowEmitter.event;
|
||||
|
||||
@@ -499,13 +512,12 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
}
|
||||
|
||||
const item = e.elements[0];
|
||||
const index = e.indexes[0];
|
||||
item.resolve().then(() => {
|
||||
this.onDidSelectEmitter.fire(item);
|
||||
this.onDidSelectEmitter.fire({ item, index, model: this.completionModel });
|
||||
alert(nls.localize('suggestionAriaAccepted', "{0}, accepted", item.suggestion.label));
|
||||
this.editor.focus();
|
||||
});
|
||||
|
||||
alert(nls.localize('suggestionAriaAccepted', "{0}, accepted", item.suggestion.label));
|
||||
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
private _getSuggestionAriaAlertLabel(item: ICompletionItem): string {
|
||||
@@ -591,7 +603,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.ignoreFocusEvents = false;
|
||||
}
|
||||
|
||||
this.updateListHeight();
|
||||
this.list.reveal(index);
|
||||
|
||||
this.currentSuggestionDetails = item.resolve()
|
||||
@@ -613,7 +624,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
.then(() => this.currentSuggestionDetails = null);
|
||||
|
||||
// emit an event
|
||||
this.onDidFocusEmitter.fire(item);
|
||||
this.onDidFocusEmitter.fire({ item, index, model: this.completionModel });
|
||||
}
|
||||
|
||||
private setState(state: State): void {
|
||||
@@ -628,8 +639,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
|
||||
switch (state) {
|
||||
case State.Hidden:
|
||||
hide(this.messageElement, this.details.element);
|
||||
show(this.listElement);
|
||||
hide(this.messageElement, this.details.element, this.listElement);
|
||||
this.hide();
|
||||
if (stateChanged) {
|
||||
this.list.splice(0, this.list.length);
|
||||
@@ -650,19 +660,12 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.show();
|
||||
break;
|
||||
case State.Open:
|
||||
hide(this.messageElement);
|
||||
hide(this.messageElement, this.details.element);
|
||||
show(this.listElement);
|
||||
if (this.expandDocsSettingFromStorage()
|
||||
&& canExpandCompletionItem(this.list.getFocusedElements()[0])) {
|
||||
show(this.details.element);
|
||||
this.expandSideOrBelow();
|
||||
} else {
|
||||
hide(this.details.element);
|
||||
}
|
||||
this.show();
|
||||
break;
|
||||
case State.Frozen:
|
||||
hide(this.messageElement, this.details.element);
|
||||
hide(this.messageElement);
|
||||
show(this.listElement);
|
||||
this.show();
|
||||
break;
|
||||
@@ -673,10 +676,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this._ariaAlert(this.details.getAriaLabel());
|
||||
break;
|
||||
}
|
||||
|
||||
if (stateChanged) {
|
||||
this.editor.layoutContentWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
showTriggered(auto: boolean) {
|
||||
@@ -738,8 +737,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.focusedItem = null;
|
||||
this.focusedItemIndex = null;
|
||||
this.list.splice(0, this.list.length, this.completionModel.items);
|
||||
this.list.setFocus([selectionIndex]);
|
||||
this.list.reveal(selectionIndex, selectionIndex);
|
||||
|
||||
if (isFrozen) {
|
||||
this.setState(State.Frozen);
|
||||
@@ -747,6 +744,9 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.setState(State.Open);
|
||||
}
|
||||
|
||||
this.list.reveal(selectionIndex, selectionIndex);
|
||||
this.list.setFocus([selectionIndex]);
|
||||
|
||||
// Reset focus border
|
||||
if (this.detailsBorderColor) {
|
||||
this.details.element.style.borderColor = this.detailsBorderColor;
|
||||
@@ -838,12 +838,16 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
}
|
||||
}
|
||||
|
||||
getFocusedItem(): ICompletionItem {
|
||||
getFocusedItem(): ISelectedSuggestion {
|
||||
if (this.state !== State.Hidden
|
||||
&& this.state !== State.Empty
|
||||
&& this.state !== State.Loading) {
|
||||
|
||||
return this.list.getFocusedElements()[0];
|
||||
return {
|
||||
item: this.list.getFocusedElements()[0],
|
||||
index: this.list.getFocus()[0],
|
||||
model: this.completionModel
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -929,7 +933,12 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
}
|
||||
|
||||
private show(): void {
|
||||
this.updateListHeight();
|
||||
const newHeight = this.updateListHeight();
|
||||
if (newHeight !== this.listHeight) {
|
||||
this.editor.layoutContentWidget(this);
|
||||
this.listHeight = newHeight;
|
||||
}
|
||||
|
||||
this.suggestWidgetVisible.set(true);
|
||||
|
||||
this.showTimeout = TPromise.timeout(100).then(() => {
|
||||
@@ -982,9 +991,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.element.style.lineHeight = `${this.unfocusedHeight}px`;
|
||||
this.listElement.style.height = `${height}px`;
|
||||
this.list.layout(height);
|
||||
|
||||
this.editor.layoutContentWidget(this);
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,37 +11,37 @@ import { CompletionModel } from 'vs/editor/contrib/suggest/completionModel';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
suite('CompletionModel', function () {
|
||||
export function createSuggestItem(label: string, overwriteBefore: number, type: SuggestionType = 'property', incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }): ISuggestionItem {
|
||||
|
||||
function createSuggestItem(label: string, overwriteBefore: number, type: SuggestionType = 'property', incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }): ISuggestionItem {
|
||||
return new class implements ISuggestionItem {
|
||||
|
||||
return new class implements ISuggestionItem {
|
||||
position = position;
|
||||
|
||||
position = position;
|
||||
suggestion: ISuggestion = {
|
||||
label,
|
||||
overwriteBefore,
|
||||
insertText: label,
|
||||
type
|
||||
};
|
||||
|
||||
suggestion: ISuggestion = {
|
||||
label,
|
||||
overwriteBefore,
|
||||
insertText: label,
|
||||
type
|
||||
};
|
||||
container: ISuggestResult = {
|
||||
incomplete,
|
||||
suggestions: [this.suggestion]
|
||||
};
|
||||
|
||||
container: ISuggestResult = {
|
||||
incomplete,
|
||||
suggestions: [this.suggestion]
|
||||
};
|
||||
|
||||
support: ISuggestSupport = {
|
||||
provideCompletionItems(): any {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
resolve(): TPromise<void> {
|
||||
return null;
|
||||
support: ISuggestSupport = {
|
||||
provideCompletionItems(): any {
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
resolve(): TPromise<void> {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
suite('CompletionModel', function () {
|
||||
|
||||
|
||||
let model: CompletionModel;
|
||||
|
||||
|
||||
@@ -10,17 +10,17 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { SuggestRegistry } from 'vs/editor/common/modes';
|
||||
import { provideSuggestionItems } from 'vs/editor/contrib/suggest/suggest';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Model } from 'vs/editor/common/model/model';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
|
||||
|
||||
suite('Suggest', function () {
|
||||
|
||||
let model: Model;
|
||||
let model: TextModel;
|
||||
let registration: IDisposable;
|
||||
|
||||
setup(function () {
|
||||
|
||||
model = Model.createFromString('FOO\nbar\BAR\nfoo', undefined, undefined, URI.parse('foo:bar/path'));
|
||||
model = TextModel.createFromString('FOO\nbar\BAR\nfoo', undefined, undefined, URI.parse('foo:bar/path'));
|
||||
registration = SuggestRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, {
|
||||
provideCompletionItems() {
|
||||
return {
|
||||
|
||||
86
src/vs/editor/contrib/suggest/test/suggestMemory.test.ts
Normal file
86
src/vs/editor/contrib/suggest/test/suggestMemory.test.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { LRUMemory, NoMemory, PrefixMemory } from 'vs/editor/contrib/suggest/suggestMemory';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { ICompletionItem } from 'vs/editor/contrib/suggest/completionModel';
|
||||
import { createSuggestItem } from 'vs/editor/contrib/suggest/test/completionModel.test';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
|
||||
suite('SuggestMemories', function () {
|
||||
|
||||
let pos: IPosition;
|
||||
let buffer: ITextModel;
|
||||
let items: ICompletionItem[];
|
||||
|
||||
setup(function () {
|
||||
pos = { lineNumber: 1, column: 1 };
|
||||
buffer = TextModel.createFromString('This is some text');
|
||||
items = [
|
||||
createSuggestItem('foo', 0),
|
||||
createSuggestItem('bar', 0)
|
||||
];
|
||||
});
|
||||
|
||||
test('NoMemory', function () {
|
||||
|
||||
const mem = new NoMemory();
|
||||
|
||||
assert.equal(mem.select(buffer, pos, items), 0);
|
||||
assert.equal(mem.select(buffer, pos, []), 0);
|
||||
|
||||
mem.memorize(buffer, pos, items[0]);
|
||||
mem.memorize(buffer, pos, null);
|
||||
});
|
||||
|
||||
test('ShyMemories', function () {
|
||||
|
||||
const mem = new LRUMemory();
|
||||
mem.memorize(buffer, pos, items[1]);
|
||||
|
||||
assert.equal(mem.select(buffer, pos, items), 1);
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 3 }, items), 0);
|
||||
|
||||
mem.memorize(buffer, pos, items[0]);
|
||||
assert.equal(mem.select(buffer, pos, items), 0);
|
||||
|
||||
assert.equal(mem.select(buffer, pos, [
|
||||
createSuggestItem('new', 0),
|
||||
createSuggestItem('bar', 0)
|
||||
]), 1);
|
||||
|
||||
assert.equal(mem.select(buffer, pos, [
|
||||
createSuggestItem('new1', 0),
|
||||
createSuggestItem('new2', 0)
|
||||
]), 0);
|
||||
|
||||
});
|
||||
|
||||
test('PrefixMemory', function () {
|
||||
|
||||
const mem = new PrefixMemory();
|
||||
buffer.setValue('constructor');
|
||||
const item0 = createSuggestItem('console', 0);
|
||||
const item1 = createSuggestItem('const', 0);
|
||||
const item2 = createSuggestItem('constructor', 0);
|
||||
const item3 = createSuggestItem('constant', 0);
|
||||
const items = [item0, item1, item2, item3];
|
||||
|
||||
mem.memorize(buffer, { lineNumber: 1, column: 2 }, item1); // c -> const
|
||||
mem.memorize(buffer, { lineNumber: 1, column: 3 }, item0); // co -> console
|
||||
mem.memorize(buffer, { lineNumber: 1, column: 4 }, item2); // con -> constructor
|
||||
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 1 }, items), 0);
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 2 }, items), 1);
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 3 }, items), 0);
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 4 }, items), 2);
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 7 }, items), 2); // find substr
|
||||
});
|
||||
|
||||
});
|
||||
@@ -8,8 +8,7 @@ import * as assert from 'assert';
|
||||
import Event from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Model } from 'vs/editor/common/model/model';
|
||||
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 { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/suggestModel';
|
||||
@@ -22,15 +21,19 @@ 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 { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
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';
|
||||
|
||||
function createMockEditor(model: Model): TestCodeEditor {
|
||||
function createMockEditor(model: TextModel): TestCodeEditor {
|
||||
const contextKeyService = new MockContextKeyService();
|
||||
const telemetryService = NullTelemetryService;
|
||||
const instantiationService = new InstantiationService(new ServiceCollection(
|
||||
[IContextKeyService, contextKeyService],
|
||||
[ITelemetryService, telemetryService]
|
||||
[ITelemetryService, telemetryService],
|
||||
[IStorageService, NullStorageService]
|
||||
));
|
||||
|
||||
const editor = new TestCodeEditor(new MockScopeLocation(), {}, instantiationService, contextKeyService);
|
||||
@@ -40,10 +43,10 @@ function createMockEditor(model: Model): TestCodeEditor {
|
||||
|
||||
suite('SuggestModel - Context', function () {
|
||||
|
||||
let model: Model;
|
||||
let model: TextModel;
|
||||
|
||||
setup(function () {
|
||||
model = Model.createFromString('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?');
|
||||
model = TextModel.createFromString('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?');
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
@@ -94,17 +97,17 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
};
|
||||
|
||||
let disposables: IDisposable[] = [];
|
||||
let model: Model;
|
||||
let model: TextModel;
|
||||
|
||||
setup(function () {
|
||||
disposables = dispose(disposables);
|
||||
model = Model.createFromString('abc def', undefined, undefined, URI.parse('test:somefile.ttt'));
|
||||
model = TextModel.createFromString('abc def', undefined, undefined, URI.parse('test:somefile.ttt'));
|
||||
disposables.push(model);
|
||||
});
|
||||
|
||||
function withOracle(callback: (model: SuggestModel, editor: ICodeEditor) => any): TPromise<any> {
|
||||
function withOracle(callback: (model: SuggestModel, editor: TestCodeEditor) => any): Promise<any> {
|
||||
|
||||
return new TPromise((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const editor = createMockEditor(model);
|
||||
const oracle = new SuggestModel(editor);
|
||||
disposables.push(oracle, editor);
|
||||
@@ -118,7 +121,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
}
|
||||
|
||||
function assertEvent<E>(event: Event<E>, action: () => any, assert: (e: E) => any) {
|
||||
return new TPromise((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const sub = event(e => {
|
||||
sub.dispose();
|
||||
try {
|
||||
@@ -138,7 +141,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
test('events - cancel/trigger', function () {
|
||||
return withOracle(model => {
|
||||
|
||||
return TPromise.join([
|
||||
return Promise.all([
|
||||
assertEvent(model.onDidCancel, function () {
|
||||
model.cancel();
|
||||
}, function (event) {
|
||||
@@ -185,7 +188,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, alwaysEmptySupport));
|
||||
|
||||
return withOracle(model => {
|
||||
return TPromise.join([
|
||||
return Promise.all([
|
||||
assertEvent(model.onDidCancel, function () {
|
||||
model.trigger({ auto: true });
|
||||
}, function (event) {
|
||||
@@ -564,4 +567,53 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Text changes for completion CodeAction are affected by the completion #39893', function () {
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: true,
|
||||
suggestions: [{
|
||||
label: 'bar',
|
||||
type: 'property',
|
||||
insertText: 'bar',
|
||||
overwriteBefore: 2,
|
||||
additionalTextEdits: [{
|
||||
text: ', bar',
|
||||
range: { startLineNumber: 1, endLineNumber: 1, startColumn: 17, endColumn: 17 }
|
||||
}]
|
||||
}]
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
model.setValue('ba; import { foo } from "./b"');
|
||||
|
||||
return withOracle(async (sugget, editor) => {
|
||||
class TestCtrl extends SuggestController {
|
||||
_onDidSelectItem(item: ISelectedSuggestion) {
|
||||
super._onDidSelectItem(item);
|
||||
}
|
||||
}
|
||||
const ctrl = <TestCtrl>editor.registerAndInstantiateContribution(TestCtrl);
|
||||
editor.registerAndInstantiateContribution(SnippetController2);
|
||||
|
||||
await assertEvent(sugget.onDidSuggest, () => {
|
||||
editor.setPosition({ lineNumber: 1, column: 3 });
|
||||
sugget.trigger({ auto: false });
|
||||
}, event => {
|
||||
|
||||
assert.equal(event.completionModel.items.length, 1);
|
||||
const [first] = event.completionModel.items;
|
||||
assert.equal(first.suggestion.label, 'bar');
|
||||
|
||||
ctrl._onDidSelectItem({ item: first, index: 0, model: event.completionModel });
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
model.getValue(),
|
||||
'bar; import { foo, bar } from "./b"'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user