diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index bc8bbdc9ff7..8822571d076 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -60,12 +60,17 @@ export function activate(context: ExtensionContext) { (Array.isArray(r) ? r : r.items).forEach(updateRanges); if (!Array.isArray(r)) { - r.isDetailsResolved = true; r.items.forEach(i => { if (i.kind === CompletionItemKind.Color) { - i.detail = i.documentation?.toString(); + i.label = { + label: i.label as string, + details: i.documentation?.toString() + }; } else { - i.detail = i.label; + i.label = { + label: i.label as string, + details: i.label as string + }; } }); } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 78495217dc3..f22125a6acb 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -369,6 +369,28 @@ export let completionKindFromString: { }; })(); +export interface CompletionItemLabel { + + /** + * The label of this completion item. By default + * this is also the text that is inserted when selecting + * this completion. + */ + label: string; + + /** + * A description of the completion item which is rendered + * less prominent. + */ + // description?: string; + + /** + * Details of the completion item that is rendered less + * prominent to the right. + */ + details?: string; +} + export const enum CompletionItemTag { Deprecated = 1 } @@ -396,7 +418,7 @@ export interface CompletionItem { * this is also the text that is inserted when selecting * this completion. */ - label: string; + label: string | CompletionItemLabel; /** * The kind of this completion item. Based on the kind * an icon is chosen by the editor. @@ -481,7 +503,6 @@ export interface CompletionItem { export interface CompletionList { suggestions: CompletionItem[]; incomplete?: boolean; - isDetailsResolved?: boolean; dispose?(): void; } diff --git a/src/vs/editor/contrib/suggest/completionModel.ts b/src/vs/editor/contrib/suggest/completionModel.ts index c1349fa7935..5382be986e9 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -192,6 +192,7 @@ export class CompletionModel { } } + const label = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.label; if (wordPos >= wordLen) { // the wordPos at which scoring starts is the whole word // and therefore the same rules as not having a word apply @@ -206,19 +207,19 @@ export class CompletionModel { if (!match) { continue; // NO match } - if (compareIgnoreCase(item.completion.filterText, item.completion.label) === 0) { + if (compareIgnoreCase(item.completion.filterText, label) === 0) { // filterText and label are actually the same -> use good highlights item.score = match; } else { // re-run the scorer on the label in the hope of a result BUT use the rank // of the filterText-match - item.score = anyScore(word, wordLow, wordPos, item.completion.label, item.labelLow, 0); + item.score = anyScore(word, wordLow, wordPos, label, item.labelLow, 0); item.score[0] = match[0]; // use score from filterText } } else { // by default match `word` against the `label` - let match = scoreFn(word, wordLow, wordPos, item.completion.label, item.labelLow, 0, false); + let match = scoreFn(word, wordLow, wordPos, label, item.labelLow, 0, false); if (!match) { continue; // NO match } diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index f408f88ec12..61813788b10 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -36,6 +36,9 @@ export class CompletionItem { readonly editInsertEnd: IPosition; readonly editReplaceEnd: IPosition; + // + readonly textLabel: string; + // perf readonly labelLow: string; readonly sortTextLow?: string; @@ -47,9 +50,6 @@ export class CompletionItem { idx?: number; word?: string; - // all details resolved, we can show them all - readonly isDetailsResolved: boolean; - constructor( readonly position: IPosition, readonly completion: modes.CompletionItem, @@ -57,8 +57,13 @@ export class CompletionItem { readonly provider: modes.CompletionItemProvider, model: ITextModel ) { + this.textLabel = typeof completion.label === 'string' + ? completion.label + : completion.label.label; + // ensure lower-variants (perf) - this.labelLow = completion.label.toLowerCase(); + this.labelLow = this.textLabel.toLowerCase(); + this.sortTextLow = completion.sortText && completion.sortText.toLowerCase(); this.filterTextLow = completion.filterText && completion.filterText.toLowerCase(); @@ -73,8 +78,6 @@ export class CompletionItem { this.editReplaceEnd = new Position(completion.range.replace.endLineNumber, completion.range.replace.endColumn); } - this.isDetailsResolved = container.isDetailsResolved || typeof provider.resolveCompletionItem === 'undefined'; - // create the suggestion resolver const { resolveCompletionItem } = provider; if (typeof resolveCompletionItem !== 'function') { @@ -188,7 +191,7 @@ export function provideSuggestionItems( } // fill in default sortText when missing if (!suggestion.sortText) { - suggestion.sortText = suggestion.label; + suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; } allSuggestions.push(new CompletionItem(position, suggestion, container, provider, model)); diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index a4330d1d438..b0fa140844a 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -343,8 +343,9 @@ export class SuggestController implements IEditorContribution { } private _alertCompletionItem({ completion: suggestion }: CompletionItem): void { + const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; if (isNonEmptyArray(suggestion.additionalTextEdits)) { - let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", suggestion.label, suggestion.additionalTextEdits.length); + let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length); alert(msg); } } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 24bb13a3043..90f1a7ba65d 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -67,8 +67,12 @@ export const editorSuggestWidgetHighlightForeground = registerColor('editorSugge const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i; function extractColor(item: CompletionItem, out: string[]): boolean { - if (item.completion.label.match(colorRegExp)) { - out[0] = item.completion.label; + const label = typeof item.completion.label === 'string' + ? item.completion.label + : item.completion.label.label; + + if (label.match(colorRegExp)) { + out[0] = label; return true; } if (typeof item.completion.documentation === 'string' && item.completion.documentation.match(colorRegExp)) { @@ -163,6 +167,7 @@ class Renderer implements IListRenderer renderElement(element: CompletionItem, _index: number, templateData: ISuggestionTemplateData): void { const data = templateData; const suggestion = (element).completion; + const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; data.icon.className = 'icon ' + completionKindToCssClass(suggestion.kind); data.colorspan.style.backgroundColor = ''; @@ -183,7 +188,7 @@ class Renderer implements IListRenderer // special logic for 'file' completion items data.icon.className = 'icon hide'; data.iconContainer.className = 'icon hide'; - const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE); + const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FILE); const detailClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE); labelOptions.extraClasses = labelClasses.length > detailClasses.length ? labelClasses : detailClasses; @@ -192,7 +197,7 @@ class Renderer implements IListRenderer data.icon.className = 'icon hide'; data.iconContainer.className = 'icon hide'; labelOptions.extraClasses = flatten([ - getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FOLDER), + getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FOLDER), getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER) ]); } else { @@ -207,8 +212,12 @@ class Renderer implements IListRenderer labelOptions.matches = []; } - data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); - data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); + data.iconLabel.setLabel(textLabel, undefined, labelOptions); + if (typeof suggestion.label === 'string') { + data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); + } else { + data.typeLabel.textContent = (suggestion.label.details || '').replace(/\n.*$/m, ''); + } if (canExpandCompletionItem(element)) { show(data.readMore); @@ -621,10 +630,11 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate MainThreadLanguageFeatures._inflateSuggestDto(result.a, d)), incomplete: result.c, - isDetailsResolved: result.d, dispose: () => typeof result.x === 'number' && this._proxy.$releaseCompletionItems(handle, result.x) }; }); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 227a885f876..5b65014b083 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -989,7 +989,7 @@ export const enum ISuggestDataDtoField { } export interface ISuggestDataDto { - [ISuggestDataDtoField.label]: string; + [ISuggestDataDtoField.label]: string | modes.CompletionItemLabel; [ISuggestDataDtoField.kind]: modes.CompletionItemKind; [ISuggestDataDtoField.detail]?: string; [ISuggestDataDtoField.documentation]?: string | IMarkdownString; @@ -1012,7 +1012,6 @@ export interface ISuggestResultDto { a: { insert: IRange, replace: IRange; }; b: ISuggestDataDto[]; c?: boolean; - d?: boolean; } export interface ISignatureHelpDto { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 737ed4be714..19fa91f1de3 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -787,8 +787,7 @@ class SuggestAdapter { x: pid, b: [], a: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) }, - c: list.isIncomplete || undefined, - d: list.isDetailsResolved || undefined + c: list.isIncomplete || undefined }; for (let i = 0; i < list.items.length; i++) { @@ -858,7 +857,11 @@ class SuggestAdapter { } private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: extHostProtocol.ChainedCacheId): extHostProtocol.ISuggestDataDto | undefined { - if (typeof item.label !== 'string' || item.label.length === 0) { + const label = typeof item.label === 'string' + ? item.label + : item.label.label; + + if (typeof label !== 'string' || label.length === 0) { this._logService.warn('INVALID text edit -> must have at least a label'); return undefined; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 5239da9499b..19863726fac 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1351,10 +1351,17 @@ export enum CompletionItemTag { Deprecated = 1, } +export interface CompletionItemLabel { + label: string; + // description?: string; + details?: string; +} + + @es5ClassCompat export class CompletionItem implements vscode.CompletionItem { - label: string; + label: string | CompletionItemLabel; kind?: CompletionItemKind; tags?: CompletionItemTag[]; detail?: string; @@ -1371,7 +1378,7 @@ export class CompletionItem implements vscode.CompletionItem { additionalTextEdits?: TextEdit[]; command?: vscode.Command; - constructor(label: string, kind?: CompletionItemKind) { + constructor(label: string | CompletionItemLabel, kind?: CompletionItemKind) { this.label = label; this.kind = kind; } @@ -1395,7 +1402,6 @@ export class CompletionItem implements vscode.CompletionItem { export class CompletionList { isIncomplete?: boolean; - isDetailsResolved?: boolean; items: vscode.CompletionItem[]; constructor(items: vscode.CompletionItem[] = [], isIncomplete: boolean = false) { diff --git a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts index e30853b4c2c..96d9f026104 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts @@ -146,7 +146,7 @@ export class SnippetCompletionProvider implements CompletionItemProvider { i = to; } } - return { suggestions, isDetailsResolved: true }; + return { suggestions }; }); }