mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-21 12:20:29 -04:00
Merge from vscode 52dcb723a39ae75bee1bd56b3312d7fcdc87aeed (#6719)
This commit is contained in:
@@ -29,8 +29,10 @@ export interface ICompletionStats {
|
||||
}
|
||||
|
||||
export class LineContext {
|
||||
leadingLineContent: string;
|
||||
characterCountDelta: number;
|
||||
constructor(
|
||||
readonly leadingLineContent: string,
|
||||
readonly characterCountDelta: number,
|
||||
) { }
|
||||
}
|
||||
|
||||
const enum Refilter {
|
||||
@@ -49,9 +51,9 @@ export class CompletionModel {
|
||||
|
||||
private _lineContext: LineContext;
|
||||
private _refilterKind: Refilter;
|
||||
private _filteredItems: StrictCompletionItem[];
|
||||
private _isIncomplete: Set<CompletionItemProvider>;
|
||||
private _stats: ICompletionStats;
|
||||
private _filteredItems?: StrictCompletionItem[];
|
||||
private _isIncomplete?: Set<CompletionItemProvider>;
|
||||
private _stats?: ICompletionStats;
|
||||
|
||||
constructor(
|
||||
items: CompletionItem[],
|
||||
@@ -89,12 +91,12 @@ export class CompletionModel {
|
||||
|
||||
get items(): CompletionItem[] {
|
||||
this._ensureCachedState();
|
||||
return this._filteredItems;
|
||||
return this._filteredItems!;
|
||||
}
|
||||
|
||||
get incomplete(): Set<CompletionItemProvider> {
|
||||
this._ensureCachedState();
|
||||
return this._isIncomplete;
|
||||
return this._isIncomplete!;
|
||||
}
|
||||
|
||||
adopt(except: Set<CompletionItemProvider>): CompletionItem[] {
|
||||
@@ -117,7 +119,7 @@ export class CompletionModel {
|
||||
|
||||
get stats(): ICompletionStats {
|
||||
this._ensureCachedState();
|
||||
return this._stats;
|
||||
return this._stats!;
|
||||
}
|
||||
|
||||
private _ensureCachedState(): void {
|
||||
@@ -136,7 +138,7 @@ export class CompletionModel {
|
||||
let wordLow = '';
|
||||
|
||||
// incrementally filter less
|
||||
const source = this._refilterKind === Refilter.All ? this._items : this._filteredItems;
|
||||
const source = this._refilterKind === Refilter.All ? this._items : this._filteredItems!;
|
||||
const target: StrictCompletionItem[] = [];
|
||||
|
||||
// picks a score function based on the number of
|
||||
|
||||
@@ -110,7 +110,9 @@
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close {
|
||||
background-image: url('./close-light.svg');
|
||||
float: right;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@@ -251,7 +253,7 @@
|
||||
text-overflow: ellipsis;
|
||||
opacity: 0.7;
|
||||
word-break: break-all;
|
||||
margin: 0;
|
||||
margin: 0px 24px 0 0;
|
||||
padding: 4px 0 12px 5px;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export const Context = {
|
||||
|
||||
export class CompletionItem {
|
||||
|
||||
_brand: 'ISuggestionItem';
|
||||
_brand!: 'ISuggestionItem';
|
||||
|
||||
readonly resolve: (token: CancellationToken) => Promise<void>;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export class SuggestAlternatives {
|
||||
|
||||
private readonly _ckOtherSuggestions: IContextKey<boolean>;
|
||||
|
||||
private _index: number;
|
||||
private _index: number = 0;
|
||||
private _model: CompletionModel | undefined;
|
||||
private _acceptNext: ((selected: ISelectedSuggestion) => any) | undefined;
|
||||
private _listener: IDisposable | undefined;
|
||||
|
||||
@@ -370,6 +370,10 @@ export class SuggestController implements IEditorContribution {
|
||||
this._widget.getValue().toggleDetails();
|
||||
}
|
||||
|
||||
toggleExplainMode(): void {
|
||||
this._widget.getValue().toggleExplainMode();
|
||||
}
|
||||
|
||||
toggleSuggestionFocus(): void {
|
||||
this._widget.getValue().toggleDetailsFocus();
|
||||
}
|
||||
@@ -521,6 +525,16 @@ registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'toggleExplainMode',
|
||||
precondition: SuggestContext.Visible,
|
||||
handler: x => x.toggleExplainMode(),
|
||||
kbOpts: {
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.US_SLASH,
|
||||
}
|
||||
}));
|
||||
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'toggleSuggestionFocus',
|
||||
precondition: SuggestContext.Visible,
|
||||
|
||||
@@ -22,10 +22,10 @@ export abstract class Memory {
|
||||
if (items.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
let topScore = items[0].score;
|
||||
let topScore = items[0].score[0];
|
||||
for (let i = 1; i < items.length; i++) {
|
||||
const { score, completion: suggestion } = items[i];
|
||||
if (score !== topScore) {
|
||||
if (score[0] !== topScore) {
|
||||
// stop when leaving the group of top matches
|
||||
break;
|
||||
}
|
||||
@@ -81,33 +81,42 @@ export class LRUMemory extends Memory {
|
||||
}
|
||||
|
||||
select(model: ITextModel, pos: IPosition, items: CompletionItem[]): number {
|
||||
// in order of completions, select the first
|
||||
// that has been used in the past
|
||||
let { word } = model.getWordUntilPosition(pos);
|
||||
if (word.length !== 0) {
|
||||
return super.select(model, pos, items);
|
||||
|
||||
if (items.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let lineSuffix = model.getLineContent(pos.lineNumber).substr(pos.column - 10, pos.column - 1);
|
||||
const lineSuffix = model.getLineContent(pos.lineNumber).substr(pos.column - 10, pos.column - 1);
|
||||
if (/\s$/.test(lineSuffix)) {
|
||||
return super.select(model, pos, items);
|
||||
}
|
||||
|
||||
let res = -1;
|
||||
let topScore = items[0].score[0];
|
||||
let indexPreselect = -1;
|
||||
let indexRecency = -1;
|
||||
let seq = -1;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const { completion: suggestion } = items[i];
|
||||
const key = `${model.getLanguageIdentifier().language}/${suggestion.label}`;
|
||||
const item = this._cache.get(key);
|
||||
if (item && item.touch > seq && item.type === suggestion.kind && item.insertText === suggestion.insertText) {
|
||||
if (items[i].score[0] !== topScore) {
|
||||
// consider only top items
|
||||
break;
|
||||
}
|
||||
const key = `${model.getLanguageIdentifier().language}/${items[i].completion.label}`;
|
||||
const item = this._cache.peek(key);
|
||||
if (item && item.touch > seq && item.type === items[i].completion.kind && item.insertText === items[i].completion.insertText) {
|
||||
seq = item.touch;
|
||||
res = i;
|
||||
indexRecency = i;
|
||||
}
|
||||
if (items[i].completion.preselect && indexPreselect === -1) {
|
||||
// stop when seeing an auto-select-item
|
||||
return indexPreselect = i;
|
||||
}
|
||||
}
|
||||
if (res === -1) {
|
||||
return super.select(model, pos, items);
|
||||
if (indexRecency !== -1) {
|
||||
return indexRecency;
|
||||
} else if (indexPreselect !== -1) {
|
||||
return indexPreselect;
|
||||
} else {
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,9 +213,9 @@ export class SuggestMemoryService extends Disposable implements ISuggestMemorySe
|
||||
private readonly _storagePrefix = 'suggest/memories';
|
||||
|
||||
private readonly _persistSoon: RunOnceScheduler;
|
||||
private _mode: MemMode;
|
||||
private _shareMem: boolean;
|
||||
private _strategy: Memory;
|
||||
private _mode!: MemMode;
|
||||
private _shareMem!: boolean;
|
||||
private _strategy!: Memory;
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
|
||||
@@ -92,8 +92,8 @@ export const enum State {
|
||||
export class SuggestModel implements IDisposable {
|
||||
|
||||
private readonly _toDispose = new DisposableStore();
|
||||
private _quickSuggestDelay: number;
|
||||
private _triggerCharacterListener: IDisposable;
|
||||
private _quickSuggestDelay: number = 10;
|
||||
private _triggerCharacterListener?: IDisposable;
|
||||
private readonly _triggerQuickSuggest = new TimeoutTimer();
|
||||
private _state: State = State.Idle;
|
||||
|
||||
@@ -161,7 +161,8 @@ export class SuggestModel implements IDisposable {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this._triggerCharacterListener, this._triggerQuickSuggest]);
|
||||
dispose(this._triggerCharacterListener);
|
||||
dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this._triggerQuickSuggest]);
|
||||
this._toDispose.dispose();
|
||||
this._completionDisposables.dispose();
|
||||
this.cancel();
|
||||
|
||||
@@ -38,8 +38,6 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
const expandSuggestionDocsByDefault = false;
|
||||
|
||||
@@ -117,7 +115,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
|
||||
const text = append(container, $('.contents'));
|
||||
const main = append(text, $('.main'));
|
||||
|
||||
data.iconLabel = new IconLabel(main, { supportHighlights: true });
|
||||
data.iconLabel = new IconLabel(main, { supportHighlights: true, supportOcticons: true });
|
||||
data.disposables.add(data.iconLabel);
|
||||
|
||||
data.typeLabel = append(main, $('span.type-label'));
|
||||
@@ -231,17 +229,6 @@ const enum State {
|
||||
}
|
||||
|
||||
|
||||
let _explainMode = false;
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'suggest.toggleExplainMode',
|
||||
handler() {
|
||||
_explainMode = !_explainMode;
|
||||
},
|
||||
when: SuggestContext.Visible,
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.US_SLASH,
|
||||
});
|
||||
|
||||
class SuggestionDetails {
|
||||
|
||||
private el: HTMLElement;
|
||||
@@ -253,7 +240,7 @@ class SuggestionDetails {
|
||||
private docs: HTMLElement;
|
||||
private ariaLabel: string | null;
|
||||
private readonly disposables: DisposableStore;
|
||||
private renderDisposeable: IDisposable;
|
||||
private renderDisposeable?: IDisposable;
|
||||
private borderWidth: number = 1;
|
||||
|
||||
constructor(
|
||||
@@ -300,13 +287,13 @@ class SuggestionDetails {
|
||||
this.docs.textContent = '';
|
||||
}
|
||||
|
||||
renderItem(item: CompletionItem): void {
|
||||
renderItem(item: CompletionItem, explainMode: boolean): void {
|
||||
this.renderDisposeable = dispose(this.renderDisposeable);
|
||||
|
||||
let { documentation, detail } = item.completion;
|
||||
// --- documentation
|
||||
|
||||
if (_explainMode) {
|
||||
if (explainMode) {
|
||||
let md = '';
|
||||
md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || item.completion.label}' with '${item.word}'` : ' (no prefix)'}\n`;
|
||||
md += `distance: ${item.distance}, see localityBonus-setting\n`;
|
||||
@@ -315,7 +302,7 @@ class SuggestionDetails {
|
||||
detail = `Provider: ${item.provider._debugDisplayName}`;
|
||||
}
|
||||
|
||||
if (!_explainMode && !canExpandCompletionItem(item)) {
|
||||
if (!explainMode && !canExpandCompletionItem(item)) {
|
||||
this.type.textContent = '';
|
||||
this.docs.textContent = '';
|
||||
addClass(this.el, 'no-docs');
|
||||
@@ -435,25 +422,24 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
readonly allowEditorOverflow = true;
|
||||
readonly suppressMouseDown = true;
|
||||
|
||||
private state: State | null;
|
||||
private isAuto: boolean;
|
||||
private state: State | null = null;
|
||||
private isAuto: boolean = false;
|
||||
private loadingTimeout: IDisposable = Disposable.None;
|
||||
private currentSuggestionDetails: CancelablePromise<void> | null;
|
||||
private currentSuggestionDetails: CancelablePromise<void> | null = null;
|
||||
private focusedItem: CompletionItem | null;
|
||||
private ignoreFocusEvents = false;
|
||||
private completionModel: CompletionModel | null;
|
||||
private ignoreFocusEvents: boolean = false;
|
||||
private completionModel: CompletionModel | null = null;
|
||||
|
||||
private element: HTMLElement;
|
||||
private messageElement: HTMLElement;
|
||||
private listElement: HTMLElement;
|
||||
private details: SuggestionDetails;
|
||||
private list: List<CompletionItem>;
|
||||
private listHeight: number;
|
||||
private listHeight?: number;
|
||||
|
||||
private readonly suggestWidgetVisible: IContextKey<boolean>;
|
||||
private readonly suggestWidgetMultipleSuggestions: IContextKey<boolean>;
|
||||
|
||||
private readonly editorBlurTimeout = new TimeoutTimer();
|
||||
private readonly showTimeout = new TimeoutTimer();
|
||||
private readonly toDispose = new DisposableStore();
|
||||
|
||||
@@ -470,13 +456,14 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
private readonly maxWidgetWidth = 660;
|
||||
private readonly listWidth = 330;
|
||||
private readonly storageService: IStorageService;
|
||||
private detailsFocusBorderColor: string;
|
||||
private detailsBorderColor: string;
|
||||
private detailsFocusBorderColor?: string;
|
||||
private detailsBorderColor?: string;
|
||||
|
||||
private firstFocusInCurrentList: boolean = false;
|
||||
|
||||
private preferDocPositionTop: boolean = false;
|
||||
private docsPositionPreviousWidgetY: number | null;
|
||||
private docsPositionPreviousWidgetY: number | null = null;
|
||||
private explainMode: boolean = false;
|
||||
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
@@ -594,7 +581,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
}
|
||||
}
|
||||
|
||||
private _lastAriaAlertLabel: string | null;
|
||||
private _lastAriaAlertLabel: string | null = null;
|
||||
private _ariaAlert(newAriaAlertLabel: string | null): void {
|
||||
if (this._lastAriaAlertLabel === newAriaAlertLabel) {
|
||||
return;
|
||||
@@ -974,12 +961,14 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.expandSideOrBelow();
|
||||
|
||||
show(this.details.element);
|
||||
|
||||
this.details.element.style.maxHeight = this.maxWidgetHeight + 'px';
|
||||
|
||||
if (loading) {
|
||||
this.details.renderLoading();
|
||||
} else {
|
||||
this.details.renderItem(this.list.getFocusedElements()[0]);
|
||||
this.details.renderItem(this.list.getFocusedElements()[0], this.explainMode);
|
||||
}
|
||||
this.details.element.style.maxHeight = this.maxWidgetHeight + 'px';
|
||||
|
||||
// Reset margin-top that was set as Fix for #26416
|
||||
this.listElement.style.marginTop = '0px';
|
||||
@@ -992,6 +981,13 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
toggleExplainMode(): void {
|
||||
if (this.list.getFocusedElements()[0] && this.expandDocsSettingFromStorage()) {
|
||||
this.explainMode = !this.explainMode;
|
||||
this.showDetails(false);
|
||||
}
|
||||
}
|
||||
|
||||
private show(): void {
|
||||
const newHeight = this.updateListHeight();
|
||||
if (newHeight !== this.listHeight) {
|
||||
@@ -1162,7 +1158,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.list.dispose();
|
||||
this.toDispose.dispose();
|
||||
this.loadingTimeout.dispose();
|
||||
this.editorBlurTimeout.dispose();
|
||||
this.showTimeout.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,29 @@ suite('SuggestMemories', function () {
|
||||
]), 0);
|
||||
});
|
||||
|
||||
test('`"editor.suggestSelection": "recentlyUsed"` should be a little more sticky #78571', function () {
|
||||
|
||||
let item1 = createSuggestItem('gamma', 0);
|
||||
let item2 = createSuggestItem('game', 0);
|
||||
items = [item1, item2];
|
||||
|
||||
let mem = new LRUMemory();
|
||||
buffer.setValue(' foo.');
|
||||
mem.memorize(buffer, { lineNumber: 1, column: 1 }, item2);
|
||||
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 2 }, items), 0); // leading whitespace -> ignore recent items
|
||||
|
||||
mem.memorize(buffer, { lineNumber: 1, column: 9 }, item2);
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 9 }, items), 1); // foo.
|
||||
|
||||
buffer.setValue(' foo.g');
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 10 }, items), 1); // foo.g, 'gamma' and 'game' have the same score
|
||||
|
||||
item1.score = [10, 0, 0];
|
||||
assert.equal(mem.select(buffer, { lineNumber: 1, column: 10 }, items), 0); // foo.g, 'gamma' has higher score
|
||||
|
||||
});
|
||||
|
||||
test('intellisense is not showing top options first #43429', function () {
|
||||
// ensure we don't memorize for whitespace prefixes
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export class WordContextKey extends Disposable {
|
||||
|
||||
private readonly _ckAtEnd: IContextKey<boolean>;
|
||||
|
||||
private _enabled: boolean;
|
||||
private _enabled: boolean = false;
|
||||
private _selectionListener?: IDisposable;
|
||||
|
||||
constructor(
|
||||
|
||||
Reference in New Issue
Block a user