diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index da4153ca2e1..afaa0c0b57b 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -686,25 +686,59 @@ export interface CompletionItemProvider { } /** - * @internal + * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. */ -export interface InlineSuggestion { - text: string; - replaceRange?: IRange; +export enum InlineCompletionTriggerKind { + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 0, + + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Explicit = 1, +} + +export interface InlineCompletionContext { + /** + * How the completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; } /** * @internal */ -export interface InlineSuggestions { +export interface InlineCompletion { + /** + * The text to insert. + * If the text contains a line break, the range must end at the end of a line. + * If existing text should be replaced, the existing text must be a prefix of the text to insert. + */ + text: string; + + /** + * The range to replace. + * Must begin and end on the same line. + */ + range?: IRange; +} + +/** + * @internal + */ +export interface InlineCompletions { items: TItem[]; } /** * @internal */ -export interface InlineSuggestionsProvider { - provideInlineSuggestions(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +export interface InlineCompletionsProvider { + provideInlineCompletions(model: model.ITextModel, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; } export interface CodeAction { @@ -1795,7 +1829,7 @@ export const CompletionProviderRegistry = new LanguageFeatureRegistry(); +export const InlineSuggestionsProviderRegistry = new LanguageFeatureRegistry(); /** * @internal diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 004d924d1fc..7dc1e7418e4 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -360,6 +360,22 @@ export enum InlayHintKind { Parameter = 2 } +/** + * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ +export enum InlineCompletionTriggerKind { + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 0, + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Explicit = 1 +} + /** * Virtual Key Codes, the value does not hold any inherent meaning. * Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx diff --git a/src/vs/editor/contrib/inlineSuggestions/ghostTextWidget.ts b/src/vs/editor/contrib/inlineSuggestions/ghostTextWidget.ts index c6264b5ca15..0310d71c060 100644 --- a/src/vs/editor/contrib/inlineSuggestions/ghostTextWidget.ts +++ b/src/vs/editor/contrib/inlineSuggestions/ghostTextWidget.ts @@ -111,26 +111,29 @@ export class GhostTextWidget extends Disposable { this.render(); } - private render(): void { - let renderData: { tabSize: number; position: Position; lines: string[]; minAdditionalLineCount: number; multiline: boolean; expandCallback: () => void; } | undefined; - - if (this.editor.hasModel() && this.model?.value) { - const { position, lines, minAdditionalLineCount, multiline, expandCallback } = this.model?.value; - - const textModel = this.editor.getModel(); - const maxColumn = textModel.getLineMaxColumn(position.lineNumber); - const { tabSize } = textModel.getOptions(); - - if (position.column !== maxColumn) { - console.warn('Can only show multiline ghost text at the end of a line'); - renderData = { tabSize, position: new Position(position.lineNumber, maxColumn), lines: [], minAdditionalLineCount, multiline, expandCallback }; - } else { - renderData = { tabSize, position, lines, minAdditionalLineCount, multiline, expandCallback }; - } - } else { - renderData = undefined; + private getRenderData() { + if (!this.editor.hasModel() || !this.model?.value) { + return undefined; } + let { position, lines, minAdditionalLineCount, multiline, expandCallback } = this.model?.value; + + const textModel = this.editor.getModel(); + const maxColumn = textModel.getLineMaxColumn(position.lineNumber); + const { tabSize } = textModel.getOptions(); + + if (position.column !== maxColumn) { + console.warn('Can only show multiline ghost text at the end of a line'); + lines = []; + position = new Position(position.lineNumber, maxColumn); + } + + return { tabSize, position, lines, minAdditionalLineCount, multiline, expandCallback }; + } + + private render(): void { + const renderData = this.getRenderData(); + if (this.hasDecoration) { this.hasDecoration = false; this._codeEditorService.removeDecorationType(this._codeEditorDecorationTypeKey); @@ -178,7 +181,7 @@ export class GhostTextWidget extends Disposable { if (heightInLines > 0) { if (renderData.multiline) { const domNode = document.createElement('div'); - this._renderLines(domNode, renderData.tabSize, remainingLines); + this.renderLines(domNode, renderData.tabSize, remainingLines); this.viewZoneId = changeAccessor.addZone({ afterLineNumber: renderData.position.lineNumber, @@ -187,14 +190,14 @@ export class GhostTextWidget extends Disposable { domNode, }); } else if (remainingLines.length > 0) { - this.viewMoreContentWidget = this._renderViewMoreLines(renderData.position, renderData.lines[0], remainingLines.length, renderData.expandCallback); + this.viewMoreContentWidget = this.renderViewMoreLines(renderData.position, renderData.lines[0], remainingLines.length, renderData.expandCallback); } } } }); } - private _renderViewMoreLines(position: Position, firstLineText: string, remainingLinesLength: number, expandCallback: () => void): ViewMoreLinesContentWidget { + private renderViewMoreLines(position: Position, firstLineText: string, remainingLinesLength: number, expandCallback: () => void): ViewMoreLinesContentWidget { const fontInfo = this.editor.getOption(EditorOption.fontInfo); const domNode = document.createElement('div'); domNode.className = 'suggest-preview-additional-widget'; @@ -226,7 +229,7 @@ export class GhostTextWidget extends Disposable { return new ViewMoreLinesContentWidget(this.editor, position, domNode, disposableStore); } - private _renderLines(domNode: HTMLElement, tabSize: number, lines: string[]): void { + private renderLines(domNode: HTMLElement, tabSize: number, lines: string[]): void { const opts = this.editor.getOptions(); const disableMonospaceOptimizations = opts.get(EditorOption.disableMonospaceOptimizations); const stopRenderingLineAfter = opts.get(EditorOption.stopRenderingLineAfter); diff --git a/src/vs/editor/contrib/inlineSuggestions/inlineSuggestionsController.ts b/src/vs/editor/contrib/inlineSuggestions/inlineSuggestionsController.ts index dbc6f7ac37d..f20edab82d4 100644 --- a/src/vs/editor/contrib/inlineSuggestions/inlineSuggestionsController.ts +++ b/src/vs/editor/contrib/inlineSuggestions/inlineSuggestionsController.ts @@ -9,7 +9,7 @@ import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/life import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ITextModel } from 'vs/editor/common/model'; -import { CompletionItemInsertTextRule, InlineSuggestion, InlineSuggestions, InlineSuggestionsProviderRegistry } from 'vs/editor/common/modes'; +import { CompletionItemInsertTextRule, InlineCompletionContext, InlineCompletionTriggerKind, InlineCompletion as InlineCompletion, InlineCompletions as InlineCompletions, InlineSuggestionsProviderRegistry as InlineCompletionsProviderRegistry } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; @@ -27,12 +27,12 @@ import { ISelectedSuggestion } from 'vs/editor/contrib/suggest/suggestWidget'; import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -class InlineSuggestionsController extends Disposable { - public static readonly inlineSuggestionVisible = new RawContextKey('inlineSuggestionVisible ', false, nls.localize('inlineSuggestionVisible ', "TODO")); +class InlineCompletionsController extends Disposable { + public static readonly inlineCompletionVisible = new RawContextKey('inlineSuggestionVisible ', false, nls.localize('inlineSuggestionVisible ', "TODO")); static ID = 'editor.contrib.inlineSuggestionsController'; - public static get(editor: ICodeEditor): InlineSuggestionsController { - return editor.getContribution(InlineSuggestionsController.ID); + public static get(editor: ICodeEditor): InlineCompletionsController { + return editor.getContribution(InlineCompletionsController.ID); } private readonly widget: GhostTextWidget; @@ -88,7 +88,7 @@ class InlineSuggestionsController extends Disposable { } class InlineSuggestionsContextKeys { - public readonly inlineSuggestionVisible = InlineSuggestionsController.inlineSuggestionVisible.bindTo(this.contextKeyService); + public readonly inlineSuggestionVisible = InlineCompletionsController.inlineCompletionVisible.bindTo(this.contextKeyService); constructor(private readonly contextKeyService: IContextKeyService) { } @@ -217,7 +217,7 @@ class InlineSuggestionsSession extends Disposable { this.widget.setModel(this.ghostTextModel); } - get currentSuggestion(): ValidatedInlineSuggestion | undefined { + get currentSuggestion(): ValidatedInlineCompletion | undefined { const cursorPos = this.editor.getPosition(); const suggestions = this.model.getInlineSuggestions(cursorPos); const validatedSuggestions = suggestions.items @@ -231,7 +231,7 @@ class InlineSuggestionsSession extends Disposable { isValid(): boolean { const pos = this.editor.getPosition(); if (this.currentSuggestion) { - return this.currentSuggestion.suggestion.replaceRange.containsPosition(pos); + return this.currentSuggestion.suggestion.range.containsPosition(pos); } return this.triggerPosition.lineNumber === pos.lineNumber; // the cursor is still on this line } @@ -247,7 +247,7 @@ class InlineSuggestionsSession extends Disposable { const lines = strings.splitLines(text); this.maxLineCount = Math.max(this.maxLineCount, lines.length); this.ghostTextModel.setValue({ - position: suggestion.suggestion.replaceRange.getStartPosition().delta(0, suggestion.committedSuggestionLength), + position: suggestion.suggestion.range.getStartPosition().delta(0, suggestion.committedSuggestionLength), lines, minAdditionalLineCount: this.maxLineCount - 1, multiline: this.multiline, @@ -289,18 +289,18 @@ class InlineSuggestionsSession extends Disposable { } } - public commit(suggestion: ValidatedInlineSuggestion): void { + public commit(suggestion: ValidatedInlineCompletion): void { this.editor.executeEdits( 'inlineSuggestions.accept', [ - EditOperation.replaceMove(suggestion.suggestion.replaceRange, suggestion.suggestion.text) + EditOperation.replaceMove(suggestion.suggestion.range, suggestion.suggestion.text) ] ); } } -interface ValidatedInlineSuggestion { - suggestion: NormalizedInlineSuggestion; +interface ValidatedInlineCompletion { + suggestion: NormalizedInlineCompletion; lineNumber: number; /** * Indicates the length of the prefix of the suggestion that agrees with the text buffer. @@ -345,7 +345,7 @@ class DelegatingInlineSuggestionsModel extends Disposable { })); } - getInlineSuggestions(position: Position): NormalizedInlineSuggestions { + getInlineSuggestions(position: Position): NormalizedInlineCompletions { return this.currentModel.getInlineSuggestions(position); } } @@ -457,14 +457,14 @@ class SuggestWidgetInlineSuggestionsModel extends Disposable { this.onDidChangeEventEmitter.fire(); } - getInlineSuggestions(position: Position): NormalizedInlineSuggestions { + getInlineSuggestions(position: Position): NormalizedInlineCompletions { if (this.currentSuggestion && this.currentSuggestion.lineNumber !== position.lineNumber) { this.currentSuggestion = null; } if (this.currentSuggestion) { return { items: [{ - replaceRange: Range.fromPositions(position.delta(0, -this.currentSuggestion.overwriteBefore), position.delta(0, this.currentSuggestion.overwriteAfter)), + range: Range.fromPositions(position.delta(0, -this.currentSuggestion.overwriteBefore), position.delta(0, this.currentSuggestion.overwriteAfter)), text: this.currentSuggestion.text }] }; @@ -480,8 +480,8 @@ class InlineSuggestionsModel extends Disposable { private readonly textModel: ITextModel = this.editor.getModel(); private isActive: boolean = false; - private updatePromise: CancelablePromise | undefined = undefined; - private cachedList: NormalizedInlineSuggestions | undefined = undefined; + private updatePromise: CancelablePromise | undefined = undefined; + private cachedList: NormalizedInlineCompletions | undefined = undefined; private cachedPosition: Position | undefined = undefined; private updateSoon = this._register(new RunOnceScheduler(() => this._update(), 50)); @@ -509,7 +509,7 @@ class InlineSuggestionsModel extends Disposable { this.updateSoon.cancel(); } - public getInlineSuggestions(position: Position): NormalizedInlineSuggestions { + public getInlineSuggestions(position: Position): NormalizedInlineCompletions { if (this.cachedList && this.cachedPosition && position.lineNumber === this.cachedPosition.lineNumber) { return this.cachedList; } @@ -521,7 +521,7 @@ class InlineSuggestionsModel extends Disposable { private _update(): void { const position = this.editor.getPosition(); this.clearGhostTextPromise(); - this.updatePromise = createCancelablePromise(token => provideInlineSuggestions(position, this.textModel, token)); + this.updatePromise = createCancelablePromise(token => provideInlineCompletions(position, this.textModel, { triggerKind: InlineCompletionTriggerKind.Automatic }, token)); this.updatePromise.then((result) => { this.cachedList = result; this.cachedPosition = position; @@ -537,19 +537,19 @@ class InlineSuggestionsModel extends Disposable { } } -function validateSuggestion(suggestion: NormalizedInlineSuggestion, model: ITextModel): ValidatedInlineSuggestion | undefined { +function validateSuggestion(suggestion: NormalizedInlineCompletion, model: ITextModel): ValidatedInlineCompletion | undefined { // Multiline replacements are not supported - if (suggestion.replaceRange.startLineNumber !== suggestion.replaceRange.endLineNumber) { + if (suggestion.range.startLineNumber !== suggestion.range.endLineNumber) { return undefined; } - const lineNumber = suggestion.replaceRange.startLineNumber; + const lineNumber = suggestion.range.startLineNumber; const suggestedLines = strings.splitLines(suggestion.text); const firstSuggestedLine = suggestedLines[0]; const modelLine = model.getLineContent(lineNumber); - const suggestionStartIdx = suggestion.replaceRange.startColumn - 1; + const suggestionStartIdx = suggestion.range.startColumn - 1; let committedSuggestionLength = 0; while ( committedSuggestionLength < firstSuggestedLine.length @@ -578,26 +578,26 @@ function validateSuggestion(suggestion: NormalizedInlineSuggestion, model: IText export class TriggerGhostTextAction extends EditorAction { constructor() { super({ - id: 'editor.action.triggerInlineSuggestions', - label: nls.localize('triggerInlineSuggestionsAction', "Trigger Inline Suggestions"), - alias: 'Trigger Inline Suggestions', + id: 'editor.action.triggerInlineCompletions', + label: nls.localize('triggerInlineCompletionsAction', "Trigger Inline Completions"), + alias: 'Trigger Inline Completions', precondition: EditorContextKeys.writable }); } public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise { - const controller = InlineSuggestionsController.get(editor); + const controller = InlineCompletionsController.get(editor); if (controller) { controller.trigger(); } } } -export interface NormalizedInlineSuggestion extends InlineSuggestion { - replaceRange: Range; +export interface NormalizedInlineCompletion extends InlineCompletion { + range: Range; } -export interface NormalizedInlineSuggestions extends InlineSuggestions { +export interface NormalizedInlineCompletions extends InlineCompletions { } function getDefaultRange(position: Position, model: ITextModel): Range { @@ -610,27 +610,28 @@ function getDefaultRange(position: Position, model: ITextModel): Range { : Range.fromPositions(position, position.with(undefined, maxColumn)); } -async function provideInlineSuggestions( +async function provideInlineCompletions( position: Position, model: ITextModel, + context: InlineCompletionContext, token: CancellationToken = CancellationToken.None -): Promise { +): Promise { - console.log(`provideInlineSuggestions at ${position}`); + console.log(`provideInlineCompletions at ${position}`); const defaultReplaceRange = getDefaultRange(position, model); - const providers = InlineSuggestionsProviderRegistry.all(model); + const providers = InlineCompletionsProviderRegistry.all(model); const results = await Promise.all( - providers.map(provider => provider.provideInlineSuggestions(model, position, token)) + providers.map(provider => provider.provideInlineCompletions(model, position, context, token)) ); - const items = new Array(); + const items = new Array(); for (const result of results) { if (result) { - items.push(...result.items.map(item => ({ + items.push(...result.items.map(item => ({ text: item.text, - replaceRange: item.replaceRange ? Range.lift(item.replaceRange) : defaultReplaceRange + range: item.range ? Range.lift(item.range) : defaultReplaceRange }))); } } @@ -638,11 +639,11 @@ async function provideInlineSuggestions( return { items }; } -const InlineSuggestionCommand = EditorCommand.bindToContribution(InlineSuggestionsController.get); +const InlineCompletionCommand = EditorCommand.bindToContribution(InlineCompletionsController.get); -registerEditorCommand(new InlineSuggestionCommand({ - id: 'commitInlineSuggestion', - precondition: InlineSuggestionsController.inlineSuggestionVisible, +registerEditorCommand(new InlineCompletionCommand({ + id: 'commitInlineCompletion', + precondition: InlineCompletionsController.inlineCompletionVisible, kbOpts: { weight: 100, primary: KeyCode.Tab, @@ -651,9 +652,9 @@ registerEditorCommand(new InlineSuggestionCommand({ x.commit(); } })); -registerEditorCommand(new InlineSuggestionCommand({ - id: 'hideInlineSuggestion', - precondition: InlineSuggestionsController.inlineSuggestionVisible, +registerEditorCommand(new InlineCompletionCommand({ + id: 'hideInlineCompletion', + precondition: InlineCompletionsController.inlineCompletionVisible, kbOpts: { weight: 100, primary: KeyCode.Escape, @@ -663,5 +664,5 @@ registerEditorCommand(new InlineSuggestionCommand({ } })); -registerEditorContribution(InlineSuggestionsController.ID, InlineSuggestionsController); +registerEditorContribution(InlineCompletionsController.ID, InlineCompletionsController); registerEditorAction(TriggerGhostTextAction); diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index b29738e6733..b4da8233937 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -625,6 +625,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { CompletionTriggerKind: standaloneEnums.CompletionTriggerKind, SignatureHelpTriggerKind: standaloneEnums.SignatureHelpTriggerKind, InlayHintKind: standaloneEnums.InlayHintKind, + InlineCompletionTriggerKind: standaloneEnums.InlineCompletionTriggerKind, // classes FoldingRangeKind: modes.FoldingRangeKind, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 757dbbc7fb6..6aa43588b02 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5823,6 +5823,29 @@ declare namespace monaco.languages { resolveCompletionItem?(item: CompletionItem, token: CancellationToken): ProviderResult; } + /** + * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ + export enum InlineCompletionTriggerKind { + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 0, + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Explicit = 1 + } + + export interface InlineCompletionContext { + /** + * How the completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; + } + export interface CodeAction { title: string; command?: Command; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 4d48d91a478..73adbd19046 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -3283,33 +3283,58 @@ declare module 'vscode' { //#region https://github.com/microsoft/vscode/issues/124024 @hediet @alexdima - export class InlineSuggestion { + export class InlineCompletionItem { + /** + * The text to insert. + * If the text contains a line break, the range must end at the end of a line. + * If existing text should be replaced, the existing text must be a prefix of the text to insert. + */ text: string; - replaceRange?: Range; + + /** + * The range to replace. + * Must begin and end on the same line. + */ + range?: Range; constructor(text: string); } - export class InlineSuggestions { - items: InlineSuggestion[]; - // incomplete: boolean; + export class InlineCompletionList { + items: InlineCompletionItem[]; - constructor(items: InlineSuggestion[]); + constructor(items: InlineCompletionItem[]); } - export interface InlineSuggestionsContext { + /** + * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ + export enum InlineCompletionTriggerKind { /** - * Communicates a preference on how many suggestions should be returned. - */ - preferredSuggestionCount: number; + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 0, + + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Explicit = 1, + } + export interface InlineCompletionContext { + /** + * How the completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; } - export interface InlineSuggestionsProvider { - provideInlineSuggestions(document: TextDocument, position: Position, token: CancellationToken/* , context: InlineSuggestionsContext */): ProviderResult; + export interface InlineCompletionItemProvider { + provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; } export namespace languages { - export function registerInlineSuggestionsProvider(selector: DocumentSelector, provider: InlineSuggestionsProvider): Disposable; + export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 7121262018b..0515a68fa54 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -499,10 +499,10 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha this._registrations.set(handle, modes.CompletionProviderRegistry.register(selector, provider)); } - $registerInlineSuggestionSupport(handle: number, selector: IDocumentFilterDto[]): void { - const provider: modes.InlineSuggestionsProvider = { - provideInlineSuggestions: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { - return this._proxy.$provideInlineSuggestions(handle, model.uri, position, token); + $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[]): void { + const provider: modes.InlineCompletionsProvider = { + provideInlineCompletions: async (model: ITextModel, position: EditorPosition, context: modes.InlineCompletionContext, token: CancellationToken): Promise => { + return this._proxy.$provideInlineCompletions(handle, model.uri, position, context, token); } }; this._registrations.set(handle, modes.InlineSuggestionsProviderRegistry.register(selector, provider)); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 0a8a19ee970..8818247826c 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -480,9 +480,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable { return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); }, - registerInlineSuggestionsProvider(selector: vscode.DocumentSelector, provider: vscode.InlineSuggestionsProvider): vscode.Disposable { + registerInlineCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider): vscode.Disposable { checkProposedApiEnabled(extension); - return extHostLanguageFeatures.registerInlineSuggestionsProvider(extension, checkSelector(selector), provider); + return extHostLanguageFeatures.registerInlineCompletionsProvider(extension, checkSelector(selector), provider); }, registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentLinkProvider(extension, checkSelector(selector), provider); @@ -1169,6 +1169,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I InlineValueText: extHostTypes.InlineValueText, InlineValueVariableLookup: extHostTypes.InlineValueVariableLookup, InlineValueEvaluatableExpression: extHostTypes.InlineValueEvaluatableExpression, + InlineCompletionTriggerKind: extHostTypes.InlineCompletionTriggerKind, EventEmitter: Emitter, ExtensionKind: extHostTypes.ExtensionKind, ExtensionMode: extHostTypes.ExtensionMode, @@ -1181,8 +1182,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I FoldingRange: extHostTypes.FoldingRange, FoldingRangeKind: extHostTypes.FoldingRangeKind, FunctionBreakpoint: extHostTypes.FunctionBreakpoint, - InlineSuggestion: extHostTypes.InlineSuggestion, - InlineSuggestions: extHostTypes.InlineSuggestions, + InlineCompletionItem: extHostTypes.InlineSuggestion, + InlineCompletionList: extHostTypes.InlineSuggestions, Hover: extHostTypes.Hover, IndentAction: languageConfiguration.IndentAction, Location: extHostTypes.Location, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8f6e77b0fe5..8356bae2cc6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -395,7 +395,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $emitDocumentSemanticTokensEvent(eventHandle: number): void; $registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void; $registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void; - $registerInlineSuggestionSupport(handle: number, selector: IDocumentFilterDto[]): void; + $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; $registerInlayHintsProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; $emitInlayHintsEvent(eventHandle: number, event?: any): void; @@ -1635,7 +1635,7 @@ export interface ExtHostLanguageFeaturesShape { $provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise; $resolveCompletionItem(handle: number, id: ChainedCacheId, token: CancellationToken): Promise; $releaseCompletionItems(handle: number, id: number): void; - $provideInlineSuggestions(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; + $provideInlineCompletions(handle: number, resource: UriComponents, position: IPosition, context: modes.InlineCompletionContext, token: CancellationToken): Promise; $provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise; $releaseSignatureHelp(handle: number, id: number): void; $provideInlayHints(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index bbe65ca0123..87af5a6f3ff 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1038,25 +1038,19 @@ class SuggestAdapter { } } -class InlineSuggestionsAdapter { +class InlineCompletionsAdapter { constructor( private readonly _documents: ExtHostDocuments, - private readonly _provider: vscode.InlineSuggestionsProvider, + private readonly _provider: vscode.InlineCompletionItemProvider, ) { } - async provideInlineSuggestions(resource: URI, position: IPosition, token: CancellationToken): Promise { + async provideInlineCompletions(resource: URI, position: IPosition, context: vscode.InlineCompletionContext, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); const pos = typeConvert.Position.to(position); - // TODO - //const wordRange = doc.getWordRangeAtPosition(pos); - //const start = wordRange ? typeConvert.Position.from(wordRange.start) : position; - //const end = typeConvert.Position.from(doc.lineAt(pos).range.end); - //const defaultReplaceRange = new Range(start, end); - - const result = await asPromise(() => this._provider.provideInlineSuggestions(doc, pos, token)); + const result = await asPromise(() => this._provider.provideInlineCompletionItems(doc, pos, context, token)); if (!result) { // undefined and null are valid results @@ -1072,7 +1066,7 @@ class InlineSuggestionsAdapter { return { items: result.items.map(item => ({ text: item.text, - replaceRange: item.replaceRange ? typeConvert.Range.from(item.replaceRange) : undefined, + replaceRange: item.range ? typeConvert.Range.from(item.range) : undefined, })), }; } @@ -1395,7 +1389,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter | InlineValuesAdapter - | LinkedEditingRangeAdapter | InlayHintsAdapter | InlineSuggestionsAdapter; + | LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionsAdapter; class AdapterData { constructor( @@ -1851,14 +1845,14 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- ghost test - registerInlineSuggestionsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineSuggestionsProvider): vscode.Disposable { - const handle = this._addNewAdapter(new InlineSuggestionsAdapter(this._documents, provider), extension); - this._proxy.$registerInlineSuggestionSupport(handle, this._transformDocumentSelector(selector)); + registerInlineCompletionsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider): vscode.Disposable { + const handle = this._addNewAdapter(new InlineCompletionsAdapter(this._documents, provider), extension); + this._proxy.$registerInlineCompletionsSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } - $provideInlineSuggestions(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, InlineSuggestionsAdapter, adapter => adapter.provideInlineSuggestions(URI.revive(resource), position, token), undefined); + $provideInlineCompletions(handle: number, resource: UriComponents, position: IPosition, context: modes.InlineCompletionContext, token: CancellationToken): Promise { + return this._withAdapter(handle, InlineCompletionsAdapter, adapter => adapter.provideInlineCompletions(URI.revive(resource), position, context, token), undefined); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index a9b58ff2ae1..72271993685 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1548,10 +1548,10 @@ export class CompletionList { } @es5ClassCompat -export class InlineSuggestion implements vscode.InlineSuggestion { +export class InlineSuggestion implements vscode.InlineCompletionItem { text: string; - replaceRange?: Range; + range?: Range; constructor(text: string) { this.text = text; @@ -1559,10 +1559,10 @@ export class InlineSuggestion implements vscode.InlineSuggestion { } @es5ClassCompat -export class InlineSuggestions implements vscode.InlineSuggestions { - items: vscode.InlineSuggestion[]; +export class InlineSuggestions implements vscode.InlineCompletionList { + items: vscode.InlineCompletionItem[]; - constructor(items: vscode.InlineSuggestion[]) { + constructor(items: vscode.InlineCompletionItem[]) { this.items = items; } } @@ -2458,6 +2458,11 @@ export class EvaluatableExpression implements vscode.EvaluatableExpression { } } +export enum InlineCompletionTriggerKind { + Automatic = 0, + Explicit = 1, +} + @es5ClassCompat export class InlineValueText implements vscode.InlineValueText { readonly range: Range;