diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index e4a3388c8e58e..2729d4014e684 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -788,30 +788,34 @@ export interface InlineCompletion { * The text can also be a snippet. In that case, a preview with default parameters is shown. * When accepting the suggestion, the full snippet is inserted. */ - readonly insertText: string | { snippet: string }; + readonly insertText: string | { snippet: string } | undefined; /** - * A text that is used to decide if this inline completion should be shown. - * An inline completion is shown if the text to replace is a subword of the filter text. - */ - readonly filterText?: string; + * The range to replace. + * Must begin and end on the same line. + * Refers to the current document or `uri` if provided. + */ + readonly range?: IRange; /** * An optional array of additional text edits that are applied when * selecting this completion. Edits must not overlap with the main edit * nor with themselves. + * Refers to the current document or `uri` if provided. */ readonly additionalTextEdits?: ISingleEditOperation[]; /** - * The range to replace. - * Must begin and end on the same line. + * The file for which the edit applies to. */ - readonly range?: IRange; + readonly uri?: UriComponents; + /** + * A command that is run upon acceptance of this item. + */ readonly command?: Command; - readonly action?: Command; + readonly gutterMenuLinkAction?: Command; /** * Is called the first time an inline completion is shown. @@ -828,11 +832,12 @@ export interface InlineCompletion { readonly isInlineEdit?: boolean; readonly showInlineEditMenu?: boolean; + /** Only show the inline suggestion when the cursor is in the showRange. */ readonly showRange?: IRange; readonly warning?: InlineCompletionWarning; - readonly displayLocation?: InlineCompletionDisplayLocation; + readonly hint?: InlineCompletionHint; /** * Used for telemetry. @@ -845,21 +850,20 @@ export interface InlineCompletionWarning { icon?: IconPath; } -export enum InlineCompletionDisplayLocationKind { +export enum InlineCompletionHintStyle { Code = 1, Label = 2 } -export interface InlineCompletionDisplayLocation { +export interface InlineCompletionHint { + /** Refers to the current document. */ range: IRange; - kind: InlineCompletionDisplayLocationKind; - label: string; + style: InlineCompletionHintStyle; + content: string; jumpToEdit: boolean; } -/** - * TODO: add `| URI | { light: URI; dark: URI }`. -*/ +// TODO: add `| URI | { light: URI; dark: URI }`. export type IconPath = ThemeIcon; export interface InlineCompletions { diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index acecb4a41c48f..1caf3ced18fa7 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -437,17 +437,17 @@ export enum InlayHintKind { Parameter = 2 } -export enum InlineCompletionDisplayLocationKind { - Code = 1, - Label = 2 -} - export enum InlineCompletionEndOfLifeReasonKind { Accepted = 0, Rejected = 1, Ignored = 2 } +export enum InlineCompletionHintStyle { + Code = 1, + Label = 2 +} + /** * How an {@link InlineCompletionsProvider inline completion provider} was triggered. */ diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index 7e1951e8e9d88..defa17938d13e 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -753,7 +753,7 @@ export class InlineCompletionsModel extends Disposable { return false; } - if (state.inlineCompletion.displayLocation) { + if (state.inlineCompletion.hint) { return false; } @@ -803,7 +803,7 @@ export class InlineCompletionsModel extends Disposable { return true; } - if (this._inAcceptFlow.read(reader) && this._appearedInsideViewport.read(reader) && !s.inlineCompletion.displayLocation?.jumpToEdit) { + if (this._inAcceptFlow.read(reader) && this._appearedInsideViewport.read(reader) && !s.inlineCompletion.hint?.jumpToEdit) { return false; } @@ -821,7 +821,7 @@ export class InlineCompletionsModel extends Disposable { if (this._tabShouldIndent.read(reader)) { return false; } - if (this._inAcceptFlow.read(reader) && this._appearedInsideViewport.read(reader) && !s.inlineCompletion.displayLocation?.jumpToEdit) { + if (this._inAcceptFlow.read(reader) && this._appearedInsideViewport.read(reader) && !s.inlineCompletion.hint?.jumpToEdit) { return true; } if (s.inlineCompletion.targetRange.startLineNumber === this._editorObs.cursorLineNumber.read(reader)) { @@ -925,7 +925,7 @@ export class InlineCompletionsModel extends Disposable { editor.edit(edit, this._getMetadata(completion, this.textModel.getLanguageId())); - if (completion.displayLocation === undefined) { + if (completion.hint === undefined) { // do not move the cursor when the completion is displayed in a different location editor.setSelections(state.kind === 'inlineEdit' ? selections.slice(-1) : selections, 'inlineCompletionAccept'); } @@ -1105,7 +1105,7 @@ export class InlineCompletionsModel extends Disposable { this._editor.setPosition(targetPosition, 'inlineCompletions.jump'); // TODO: consider using view information to reveal it - const isSingleLineChange = targetRange.isSingleLine() && (s.inlineCompletion.displayLocation || !s.inlineCompletion.insertText.includes('\n')); + const isSingleLineChange = targetRange.isSingleLine() && (s.inlineCompletion.hint || !s.inlineCompletion.insertText.includes('\n')); if (isSingleLineChange) { this._editor.revealPosition(targetPosition); } else { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts index 0e5b5cba9de78..80f78e37d5941 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts @@ -270,7 +270,7 @@ export class InlineCompletionsSource extends Disposable { const result = suggestions.map(c => ({ range: c.editRange.toString(), text: c.insertText, - displayLocation: c.displayLocation ? { label: c.displayLocation.label, range: c.displayLocation.range.toString(), kind: c.displayLocation.kind, jumpToEdit: c.displayLocation.jumpToEdit } : undefined, + hint: c.hint, isInlineEdit: c.isInlineEdit, showInlineEditMenu: c.showInlineEditMenu, providerId: c.source.provider.providerId?.toString(), diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts index 0d7a85d3fb1bc..6444e2040b2f1 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts @@ -7,6 +7,7 @@ import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { matchesSubString } from '../../../../../base/common/filters.js'; import { IObservable, ITransaction, observableSignal, observableValue } from '../../../../../base/common/observable.js'; import { commonPrefixLength, commonSuffixLength, splitLines } from '../../../../../base/common/strings.js'; +import { URI } from '../../../../../base/common/uri.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { ISingleEditOperation } from '../../../../common/core/editOperation.js'; import { applyEditsToRanges, StringEdit, StringReplacement } from '../../../../common/core/edits/stringEdit.js'; @@ -19,11 +20,11 @@ import { getPositionOffsetTransformerFromTextModel } from '../../../../common/co import { PositionOffsetTransformerBase } from '../../../../common/core/text/positionToOffset.js'; import { TextLength } from '../../../../common/core/text/textLength.js'; import { linesDiffComputers } from '../../../../common/diff/linesDiffComputers.js'; -import { Command, InlineCompletion, InlineCompletionDisplayLocationKind, InlineCompletionEndOfLifeReason, InlineCompletionTriggerKind, InlineCompletionWarning, PartialAcceptInfo } from '../../../../common/languages.js'; +import { Command, InlineCompletion, InlineCompletionHintStyle, InlineCompletionEndOfLifeReason, InlineCompletionTriggerKind, InlineCompletionWarning, PartialAcceptInfo, InlineCompletionHint } from '../../../../common/languages.js'; import { EndOfLinePreference, ITextModel } from '../../../../common/model.js'; import { TextModelText } from '../../../../common/model/textModelText.js'; import { InlineCompletionViewData, InlineCompletionViewKind } from '../view/inlineEdits/inlineEditsViewInterface.js'; -import { IDisplayLocation, InlineSuggestData, InlineSuggestionList, PartialAcceptance, SnippetInfo } from './provideInlineCompletions.js'; +import { InlineSuggestData, InlineSuggestionList, PartialAcceptance, SnippetInfo } from './provideInlineCompletions.js'; import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js'; export type InlineSuggestionItem = InlineEditItem | InlineCompletionItem; @@ -33,7 +34,7 @@ export namespace InlineSuggestionItem { data: InlineSuggestData, textModel: ITextModel, ): InlineSuggestionItem { - if (!data.isInlineEdit) { + if (!data.isInlineEdit && !data.uri) { return InlineCompletionItem.create(data, textModel); } else { return InlineEditItem.create(data, textModel); @@ -45,7 +46,7 @@ abstract class InlineSuggestionItemBase { constructor( protected readonly _data: InlineSuggestData, public readonly identity: InlineSuggestionIdentity, - public readonly displayLocation: InlineSuggestDisplayLocation | undefined + public readonly hint: InlineSuggestHint | undefined ) { } /** @@ -57,10 +58,10 @@ abstract class InlineSuggestionItemBase { public get isFromExplicitRequest(): boolean { return this._data.context.triggerKind === InlineCompletionTriggerKind.Explicit; } public get forwardStable(): boolean { return this.source.inlineSuggestions.enableForwardStability ?? false; } public get editRange(): Range { return this.getSingleTextEdit().range; } - public get targetRange(): Range { return this.displayLocation?.range && !this.displayLocation.jumpToEdit ? this.displayLocation?.range : this.editRange; } + public get targetRange(): Range { return this.hint?.range && !this.hint.jumpToEdit ? this.hint?.range : this.editRange; } public get insertText(): string { return this.getSingleTextEdit().text; } public get semanticId(): string { return this.hash; } - public get action(): Command | undefined { return this._sourceInlineCompletion.action; } + public get action(): Command | undefined { return this._sourceInlineCompletion.gutterMenuLinkAction; } public get command(): Command | undefined { return this._sourceInlineCompletion.command; } public get warning(): InlineCompletionWarning | undefined { return this._sourceInlineCompletion.warning; } public get showInlineEditMenu(): boolean { return !!this._sourceInlineCompletion.showInlineEditMenu; } @@ -163,25 +164,25 @@ export class InlineSuggestionIdentity { } } -class InlineSuggestDisplayLocation implements IDisplayLocation { +export class InlineSuggestHint { - public static create(displayLocation: IDisplayLocation) { - return new InlineSuggestDisplayLocation( - displayLocation.range, - displayLocation.label, - displayLocation.kind, + public static create(displayLocation: InlineCompletionHint) { + return new InlineSuggestHint( + Range.lift(displayLocation.range), + displayLocation.content, + displayLocation.style, displayLocation.jumpToEdit ); } private constructor( public readonly range: Range, - public readonly label: string, - public readonly kind: InlineCompletionDisplayLocationKind, + public readonly content: string, + public readonly style: InlineCompletionHintStyle, public readonly jumpToEdit: boolean, ) { } - public withEdit(edit: StringEdit, positionOffsetTransformer: PositionOffsetTransformerBase): InlineSuggestDisplayLocation | undefined { + public withEdit(edit: StringEdit, positionOffsetTransformer: PositionOffsetTransformerBase): InlineSuggestHint | undefined { const offsetRange = new OffsetRange( positionOffsetTransformer.getOffset(this.range.getStartPosition()), positionOffsetTransformer.getOffset(this.range.getEndPosition()) @@ -194,7 +195,7 @@ class InlineSuggestDisplayLocation implements IDisplayLocation { const newRange = positionOffsetTransformer.getRange(newOffsetRange); - return new InlineSuggestDisplayLocation(newRange, this.label, this.kind, this.jumpToEdit); + return new InlineSuggestHint(newRange, this.content, this.style, this.jumpToEdit); } } @@ -212,7 +213,7 @@ export class InlineCompletionItem extends InlineSuggestionItemBase { const trimmedEdit = edit.removeCommonSuffixAndPrefix(textModel.getValue()); const textEdit = transformer.getTextReplacement(edit); - const displayLocation = data.displayLocation ? InlineSuggestDisplayLocation.create(data.displayLocation) : undefined; + const displayLocation = data.hint ? InlineSuggestHint.create(data.hint) : undefined; return new InlineCompletionItem(edit, trimmedEdit, textEdit, textEdit.range, data.snippetInfo, data.additionalTextEdits, data, identity, displayLocation); } @@ -229,7 +230,7 @@ export class InlineCompletionItem extends InlineSuggestionItemBase { data: InlineSuggestData, identity: InlineSuggestionIdentity, - displayLocation: InlineSuggestDisplayLocation | undefined, + displayLocation: InlineSuggestHint | undefined, ) { super(data, identity, displayLocation); } @@ -250,7 +251,7 @@ export class InlineCompletionItem extends InlineSuggestionItemBase { this.additionalTextEdits, this._data, identity, - this.displayLocation + this.hint ); } @@ -263,7 +264,7 @@ export class InlineCompletionItem extends InlineSuggestionItemBase { const positionOffsetTransformer = getPositionOffsetTransformerFromTextModel(textModel); const newTextEdit = positionOffsetTransformer.getTextReplacement(newEdit); - let newDisplayLocation = this.displayLocation; + let newDisplayLocation = this.hint; if (newDisplayLocation) { newDisplayLocation = newDisplayLocation.withEdit(textModelEdit, positionOffsetTransformer); if (!newDisplayLocation) { @@ -358,8 +359,8 @@ export class InlineEditItem extends InlineSuggestionItemBase { const replacedText = textModel.getValueInRange(replacedRange); return SingleUpdatedNextEdit.create(edit, replacedText); }); - const displayLocation = data.displayLocation ? InlineSuggestDisplayLocation.create(data.displayLocation) : undefined; - return new InlineEditItem(offsetEdit, singleTextEdit, data, identity, edits, displayLocation, false, textModel.getVersionId()); + const hint = data.hint ? InlineSuggestHint.create(data.hint) : undefined; + return new InlineEditItem(offsetEdit, singleTextEdit, data.uri, data, identity, edits, hint, false, textModel.getVersionId()); } public readonly snippetInfo: SnippetInfo | undefined = undefined; @@ -369,16 +370,17 @@ export class InlineEditItem extends InlineSuggestionItemBase { private constructor( private readonly _edit: StringEdit, private readonly _textEdit: TextReplacement, + public readonly uri: URI | undefined, data: InlineSuggestData, identity: InlineSuggestionIdentity, private readonly _edits: readonly SingleUpdatedNextEdit[], - displayLocation: InlineSuggestDisplayLocation | undefined, + hint: InlineSuggestHint | undefined, private readonly _lastChangePartOfInlineEdit = false, private readonly _inlineEditModelVersion: number, ) { - super(data, identity, displayLocation); + super(data, identity, hint); } public get updatedEditModelVersion(): number { return this._inlineEditModelVersion; } @@ -392,10 +394,11 @@ export class InlineEditItem extends InlineSuggestionItemBase { return new InlineEditItem( this._edit, this._textEdit, + this.uri, this._data, identity, this._edits, - this.displayLocation, + this.hint, this._lastChangePartOfInlineEdit, this._inlineEditModelVersion, ); @@ -439,7 +442,7 @@ export class InlineEditItem extends InlineSuggestionItemBase { const positionOffsetTransformer = getPositionOffsetTransformerFromTextModel(textModel); const newTextEdit = positionOffsetTransformer.getTextEdit(newEdit).toReplacement(new TextModelText(textModel)); - let newDisplayLocation = this.displayLocation; + let newDisplayLocation = this.hint; if (newDisplayLocation) { newDisplayLocation = newDisplayLocation.withEdit(textModelChanges, positionOffsetTransformer); if (!newDisplayLocation) { @@ -450,6 +453,7 @@ export class InlineEditItem extends InlineSuggestionItemBase { return new InlineEditItem( newEdit, newTextEdit, + this.uri, this._data, this.identity, edits, diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts index c72db52c8499c..2a8813dca58c3 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts @@ -16,7 +16,7 @@ import { OffsetRange } from '../../../../common/core/ranges/offsetRange.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; import { TextReplacement } from '../../../../common/core/edits/textEdit.js'; -import { InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletionDisplayLocationKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, PartialAcceptInfo, InlineCompletionsDisposeReason, LifetimeSummary, ProviderId } from '../../../../common/languages.js'; +import { InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, PartialAcceptInfo, InlineCompletionsDisposeReason, LifetimeSummary, ProviderId, InlineCompletionHint } from '../../../../common/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { ITextModel } from '../../../../common/model.js'; import { fixBracketsInLine } from '../../../../common/model/bracketPairsTextModelPart/fixBrackets.js'; @@ -29,6 +29,7 @@ import { InlineCompletionViewData, InlineCompletionViewKind } from '../view/inli import { isDefined } from '../../../../../base/common/types.js'; import { inlineCompletionIsVisible } from './inlineSuggestionItem.js'; import { EditDeltaInfo } from '../../../../common/textModelEditSource.js'; +import { URI } from '../../../../../base/common/uri.js'; export type InlineCompletionContextWithoutUuid = Omit; @@ -74,12 +75,14 @@ export function provideInlineCompletions( const result = await queryProvider.get(p); if (result) { for (const item of result.inlineSuggestions.items) { - if (item.isInlineEdit || typeof item.insertText !== 'string') { + if (item.isInlineEdit || typeof item.insertText !== 'string' && item.insertText !== undefined) { return undefined; } - const t = new TextReplacement(Range.lift(item.range) ?? defaultReplaceRange, item.insertText); - if (inlineCompletionIsVisible(t, undefined, model, position)) { - return undefined; + if (item.insertText !== undefined) { + const t = new TextReplacement(Range.lift(item.range) ?? defaultReplaceRange, item.insertText); + if (inlineCompletionIsVisible(t, undefined, model, position)) { + return undefined; + } } // else: inline completion is not visible, so lets not block @@ -194,6 +197,10 @@ function toInlineSuggestData( } snippetInfo = undefined; + } else if (inlineCompletion.insertText === undefined) { + insertText = ''; // TODO use undefined + snippetInfo = undefined; + range = new Range(1, 1, 1, 1); } else if ('snippet' in inlineCompletion.insertText) { const preBracketCompletionLength = inlineCompletion.insertText.snippet.length; @@ -228,18 +235,12 @@ function toInlineSuggestData( assertNever(inlineCompletion.insertText); } - const displayLocation = inlineCompletion.displayLocation ? { - range: Range.lift(inlineCompletion.displayLocation.range), - label: inlineCompletion.displayLocation.label, - kind: inlineCompletion.displayLocation.kind, - jumpToEdit: inlineCompletion.displayLocation.jumpToEdit, - } : undefined; - return new InlineSuggestData( range, insertText, snippetInfo, - displayLocation, + URI.revive(inlineCompletion.uri), + inlineCompletion.hint, inlineCompletion.additionalTextEdits || getReadonlyEmptyArray(), inlineCompletion, source, @@ -298,7 +299,8 @@ export class InlineSuggestData { public readonly range: Range, public readonly insertText: string, public readonly snippetInfo: SnippetInfo | undefined, - public readonly displayLocation: IDisplayLocation | undefined, + public readonly uri: URI | undefined, + public readonly hint: InlineCompletionHint | undefined, public readonly additionalTextEdits: readonly ISingleEditOperation[], public readonly sourceInlineCompletion: InlineCompletion, @@ -463,13 +465,6 @@ export interface SnippetInfo { range: Range; } -export interface IDisplayLocation { - range: Range; - label: string; - kind: InlineCompletionDisplayLocationKind; - jumpToEdit: boolean; -} - export enum InlineCompletionEditorType { TextEditor = 'textEditor', DiffEditor = 'diffEditor', diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts index 96bc17bdbab52..e4fdbbe74480c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts @@ -36,7 +36,7 @@ export class InlineEditWithChanges { public readonly cursorPosition: Position, public readonly multiCursorPositions: readonly Position[], public readonly commands: readonly InlineCompletionCommand[], - public readonly inlineCompletion: InlineSuggestionItem + public readonly inlineCompletion: InlineSuggestionItem, ) { } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts index c475f9b654597..b387c46ca1ceb 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts @@ -11,9 +11,9 @@ import { observableCodeEditor } from '../../../../../browser/observableCodeEdito import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { TextEdit } from '../../../../../common/core/edits/textEdit.js'; import { StringText } from '../../../../../common/core/text/abstractText.js'; -import { Command, InlineCompletionCommand, InlineCompletionDisplayLocation } from '../../../../../common/languages.js'; +import { Command, InlineCompletionCommand } from '../../../../../common/languages.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; -import { InlineCompletionItem } from '../../model/inlineSuggestionItem.js'; +import { InlineCompletionItem, InlineSuggestHint } from '../../model/inlineSuggestionItem.js'; import { IInlineEditHost, IInlineEditModel, InlineCompletionViewData, InlineCompletionViewKind, InlineEditTabAction } from './inlineEditsViewInterface.js'; import { InlineEditWithChanges } from './inlineEditWithChanges.js'; @@ -24,7 +24,7 @@ export class InlineEditModel implements IInlineEditModel { readonly extensionCommands: InlineCompletionCommand[]; readonly isInDiffEditor: boolean; - readonly displayLocation: InlineCompletionDisplayLocation | undefined; + readonly displayLocation: InlineSuggestHint | undefined; readonly showCollapsed: IObservable; constructor( @@ -37,7 +37,7 @@ export class InlineEditModel implements IInlineEditModel { this.extensionCommands = this.inlineEdit.inlineCompletion.source.inlineSuggestions.commands ?? []; this.isInDiffEditor = this._model.isInDiffEditor; - this.displayLocation = this.inlineEdit.inlineCompletion.displayLocation; + this.displayLocation = this.inlineEdit.inlineCompletion.hint; this.showCollapsed = this._model.showCollapsed; } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts index 330c49c7ca325..70324757f872f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts @@ -21,6 +21,7 @@ import { AbstractText, StringText } from '../../../../../common/core/text/abstra import { TextLength } from '../../../../../common/core/text/textLength.js'; import { DetailedLineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../../../../../common/diff/rangeMapping.js'; import { TextModel } from '../../../../../common/model/textModel.js'; +import { InlineEditItem } from '../../model/inlineSuggestionItem.js'; import { InlineEditsGutterIndicator } from './components/gutterIndicatorView.js'; import { InlineEditWithChanges } from './inlineEditWithChanges.js'; import { GhostTextIndicator, InlineEditHost, InlineEditModel } from './inlineEditsModel.js'; @@ -395,6 +396,10 @@ export class InlineEditsView extends Disposable { return this._previousView!.view; } + if (model.inlineEdit.inlineCompletion instanceof InlineEditItem && model.inlineEdit.inlineCompletion.uri) { + return InlineCompletionViewKind.Custom; + } + if (model.displayLocation && !model.inlineEdit.inlineCompletion.identity.jumpedTo.read(reader)) { return InlineCompletionViewKind.Custom; } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts index d93a10438dc53..3e225b6a7c4ef 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts @@ -6,7 +6,8 @@ import { IMouseEvent } from '../../../../../../base/browser/mouseEvent.js'; import { Event } from '../../../../../../base/common/event.js'; import { IObservable } from '../../../../../../base/common/observable.js'; -import { Command, InlineCompletionCommand, InlineCompletionDisplayLocation } from '../../../../../common/languages.js'; +import { Command, InlineCompletionCommand } from '../../../../../common/languages.js'; +import { InlineSuggestHint } from '../../model/inlineSuggestionItem.js'; import { InlineEditWithChanges } from './inlineEditWithChanges.js'; export enum InlineEditTabAction { @@ -34,7 +35,7 @@ export interface IInlineEditModel { inlineEdit: InlineEditWithChanges; tabAction: IObservable; showCollapsed: IObservable; - displayLocation: InlineCompletionDisplayLocation | undefined; + displayLocation: InlineSuggestHint | undefined; handleInlineEditShown(viewKind: string, viewData?: InlineCompletionViewData): void; accept(): void; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts index 2bd2e77ce714e..b6d96623118d8 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts @@ -16,9 +16,10 @@ import { LineSource, renderLines, RenderOptions } from '../../../../../../browse import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineRange } from '../../../../../../common/core/ranges/lineRange.js'; -import { InlineCompletionDisplayLocation, InlineCompletionDisplayLocationKind } from '../../../../../../common/languages.js'; +import { InlineCompletionHintStyle } from '../../../../../../common/languages.js'; import { ILanguageService } from '../../../../../../common/languages/language.js'; import { LineTokens, TokenArray } from '../../../../../../common/tokens/lineTokens.js'; +import { InlineSuggestHint } from '../../../model/inlineSuggestionItem.js'; import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getEditorBlendedColor, inlineEditIndicatorPrimaryBackground, inlineEditIndicatorSecondaryBackground, inlineEditIndicatorsuccessfulBackground } from '../theme.js'; import { getContentRenderWidth, maxContentWidthInRange, rectToProps } from '../utils/utils.js'; @@ -45,7 +46,7 @@ export class InlineEditsCustomView extends Disposable implements IInlineEditsVie constructor( private readonly _editor: ICodeEditor, - displayLocation: IObservable, + displayLocation: IObservable, tabAction: IObservable, @IThemeService themeService: IThemeService, @ILanguageService private readonly _languageService: ILanguageService, @@ -127,7 +128,7 @@ export class InlineEditsCustomView extends Disposable implements IInlineEditsVie return maxOriginalContent + maxModifiedContent + padding < editorWidth - editorContentLeft - editorVerticalScrollbar - minimapWidth; } - private getState(displayLocation: InlineCompletionDisplayLocation): { rect: IObservable; label: string; kind: InlineCompletionDisplayLocationKind } { + private getState(displayLocation: InlineSuggestHint): { rect: IObservable; label: string; kind: InlineCompletionHintStyle } { const contentState = derived(this, (reader) => { const startLineNumber = displayLocation.range.startLineNumber; @@ -154,7 +155,7 @@ export class InlineEditsCustomView extends Disposable implements IInlineEditsVie const startLineNumber = displayLocation.range.startLineNumber; const endLineNumber = displayLocation.range.endLineNumber; // only check viewport once in the beginning when rendering the view - const fitsInsideViewport = this.fitsInsideViewport(new LineRange(startLineNumber, endLineNumber + 1), displayLocation.label, undefined); + const fitsInsideViewport = this.fitsInsideViewport(new LineRange(startLineNumber, endLineNumber + 1), displayLocation.content, undefined); const rect = derived(this, reader => { const w = this._editorObs.getOption(EditorOption.fontInfo).read(reader).typicalHalfwidthCharacterWidth; @@ -208,7 +209,7 @@ export class InlineEditsCustomView extends Disposable implements IInlineEditsVie const textRect = Rect.fromLeftTopWidthHeight( contentLeft + contentStartOffset - scrollLeft, topOfLine - scrollTop, - w * displayLocation.label.length, + w * displayLocation.content.length, lineHeight ); @@ -217,17 +218,17 @@ export class InlineEditsCustomView extends Disposable implements IInlineEditsVie return { rect, - label: displayLocation.label, - kind: displayLocation.kind + label: displayLocation.content, + kind: displayLocation.style }; } - private getRendering(state: { rect: IObservable; label: string; kind: InlineCompletionDisplayLocationKind }, styles: IObservable<{ background: string; border: string }>) { + private getRendering(state: { rect: IObservable; label: string; kind: InlineCompletionHintStyle }, styles: IObservable<{ background: string; border: string }>) { const line = document.createElement('div'); const t = this._editor.getModel()!.tokenization.tokenizeLinesAt(1, [state.label])?.[0]; let tokens: LineTokens; - if (t && state.kind === InlineCompletionDisplayLocationKind.Code) { + if (t && state.kind === InlineCompletionHintStyle.Code) { tokens = TokenArray.fromLineTokens(t).toLineTokens(state.label, this._languageService.languageIdCodec); } else { tokens = LineTokens.createEmpty(state.label, this._languageService.languageIdCodec); diff --git a/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts b/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts index 45127df8f0e9a..ae310dc3616c5 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts @@ -33,7 +33,7 @@ class SuggestInlineCompletion implements InlineCompletion { readonly filterText: string, readonly additionalTextEdits: ISingleEditOperation[] | undefined, readonly command: Command | undefined, - readonly action: Command | undefined, + readonly gutterMenuLinkAction: Command | undefined, readonly completion: CompletionItem, ) { } } diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 91125ebc41a50..31b294e1d0339 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -842,7 +842,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { PartialAcceptTriggerKind: standaloneEnums.PartialAcceptTriggerKind, HoverVerbosityAction: standaloneEnums.HoverVerbosityAction, InlineCompletionEndOfLifeReasonKind: standaloneEnums.InlineCompletionEndOfLifeReasonKind, - InlineCompletionDisplayLocationKind: standaloneEnums.InlineCompletionDisplayLocationKind, + InlineCompletionHintStyle: standaloneEnums.InlineCompletionHintStyle, // classes FoldingRangeKind: languages.FoldingRangeKind, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 4767927d7bf9d..986248861e78e 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7512,25 +7512,29 @@ declare namespace monaco.languages { */ readonly insertText: string | { snippet: string; - }; + } | undefined; /** - * A text that is used to decide if this inline completion should be shown. - * An inline completion is shown if the text to replace is a subword of the filter text. - */ - readonly filterText?: string; + * The range to replace. + * Must begin and end on the same line. + * Refers to the current document or `uri` if provided. + */ + readonly range?: IRange; /** * An optional array of additional text edits that are applied when * selecting this completion. Edits must not overlap with the main edit * nor with themselves. + * Refers to the current document or `uri` if provided. */ readonly additionalTextEdits?: editor.ISingleEditOperation[]; /** - * The range to replace. - * Must begin and end on the same line. + * The file for which the edit applies to. + */ + readonly uri?: UriComponents; + /** + * A command that is run upon acceptance of this item. */ - readonly range?: IRange; readonly command?: Command; - readonly action?: Command; + readonly gutterMenuLinkAction?: Command; /** * Is called the first time an inline completion is shown. * @deprecated. Use `onDidShow` of the provider instead. @@ -7543,9 +7547,10 @@ declare namespace monaco.languages { readonly completeBracketPairs?: boolean; readonly isInlineEdit?: boolean; readonly showInlineEditMenu?: boolean; + /** Only show the inline suggestion when the cursor is in the showRange. */ readonly showRange?: IRange; readonly warning?: InlineCompletionWarning; - readonly displayLocation?: InlineCompletionDisplayLocation; + readonly hint?: InlineCompletionHint; /** * Used for telemetry. */ @@ -7557,21 +7562,19 @@ declare namespace monaco.languages { icon?: IconPath; } - export enum InlineCompletionDisplayLocationKind { + export enum InlineCompletionHintStyle { Code = 1, Label = 2 } - export interface InlineCompletionDisplayLocation { + export interface InlineCompletionHint { + /** Refers to the current document. */ range: IRange; - kind: InlineCompletionDisplayLocationKind; - label: string; + style: InlineCompletionHintStyle; + content: string; jumpToEdit: boolean; } - /** - * TODO: add `| Uri | { light: Uri; dark: Uri }`. - */ export type IconPath = editor.ThemeIcon; export interface InlineCompletions { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index fd3fcf4765392..86f4da85d7b19 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -36,7 +36,7 @@ import { ExtHostDiagnostics } from './extHostDiagnostics.js'; import { ExtHostDocuments } from './extHostDocuments.js'; import { ExtHostTelemetry, IExtHostTelemetry } from './extHostTelemetry.js'; import * as typeConvert from './extHostTypeConverters.js'; -import { CodeAction, CodeActionKind, CompletionList, DataTransfer, Disposable, DocumentDropOrPasteEditKind, DocumentSymbol, InlineCompletionsDisposeReasonKind, InlineCompletionDisplayLocationKind, InlineCompletionTriggerKind, InternalDataTransferItem, Location, NewSymbolNameTriggerKind, Range, SemanticTokens, SemanticTokensEdit, SemanticTokensEdits, SnippetString, SymbolInformation, SyntaxTokenType } from './extHostTypes.js'; +import { CodeAction, CodeActionKind, CompletionList, DataTransfer, Disposable, DocumentDropOrPasteEditKind, DocumentSymbol, InlineCompletionsDisposeReasonKind, InlineCompletionTriggerKind, InternalDataTransferItem, Location, NewSymbolNameTriggerKind, Range, SemanticTokens, SemanticTokensEdit, SemanticTokensEdits, SnippetString, SymbolInformation, SyntaxTokenType } from './extHostTypes.js'; import { Emitter } from '../../../base/common/event.js'; import { IInlineCompletionsUnificationState } from '../../services/inlineCompletions/common/inlineCompletionsUnification.js'; @@ -1426,20 +1426,19 @@ class InlineCompletionAdapter { const insertText = item.insertText; return ({ - insertText: typeof insertText === 'string' ? insertText : { snippet: insertText.value }, - filterText: item.filterText, + insertText: insertText ? (typeof insertText === 'string' ? insertText : { snippet: insertText.value }) : undefined, range: item.range ? typeConvert.Range.from(item.range) : undefined, showRange: (this._isAdditionsProposedApiEnabled && item.showRange) ? typeConvert.Range.from(item.showRange) : undefined, command, - action, + gutterMenuLinkAction: action, idx: idx, completeBracketPairs: this._isAdditionsProposedApiEnabled ? item.completeBracketPairs : false, isInlineEdit: this._isAdditionsProposedApiEnabled ? item.isInlineEdit : false, showInlineEditMenu: this._isAdditionsProposedApiEnabled ? item.showInlineEditMenu : false, - displayLocation: (item.displayLocation && this._isAdditionsProposedApiEnabled) ? { + hint: (item.displayLocation && this._isAdditionsProposedApiEnabled) ? { range: typeConvert.Range.from(item.displayLocation.range), - label: item.displayLocation.label, - kind: item.displayLocation.kind ? typeConvert.InlineCompletionDisplayLocationKind.from(item.displayLocation.kind) : InlineCompletionDisplayLocationKind.Code, + content: item.displayLocation.label, + style: item.displayLocation.kind ? typeConvert.InlineCompletionHintStyle.from(item.displayLocation.kind) : languages.InlineCompletionHintStyle.Code, jumpToEdit: item.displayLocation.jumpToEdit ?? false, } : undefined, warning: (item.warning && this._isAdditionsProposedApiEnabled) ? { @@ -1448,6 +1447,7 @@ class InlineCompletionAdapter { } : undefined, correlationId: this._isAdditionsProposedApiEnabled ? item.correlationId : undefined, suggestionId: undefined, + uri: (this._isAdditionsProposedApiEnabled && item.uri) ? item.uri : undefined, }); }), commands: commands.map(c => { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 72978c57bc62f..a8532f4a74909 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -3537,18 +3537,18 @@ export namespace InlineCompletionEndOfLifeReason { } } -export namespace InlineCompletionDisplayLocationKind { - export function from(value: vscode.InlineCompletionDisplayLocationKind): types.InlineCompletionDisplayLocationKind { +export namespace InlineCompletionHintStyle { + export function from(value: vscode.InlineCompletionDisplayLocationKind): languages.InlineCompletionHintStyle { if (value === types.InlineCompletionDisplayLocationKind.Label) { - return types.InlineCompletionDisplayLocationKind.Label; + return languages.InlineCompletionHintStyle.Label; } else { - return types.InlineCompletionDisplayLocationKind.Code; + return languages.InlineCompletionHintStyle.Code; } } - export function to(kind: languages.InlineCompletionDisplayLocationKind): types.InlineCompletionDisplayLocationKind { + export function to(kind: languages.InlineCompletionHintStyle): types.InlineCompletionDisplayLocationKind { switch (kind) { - case languages.InlineCompletionDisplayLocationKind.Label: + case languages.InlineCompletionHintStyle.Label: return types.InlineCompletionDisplayLocationKind.Label; default: return types.InlineCompletionDisplayLocationKind.Code; diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts index 2fad488e5615d..c999b7466f7ef 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts @@ -32,23 +32,8 @@ declare module 'vscode' { export const onDidChangeCompletionsUnificationState: Event; } - /** - * temporary: to be removed - */ - export interface InlineCompletionsUnificationState { - codeUnification: boolean; - modelUnification: boolean; - expAssignments: string[]; - } - export interface InlineCompletionItem { - /** - * If set to `true`, unopened closing brackets are removed and unclosed opening brackets are closed. - * Defaults to `false`. - */ - completeBracketPairs?: boolean; - - warning?: InlineCompletionWarning; + // insertText: string | SnippetString | undefined; /** If set to `true`, this item is treated as inline edit. */ isInlineEdit?: boolean; @@ -61,19 +46,29 @@ declare module 'vscode' { showInlineEditMenu?: boolean; + /** + * If set, specifies where insertText, filterText and range apply to. + */ + uri?: Uri; + + // TODO: rename to gutterMenuLinkAction action?: Command; displayLocation?: InlineCompletionDisplayLocation; /** Used for telemetry. Can be an arbitrary string. */ correlationId?: string; - } - export enum InlineCompletionDisplayLocationKind { - Code = 1, - Label = 2 + /** + * If set to `true`, unopened closing brackets are removed and unclosed opening brackets are closed. + * Defaults to `false`. + */ + completeBracketPairs?: boolean; + + warning?: InlineCompletionWarning; } + export interface InlineCompletionDisplayLocation { range: Range; kind: InlineCompletionDisplayLocationKind; @@ -81,6 +76,11 @@ declare module 'vscode' { jumpToEdit?: boolean; } + export enum InlineCompletionDisplayLocationKind { + Code = 1, + Label = 2 + } + export interface InlineCompletionWarning { message: MarkdownString | string; icon?: ThemeIcon; @@ -218,4 +218,13 @@ declare module 'vscode' { */ enableForwardStability?: boolean; } + + /** + * temporary: to be removed + */ + export interface InlineCompletionsUnificationState { + codeUnification: boolean; + modelUnification: boolean; + expAssignments: string[]; + } }