inline completions code cleanup (#228328)

* inline completions code cleanup

* Fixes CI
This commit is contained in:
Henning Dieterichs
2024-09-12 12:26:37 +02:00
committed by GitHub
parent 1d3895d045
commit 8bb2e41e2e
15 changed files with 140 additions and 147 deletions
+1 -1
View File
@@ -783,7 +783,7 @@ export interface InlineCompletionsProvider<T extends InlineCompletions = InlineC
* @experimental
* @internal
*/
provideInlineEdits?(model: model.ITextModel, range: Range, context: InlineCompletionContext, token: CancellationToken): ProviderResult<T>;
provideInlineEditsForRange?(model: model.ITextModel, range: Range, context: InlineCompletionContext, token: CancellationToken): ProviderResult<T>;
/**
* Will be called when an item is shown.
@@ -8,8 +8,8 @@ import { alert } from '../../../../../base/browser/ui/aria/aria.js';
import { timeout } from '../../../../../base/common/async.js';
import { cancelOnDispose } from '../../../../../base/common/cancellation.js';
import { readHotReloadableExport } from '../../../../../base/common/hotReloadHelpers.js';
import { Disposable, DisposableStore, toDisposable } from '../../../../../base/common/lifecycle.js';
import { IObservable, ISettableObservable, ITransaction, autorun, constObservable, derived, derivedDisposable, derivedObservableWithCache, mapObservableArrayCached, observableFromEvent, observableSignal, observableValue, runOnChange, runOnChangeWithStore, transaction, waitForState } from '../../../../../base/common/observable.js';
import { Disposable, toDisposable } from '../../../../../base/common/lifecycle.js';
import { ITransaction, autorun, constObservable, derived, derivedDisposable, derivedObservableWithCache, mapObservableArrayCached, observableFromEvent, observableSignal, runOnChange, runOnChangeWithStore, transaction, waitForState } from '../../../../../base/common/observable.js';
import { isUndefined } from '../../../../../base/common/types.js';
import { localize } from '../../../../../nls.js';
import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js';
@@ -31,6 +31,7 @@ import { ILanguageFeaturesService } from '../../../../common/services/languageFe
import { InlineCompletionsHintsWidget, InlineSuggestionHintsContentWidget } from '../hintsWidget/inlineCompletionsHintsWidget.js';
import { InlineCompletionsModel } from '../model/inlineCompletionsModel.js';
import { SuggestWidgetAdaptor } from '../model/suggestWidgetAdaptor.js';
import { convertItemsToStableObservables } from '../utils.js';
import { GhostTextView } from '../view/ghostTextView.js';
import { inlineSuggestCommitId } from './commandIds.js';
import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js';
@@ -273,27 +274,3 @@ export class InlineCompletionsController extends Disposable {
});
}
}
function convertItemsToStableObservables<T>(items: IObservable<readonly T[]>, store: DisposableStore): IObservable<IObservable<T>[]> {
const result = observableValue<IObservable<T>[]>('result', []);
const innerObservables: ISettableObservable<T>[] = [];
store.add(autorun(reader => {
const itemsValue = items.read(reader);
transaction(tx => {
if (itemsValue.length !== innerObservables.length) {
innerObservables.length = itemsValue.length;
for (let i = 0; i < innerObservables.length; i++) {
if (!innerObservables[i]) {
innerObservables[i] = observableValue<T>('item', itemsValue[i]);
}
}
result.set([...innerObservables], tx);
}
innerObservables.forEach((o, i) => o.set(itemsValue[i], tx));
});
}));
return result;
}
@@ -4,31 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import { IDiffChange, LcsDiff } from '../../../../../base/common/diff/diff.js';
import { commonPrefixLength, getLeadingWhitespace } from '../../../../../base/common/strings.js';
import { getLeadingWhitespace } from '../../../../../base/common/strings.js';
import { Position } from '../../../../common/core/position.js';
import { Range } from '../../../../common/core/range.js';
import { TextLength } from '../../../../common/core/textLength.js';
import { SingleTextEdit } from '../../../../common/core/textEdit.js';
import { EndOfLinePreference, ITextModel } from '../../../../common/model.js';
import { ITextModel } from '../../../../common/model.js';
import { GhostText, GhostTextPart } from './ghostText.js';
export function singleTextRemoveCommonPrefix(edit: SingleTextEdit, model: ITextModel, validModelRange?: Range): SingleTextEdit {
const modelRange = validModelRange ? edit.range.intersectRanges(validModelRange) : edit.range;
if (!modelRange) {
return edit;
}
const valueToReplace = model.getValueInRange(modelRange, EndOfLinePreference.LF);
const commonPrefixLen = commonPrefixLength(valueToReplace, edit.text);
const start = TextLength.ofText(valueToReplace.substring(0, commonPrefixLen)).addToPosition(edit.range.getStartPosition());
const text = edit.text.substring(commonPrefixLen);
const range = Range.fromPositions(start, edit.range.getEndPosition());
return new SingleTextEdit(range, text);
}
export function singleTextEditAugments(edit: SingleTextEdit, base: SingleTextEdit): boolean {
// The augmented completion must replace the base range, but can replace even more
return edit.text.startsWith(base.text) && rangeExtends(edit.range, base.range);
}
import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js';
/**
* @param previewSuffixLength Sets where to split `inlineCompletion.text`.
@@ -58,27 +40,23 @@ export function computeGhostText(
// ^^^^^^^^^^ ^^^^^^ sourceIndentationLength
// ^^^^^^ replacedIndentation.length
// ^^^ rangeThatDoesNotReplaceIndentation
// inlineCompletion.text: '··foo'
// ^^ suggestionAddedIndentationLength
const suggestionAddedIndentationLength = getLeadingWhitespace(e.text).length;
const replacedIndentation = sourceLine.substring(e.range.startColumn - 1, sourceIndentationLength);
const [startPosition, endPosition] = [e.range.getStartPosition(), e.range.getEndPosition()];
const newStartPosition =
startPosition.column + replacedIndentation.length <= endPosition.column
? startPosition.delta(0, replacedIndentation.length)
: endPosition;
const newStartPosition = startPosition.column + replacedIndentation.length <= endPosition.column
? startPosition.delta(0, replacedIndentation.length)
: endPosition;
const rangeThatDoesNotReplaceIndentation = Range.fromPositions(newStartPosition, endPosition);
const suggestionWithoutIndentationChange =
e.text.startsWith(replacedIndentation)
// Adds more indentation without changing existing indentation: We can add ghost text for this
? e.text.substring(replacedIndentation.length)
// Changes or removes existing indentation. Only add ghost text for the non-indentation part.
: e.text.substring(suggestionAddedIndentationLength);
const suggestionWithoutIndentationChange = e.text.startsWith(replacedIndentation)
// Adds more indentation without changing existing indentation: We can add ghost text for this
? e.text.substring(replacedIndentation.length)
// Changes or removes existing indentation. Only add ghost text for the non-indentation part.
: e.text.substring(suggestionAddedIndentationLength);
e = new SingleTextEdit(rangeThatDoesNotReplaceIndentation, suggestionWithoutIndentationChange);
}
@@ -139,11 +117,6 @@ export function computeGhostText(
return new GhostText(lineNumber, parts);
}
function rangeExtends(extendingRange: Range, rangeToExtend: Range): boolean {
return rangeToExtend.getStartPosition().equals(extendingRange.getStartPosition())
&& rangeToExtend.getEndPosition().isBeforeOrEqual(extendingRange.getEndPosition());
}
let lastRequest: { originalValue: string; newValue: string; changes: readonly IDiffChange[] | undefined } | undefined = undefined;
function cachingDiff(originalValue: string, newValue: string): readonly IDiffChange[] | undefined {
if (lastRequest?.originalValue === originalValue && lastRequest?.newValue === newValue) {
@@ -3,20 +3,21 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { compareBy, Permutation } from '../../../../../base/common/arrays.js';
import { mapFindFirst } from '../../../../../base/common/arraysFind.js';
import { itemsEquals } from '../../../../../base/common/equals.js';
import { BugIndicatingError, onUnexpectedError, onUnexpectedExternalError } from '../../../../../base/common/errors.js';
import { Disposable } from '../../../../../base/common/lifecycle.js';
import { IObservable, IReader, ITransaction, autorun, derived, derivedHandleChanges, derivedOpts, observableSignal, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from '../../../../../base/common/observable.js';
import { commonPrefixLength, splitLinesIncludeSeparators } from '../../../../../base/common/strings.js';
import { commonPrefixLength } from '../../../../../base/common/strings.js';
import { isDefined } from '../../../../../base/common/types.js';
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
import { ICodeEditor } from '../../../../browser/editorBrowser.js';
import { EditOperation } from '../../../../common/core/editOperation.js';
import { Position } from '../../../../common/core/position.js';
import { Range } from '../../../../common/core/range.js';
import { Selection } from '../../../../common/core/selection.js';
import { SingleTextEdit, TextEdit } from '../../../../common/core/textEdit.js';
import { SingleTextEdit } from '../../../../common/core/textEdit.js';
import { TextLength } from '../../../../common/core/textLength.js';
import { ScrollType } from '../../../../common/editorCommon.js';
import { Command, InlineCompletionContext, InlineCompletionTriggerKind, PartialAcceptTriggerKind } from '../../../../common/languages.js';
@@ -24,14 +25,13 @@ import { ILanguageConfigurationService } from '../../../../common/languages/lang
import { EndOfLinePreference, ITextModel } from '../../../../common/model.js';
import { IFeatureDebounceInformation } from '../../../../common/services/languageFeatureDebounce.js';
import { IModelContentChangedEvent } from '../../../../common/textModelEvents.js';
import { SnippetController2 } from '../../../snippet/browser/snippetController2.js';
import { addPositions, getEndPositionsAfterApplying, substringPos, subtractPositions } from '../utils.js';
import { computeGhostText } from './computeGhostText.js';
import { GhostText, GhostTextOrReplacement, ghostTextOrReplacementEquals, ghostTextsOrReplacementsEqual } from './ghostText.js';
import { InlineCompletionWithUpdatedRange, InlineCompletionsSource } from './inlineCompletionsSource.js';
import { computeGhostText, singleTextEditAugments, singleTextRemoveCommonPrefix } from './singleTextEdit.js';
import { singleTextEditAugments, singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js';
import { SuggestItemInfo } from './suggestWidgetAdaptor.js';
import { addPositions, subtractPositions } from '../utils.js';
import { SnippetController2 } from '../../../snippet/browser/snippetController2.js';
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
export class InlineCompletionsModel extends Disposable {
private readonly _source = this._register(this._instantiationService.createInstance(InlineCompletionsSource, this.textModel, this._textModelVersionId, this._debounceValue));
@@ -515,20 +515,3 @@ export function getSecondaryEdits(textModel: ITextModel, positions: readonly Pos
return new SingleTextEdit(range, secondaryEditText);
});
}
function substringPos(text: string, pos: Position): string {
let subtext = '';
const lines = splitLinesIncludeSeparators(text);
for (let i = pos.lineNumber - 1; i < lines.length; i++) {
subtext += lines[i].substring(i === pos.lineNumber - 1 ? pos.column - 1 : 0);
}
return subtext;
}
function getEndPositionsAfterApplying(edits: readonly SingleTextEdit[]): Position[] {
const sortPerm = Permutation.createSortPermutation(edits, compareBy(e => e.range, Range.compareRangesUsingStarts));
const edit = new TextEdit(sortPerm.apply(edits));
const sortedNewRanges = edit.getNewRanges();
const newRanges = sortPerm.inverse().apply(sortedNewRanges);
return newRanges.map(range => range.getEndPosition());
}
@@ -18,7 +18,7 @@ import { EndOfLinePreference, ITextModel } from '../../../../common/model.js';
import { IFeatureDebounceInformation } from '../../../../common/services/languageFeatureDebounce.js';
import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js';
import { InlineCompletionItem, InlineCompletionProviderResult, provideInlineCompletions } from './provideInlineCompletions.js';
import { singleTextRemoveCommonPrefix } from './singleTextEdit.js';
import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js';
export class InlineCompletionsSource extends Disposable {
private readonly _updateOperation = this._register(new MutableDisposable<UpdateOperation>());
@@ -26,21 +26,21 @@ export class InlineCompletionsSource extends Disposable {
public readonly suggestWidgetInlineCompletions = disposableObservableValue<UpToDateInlineCompletions | undefined>('suggestWidgetInlineCompletions', undefined);
constructor(
private readonly textModel: ITextModel,
private readonly versionId: IObservable<number | null>,
private readonly _textModel: ITextModel,
private readonly _versionId: IObservable<number | null>,
private readonly _debounceValue: IFeatureDebounceInformation,
@ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService,
@ILanguageConfigurationService private readonly languageConfigurationService: ILanguageConfigurationService,
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService,
) {
super();
this._register(this.textModel.onDidChangeContent(() => {
this._register(this._textModel.onDidChangeContent(() => {
this._updateOperation.clear();
}));
}
public fetch(position: Position, context: InlineCompletionContext, activeInlineCompletion: InlineCompletionWithUpdatedRange | undefined): Promise<boolean> {
const request = new UpdateRequest(position, context, this.textModel.getVersionId());
const request = new UpdateRequest(position, context, this._textModel.getVersionId());
const target = context.selectedSuggestionInfo ? this.suggestWidgetInlineCompletions : this.inlineCompletions;
@@ -59,34 +59,34 @@ export class InlineCompletionsSource extends Disposable {
const shouldDebounce = updateOngoing || context.triggerKind === InlineCompletionTriggerKind.Automatic;
if (shouldDebounce) {
// This debounces the operation
await wait(this._debounceValue.get(this.textModel), source.token);
await wait(this._debounceValue.get(this._textModel), source.token);
}
if (source.token.isCancellationRequested || this._store.isDisposed || this.textModel.getVersionId() !== request.versionId) {
if (source.token.isCancellationRequested || this._store.isDisposed || this._textModel.getVersionId() !== request.versionId) {
return false;
}
const startTime = new Date();
const updatedCompletions = await provideInlineCompletions(
this.languageFeaturesService.inlineCompletionsProvider,
this._languageFeaturesService.inlineCompletionsProvider,
position,
this.textModel,
this._textModel,
context,
source.token,
this.languageConfigurationService
this._languageConfigurationService
);
if (source.token.isCancellationRequested || this._store.isDisposed || this.textModel.getVersionId() !== request.versionId) {
if (source.token.isCancellationRequested || this._store.isDisposed || this._textModel.getVersionId() !== request.versionId) {
return false;
}
const endTime = new Date();
this._debounceValue.update(this.textModel, endTime.getTime() - startTime.getTime());
this._debounceValue.update(this._textModel, endTime.getTime() - startTime.getTime());
const completions = new UpToDateInlineCompletions(updatedCompletions, request, this.textModel, this.versionId);
const completions = new UpToDateInlineCompletions(updatedCompletions, request, this._textModel, this._versionId);
if (activeInlineCompletion) {
const asInlineCompletion = activeInlineCompletion.toInlineCompletion(undefined);
if (activeInlineCompletion.canBeReused(this.textModel, position) && !updatedCompletions.has(asInlineCompletion)) {
if (activeInlineCompletion.canBeReused(this._textModel, position) && !updatedCompletions.has(asInlineCompletion)) {
completions.prepend(activeInlineCompletion.inlineCompletion, asInlineCompletion.range, true);
}
}
@@ -106,7 +106,7 @@ export async function provideInlineCompletions(
const completions = await provider.provideInlineCompletions(model, positionOrRange, context, token);
return completions;
} else {
const completions = await provider.provideInlineEdits?.(model, positionOrRange, context, token);
const completions = await provider.provideInlineEditsForRange?.(model, positionOrRange, context, token);
return completions;
}
} catch (e) {
@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { commonPrefixLength } from '../../../../../base/common/strings.js';
import { Range } from '../../../../common/core/range.js';
import { TextLength } from '../../../../common/core/textLength.js';
import { SingleTextEdit } from '../../../../common/core/textEdit.js';
import { EndOfLinePreference, ITextModel } from '../../../../common/model.js';
export function singleTextRemoveCommonPrefix(edit: SingleTextEdit, model: ITextModel, validModelRange?: Range): SingleTextEdit {
const modelRange = validModelRange ? edit.range.intersectRanges(validModelRange) : edit.range;
if (!modelRange) {
return edit;
}
const valueToReplace = model.getValueInRange(modelRange, EndOfLinePreference.LF);
const commonPrefixLen = commonPrefixLength(valueToReplace, edit.text);
const start = TextLength.ofText(valueToReplace.substring(0, commonPrefixLen)).addToPosition(edit.range.getStartPosition());
const text = edit.text.substring(commonPrefixLen);
const range = Range.fromPositions(start, edit.range.getEndPosition());
return new SingleTextEdit(range, text);
}
export function singleTextEditAugments(edit: SingleTextEdit, base: SingleTextEdit): boolean {
// The augmented completion must replace the base range, but can replace even more
return edit.text.startsWith(base.text) && rangeExtends(edit.range, base.range);
}
function rangeExtends(extendingRange: Range, rangeToExtend: Range): boolean {
return rangeToExtend.getStartPosition().equals(extendingRange.getStartPosition())
&& rangeToExtend.getEndPosition().isBeforeOrEqual(extendingRange.getEndPosition());
}
@@ -13,7 +13,7 @@ import { Range } from '../../../../common/core/range.js';
import { SingleTextEdit } from '../../../../common/core/textEdit.js';
import { CompletionItemInsertTextRule, CompletionItemKind, SelectedSuggestionInfo } from '../../../../common/languages.js';
import { ITextModel } from '../../../../common/model.js';
import { singleTextEditAugments, singleTextRemoveCommonPrefix } from './singleTextEdit.js';
import { singleTextEditAugments, singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js';
import { SnippetParser } from '../../../snippet/browser/snippetParser.js';
import { SnippetSession } from '../../../snippet/browser/snippetSession.js';
import { CompletionItem } from '../../../suggest/browser/suggest.js';
@@ -3,13 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Permutation, compareBy } from '../../../../base/common/arrays.js';
import { BugIndicatingError } from '../../../../base/common/errors.js';
import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';
import { IObservable, autorunOpts } from '../../../../base/common/observable.js';
import { ICodeEditor } from '../../../browser/editorBrowser.js';
import { DisposableStore } from '../../../../base/common/lifecycle.js';
import { IObservable, observableValue, ISettableObservable, autorun, transaction } from '../../../../base/common/observable.js';
import { splitLinesIncludeSeparators } from '../../../../base/common/strings.js';
import { Position } from '../../../common/core/position.js';
import { Range } from '../../../common/core/range.js';
import { IModelDeltaDecoration } from '../../../common/model.js';
import { SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js';
const array: ReadonlyArray<any> = [];
export function getReadonlyEmptyArray<T>(): readonly T[] {
@@ -36,25 +37,6 @@ export class ColumnRange {
}
}
/**
* Use observableCodeEditor(editor).applyDecorations(decorations) instead.
* @deprecated
*/
export function applyObservableDecorations(editor: ICodeEditor, decorations: IObservable<IModelDeltaDecoration[]>): IDisposable {
const d = new DisposableStore();
const decorationsCollection = editor.createDecorationsCollection();
d.add(autorunOpts({ debugName: () => `Apply decorations from ${decorations.debugName}` }, reader => {
const d = decorations.read(reader);
decorationsCollection.set(d);
}));
d.add({
dispose: () => {
decorationsCollection.clear();
}
});
return d;
}
export function addPositions(pos1: Position, pos2: Position): Position {
return new Position(pos1.lineNumber + pos2.lineNumber - 1, pos2.lineNumber === 1 ? pos1.column + pos2.column - 1 : pos2.column);
}
@@ -62,3 +44,44 @@ export function addPositions(pos1: Position, pos2: Position): Position {
export function subtractPositions(pos1: Position, pos2: Position): Position {
return new Position(pos1.lineNumber - pos2.lineNumber + 1, pos1.lineNumber - pos2.lineNumber === 0 ? pos1.column - pos2.column + 1 : pos1.column);
}
export function substringPos(text: string, pos: Position): string {
let subtext = '';
const lines = splitLinesIncludeSeparators(text);
for (let i = pos.lineNumber - 1; i < lines.length; i++) {
subtext += lines[i].substring(i === pos.lineNumber - 1 ? pos.column - 1 : 0);
}
return subtext;
}
export function getEndPositionsAfterApplying(edits: readonly SingleTextEdit[]): Position[] {
const sortPerm = Permutation.createSortPermutation(edits, compareBy(e => e.range, Range.compareRangesUsingStarts));
const edit = new TextEdit(sortPerm.apply(edits));
const sortedNewRanges = edit.getNewRanges();
const newRanges = sortPerm.inverse().apply(sortedNewRanges);
return newRanges.map(range => range.getEndPosition());
}
export function convertItemsToStableObservables<T>(items: IObservable<readonly T[]>, store: DisposableStore): IObservable<IObservable<T>[]> {
const result = observableValue<IObservable<T>[]>('result', []);
const innerObservables: ISettableObservable<T>[] = [];
store.add(autorun(reader => {
const itemsValue = items.read(reader);
transaction(tx => {
if (itemsValue.length !== innerObservables.length) {
innerObservables.length = itemsValue.length;
for (let i = 0; i < innerObservables.length; i++) {
if (!innerObservables[i]) {
innerObservables[i] = observableValue<T>('item', itemsValue[i]);
}
}
result.set([...innerObservables], tx);
}
innerObservables.forEach((o, i) => o.set(itemsValue[i], tx));
});
}));
return result;
}
@@ -22,7 +22,7 @@ import { createTextModel } from '../../../../test/common/testTextModel.js';
import { IAccessibilitySignalService } from '../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js';
import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js';
import { Selection } from '../../../../common/core/selection.js';
import { computeGhostText } from '../../browser/model/singleTextEdit.js';
import { computeGhostText } from '../../browser/model/computeGhostText.js';
suite('Inline Completions', () => {
ensureNoDisposablesAreLeakedInTestSuite();
@@ -15,9 +15,10 @@ import { LineDecoration } from '../../../common/viewLayout/lineDecorations.js';
import { InlineDecorationType } from '../../../common/viewModel.js';
import { AdditionalLinesWidget, LineData } from '../../inlineCompletions/browser/view/ghostTextView.js';
import { GhostText } from '../../inlineCompletions/browser/model/ghostText.js';
import { ColumnRange, applyObservableDecorations } from '../../inlineCompletions/browser/utils.js';
import { ColumnRange } from '../../inlineCompletions/browser/utils.js';
import { diffDeleteDecoration, diffLineDeleteDecorationBackgroundWithIndicator } from '../../../browser/widget/diffEditor/registrations.contribution.js';
import { LineTokens } from '../../../common/tokens/lineTokens.js';
import { observableCodeEditor } from '../../../browser/observableCodeEditor.js';
export const INLINE_EDIT_DESCRIPTION = 'inline-edit';
export interface IGhostTextWidgetModel {
@@ -29,17 +30,20 @@ export interface IGhostTextWidgetModel {
export class GhostTextWidget extends Disposable {
private readonly isDisposed = observableValue(this, false);
private readonly currentTextModel = observableFromEvent(this, this.editor.onDidChangeModel, () => /** @description editor.model */ this.editor.getModel());
private readonly currentTextModel = observableFromEvent(this, this._editor.onDidChangeModel, () => /** @description editor.model */ this._editor.getModel());
private readonly _editorObs = observableCodeEditor(this._editor);
constructor(
private readonly editor: ICodeEditor,
private readonly _editor: ICodeEditor,
readonly model: IGhostTextWidgetModel,
@ILanguageService private readonly languageService: ILanguageService,
) {
super();
this._register(toDisposable(() => { this.isDisposed.set(true, undefined); }));
this._register(applyObservableDecorations(this.editor, this.decorations));
this._register(this._editorObs.setDecorations(this.decorations));
}
private readonly uiState = derived(this, reader => {
@@ -214,7 +218,7 @@ export class GhostTextWidget extends Disposable {
private readonly additionalLinesWidget = this._register(
new AdditionalLinesWidget(
this.editor,
this._editor,
derived(reader => {
/** @description lines */
const uiState = this.uiState.read(reader);
@@ -613,8 +613,8 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
provideInlineCompletions: async (model: ITextModel, position: EditorPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<IdentifiableInlineCompletions | undefined> => {
return this._proxy.$provideInlineCompletions(handle, model.uri, position, context, token);
},
provideInlineEdits: async (model: ITextModel, range: EditorRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<IdentifiableInlineCompletions | undefined> => {
return this._proxy.$provideInlineEdits(handle, model.uri, range, context, token);
provideInlineEditsForRange: async (model: ITextModel, range: EditorRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<IdentifiableInlineCompletions | undefined> => {
return this._proxy.$provideInlineEditsForRange(handle, model.uri, range, context, token);
},
handleItemDidShow: async (completions: IdentifiableInlineCompletions, item: IdentifiableInlineCompletion, updatedInsertText: string): Promise<void> => {
if (supportsHandleEvents) {
@@ -2227,7 +2227,7 @@ export interface ExtHostLanguageFeaturesShape {
$resolveCompletionItem(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<ISuggestDataDto | undefined>;
$releaseCompletionItems(handle: number, id: number): void;
$provideInlineCompletions(handle: number, resource: UriComponents, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<IdentifiableInlineCompletions | undefined>;
$provideInlineEdits(handle: number, resource: UriComponents, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<IdentifiableInlineCompletions | undefined>;
$provideInlineEditsForRange(handle: number, resource: UriComponents, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<IdentifiableInlineCompletions | undefined>;
$handleInlineCompletionDidShow(handle: number, pid: number, idx: number, updatedInsertText: string): void;
$handleInlineCompletionPartialAccept(handle: number, pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void;
$freeInlineCompletionsList(handle: number, pid: number): void;
@@ -1287,7 +1287,7 @@ class InlineCompletionAdapterBase {
return undefined;
}
async provideInlineEdits(resource: URI, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
async provideInlineEditsForRange(resource: URI, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
return undefined;
}
@@ -1396,8 +1396,8 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase {
};
}
override async provideInlineEdits(resource: URI, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
if (!this._provider.provideInlineEdits) {
override async provideInlineEditsForRange(resource: URI, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
if (!this._provider.provideInlineEditsForRange) {
return undefined;
}
checkProposedApiEnabled(this._extension, 'inlineCompletionsAdditions');
@@ -1405,7 +1405,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase {
const doc = this._documents.getDocument(resource);
const r = typeConvert.Range.to(range);
const result = await this._provider.provideInlineEdits(doc, r, {
const result = await this._provider.provideInlineEditsForRange(doc, r, {
selectedCompletionInfo:
context.selectedSuggestionInfo
? {
@@ -2674,8 +2674,8 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
return this._withAdapter(handle, InlineCompletionAdapterBase, adapter => adapter.provideInlineCompletions(URI.revive(resource), position, context, token), undefined, token);
}
$provideInlineEdits(handle: number, resource: UriComponents, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
return this._withAdapter(handle, InlineCompletionAdapterBase, adapter => adapter.provideInlineEdits(URI.revive(resource), range, context, token), undefined, token);
$provideInlineEditsForRange(handle: number, resource: UriComponents, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
return this._withAdapter(handle, InlineCompletionAdapterBase, adapter => adapter.provideInlineEditsForRange(URI.revive(resource), range, context, token), undefined, token);
}
$handleInlineCompletionDidShow(handle: number, pid: number, idx: number, updatedInsertText: string): void {
@@ -61,7 +61,7 @@ declare module 'vscode' {
// eslint-disable-next-line local/vscode-dts-provider-naming
handleDidPartiallyAcceptCompletionItem?(completionItem: InlineCompletionItem, info: PartialAcceptInfo): void;
provideInlineEdits?(document: TextDocument, range: Range, context: InlineCompletionContext, token: CancellationToken): ProviderResult<InlineCompletionItem[] | InlineCompletionList>;
provideInlineEditsForRange?(document: TextDocument, range: Range, context: InlineCompletionContext, token: CancellationToken): ProviderResult<InlineCompletionItem[] | InlineCompletionList>;
}
export interface InlineCompletionContext {