diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index e52022c055d..2362655f693 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -790,7 +790,7 @@ 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 text: string | { snippet: string }; + readonly insertText: string | { snippet: string }; /** * The range to replace. diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionToGhostText.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionToGhostText.ts index 7edf0d8480a..19d9593bc1a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionToGhostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionToGhostText.ts @@ -16,7 +16,7 @@ import { GhostText, GhostTextPart } from 'vs/editor/contrib/inlineCompletions/br */ export interface NormalizedInlineCompletion extends InlineCompletion { readonly range: Range; - readonly text: string; + readonly insertText: string; readonly snippetInfo: | { @@ -27,6 +27,33 @@ export interface NormalizedInlineCompletion extends InlineCompletion { | undefined; } +/** + * Shrinks the range if the text has a suffix/prefix that agrees with the text buffer. + * E.g. text buffer: `ab[cdef]ghi`, [...] is the replace range, `cxyzf` is the new text. + * Then the minimized inline completion has range `abc[de]fghi` and text `xyz`. + */ +export function minimizeInlineCompletion(model: ITextModel, inlineCompletion: NormalizedInlineCompletion): NormalizedInlineCompletion; +export function minimizeInlineCompletion(model: ITextModel, inlineCompletion: NormalizedInlineCompletion | undefined): NormalizedInlineCompletion | undefined; +export function minimizeInlineCompletion(model: ITextModel, inlineCompletion: NormalizedInlineCompletion | undefined): NormalizedInlineCompletion | undefined { + if (!inlineCompletion) { + return inlineCompletion; + } + const valueToReplace = model.getValueInRange(inlineCompletion.range); + const commonPrefixLen = strings.commonPrefixLength(valueToReplace, inlineCompletion.insertText); + const startOffset = model.getOffsetAt(inlineCompletion.range.getStartPosition()) + commonPrefixLen; + const start = model.getPositionAt(startOffset); + + const remainingValueToReplace = valueToReplace.substr(commonPrefixLen); + const commonSuffixLen = strings.commonSuffixLength(remainingValueToReplace, inlineCompletion.insertText); + const end = model.getPositionAt(Math.max(startOffset, model.getOffsetAt(inlineCompletion.range.getEndPosition()) - commonSuffixLen)); + + return { + range: Range.fromPositions(start, end), + insertText: inlineCompletion.insertText.substr(commonPrefixLen, inlineCompletion.insertText.length - commonPrefixLen - commonSuffixLen), + snippetInfo: inlineCompletion.snippetInfo + }; +} + export function normalizedInlineCompletionsEquals(a: NormalizedInlineCompletion | undefined, b: NormalizedInlineCompletion | undefined): boolean { if (a === b) { return true; @@ -34,7 +61,7 @@ export function normalizedInlineCompletionsEquals(a: NormalizedInlineCompletion if (!a || !b) { return false; } - return a.range.equalsRange(b.range) && a.text === b.text && a.command === b.command; + return a.range.equalsRange(b.range) && a.insertText === b.insertText && a.command === b.command; } /** @@ -67,7 +94,7 @@ export function inlineCompletionToGhostText( // inlineCompletion.text: '··foo' // ^^ suggestionAddedIndentationLength - const suggestionAddedIndentationLength = strings.getLeadingWhitespace(inlineCompletion.text).length; + const suggestionAddedIndentationLength = strings.getLeadingWhitespace(inlineCompletion.insertText).length; const replacedIndentation = sourceLine.substring(inlineCompletion.range.startColumn - 1, sourceIndentationLength); const rangeThatDoesNotReplaceIndentation = Range.fromPositions( @@ -76,15 +103,15 @@ export function inlineCompletionToGhostText( ); const suggestionWithoutIndentationChange = - inlineCompletion.text.startsWith(replacedIndentation) + inlineCompletion.insertText.startsWith(replacedIndentation) // Adds more indentation without changing existing indentation: We can add ghost text for this - ? inlineCompletion.text.substring(replacedIndentation.length) + ? inlineCompletion.insertText.substring(replacedIndentation.length) // Changes or removes existing indentation. Only add ghost text for the non-indentation part. - : inlineCompletion.text.substring(suggestionAddedIndentationLength); + : inlineCompletion.insertText.substring(suggestionAddedIndentationLength); inlineCompletion = { range: rangeThatDoesNotReplaceIndentation, - text: suggestionWithoutIndentationChange, + insertText: suggestionWithoutIndentationChange, command: inlineCompletion.command, snippetInfo: undefined, }; @@ -93,7 +120,7 @@ export function inlineCompletionToGhostText( // This is a single line string const valueToBeReplaced = textModel.getValueInRange(inlineCompletion.range); - const changes = cachingDiff(valueToBeReplaced, inlineCompletion.text); + const changes = cachingDiff(valueToBeReplaced, inlineCompletion.insertText); if (!changes) { // No ghost text in case the diff would be too slow to compute @@ -112,7 +139,7 @@ export function inlineCompletionToGhostText( } } - const previewStartInCompletionText = inlineCompletion.text.length - previewSuffixLength; + const previewStartInCompletionText = inlineCompletion.insertText.length - previewSuffixLength; for (const c of changes) { const insertColumn = inlineCompletion.range.startColumn + c.originalStart + c.originalLength; @@ -132,8 +159,8 @@ export function inlineCompletionToGhostText( const modifiedEnd = c.modifiedStart + c.modifiedLength; const nonPreviewTextEnd = Math.max(c.modifiedStart, Math.min(modifiedEnd, previewStartInCompletionText)); - const nonPreviewText = inlineCompletion.text.substring(c.modifiedStart, nonPreviewTextEnd); - const italicText = inlineCompletion.text.substring(nonPreviewTextEnd, Math.max(c.modifiedStart, modifiedEnd)); + const nonPreviewText = inlineCompletion.insertText.substring(c.modifiedStart, nonPreviewTextEnd); + const italicText = inlineCompletion.insertText.substring(nonPreviewTextEnd, Math.max(c.modifiedStart, modifiedEnd)); if (nonPreviewText.length > 0) { const lines = strings.splitLines(nonPreviewText); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index 307111e50b7..99e07396547 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -31,9 +31,7 @@ import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { assertNever } from 'vs/base/common/types'; -export class InlineCompletionsModel - extends Disposable - implements GhostTextWidgetModel { +export class InlineCompletionsModel extends Disposable implements GhostTextWidgetModel { protected readonly onDidChangeEmitter = new Emitter(); public readonly onDidChange = this.onDidChangeEmitter.event; @@ -53,12 +51,9 @@ export class InlineCompletionsModel private readonly editor: IActiveCodeEditor, private readonly cache: SharedInlineCompletionCache, @ICommandService private readonly commandService: ICommandService, - @ILanguageConfigurationService - private readonly languageConfigurationService: ILanguageConfigurationService, - @ILanguageFeaturesService - private readonly languageFeaturesService: ILanguageFeaturesService, - @ILanguageFeatureDebounceService - private readonly debounceService: ILanguageFeatureDebounceService + @ILanguageConfigurationService private readonly languageConfigurationService: ILanguageConfigurationService, + @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, + @ILanguageFeatureDebounceService private readonly debounceService: ILanguageFeatureDebounceService ) { super(); @@ -449,7 +444,7 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { this.editor.executeEdits( 'inlineSuggestion.accept', [ - EditOperation.replaceMove(completion.range, completion.text) + EditOperation.replaceMove(completion.range, completion.insertText) ] ); } @@ -532,7 +527,7 @@ export class SynchronizedInlineCompletionsCache extends Disposable { class CachedInlineCompletion { public readonly semanticId: string = JSON.stringify({ - text: this.inlineCompletion.text, + text: this.inlineCompletion.insertText, startLine: this.inlineCompletion.range.startLineNumber, startColumn: this.inlineCompletion.range.startColumn, command: this.inlineCompletion.command @@ -552,7 +547,7 @@ class CachedInlineCompletion { public toLiveInlineCompletion(): TrackedInlineCompletion | undefined { return { - text: this.inlineCompletion.text, + insertText: this.inlineCompletion.insertText, range: this.synchronizedRange, command: this.inlineCompletion.command, sourceProvider: this.inlineCompletion.sourceProvider, @@ -564,42 +559,6 @@ class CachedInlineCompletion { } } -/** - * A normalized inline completion that tracks which inline completion it has been constructed from. -*/ -export interface TrackedInlineCompletion extends NormalizedInlineCompletion { - sourceProvider: InlineCompletionsProvider; - - /** - * A reference to the original inline completion this inline completion has been constructed from. - * Used for event data to ensure referential equality. - */ - sourceInlineCompletion: InlineCompletion; - - /** - * A reference to the original inline completion list this inline completion has been constructed from. - * Used for event data to ensure referential equality. - */ - sourceInlineCompletions: InlineCompletions; -} - -/** - * Contains no duplicated items. -*/ -export interface TrackedInlineCompletions extends InlineCompletions { - dispose(): void; -} - -function getDefaultRange(position: Position, model: ITextModel): Range { - const word = model.getWordAtPosition(position); - const maxColumn = model.getLineMaxColumn(position.lineNumber); - // By default, always replace up until the end of the current line. - // This default might be subject to change! - return word - ? new Range(position.lineNumber, word.startColumn, position.lineNumber, maxColumn) - : Range.fromPositions(position, position.with(undefined, maxColumn)); -} - export async function provideInlineCompletions( registry: LanguageFeatureRegistry, position: Position, @@ -631,60 +590,63 @@ export async function provideInlineCompletions( const itemsByHash = new Map(); for (const result of results) { const completions = result.completions; - if (completions) { - for (const item of completions.items) { - const range = item.range ? Range.lift(item.range) : defaultReplaceRange; - - if (range.startLineNumber !== range.endLineNumber) { - // Ignore invalid ranges. - continue; - } - - const textOrSnippet = - languageConfigurationService && item.completeBracketPairs && typeof item.text === 'string' - ? closeBrackets( - item.text, - range.getStartPosition(), - model, - languageConfigurationService - ) - : item.text; - - let text: string; - let snippetInfo: { - snippet: string; - /* Could be different than the main range */ - range: Range; - } - | undefined; - - if (typeof textOrSnippet === 'string') { - text = textOrSnippet; - snippetInfo = undefined; - } else if ('snippet' in textOrSnippet) { - const snippet = new SnippetParser().parse(textOrSnippet.snippet); - text = snippet.toString(); - snippetInfo = { - snippet: textOrSnippet.snippet, - range: range - }; - } else { - assertNever(textOrSnippet); - } - - const trackedItem: TrackedInlineCompletion = ({ - text, - snippetInfo, - range, - command: item.command, - sourceProvider: result.provider, - sourceInlineCompletions: completions, - sourceInlineCompletion: item - }); - - itemsByHash.set(JSON.stringify({ text, range: item.range }), trackedItem); - } + if (!completions) { + continue; } + + for (const item of completions.items) { + const range = item.range ? Range.lift(item.range) : defaultReplaceRange; + + if (range.startLineNumber !== range.endLineNumber) { + // Ignore invalid ranges. + continue; + } + + const textOrSnippet = + languageConfigurationService && item.completeBracketPairs && typeof item.insertText === 'string' + ? closeBrackets( + item.insertText, + range.getStartPosition(), + model, + languageConfigurationService + ) + : item.insertText; + + let insertText: string; + let snippetInfo: { + snippet: string; + /* Could be different than the main range */ + range: Range; + } + | undefined; + + if (typeof textOrSnippet === 'string') { + insertText = textOrSnippet; + snippetInfo = undefined; + } else if ('snippet' in textOrSnippet) { + const snippet = new SnippetParser().parse(textOrSnippet.snippet); + insertText = snippet.toString(); + snippetInfo = { + snippet: textOrSnippet.snippet, + range: range + }; + } else { + assertNever(textOrSnippet); + } + + const trackedItem: TrackedInlineCompletion = ({ + insertText, + snippetInfo, + range, + command: item.command, + sourceProvider: result.provider, + sourceInlineCompletions: completions, + sourceInlineCompletion: item + }); + + itemsByHash.set(JSON.stringify({ insertText, range: item.range }), trackedItem); + } + } return { @@ -697,6 +659,42 @@ export async function provideInlineCompletions( }; } +/** + * A normalized inline completion that tracks which inline completion it has been constructed from. +*/ +export interface TrackedInlineCompletion extends NormalizedInlineCompletion { + sourceProvider: InlineCompletionsProvider; + + /** + * A reference to the original inline completion this inline completion has been constructed from. + * Used for event data to ensure referential equality. + */ + sourceInlineCompletion: InlineCompletion; + + /** + * A reference to the original inline completion list this inline completion has been constructed from. + * Used for event data to ensure referential equality. + */ + sourceInlineCompletions: InlineCompletions; +} + +/** + * Contains no duplicated items and can be disposed. +*/ +export interface TrackedInlineCompletions extends InlineCompletions { + dispose(): void; +} + +function getDefaultRange(position: Position, model: ITextModel): Range { + const word = model.getWordAtPosition(position); + const maxColumn = model.getLineMaxColumn(position.lineNumber); + // By default, always replace up until the end of the current line. + // This default might be subject to change! + return word + ? new Range(position.lineNumber, word.startColumn, position.lineNumber, maxColumn) + : Range.fromPositions(position, position.with(undefined, maxColumn)); +} + function closeBrackets(text: string, position: Position, model: ITextModel, languageConfigurationService: ILanguageConfigurationService): string { const lineStart = model.getLineContent(position.lineNumber).substring(0, position.column - 1); const newLine = lineStart + text; @@ -711,30 +709,3 @@ function closeBrackets(text: string, position: Position, model: ITextModel, lang return newText; } - -/** - * Shrinks the range if the text has a suffix/prefix that agrees with the text buffer. - * E.g. text buffer: `ab[cdef]ghi`, [...] is the replace range, `cxyzf` is the new text. - * Then the minimized inline completion has range `abc[de]fghi` and text `xyz`. - */ -export function minimizeInlineCompletion(model: ITextModel, inlineCompletion: NormalizedInlineCompletion): NormalizedInlineCompletion; -export function minimizeInlineCompletion(model: ITextModel, inlineCompletion: NormalizedInlineCompletion | undefined): NormalizedInlineCompletion | undefined; -export function minimizeInlineCompletion(model: ITextModel, inlineCompletion: NormalizedInlineCompletion | undefined): NormalizedInlineCompletion | undefined { - if (!inlineCompletion) { - return inlineCompletion; - } - const valueToReplace = model.getValueInRange(inlineCompletion.range); - const commonPrefixLen = commonPrefixLength(valueToReplace, inlineCompletion.text); - const startOffset = model.getOffsetAt(inlineCompletion.range.getStartPosition()) + commonPrefixLen; - const start = model.getPositionAt(startOffset); - - const remainingValueToReplace = valueToReplace.substr(commonPrefixLen); - const commonSuffixLen = commonSuffixLength(remainingValueToReplace, inlineCompletion.text); - const end = model.getPositionAt(Math.max(startOffset, model.getOffsetAt(inlineCompletion.range.getEndPosition()) - commonSuffixLen)); - - return { - range: Range.fromPositions(start, end), - text: inlineCompletion.text.substr(commonPrefixLen, inlineCompletion.text.length - commonPrefixLen - commonSuffixLen), - snippetInfo: inlineCompletion.snippetInfo - }; -} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts index e23a79d0667..44a5251651a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts @@ -15,8 +15,7 @@ import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { SnippetSession } from 'vs/editor/contrib/snippet/browser/snippetSession'; import { CompletionItem } from 'vs/editor/contrib/suggest/browser/suggest'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; -import { minimizeInlineCompletion } from './inlineCompletionsModel'; -import { NormalizedInlineCompletion, normalizedInlineCompletionsEquals } from './inlineCompletionToGhostText'; +import { minimizeInlineCompletion, NormalizedInlineCompletion, normalizedInlineCompletionsEquals } from './inlineCompletionToGhostText'; export interface SuggestWidgetState { /** @@ -101,8 +100,8 @@ export class SuggestWidgetInlineCompletionProvider extends Disposable { } const valid = rangeStartsWith(normalizedItemToPreselect.range, normalizedSuggestItem.range) && - normalizedItemToPreselect.text.startsWith(normalizedSuggestItem.text); - return { index, valid, prefixLength: normalizedSuggestItem.text.length, suggestItem }; + normalizedItemToPreselect.insertText.startsWith(normalizedSuggestItem.insertText); + return { index, valid, prefixLength: normalizedSuggestItem.insertText.length, suggestItem }; }) .filter(item => item && item.valid); @@ -228,7 +227,7 @@ function suggestionToSuggestItemInfo(suggestController: SuggestController, posit normalizedInlineCompletion: { // Dummy element, so that space is reserved, but no text is shown range: Range.fromPositions(position, position), - text: '', + insertText: '', snippetInfo: undefined, }, }; @@ -256,7 +255,7 @@ function suggestionToSuggestItemInfo(suggestController: SuggestController, posit isSnippetText, completionItemKind: item.completion.kind, normalizedInlineCompletion: { - text: insertText, + insertText: insertText, range: Range.fromPositions( position.delta(0, -info.overwriteBefore), position.delta(0, Math.max(info.overwriteAfter, 0)) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel.ts index dc7917d5556..47fa211d8b5 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel.ts @@ -12,8 +12,8 @@ import { InlineCompletionTriggerKind, SelectedSuggestionInfo } from 'vs/editor/c import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { SharedInlineCompletionCache } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextModel'; import { BaseGhostTextWidgetModel, GhostText } from './ghostText'; -import { minimizeInlineCompletion, provideInlineCompletions, UpdateOperation } from './inlineCompletionsModel'; -import { inlineCompletionToGhostText, NormalizedInlineCompletion } from './inlineCompletionToGhostText'; +import { provideInlineCompletions, UpdateOperation } from './inlineCompletionsModel'; +import { inlineCompletionToGhostText, minimizeInlineCompletion, NormalizedInlineCompletion } from './inlineCompletionToGhostText'; import { SuggestWidgetInlineCompletionProvider } from './suggestWidgetInlineCompletionProvider'; export class SuggestWidgetPreviewModel extends BaseGhostTextWidgetModel { @@ -86,7 +86,7 @@ export class SuggestWidgetPreviewModel extends BaseGhostTextWidgetModel { } const info: SelectedSuggestionInfo = { - text: state.selectedItem.normalizedInlineCompletion.text, + text: state.selectedItem.normalizedInlineCompletion.insertText, range: state.selectedItem.normalizedInlineCompletion.range, isSnippetText: state.selectedItem.isSnippetText, completionKind: state.selectedItem.completionItemKind, @@ -133,7 +133,7 @@ export class SuggestWidgetPreviewModel extends BaseGhostTextWidgetModel { const isAugmentedCompletionValid = augmentedCompletion && suggestInlineCompletion - && augmentedCompletion.text.startsWith(suggestInlineCompletion.text) + && augmentedCompletion.insertText.startsWith(suggestInlineCompletion.insertText) && augmentedCompletion.range.equalsRange(suggestInlineCompletion.range); if (!isSuggestionPreviewEnabled && !isAugmentedCompletionValid) { @@ -143,7 +143,7 @@ export class SuggestWidgetPreviewModel extends BaseGhostTextWidgetModel { // If the augmented completion is not valid and there is no suggest inline completion, we still show the augmented completion. const finalCompletion = isAugmentedCompletionValid ? augmentedCompletion : (suggestInlineCompletion || augmentedCompletion); - const inlineCompletionPreviewLength = isAugmentedCompletionValid ? finalCompletion!.text.length - suggestInlineCompletion.text.length : 0; + const inlineCompletionPreviewLength = isAugmentedCompletionValid ? finalCompletion!.insertText.length - suggestInlineCompletion.insertText.length : 0; const newGhostText = this.toGhostText(finalCompletion, inlineCompletionPreviewLength); return newGhostText; diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts index 24f4d7a7a7b..6a043563d97 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts @@ -35,7 +35,7 @@ suite('Inline Completions', () => { const options = ['prefix', 'subword'] as const; const result = {} as any; for (const option of options) { - result[option] = inlineCompletionToGhostText({ text: suggestion, snippetInfo: undefined, range }, tempModel, option)?.render(cleanedText, true); + result[option] = inlineCompletionToGhostText({ insertText: suggestion, snippetInfo: undefined, range }, tempModel, option)?.render(cleanedText, true); } tempModel.dispose(); @@ -124,7 +124,7 @@ suite('Inline Completions', () => { model.setActive(true); context.keyboardType('foo'); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 4) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) }); model.trigger(InlineCompletionTriggerKind.Explicit); await timeout(1000); @@ -144,7 +144,7 @@ suite('Inline Completions', () => { model.setActive(true); context.keyboardType('foo'); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 4) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) }); await timeout(1000); assert.deepStrictEqual(provider.getAndClearCallHistory(), [ @@ -162,12 +162,12 @@ suite('Inline Completions', () => { async ({ editor, editorViewModel, model, context }) => { model.setActive(true); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 4) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) }); context.keyboardType('foo'); model.trigger(InlineCompletionTriggerKind.Explicit); await timeout(1000); - provider.setReturnValue({ text: 'foobizz', range: new Range(1, 1, 1, 6) }); + provider.setReturnValue({ insertText: 'foobizz', range: new Range(1, 1, 1, 6) }); context.keyboardType('b'); context.keyboardType('i'); await timeout(1000); @@ -192,7 +192,7 @@ suite('Inline Completions', () => { model.setActive(true); context.keyboardType(' '); - provider.setReturnValue({ text: 'foo', range: new Range(1, 2, 1, 3) }); + provider.setReturnValue({ insertText: 'foo', range: new Range(1, 2, 1, 3) }); model.trigger(InlineCompletionTriggerKind.Explicit); await timeout(1000); @@ -217,7 +217,7 @@ suite('Inline Completions', () => { model.setActive(true); context.keyboardType('\t\t'); - provider.setReturnValue({ text: 'foo', range: new Range(1, 2, 1, 3) }); + provider.setReturnValue({ insertText: 'foo', range: new Range(1, 2, 1, 3) }); model.trigger(InlineCompletionTriggerKind.Explicit); await timeout(1000); @@ -242,7 +242,7 @@ suite('Inline Completions', () => { model.setActive(true); context.keyboardType('buzz '); - provider.setReturnValue({ text: 'foo', range: new Range(1, 6, 1, 7) }); + provider.setReturnValue({ insertText: 'foo', range: new Range(1, 6, 1, 7) }); model.trigger(InlineCompletionTriggerKind.Explicit); await timeout(1000); @@ -267,7 +267,7 @@ suite('Inline Completions', () => { model.setActive(true); context.keyboardType('foo'); - provider.setReturnValue({ text: 'foobar1', range: new Range(1, 1, 1, 4) }); + provider.setReturnValue({ insertText: 'foobar1', range: new Range(1, 1, 1, 4) }); model.trigger(InlineCompletionTriggerKind.Automatic); await timeout(1000); @@ -277,9 +277,9 @@ suite('Inline Completions', () => { ); provider.setReturnValues([ - { text: 'foobar1', range: new Range(1, 1, 1, 4) }, - { text: 'foobizz2', range: new Range(1, 1, 1, 4) }, - { text: 'foobuzz3', range: new Range(1, 1, 1, 4) } + { insertText: 'foobar1', range: new Range(1, 1, 1, 4) }, + { insertText: 'foobizz2', range: new Range(1, 1, 1, 4) }, + { insertText: 'foobuzz3', range: new Range(1, 1, 1, 4) } ]); model.showNext(); @@ -351,7 +351,7 @@ suite('Inline Completions', () => { context.keyboardType('foo'); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 4) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) }); await timeout(1000); for (let j = 0; j < 2; j++) { @@ -378,7 +378,7 @@ suite('Inline Completions', () => { async ({ editor, editorViewModel, model, context }) => { model.setActive(true); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 4) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) }); context.keyboardType('foo'); model.trigger(InlineCompletionTriggerKind.Automatic); await timeout(1000); @@ -387,7 +387,7 @@ suite('Inline Completions', () => { ]); assert.deepStrictEqual(context.getAndClearViewStates(), ['', 'foo[bar]']); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 5) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 5) }); context.keyboardType('b'); assert.deepStrictEqual(context.currentPrettyViewState, 'foob[ar]'); await timeout(1000); @@ -396,7 +396,7 @@ suite('Inline Completions', () => { ]); assert.deepStrictEqual(context.getAndClearViewStates(), ['foob[ar]']); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 6) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 6) }); context.keyboardType('a'); assert.deepStrictEqual(context.currentPrettyViewState, 'fooba[r]'); await timeout(1000); @@ -416,7 +416,7 @@ suite('Inline Completions', () => { { fakeClock: true, provider }, async ({ editor, editorViewModel, model, context }) => { model.setActive(true); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 4) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) }); context.keyboardType('foo'); model.trigger(InlineCompletionTriggerKind.Explicit); await timeout(100); @@ -425,7 +425,7 @@ suite('Inline Completions', () => { ]); assert.deepStrictEqual(context.getAndClearViewStates(), ['', 'foo[bar]']); - provider.setReturnValue({ text: 'foobaz', range: new Range(1, 1, 1, 5) }); + provider.setReturnValue({ insertText: 'foobaz', range: new Range(1, 1, 1, 5) }); context.keyboardType('b'); assert.deepStrictEqual(context.currentPrettyViewState, 'foob[ar]'); await timeout(100); @@ -448,7 +448,7 @@ suite('Inline Completions', () => { context.keyboardType('fooba'); - provider.setReturnValue({ text: 'foobar', range: new Range(1, 1, 1, 6) }); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 6) }); model.trigger(InlineCompletionTriggerKind.Explicit); await timeout(1000); @@ -457,7 +457,7 @@ suite('Inline Completions', () => { ]); assert.deepStrictEqual(context.getAndClearViewStates(), ['', 'fooba[r]']); - provider.setReturnValue({ text: 'foobaz', range: new Range(1, 1, 1, 5) }); + provider.setReturnValue({ insertText: 'foobaz', range: new Range(1, 1, 1, 5) }); context.leftDelete(); await timeout(1000); assert.deepStrictEqual(provider.getAndClearCallHistory(), [ @@ -478,13 +478,13 @@ suite('Inline Completions', () => { async ({ editor, editorViewModel, model, context }) => { model.setActive(true); context.keyboardType('h'); - provider.setReturnValue({ text: 'helloworld', range: new Range(1, 1, 1, 2) }, 1000); + provider.setReturnValue({ insertText: 'helloworld', range: new Range(1, 1, 1, 2) }, 1000); model.trigger(InlineCompletionTriggerKind.Explicit); await timeout(1030); context.keyboardType('ello'); - provider.setReturnValue({ text: 'helloworld', range: new Range(1, 1, 1, 6) }, 1000); + provider.setReturnValue({ insertText: 'helloworld', range: new Range(1, 1, 1, 6) }, 1000); // after 20ms: Inline completion provider answers back // after 50ms: Debounce is triggered @@ -505,7 +505,7 @@ suite('Inline Completions', () => { model.setActive(true); context.keyboardType('hello\n'); context.cursorLeft(); - provider.setReturnValue({ text: 'helloworld', range: new Range(1, 1, 1, 6) }, 1000); + provider.setReturnValue({ insertText: 'helloworld', range: new Range(1, 1, 1, 6) }, 1000); await timeout(2000); assert.deepStrictEqual(provider.getAndClearCallHistory(), [ @@ -516,7 +516,7 @@ suite('Inline Completions', () => { } ]); - provider.setReturnValue({ text: 'helloworld', range: new Range(2, 1, 2, 6) }, 1000); + provider.setReturnValue({ insertText: 'helloworld', range: new Range(2, 1, 2, 6) }, 1000); context.cursorDown(); context.keyboardType('hello'); diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts index 0f18a2a3ae7..fe8c80c27a6 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts @@ -31,10 +31,10 @@ import assert = require('assert'); import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { minimizeInlineCompletion } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel'; import { rangeStartsWith } from 'vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider'; import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { minimizeInlineCompletion } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionToGhostText'; suite('Suggest Widget Model', () => { test('rangeStartsWith', () => { @@ -104,11 +104,11 @@ suite('Suggest Widget Model', () => { test('minimizeInlineCompletion', async () => { const model = createTextModel('fun'); - const result = minimizeInlineCompletion(model, { range: new Range(1, 1, 1, 4), text: 'function', snippetInfo: undefined })!; + const result = minimizeInlineCompletion(model, { range: new Range(1, 1, 1, 4), insertText: 'function', snippetInfo: undefined })!; assert.deepStrictEqual({ range: result.range.toString(), - text: result.text + text: result.insertText }, { range: '[1,4 -> 1,4]', text: 'ction' diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index fc8baba75f5..a5cf3d09b6a 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6271,7 +6271,7 @@ declare namespace monaco.languages { * 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 text: string | { + readonly insertText: string | { snippet: string; }; /** diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index be656cf7ee6..9d601d2413f 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1084,7 +1084,7 @@ class InlineCompletionAdapter { throw new Error('text or insertText must be defined'); } return ({ - text: typeof insertText === 'string' ? insertText : { snippet: insertText.value }, + insertText: typeof insertText === 'string' ? insertText : { snippet: insertText.value }, range: item.range ? typeConvert.Range.from(item.range) : undefined, command, idx: idx, @@ -1179,7 +1179,7 @@ class InlineCompletionAdapterNew { throw new Error('text or insertText must be defined'); } return ({ - text: typeof insertText === 'string' ? insertText : { snippet: insertText.value }, + insertText: typeof insertText === 'string' ? insertText : { snippet: insertText.value }, range: item.range ? typeConvert.Range.from(item.range) : undefined, command, idx: idx,