Skip to content

Commit 24c0076

Browse files
authored
Merge pull request #280159 from microsoft/benibenj/peculiar-peacock
New rename UX
2 parents 4c2e103 + e1b8f79 commit 24c0076

32 files changed

+348
-233
lines changed

src/vs/editor/common/languages.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,8 +1075,10 @@ export type LifetimeSummary = {
10751075
availableProviders: string;
10761076
sku: string | undefined;
10771077
renameCreated: boolean;
1078-
renameDuration?: number;
1078+
renameDuration: number | undefined;
10791079
renameTimedOut: boolean;
1080+
renameDroppedOtherEdits: number | undefined;
1081+
renameDroppedRenameEdits: number | undefined;
10801082
editKind: string | undefined;
10811083
longDistanceHintVisible?: boolean;
10821084
longDistanceHintDistance?: number;

src/vs/editor/contrib/inlineCompletions/browser/controller/commandIds.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
export const inlineSuggestCommitId = 'editor.action.inlineSuggest.commit';
77

8+
export const inlineSuggestCommitAlternativeActionId = 'editor.action.inlineSuggest.commitAlternativeAction';
9+
810
export const showPreviousInlineSuggestionActionId = 'editor.action.inlineSuggest.showPrevious';
911

1012
export const showNextInlineSuggestionActionId = 'editor.action.inlineSuggest.showNext';

src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { EditorContextKeys } from '../../../../common/editorContextKeys.js';
2121
import { InlineCompletionsProvider } from '../../../../common/languages.js';
2222
import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js';
2323
import { Context as SuggestContext } from '../../../suggest/browser/suggest.js';
24-
import { hideInlineCompletionId, inlineSuggestCommitId, jumpToNextInlineEditId, showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId, toggleShowCollapsedId } from './commandIds.js';
24+
import { hideInlineCompletionId, inlineSuggestCommitAlternativeActionId, inlineSuggestCommitId, jumpToNextInlineEditId, showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId, toggleShowCollapsedId } from './commandIds.js';
2525
import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js';
2626
import { InlineCompletionsController } from './inlineCompletionsController.js';
2727

@@ -243,6 +243,37 @@ KeybindingsRegistry.registerKeybindingRule({
243243
when: ContextKeyExpr.and(InlineCompletionContextKeys.inInlineEditsPreviewEditor)
244244
});
245245

246+
export class AcceptInlineCompletionAlternativeAction extends EditorAction {
247+
constructor() {
248+
super({
249+
id: inlineSuggestCommitAlternativeActionId,
250+
label: nls.localize2('action.inlineSuggest.acceptAlternativeAction', "Accept Inline Suggestion Alternative Action"),
251+
precondition: ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionAlternativeActionVisible, InlineCompletionContextKeys.inlineEditVisible),
252+
menuOpts: [],
253+
kbOpts: [
254+
{
255+
primary: KeyMod.Shift | KeyCode.Tab,
256+
weight: 203,
257+
}
258+
],
259+
});
260+
}
261+
262+
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
263+
const controller = InlineCompletionsController.getInFocusedEditorOrParent(accessor);
264+
if (controller) {
265+
controller.model.get()?.accept(controller.editor, true);
266+
controller.editor.focus();
267+
}
268+
}
269+
}
270+
KeybindingsRegistry.registerKeybindingRule({
271+
id: inlineSuggestCommitAlternativeActionId,
272+
weight: 203,
273+
primary: KeyMod.Shift | KeyCode.Tab,
274+
when: ContextKeyExpr.and(InlineCompletionContextKeys.inInlineEditsPreviewEditor)
275+
});
276+
246277
export class JumpToNextInlineEdit extends EditorAction {
247278
constructor() {
248279
super({

src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as nls from '../../../../../nls.js';
1010
export abstract class InlineCompletionContextKeys {
1111

1212
public static readonly inlineSuggestionVisible = new RawContextKey<boolean>('inlineSuggestionVisible', false, localize('inlineSuggestionVisible', "Whether an inline suggestion is visible"));
13+
public static readonly inlineSuggestionAlternativeActionVisible = new RawContextKey<boolean>('inlineSuggestionAlternativeActionVisible', false, localize('inlineSuggestionAlternativeActionVisible', "Whether an alternative action for the inline suggestion is visible."));
1314
public static readonly inlineSuggestionHasIndentation = new RawContextKey<boolean>('inlineSuggestionHasIndentation', false, localize('inlineSuggestionHasIndentation', "Whether the inline suggestion starts with whitespace"));
1415
public static readonly inlineSuggestionHasIndentationLessThanTabSize = new RawContextKey<boolean>('inlineSuggestionHasIndentationLessThanTabSize', true, localize('inlineSuggestionHasIndentationLessThanTabSize', "Whether the inline suggestion starts with whitespace that is less than what would be inserted by tab"));
1516
public static readonly suppressSuggestions = new RawContextKey<boolean | undefined>('inlineSuggestionSuppressSuggestions', undefined, localize('suppressSuggestions', "Whether suggestions should be suppressed for the current suggestion"));

src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ export class InlineCompletionsController extends Disposable {
369369
const state = model?.inlineCompletionState.read(reader);
370370
return state?.primaryGhostText && state?.inlineSuggestion ? state.inlineSuggestion.source.inlineSuggestions.suppressSuggestions : undefined;
371371
}));
372+
this._register(contextKeySvcObs.bind(InlineCompletionContextKeys.inlineSuggestionAlternativeActionVisible, reader => {
373+
const model = this.model.read(reader);
374+
const state = model?.inlineEditState.read(reader);
375+
const action = state?.inlineSuggestion.action;
376+
return action && action.kind === 'edit' && action.alternativeAction !== undefined;
377+
}));
372378
this._register(contextKeySvcObs.bind(InlineCompletionContextKeys.inlineSuggestionVisible, reader => {
373379
const model = this.model.read(reader);
374380
const state = model?.inlineCompletionState.read(reader);

src/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { registerAction2 } from '../../../../platform/actions/common/actions.js'
88
import { wrapInHotClass1 } from '../../../../platform/observable/common/wrapInHotClass.js';
99
import { EditorContributionInstantiation, registerEditorAction, registerEditorContribution } from '../../../browser/editorExtensions.js';
1010
import { HoverParticipantRegistry } from '../../hover/browser/hoverTypes.js';
11-
import { AcceptInlineCompletion, AcceptNextLineOfInlineCompletion, AcceptNextWordOfInlineCompletion, DevExtractReproSample, HideInlineCompletion, JumpToNextInlineEdit, ShowNextInlineSuggestionAction, ShowPreviousInlineSuggestionAction, ToggleAlwaysShowInlineSuggestionToolbar, TriggerInlineSuggestionAction, ToggleInlineCompletionShowCollapsed } from './controller/commands.js';
11+
import { AcceptInlineCompletion, AcceptNextLineOfInlineCompletion, AcceptNextWordOfInlineCompletion, DevExtractReproSample, HideInlineCompletion, JumpToNextInlineEdit, ShowNextInlineSuggestionAction, ShowPreviousInlineSuggestionAction, ToggleAlwaysShowInlineSuggestionToolbar, TriggerInlineSuggestionAction, ToggleInlineCompletionShowCollapsed, AcceptInlineCompletionAlternativeAction } from './controller/commands.js';
1212
import { InlineCompletionsController } from './controller/inlineCompletionsController.js';
1313
import { InlineCompletionsHoverParticipant } from './hintsWidget/hoverParticipant.js';
1414
import { InlineCompletionsAccessibleView } from './inlineCompletionsAccessibleView.js';
@@ -22,6 +22,7 @@ registerEditorAction(ShowPreviousInlineSuggestionAction);
2222
registerEditorAction(AcceptNextWordOfInlineCompletion);
2323
registerEditorAction(AcceptNextLineOfInlineCompletion);
2424
registerEditorAction(AcceptInlineCompletion);
25+
registerEditorAction(AcceptInlineCompletionAlternativeAction);
2526
registerEditorAction(ToggleInlineCompletionShowCollapsed);
2627
registerEditorAction(HideInlineCompletion);
2728
registerEditorAction(JumpToNextInlineEdit);

src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import { StringReplacement } from '../../../../common/core/edits/stringEdit.js';
5151
import { OffsetRange } from '../../../../common/core/ranges/offsetRange.js';
5252
import { URI } from '../../../../../base/common/uri.js';
5353
import { IDefaultAccountService } from '../../../../../platform/defaultAccount/common/defaultAccount.js';
54-
import { ModifierKeyEmitter } from '../../../../../base/browser/dom.js';
5554

5655
export class InlineCompletionsModel extends Disposable {
5756
private readonly _source;
@@ -638,7 +637,7 @@ export class InlineCompletionsModel extends Disposable {
638637
return undefined;
639638
}
640639
const cursorAtInlineEdit = this.primaryPosition.map(cursorPos => LineRange.fromRangeInclusive(inlineEditResult.targetRange).addMargin(1, 1).contains(cursorPos.lineNumber));
641-
const stringEdit = inlineEditResult.action?.kind === 'edit' || inlineEditResult.action?.kind === 'rename' ? inlineEditResult.action.stringEdit : undefined;
640+
const stringEdit = inlineEditResult.action?.kind === 'edit' ? inlineEditResult.action.stringEdit : undefined;
642641
const replacements = stringEdit ? TextEdit.fromStringEdit(stringEdit, new TextModelText(this.textModel)).replacements : [];
643642

644643
const nextEditUri = (item.inlineEdit?.command?.id === 'vscode.open' || item.inlineEdit?.command?.id === '_workbench.open') &&
@@ -893,7 +892,7 @@ export class InlineCompletionsModel extends Disposable {
893892
}
894893
}
895894

896-
public async accept(editor: ICodeEditor = this._editor): Promise<void> {
895+
public async accept(editor: ICodeEditor = this._editor, alternativeAction: boolean = false): Promise<void> {
897896
if (editor.getModel() !== this.textModel) {
898897
throw new BugIndicatingError();
899898
}
@@ -920,13 +919,13 @@ export class InlineCompletionsModel extends Disposable {
920919
editor.pushUndoStop();
921920
if (isNextEditUri) {
922921
// Do nothing
923-
} else if (completion.action?.kind === 'edit' || completion.action?.kind === 'rename') {
922+
} else if (completion.action?.kind === 'edit') {
924923
const action = completion.action;
925-
if (action.kind === 'rename' && !ModifierKeyEmitter.getInstance().keyStatus.altKey) {
924+
if (action.alternativeAction && alternativeAction) {
926925
await this._commandService
927-
.executeCommand(action.command.id, ...(action.command.arguments || []))
926+
.executeCommand(action.alternativeAction?.id, ...(action.alternativeAction?.arguments || []))
928927
.then(undefined, onUnexpectedExternalError);
929-
} else if (action.kind === 'edit' && action.snippetInfo) {
928+
} else if (action.snippetInfo) {
930929
const mainEdit = TextReplacement.delete(action.textReplacement.range);
931930
const additionalEdits = completion.additionalTextEdits.map(e => new TextReplacement(Range.lift(e.range), e.text ?? ''));
932931
const edit = TextEdit.fromParallelReplacementsUnsorted([mainEdit, ...additionalEdits]);

src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,8 @@ export class InlineCompletionsSource extends Disposable {
497497
renameCreated: false,
498498
renameDuration: undefined,
499499
renameTimedOut: false,
500+
renameDroppedOtherEdits: undefined,
501+
renameDroppedRenameEdits: undefined,
500502
performanceMarkers: undefined,
501503
editKind: undefined,
502504
};

src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { EndOfLinePreference, ITextModel } from '../../../../common/model.js';
2525
import { TextModelText } from '../../../../common/model/textModelText.js';
2626
import { InlineCompletionViewData, InlineCompletionViewKind } from '../view/inlineEdits/inlineEditsViewInterface.js';
2727
import { computeEditKind, InlineSuggestionEditKind } from './editKind.js';
28-
import { IInlineSuggestDataActionEdit, IInlineSuggestDataActionRename, InlineSuggestData, InlineSuggestionList, PartialAcceptance, RenameInfo, SnippetInfo } from './provideInlineCompletions.js';
28+
import { IInlineSuggestDataAction, IInlineSuggestDataActionEdit, InlineSuggestData, InlineSuggestionList, PartialAcceptance, RenameInfo, SnippetInfo } from './provideInlineCompletions.js';
2929
import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js';
3030

3131
export type InlineSuggestionItem = InlineEditItem | InlineCompletionItem;
@@ -43,14 +43,15 @@ export namespace InlineSuggestionItem {
4343
}
4444
}
4545

46-
export type InlineSuggestionAction = IInlineSuggestionActionEdit | IInlineSuggestionActionJumpTo | IInlineSuggestionActionRename;
46+
export type InlineSuggestionAction = IInlineSuggestionActionEdit | IInlineSuggestionActionJumpTo;
4747

4848
export interface IInlineSuggestionActionEdit {
4949
kind: 'edit';
5050
textReplacement: TextReplacement;
5151
snippetInfo: SnippetInfo | undefined;
5252
stringEdit: StringEdit;
5353
uri: URI | undefined;
54+
alternativeAction: Command | undefined;
5455
}
5556

5657
export interface IInlineSuggestionActionJumpTo {
@@ -60,16 +61,8 @@ export interface IInlineSuggestionActionJumpTo {
6061
uri: URI | undefined;
6162
}
6263

63-
export interface IInlineSuggestionActionRename {
64-
kind: 'rename';
65-
textReplacement: TextReplacement;
66-
stringEdit: StringEdit;
67-
uri: URI | undefined;
68-
command: Command;
69-
}
70-
7164
function hashInlineSuggestionAction(action: InlineSuggestionAction | undefined): string {
72-
const obj = action?.kind === 'rename' ? { ...action, command: action.command.id } : action;
65+
const obj = action?.kind === 'edit' ? { ...action, alternativeAction: action.alternativeAction?.id } : action;
7366
return JSON.stringify(obj);
7467
}
7568

@@ -96,7 +89,7 @@ abstract class InlineSuggestionItemBase {
9689
if (this.hint) {
9790
return this.hint.range;
9891
}
99-
if (this.action?.kind === 'edit' || this.action?.kind === 'rename') {
92+
if (this.action?.kind === 'edit') {
10093
return this.action.textReplacement.range;
10194
} else if (this.action?.kind === 'jumpTo') {
10295
return Range.fromPositions(this.action.position);
@@ -145,7 +138,7 @@ abstract class InlineSuggestionItemBase {
145138
}
146139

147140
public reportInlineEditShown(commandService: ICommandService, viewKind: InlineCompletionViewKind, viewData: InlineCompletionViewData, model: ITextModel) {
148-
const insertText = this.action?.kind === 'edit' || this.action?.kind === 'rename' ? this.action.textReplacement.text : ''; // TODO@hediet support insertText === undefined
141+
const insertText = this.action?.kind === 'edit' ? this.action.textReplacement.text : ''; // TODO@hediet support insertText === undefined
149142
this._data.reportInlineEditShown(commandService, insertText, viewKind, viewData, this.computeEditKind(model));
150143
}
151144

@@ -180,8 +173,8 @@ abstract class InlineSuggestionItemBase {
180173
this._data.setRenameProcessingInfo(info);
181174
}
182175

183-
public withRename(renameAction: IInlineSuggestDataActionRename): InlineSuggestData {
184-
return this._data.withRename(renameAction);
176+
public withAction(action: IInlineSuggestDataAction): InlineSuggestData {
177+
return this._data.withAction(action);
185178
}
186179

187180
public addPerformanceMarker(marker: string): void {
@@ -295,6 +288,7 @@ export class InlineCompletionItem extends InlineSuggestionItemBase {
295288
snippetInfo: this.snippetInfo,
296289
stringEdit: new StringEdit([this._trimmedEdit]),
297290
uri: undefined,
291+
alternativeAction: undefined,
298292
};
299293
}
300294

@@ -438,6 +432,7 @@ export class InlineEditItem extends InlineSuggestionItemBase {
438432
stringEdit: offsetEdit,
439433
textReplacement: singleTextEdit,
440434
uri: data.action.uri,
435+
alternativeAction: data.action.alternativeAction,
441436
};
442437
} else if (data.action?.kind === 'jumpTo') {
443438
action = {
@@ -446,8 +441,6 @@ export class InlineEditItem extends InlineSuggestionItemBase {
446441
offset: textModel.getOffsetAt(data.action.position),
447442
uri: data.action.uri,
448443
};
449-
} else if (data.action?.kind === 'rename') {
450-
action = data.action;
451444
} else {
452445
action = undefined;
453446
if (!data.hint) {
@@ -548,6 +541,7 @@ export class InlineEditItem extends InlineSuggestionItemBase {
548541
snippetInfo: this.snippetInfo,
549542
stringEdit: newEdit,
550543
uri: this.action.uri,
544+
alternativeAction: this.action.alternativeAction,
551545
};
552546
} else if (this.action?.kind === 'jumpTo') {
553547
const jumpToOffset = this.action.offset;
@@ -587,7 +581,7 @@ export class InlineEditItem extends InlineSuggestionItemBase {
587581
}
588582

589583
override computeEditKind(model: ITextModel): InlineSuggestionEditKind | undefined {
590-
const edit = this.action?.kind === 'edit' || this.action?.kind === 'rename' ? this.action.stringEdit : undefined;
584+
const edit = this.action?.kind === 'edit' ? this.action.stringEdit : undefined;
591585
if (!edit) {
592586
return undefined;
593587
}

src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js
1111
import { prefixedUuid } from '../../../../../base/common/uuid.js';
1212
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
1313
import { ISingleEditOperation } from '../../../../common/core/editOperation.js';
14-
import { StringEdit, StringReplacement } from '../../../../common/core/edits/stringEdit.js';
14+
import { StringReplacement } from '../../../../common/core/edits/stringEdit.js';
1515
import { OffsetRange } from '../../../../common/core/ranges/offsetRange.js';
1616
import { Position } from '../../../../common/core/position.js';
1717
import { Range } from '../../../../common/core/range.js';
@@ -253,6 +253,7 @@ function toInlineSuggestData(
253253
insertText,
254254
snippetInfo,
255255
uri,
256+
alternativeAction: undefined,
256257
};
257258
} else {
258259
action = undefined;
@@ -302,6 +303,8 @@ export type RenameInfo = {
302303
createdRename: boolean;
303304
duration: number;
304305
timedOut?: boolean;
306+
droppedOtherEdits?: number;
307+
droppedRenameEdits?: number;
305308
};
306309

307310
export type InlineSuggestViewData = {
@@ -310,14 +313,15 @@ export type InlineSuggestViewData = {
310313
viewKind?: InlineCompletionViewKind;
311314
};
312315

313-
export type IInlineSuggestDataAction = IInlineSuggestDataActionEdit | IInlineSuggestDataActionJumpTo | IInlineSuggestDataActionRename;
316+
export type IInlineSuggestDataAction = IInlineSuggestDataActionEdit | IInlineSuggestDataActionJumpTo;
314317

315318
export interface IInlineSuggestDataActionEdit {
316319
kind: 'edit';
317320
range: Range;
318321
insertText: string;
319322
snippetInfo: SnippetInfo | undefined;
320323
uri: URI | undefined;
324+
alternativeAction: Command | undefined;
321325
}
322326

323327
export interface IInlineSuggestDataActionJumpTo {
@@ -326,14 +330,6 @@ export interface IInlineSuggestDataActionJumpTo {
326330
uri: URI | undefined;
327331
}
328332

329-
export interface IInlineSuggestDataActionRename {
330-
kind: 'rename';
331-
textReplacement: TextReplacement;
332-
stringEdit: StringEdit;
333-
uri: URI | undefined;
334-
command: Command;
335-
}
336-
337333
export class InlineSuggestData {
338334
private _didShow = false;
339335
private _timeUntilShown: number | undefined = undefined;
@@ -352,8 +348,12 @@ export class InlineSuggestData {
352348
private _renameInfo: RenameInfo | undefined = undefined;
353349
private _editKind: InlineSuggestionEditKind | undefined = undefined;
354350

351+
get action(): IInlineSuggestDataAction | undefined {
352+
return this._action;
353+
}
354+
355355
constructor(
356-
public readonly action: IInlineSuggestDataAction | undefined,
356+
private _action: IInlineSuggestDataAction | undefined,
357357
public readonly hint: IInlineCompletionHint | undefined,
358358
public readonly additionalTextEdits: readonly ISingleEditOperation[],
359359
public readonly sourceInlineCompletion: InlineCompletion,
@@ -454,11 +454,13 @@ export class InlineSuggestData {
454454
renameCreated: this._renameInfo?.createdRename ?? false,
455455
renameDuration: this._renameInfo?.duration,
456456
renameTimedOut: this._renameInfo?.timedOut ?? false,
457+
renameDroppedOtherEdits: this._renameInfo?.droppedOtherEdits,
458+
renameDroppedRenameEdits: this._renameInfo?.droppedRenameEdits,
457459
typingInterval: this._requestInfo.typingInterval,
458460
typingIntervalCharacterCount: this._requestInfo.typingIntervalCharacterCount,
459461
sku: this._requestInfo.sku,
460462
availableProviders: this._requestInfo.availableProviders.map(p => p.toString()).join(','),
461-
...this._viewData.renderData,
463+
...this._viewData.renderData?.getData(),
462464
};
463465
this.source.provider.handleEndOfLifetime(this.source.inlineSuggestions, this.sourceInlineCompletion, reason, summary);
464466
}
@@ -523,20 +525,9 @@ export class InlineSuggestData {
523525
this._renameInfo = info;
524526
}
525527

526-
public withRename(renameAction: IInlineSuggestDataActionRename): InlineSuggestData {
527-
return new InlineSuggestData(
528-
renameAction,
529-
this.hint,
530-
this.additionalTextEdits,
531-
this.sourceInlineCompletion,
532-
this.source,
533-
this.context,
534-
this.isInlineEdit,
535-
this.supportsRename,
536-
this._requestInfo,
537-
this._providerRequestInfo,
538-
this._correlationId,
539-
);
528+
public withAction(action: IInlineSuggestDataAction): InlineSuggestData {
529+
this._action = action;
530+
return this;
540531
}
541532

542533
private performance = new InlineSuggestionsPerformance();

0 commit comments

Comments
 (0)