mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Merge pull request #279147 from microsoft/benibenj/wet-skunk
Add support for rename inline suggestions
This commit is contained in:
@@ -837,7 +837,9 @@ export interface InlineCompletion {
|
||||
|
||||
readonly warning?: InlineCompletionWarning;
|
||||
|
||||
readonly hint?: InlineCompletionHint;
|
||||
readonly hint?: IInlineCompletionHint;
|
||||
|
||||
readonly supportsRename?: boolean;
|
||||
|
||||
/**
|
||||
* Used for telemetry.
|
||||
@@ -855,7 +857,7 @@ export enum InlineCompletionHintStyle {
|
||||
Label = 2
|
||||
}
|
||||
|
||||
export interface InlineCompletionHint {
|
||||
export interface IInlineCompletionHint {
|
||||
/** Refers to the current document. */
|
||||
range: IRange;
|
||||
style: InlineCompletionHintStyle;
|
||||
@@ -1052,6 +1054,9 @@ export type LifetimeSummary = {
|
||||
typingIntervalCharacterCount: number;
|
||||
selectedSuggestionInfo: boolean;
|
||||
availableProviders: string;
|
||||
renameCreated: boolean;
|
||||
renameDuration?: number;
|
||||
renameTimedOut: boolean;
|
||||
};
|
||||
|
||||
export interface CodeAction {
|
||||
|
||||
@@ -14,3 +14,5 @@ export const jumpToNextInlineEditId = 'editor.action.inlineSuggest.jump';
|
||||
export const hideInlineCompletionId = 'editor.action.inlineSuggest.hide';
|
||||
|
||||
export const toggleShowCollapsedId = 'editor.action.inlineSuggest.toggleShowCollapsed';
|
||||
|
||||
export const renameSymbolCommandId = 'editor.action.inlineSuggest.renameSymbol';
|
||||
|
||||
@@ -947,6 +947,12 @@ export class InlineCompletionsModel extends Disposable {
|
||||
// Reset before invoking the command, as the command might cause a follow up trigger (which we don't want to reset).
|
||||
this.stop();
|
||||
|
||||
if (completion.renameCommand) {
|
||||
await this._commandService
|
||||
.executeCommand(completion.renameCommand.id, ...(completion.renameCommand.arguments || []))
|
||||
.then(undefined, onUnexpectedExternalError);
|
||||
}
|
||||
|
||||
if (completion.command) {
|
||||
await this._commandService
|
||||
.executeCommand(completion.command.id, ...(completion.command.arguments || []))
|
||||
|
||||
@@ -33,6 +33,7 @@ import { InlineCompletionEndOfLifeEvent, sendInlineCompletionsEndOfLifeTelemetry
|
||||
import { wait } from '../utils.js';
|
||||
import { InlineSuggestionIdentity, InlineSuggestionItem } from './inlineSuggestionItem.js';
|
||||
import { InlineCompletionContextWithoutUuid, InlineSuggestRequestInfo, provideInlineCompletions, runWhenCancelled } from './provideInlineCompletions.js';
|
||||
import { RenameSymbolProcessor } from './renameSymbolProcessor.js';
|
||||
|
||||
export class InlineCompletionsSource extends Disposable {
|
||||
private static _requestId = 0;
|
||||
@@ -75,6 +76,8 @@ export class InlineCompletionsSource extends Disposable {
|
||||
public readonly inlineCompletions = this._state.map(this, v => v.inlineCompletions);
|
||||
public readonly suggestWidgetInlineCompletions = this._state.map(this, v => v.suggestWidgetInlineCompletions);
|
||||
|
||||
private readonly _renameProcessor: RenameSymbolProcessor;
|
||||
|
||||
private _completionsEnabled: Record<string, boolean> | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
@@ -98,6 +101,8 @@ export class InlineCompletionsSource extends Disposable {
|
||||
'editor.inlineSuggest.logFetch.commandId'
|
||||
));
|
||||
|
||||
this._renameProcessor = this._store.add(this._instantiationService.createInstance(RenameSymbolProcessor));
|
||||
|
||||
this.clearOperationOnTextModelChange.recomputeInitiallyAndOnChange(this._store);
|
||||
|
||||
const enablementSetting = product.defaultChatAgent?.completionsEnablementSetting ?? undefined;
|
||||
@@ -225,7 +230,7 @@ export class InlineCompletionsSource extends Disposable {
|
||||
let shouldStopEarly = false;
|
||||
let producedSuggestion = false;
|
||||
|
||||
const suggestions: InlineSuggestionItem[] = [];
|
||||
const providerSuggestions: InlineSuggestionItem[] = [];
|
||||
for await (const list of providerResult.lists) {
|
||||
if (!list) {
|
||||
continue;
|
||||
@@ -245,7 +250,7 @@ export class InlineCompletionsSource extends Disposable {
|
||||
}
|
||||
|
||||
const i = InlineSuggestionItem.create(item, this._textModel);
|
||||
suggestions.push(i);
|
||||
providerSuggestions.push(i);
|
||||
// Stop after first visible inline completion
|
||||
if (!i.isInlineEdit && !i.showInlineEditMenu && context.triggerKind === InlineCompletionTriggerKind.Automatic) {
|
||||
if (i.isVisible(this._textModel, this._cursorPosition.get())) {
|
||||
@@ -259,6 +264,10 @@ export class InlineCompletionsSource extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
const suggestions: InlineSuggestionItem[] = await Promise.all(providerSuggestions.map(async s => {
|
||||
return this._renameProcessor.proposeRenameRefactoring(this._textModel, s);
|
||||
}));
|
||||
|
||||
providerResult.cancelAndDispose({ kind: 'lostRace' });
|
||||
|
||||
if (this._loggingEnabled.get() || this._structuredFetchLogger.isEnabled.get()) {
|
||||
@@ -440,6 +449,9 @@ export class InlineCompletionsSource extends Disposable {
|
||||
disjointReplacements: undefined,
|
||||
sameShapeReplacements: undefined,
|
||||
notShownReason: undefined,
|
||||
renameCreated: false,
|
||||
renameDuration: undefined,
|
||||
renameTimedOut: false,
|
||||
};
|
||||
|
||||
const dataChannel = this._instantiationService.createInstance(DataChannelForwardingTelemetryService);
|
||||
|
||||
@@ -20,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, InlineCompletionHintStyle, InlineCompletionEndOfLifeReason, InlineCompletionTriggerKind, InlineCompletionWarning, PartialAcceptInfo, InlineCompletionHint } from '../../../../common/languages.js';
|
||||
import { Command, InlineCompletion, InlineCompletionHintStyle, InlineCompletionEndOfLifeReason, InlineCompletionTriggerKind, InlineCompletionWarning, PartialAcceptInfo, IInlineCompletionHint } 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 { InlineSuggestData, InlineSuggestionList, PartialAcceptance, SnippetInfo } from './provideInlineCompletions.js';
|
||||
import { InlineSuggestData, InlineSuggestionList, PartialAcceptance, RenameInfo, SnippetInfo } from './provideInlineCompletions.js';
|
||||
import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js';
|
||||
|
||||
export type InlineSuggestionItem = InlineEditItem | InlineCompletionItem;
|
||||
@@ -63,6 +63,8 @@ abstract class InlineSuggestionItemBase {
|
||||
public get semanticId(): string { return this.hash; }
|
||||
public get action(): Command | undefined { return this._sourceInlineCompletion.gutterMenuLinkAction; }
|
||||
public get command(): Command | undefined { return this._sourceInlineCompletion.command; }
|
||||
public get supportsRename(): boolean { return this._data.supportsRename; }
|
||||
public get renameCommand(): Command | undefined { return this._data.renameCommand; }
|
||||
public get warning(): InlineCompletionWarning | undefined { return this._sourceInlineCompletion.warning; }
|
||||
public get showInlineEditMenu(): boolean { return !!this._sourceInlineCompletion.showInlineEditMenu; }
|
||||
public get hash() {
|
||||
@@ -133,6 +135,14 @@ abstract class InlineSuggestionItemBase {
|
||||
public getSourceCompletion(): InlineCompletion {
|
||||
return this._sourceInlineCompletion;
|
||||
}
|
||||
|
||||
public setRenameProcessingInfo(info: RenameInfo): void {
|
||||
this._data.setRenameProcessingInfo(info);
|
||||
}
|
||||
|
||||
public withRename(command: Command, hint: InlineSuggestHint): InlineSuggestData {
|
||||
return this._data.withRename(command, hint);
|
||||
}
|
||||
}
|
||||
|
||||
export class InlineSuggestionIdentity {
|
||||
@@ -166,11 +176,11 @@ export class InlineSuggestionIdentity {
|
||||
|
||||
export class InlineSuggestHint {
|
||||
|
||||
public static create(displayLocation: InlineCompletionHint) {
|
||||
public static create(hint: IInlineCompletionHint) {
|
||||
return new InlineSuggestHint(
|
||||
Range.lift(displayLocation.range),
|
||||
displayLocation.content,
|
||||
displayLocation.style,
|
||||
Range.lift(hint.range),
|
||||
hint.content,
|
||||
hint.style,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { assertNever } from '../../../../../base/common/assert.js';
|
||||
import { AsyncIterableProducer } from '../../../../../base/common/async.js';
|
||||
import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js';
|
||||
import { onUnexpectedExternalError } from '../../../../../base/common/errors.js';
|
||||
import { BugIndicatingError, onUnexpectedExternalError } from '../../../../../base/common/errors.js';
|
||||
import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { prefixedUuid } from '../../../../../base/common/uuid.js';
|
||||
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
|
||||
@@ -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, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, PartialAcceptInfo, InlineCompletionsDisposeReason, LifetimeSummary, ProviderId, InlineCompletionHint } from '../../../../common/languages.js';
|
||||
import { InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, PartialAcceptInfo, InlineCompletionsDisposeReason, LifetimeSummary, ProviderId, IInlineCompletionHint, Command } 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';
|
||||
@@ -246,6 +246,8 @@ function toInlineSuggestData(
|
||||
source,
|
||||
context,
|
||||
inlineCompletion.isInlineEdit ?? false,
|
||||
inlineCompletion.supportsRename ?? false,
|
||||
undefined,
|
||||
requestInfo,
|
||||
providerRequestInfo,
|
||||
inlineCompletion.correlationId,
|
||||
@@ -273,6 +275,12 @@ export type PartialAcceptance = {
|
||||
ratio: number;
|
||||
};
|
||||
|
||||
export type RenameInfo = {
|
||||
createdRename: boolean;
|
||||
duration: number;
|
||||
timedOut?: boolean;
|
||||
};
|
||||
|
||||
export type InlineSuggestViewData = {
|
||||
editorType: InlineCompletionEditorType;
|
||||
renderData?: InlineCompletionViewData;
|
||||
@@ -294,19 +302,22 @@ export class InlineSuggestData {
|
||||
private _isPreceeded = false;
|
||||
private _partiallyAcceptedCount = 0;
|
||||
private _partiallyAcceptedSinceOriginal: PartialAcceptance = { characters: 0, ratio: 0, count: 0 };
|
||||
private _renameInfo: RenameInfo | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
public readonly range: Range,
|
||||
public readonly insertText: string,
|
||||
public readonly snippetInfo: SnippetInfo | undefined,
|
||||
public readonly uri: URI | undefined,
|
||||
public readonly hint: InlineCompletionHint | undefined,
|
||||
public readonly hint: IInlineCompletionHint | undefined,
|
||||
public readonly additionalTextEdits: readonly ISingleEditOperation[],
|
||||
|
||||
public readonly sourceInlineCompletion: InlineCompletion,
|
||||
public readonly source: InlineSuggestionList,
|
||||
public readonly context: InlineCompletionContext,
|
||||
public readonly isInlineEdit: boolean,
|
||||
public readonly supportsRename: boolean,
|
||||
public readonly renameCommand: Command | undefined,
|
||||
|
||||
private readonly _requestInfo: InlineSuggestRequestInfo,
|
||||
private readonly _providerRequestInfo: InlineSuggestProviderRequestInfo,
|
||||
@@ -397,6 +408,9 @@ export class InlineSuggestData {
|
||||
requestReason: this._requestInfo.reason,
|
||||
viewKind: this._viewData.viewKind,
|
||||
notShownReason: this._notShownReason,
|
||||
renameCreated: this._renameInfo?.createdRename ?? false,
|
||||
renameDuration: this._renameInfo?.duration,
|
||||
renameTimedOut: this._renameInfo?.timedOut ?? false,
|
||||
typingInterval: this._requestInfo.typingInterval,
|
||||
typingIntervalCharacterCount: this._requestInfo.typingIntervalCharacterCount,
|
||||
availableProviders: this._requestInfo.availableProviders.map(p => p.toString()).join(','),
|
||||
@@ -457,6 +471,33 @@ export class InlineSuggestData {
|
||||
this._showUncollapsedDuration += timeNow - this._showUncollapsedStartTime;
|
||||
this._showUncollapsedStartTime = undefined;
|
||||
}
|
||||
|
||||
public setRenameProcessingInfo(info: RenameInfo): void {
|
||||
if (this._renameInfo) {
|
||||
throw new BugIndicatingError('Rename info has already been set.');
|
||||
}
|
||||
this._renameInfo = info;
|
||||
}
|
||||
|
||||
public withRename(command: Command, hint: IInlineCompletionHint): InlineSuggestData {
|
||||
return new InlineSuggestData(
|
||||
new Range(1, 1, 1, 1),
|
||||
'',
|
||||
this.snippetInfo,
|
||||
this.uri,
|
||||
hint,
|
||||
this.additionalTextEdits,
|
||||
this.sourceInlineCompletion,
|
||||
this.source,
|
||||
this.context,
|
||||
this.isInlineEdit,
|
||||
this.supportsRename,
|
||||
command,
|
||||
this._requestInfo,
|
||||
this._providerRequestInfo,
|
||||
this._correlationId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface SnippetInfo {
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { raceTimeout } from '../../../../../base/common/async.js';
|
||||
import { LcsDiff, StringDiffSequence } from '../../../../../base/common/diff/diff.js';
|
||||
import { Disposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { localize } from '../../../../../nls.js';
|
||||
import { CommandsRegistry } from '../../../../../platform/commands/common/commands.js';
|
||||
import { ServicesAccessor } from '../../../../browser/editorExtensions.js';
|
||||
import { IBulkEditService } from '../../../../browser/services/bulkEditService.js';
|
||||
import { TextEdit } from '../../../../common/core/edits/textEdit.js';
|
||||
import { Position } from '../../../../common/core/position.js';
|
||||
import { Range } from '../../../../common/core/range.js';
|
||||
import { StandardTokenType } from '../../../../common/encodedTokenAttributes.js';
|
||||
import { Command, InlineCompletionHintStyle } from '../../../../common/languages.js';
|
||||
import { ITextModel } from '../../../../common/model.js';
|
||||
import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js';
|
||||
import { prepareRename, rename } from '../../../rename/browser/rename.js';
|
||||
import { renameSymbolCommandId } from '../controller/commandIds.js';
|
||||
import { InlineSuggestHint, InlineSuggestionItem } from './inlineSuggestionItem.js';
|
||||
|
||||
type SingleEdits = {
|
||||
renames: { edits: TextEdit[]; position: Position; oldName: string; newName: string };
|
||||
others: { edits: TextEdit[] };
|
||||
};
|
||||
|
||||
export class RenameSymbolProcessor extends Disposable {
|
||||
|
||||
constructor(
|
||||
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
|
||||
@IBulkEditService bulkEditService: IBulkEditService,
|
||||
) {
|
||||
super();
|
||||
this._register(CommandsRegistry.registerCommand(renameSymbolCommandId, async (_: ServicesAccessor, textModel: ITextModel, position: Position, newName: string) => {
|
||||
try {
|
||||
const result = await rename(this._languageFeaturesService.renameProvider, textModel, position, newName);
|
||||
if (result.rejectReason) {
|
||||
return;
|
||||
}
|
||||
bulkEditService.apply(result);
|
||||
} catch (error) {
|
||||
// The actual rename failed we should log this.
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public async proposeRenameRefactoring(textModel: ITextModel, suggestItem: InlineSuggestionItem): Promise<InlineSuggestionItem> {
|
||||
if (!suggestItem.supportsRename) {
|
||||
return suggestItem;
|
||||
}
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
const edits = this.createSingleEdits(textModel, suggestItem.editRange, suggestItem.insertText);
|
||||
if (edits === undefined || edits.renames.edits.length === 0) {
|
||||
return suggestItem;
|
||||
}
|
||||
|
||||
const { oldName, newName, position } = edits.renames;
|
||||
let timedOut = false;
|
||||
const loc = await raceTimeout(prepareRename(this._languageFeaturesService.renameProvider, textModel, position), 1000, () => { timedOut = true; });
|
||||
const renamePossible = loc !== undefined && !loc.rejectReason;
|
||||
|
||||
suggestItem.setRenameProcessingInfo({ createdRename: renamePossible, duration: Date.now() - start, timedOut });
|
||||
|
||||
if (!renamePossible) {
|
||||
return suggestItem;
|
||||
}
|
||||
|
||||
const hintRange = edits.renames.edits[0].replacements[0].range;
|
||||
const label = localize('renameSymbol', "Rename '{0}' to '{1}'", oldName, newName);
|
||||
const command: Command = {
|
||||
id: renameSymbolCommandId,
|
||||
title: label,
|
||||
arguments: [textModel, position, newName],
|
||||
};
|
||||
const hint = InlineSuggestHint.create({ range: hintRange, content: label, style: InlineCompletionHintStyle.Code });
|
||||
return InlineSuggestionItem.create(suggestItem.withRename(command, hint), textModel);
|
||||
}
|
||||
|
||||
private createSingleEdits(textModel: ITextModel, nesRange: Range, modifiedText: string): SingleEdits | undefined {
|
||||
const others: TextEdit[] = [];
|
||||
const renames: TextEdit[] = [];
|
||||
let oldName: string | undefined = undefined;
|
||||
let newName: string | undefined = undefined;
|
||||
let position: Position | undefined = undefined;
|
||||
|
||||
const originalText = textModel.getValueInRange(nesRange);
|
||||
const nesOffset = textModel.getOffsetAt(nesRange.getStartPosition());
|
||||
|
||||
const { changes } = (new LcsDiff(new StringDiffSequence(originalText), new StringDiffSequence(modifiedText))).ComputeDiff(true);
|
||||
if (changes.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let tokenDiff: number = 0;
|
||||
for (const change of changes) {
|
||||
const startOffset = nesOffset + change.originalStart;
|
||||
const startPos = textModel.getPositionAt(startOffset);
|
||||
const wordRange = textModel.getWordAtPosition(startPos);
|
||||
// If we don't have a word range at the start position of the current document then we
|
||||
// don't treat it as a rename assuming that the rename refactoring will fail as well since
|
||||
// there can't be an identifier at that position.
|
||||
if (wordRange === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const endOffset = startOffset + change.originalLength;
|
||||
const endPos = textModel.getPositionAt(endOffset);
|
||||
const range = Range.fromPositions(startPos, endPos);
|
||||
const text = modifiedText.substring(change.modifiedStart, change.modifiedStart + change.modifiedLength);
|
||||
|
||||
const tokenInfo = getTokenAtPosition(textModel, startPos);
|
||||
if (tokenInfo.type === StandardTokenType.Other) {
|
||||
|
||||
let identifier = textModel.getValueInRange(tokenInfo.range);
|
||||
if (oldName === undefined) {
|
||||
oldName = identifier;
|
||||
} else if (oldName !== identifier) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// We assume that the new name starts at the same position as the old name from a token range perspective.
|
||||
const diff = text.length - change.originalLength;
|
||||
const tokenStartPos = textModel.getOffsetAt(tokenInfo.range.getStartPosition()) - nesOffset + tokenDiff;
|
||||
const tokenEndPos = textModel.getOffsetAt(tokenInfo.range.getEndPosition()) - nesOffset + tokenDiff;
|
||||
identifier = modifiedText.substring(tokenStartPos, tokenEndPos + diff);
|
||||
if (newName === undefined) {
|
||||
newName = identifier;
|
||||
} else if (newName !== identifier) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (position === undefined) {
|
||||
position = tokenInfo.range.getStartPosition();
|
||||
}
|
||||
|
||||
renames.push(TextEdit.replace(range, text));
|
||||
tokenDiff += diff;
|
||||
} else {
|
||||
others.push(TextEdit.replace(range, text));
|
||||
}
|
||||
}
|
||||
|
||||
if (oldName === undefined || newName === undefined || position === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
renames: { edits: renames, position, oldName, newName },
|
||||
others: { edits: others }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getTokenAtPosition(textModel: ITextModel, position: Position): { type: StandardTokenType; range: Range } {
|
||||
textModel.tokenization.tokenizeIfCheap(position.lineNumber);
|
||||
const tokens = textModel.tokenization.getLineTokens(position.lineNumber);
|
||||
const idx = tokens.findTokenIndexAtOffset(position.column - 1);
|
||||
return {
|
||||
type: tokens.getStandardTokenType(idx),
|
||||
range: new Range(position.lineNumber, 1 + tokens.getStartOffset(idx), position.lineNumber, 1 + tokens.getEndOffset(idx))
|
||||
};
|
||||
}
|
||||
@@ -39,6 +39,9 @@ export type InlineCompletionEndOfLifeEvent = {
|
||||
preceeded: boolean | undefined;
|
||||
superseded: boolean | undefined;
|
||||
notShownReason: string | undefined;
|
||||
renameCreated: boolean;
|
||||
renameDuration: number | undefined;
|
||||
renameTimedOut: boolean;
|
||||
// rendering
|
||||
viewKind: string | undefined;
|
||||
cursorColumnDistance: number | undefined;
|
||||
@@ -79,6 +82,9 @@ type InlineCompletionsEndOfLifeClassification = {
|
||||
requestReason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The reason for the inline completion request' };
|
||||
typingInterval: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The average typing interval of the user at the moment the inline completion was requested' };
|
||||
typingIntervalCharacterCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The character count involved in the typing interval calculation' };
|
||||
renameCreated: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether a rename operation was created' };
|
||||
renameDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The duration of the rename processor' };
|
||||
renameTimedOut: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the rename prepare operation timed out' };
|
||||
superseded: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the inline completion was superseded by another one' };
|
||||
editorType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of the editor where the inline completion was shown' };
|
||||
viewKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The kind of the view where the inline completion was shown' };
|
||||
|
||||
@@ -25,6 +25,7 @@ import { TextEdit } from '../../../../common/core/edits/textEdit.js';
|
||||
import { BugIndicatingError } from '../../../../../base/common/errors.js';
|
||||
import { PositionOffsetTransformer } from '../../../../common/core/text/positionToOffset.js';
|
||||
import { InlineSuggestionsView } from '../../browser/view/inlineSuggestionsView.js';
|
||||
import { IBulkEditService } from '../../../../browser/services/bulkEditService.js';
|
||||
|
||||
export class MockInlineCompletionsProvider implements InlineCompletionsProvider {
|
||||
private returnValue: InlineCompletion[] = [];
|
||||
@@ -243,6 +244,13 @@ export async function withAsyncTestCodeEditorAndInlineCompletionsModel<T>(
|
||||
playSignal: async () => { },
|
||||
isSoundEnabled(signal: unknown) { return false; },
|
||||
} as any);
|
||||
options.serviceCollection.set(IBulkEditService, {
|
||||
apply: async () => { throw new Error('IBulkEditService.apply not implemented'); },
|
||||
hasPreviewHandler: () => { throw new Error('IBulkEditService.hasPreviewHandler not implemented'); },
|
||||
setPreviewHandler: () => { throw new Error('IBulkEditService.setPreviewHandler not implemented'); },
|
||||
_serviceBrand: undefined,
|
||||
});
|
||||
|
||||
const d = languageFeaturesService.inlineCompletionsProvider.register({ pattern: '**' }, options.provider);
|
||||
disposableStore.add(d);
|
||||
}
|
||||
|
||||
@@ -128,6 +128,11 @@ export async function rename(registry: LanguageFeatureRegistry<RenameProvider>,
|
||||
return skeleton.provideRenameEdits(newName, CancellationToken.None);
|
||||
}
|
||||
|
||||
export async function prepareRename(registry: LanguageFeatureRegistry<RenameProvider>, model: ITextModel, position: Position): Promise<RenameLocation & Rejection | undefined> {
|
||||
const skeleton = new RenameSkeleton(model, position, registry);
|
||||
return skeleton.resolveRenameLocation(CancellationToken.None);
|
||||
}
|
||||
|
||||
// --- register actions and commands
|
||||
|
||||
class RenameController implements IEditorContribution {
|
||||
|
||||
Vendored
+6
-2
@@ -7555,7 +7555,8 @@ declare namespace monaco.languages {
|
||||
/** Only show the inline suggestion when the cursor is in the showRange. */
|
||||
readonly showRange?: IRange;
|
||||
readonly warning?: InlineCompletionWarning;
|
||||
readonly hint?: InlineCompletionHint;
|
||||
readonly hint?: IInlineCompletionHint;
|
||||
readonly supportsRename?: boolean;
|
||||
/**
|
||||
* Used for telemetry.
|
||||
*/
|
||||
@@ -7572,7 +7573,7 @@ declare namespace monaco.languages {
|
||||
Label = 2
|
||||
}
|
||||
|
||||
export interface InlineCompletionHint {
|
||||
export interface IInlineCompletionHint {
|
||||
/** Refers to the current document. */
|
||||
range: IRange;
|
||||
style: InlineCompletionHintStyle;
|
||||
@@ -7694,6 +7695,9 @@ declare namespace monaco.languages {
|
||||
typingIntervalCharacterCount: number;
|
||||
selectedSuggestionInfo: boolean;
|
||||
availableProviders: string;
|
||||
renameCreated: boolean;
|
||||
renameDuration?: number;
|
||||
renameTimedOut: boolean;
|
||||
};
|
||||
|
||||
export interface CodeAction {
|
||||
|
||||
@@ -752,6 +752,9 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
|
||||
: reason.kind === InlineCompletionEndOfLifeReasonKind.Ignored ? 'ignored' : undefined,
|
||||
noSuggestionReason: undefined,
|
||||
notShownReason: lifetimeSummary.notShownReason,
|
||||
renameCreated: lifetimeSummary.renameCreated,
|
||||
renameDuration: lifetimeSummary.renameDuration,
|
||||
renameTimedOut: lifetimeSummary.renameTimedOut,
|
||||
...forwardToChannelIf(isCopilotLikeExtension(extensionId)),
|
||||
};
|
||||
|
||||
|
||||
@@ -1447,6 +1447,7 @@ class InlineCompletionAdapter {
|
||||
correlationId: this._isAdditionsProposedApiEnabled ? item.correlationId : undefined,
|
||||
suggestionId: undefined,
|
||||
uri: (this._isAdditionsProposedApiEnabled && item.uri) ? item.uri : undefined,
|
||||
supportsRename: this._isAdditionsProposedApiEnabled ? item.supportsRename : false,
|
||||
});
|
||||
}),
|
||||
commands: commands.map(c => {
|
||||
|
||||
@@ -66,6 +66,8 @@ declare module 'vscode' {
|
||||
completeBracketPairs?: boolean;
|
||||
|
||||
warning?: InlineCompletionWarning;
|
||||
|
||||
supportsRename?: boolean;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user