mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
Merge pull request #124828 from microsoft/alex/ghost-text
More Ghost Text Improvements
This commit is contained in:
commit
24143e9132
|
@ -3175,14 +3175,18 @@ export interface ISuggestOptions {
|
|||
*/
|
||||
showStatusBar?: boolean;
|
||||
/**
|
||||
* Enable or disable the rendering of the suggestion inline.
|
||||
* Enable or disable the rendering of the suggestion preview.
|
||||
*/
|
||||
showSuggestionPreview?: boolean;
|
||||
/**
|
||||
* Enable or disable the default expansion of the suggestion preview.
|
||||
* Defaults to false.
|
||||
* Enable or disable the rendering of automatic inline completions.
|
||||
*/
|
||||
showInlineCompletions?: boolean;
|
||||
/**
|
||||
* Enable or disable the default expansion of the ghost text as used
|
||||
* by the suggestion preview or the inline completions.
|
||||
*/
|
||||
suggestionPreviewExpanded?: boolean;
|
||||
ghostTextExpanded?: boolean;
|
||||
/**
|
||||
* Show details inline with the label. Defaults to true.
|
||||
*/
|
||||
|
@ -3315,7 +3319,8 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
|||
showIcons: true,
|
||||
showStatusBar: false,
|
||||
showSuggestionPreview: false,
|
||||
suggestionPreviewExpanded: true,
|
||||
ghostTextExpanded: true,
|
||||
showInlineCompletions: false,
|
||||
showInlineDetails: true,
|
||||
showMethods: true,
|
||||
showFunctions: true,
|
||||
|
@ -3394,12 +3399,16 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
|||
default: defaults.showSuggestionPreview,
|
||||
description: nls.localize('suggest.showSuggestionPreview', "Controls whether to preview the suggestion outcome in the editor.")
|
||||
},
|
||||
'editor.suggest.suggestionPreviewExpanded': {
|
||||
'editor.suggest.showInlineCompletions': {
|
||||
type: 'boolean',
|
||||
default: defaults.suggestionPreviewExpanded,
|
||||
description: nls.localize('suggest.suggestionPreviewExpanded', "Controls whether the suggestiion preview is expanded by default.")
|
||||
default: defaults.showInlineCompletions,
|
||||
description: nls.localize('suggest.showInlineCompletions', "Controls whether to show inline completions in the editor.")
|
||||
},
|
||||
'editor.suggest.ghostTextExpanded': {
|
||||
type: 'boolean',
|
||||
default: defaults.ghostTextExpanded,
|
||||
description: nls.localize('suggest.ghostTextExpanded', "Controls whether the ghost text that is used by the suggestion preview or the inline completions is expanted by default.")
|
||||
},
|
||||
|
||||
'editor.suggest.showInlineDetails': {
|
||||
type: 'boolean',
|
||||
default: defaults.showInlineDetails,
|
||||
|
@ -3576,7 +3585,8 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
|||
showIcons: boolean(input.showIcons, this.defaultValue.showIcons),
|
||||
showStatusBar: boolean(input.showStatusBar, this.defaultValue.showStatusBar),
|
||||
showSuggestionPreview: boolean(input.showSuggestionPreview, this.defaultValue.showSuggestionPreview),
|
||||
suggestionPreviewExpanded: boolean(input.suggestionPreviewExpanded, this.defaultValue.suggestionPreviewExpanded),
|
||||
ghostTextExpanded: boolean(input.ghostTextExpanded, this.defaultValue.ghostTextExpanded),
|
||||
showInlineCompletions: boolean(input.showInlineCompletions, this.defaultValue.showInlineCompletions),
|
||||
showInlineDetails: boolean(input.showInlineDetails, this.defaultValue.showInlineDetails),
|
||||
showMethods: boolean(input.showMethods, this.defaultValue.showMethods),
|
||||
showFunctions: boolean(input.showFunctions, this.defaultValue.showFunctions),
|
||||
|
|
|
@ -30,8 +30,8 @@ export class GhostTextController extends Disposable {
|
|||
|
||||
private readonly widget: GhostTextWidget;
|
||||
private readonly activeController = this._register(new MutableDisposable<ActiveGhostTextController>());
|
||||
|
||||
private readonly contextKeys: GhostTextContextKeys;
|
||||
private triggeredExplicitly = false;
|
||||
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
|
@ -54,13 +54,21 @@ export class GhostTextController extends Disposable {
|
|||
this.updateModelController();
|
||||
}
|
||||
|
||||
// Don't call this method when not neccessary. It will recreate the activeController.
|
||||
private updateModelController(): void {
|
||||
const suggestOptions = this.editor.getOption(EditorOption.suggest);
|
||||
|
||||
this.activeController.value = undefined;
|
||||
this.activeController.value = this.editor.hasModel() && suggestOptions.showSuggestionPreview
|
||||
? this.instantiationService.createInstance(ActiveGhostTextController, this.editor, this.widget, this.contextKeys)
|
||||
: undefined;
|
||||
// ActiveGhostTextController is only created if one of those settings is set or if the inline completions are triggered explicitly.
|
||||
this.activeController.value =
|
||||
this.editor.hasModel() && (suggestOptions.showSuggestionPreview || suggestOptions.showInlineCompletions || this.triggeredExplicitly)
|
||||
? this.instantiationService.createInstance(
|
||||
ActiveGhostTextController,
|
||||
this.editor,
|
||||
this.widget,
|
||||
this.contextKeys
|
||||
)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
public shouldShowHoverAt(hoverRange: Range): boolean {
|
||||
|
@ -72,15 +80,19 @@ export class GhostTextController extends Disposable {
|
|||
}
|
||||
|
||||
public trigger(): void {
|
||||
this.activeController.value?.trigger();
|
||||
this.triggeredExplicitly = true;
|
||||
if (!this.activeController.value) {
|
||||
this.updateModelController();
|
||||
}
|
||||
this.activeController.value?.triggerInlineCompletion();
|
||||
}
|
||||
|
||||
public commit(): void {
|
||||
this.activeController.value?.commit();
|
||||
this.activeController.value?.commitInlineCompletion();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this.activeController.value?.hide();
|
||||
this.activeController.value?.hideInlineCompletion();
|
||||
}
|
||||
|
||||
public showNextInlineCompletion(): void {
|
||||
|
@ -104,8 +116,15 @@ class GhostTextContextKeys {
|
|||
* The controller for a text editor with an initialized text model.
|
||||
*/
|
||||
export class ActiveGhostTextController extends Disposable {
|
||||
private readonly suggestWidgetAdapterModel = new SuggestWidgetAdapterModel(this.editor);
|
||||
private readonly inlineCompletionsModel = new InlineCompletionsModel(this.editor, this.commandService);
|
||||
private readonly suggestWidgetAdapterModel = this._register(new SuggestWidgetAdapterModel(this.editor));
|
||||
private readonly inlineCompletionsModel = this._register(new InlineCompletionsModel(this.editor, this.commandService));
|
||||
|
||||
private get activeInlineCompletionsModel(): InlineCompletionsModel | undefined {
|
||||
if (this.widget.model === this.inlineCompletionsModel) {
|
||||
return this.inlineCompletionsModel;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly editor: IActiveCodeEditor,
|
||||
|
@ -118,7 +137,6 @@ export class ActiveGhostTextController extends Disposable {
|
|||
this._register(this.suggestWidgetAdapterModel.onDidChange(() => {
|
||||
this.updateModel();
|
||||
}));
|
||||
|
||||
this.updateModel();
|
||||
|
||||
this._register(toDisposable(() => {
|
||||
|
@ -127,18 +145,19 @@ export class ActiveGhostTextController extends Disposable {
|
|||
}
|
||||
}));
|
||||
|
||||
this._register(this.inlineCompletionsModel.onDidChange(() => {
|
||||
this.updateContextKeys();
|
||||
}));
|
||||
if (this.inlineCompletionsModel) {
|
||||
this._register(this.inlineCompletionsModel.onDidChange(() => {
|
||||
this.updateContextKeys();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private updateContextKeys(): void {
|
||||
this.contextKeys.inlineCompletionVisible.set(
|
||||
this.widget.model === this.inlineCompletionsModel
|
||||
&& this.inlineCompletionsModel.ghostText !== undefined
|
||||
this.activeInlineCompletionsModel?.ghostText !== undefined
|
||||
);
|
||||
|
||||
if (this.inlineCompletionsModel.ghostText) {
|
||||
if (this.inlineCompletionsModel?.ghostText) {
|
||||
const firstLine = this.inlineCompletionsModel.ghostText.lines[0] || '';
|
||||
const suggestionStartsWithWs = firstLine.startsWith(' ') || firstLine.startsWith('\t');
|
||||
const p = this.inlineCompletionsModel.ghostText.position;
|
||||
|
@ -155,48 +174,40 @@ export class ActiveGhostTextController extends Disposable {
|
|||
}
|
||||
|
||||
public shouldShowHoverAt(hoverRange: Range): boolean {
|
||||
if (this.widget.model === this.inlineCompletionsModel) {
|
||||
const ghostText = this.widget.model.ghostText;
|
||||
if (ghostText) {
|
||||
return hoverRange.containsPosition(ghostText.position);
|
||||
}
|
||||
const ghostText = this.activeInlineCompletionsModel?.ghostText;
|
||||
if (ghostText) {
|
||||
return hoverRange.containsPosition(ghostText.position);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public trigger(): void {
|
||||
if (this.widget.model === this.inlineCompletionsModel) {
|
||||
this.inlineCompletionsModel.startSession();
|
||||
}
|
||||
public triggerInlineCompletion(): void {
|
||||
this.activeInlineCompletionsModel?.startSession();
|
||||
}
|
||||
|
||||
public commit(): void {
|
||||
if (this.widget.model === this.inlineCompletionsModel) {
|
||||
this.inlineCompletionsModel.commitCurrentSuggestion();
|
||||
}
|
||||
public commitInlineCompletion(): void {
|
||||
this.activeInlineCompletionsModel?.commitCurrentSuggestion();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
if (this.widget.model === this.inlineCompletionsModel) {
|
||||
this.inlineCompletionsModel.hide();
|
||||
}
|
||||
public hideInlineCompletion(): void {
|
||||
this.activeInlineCompletionsModel?.hide();
|
||||
}
|
||||
|
||||
public showNextInlineCompletion(): void {
|
||||
if (this.widget.model === this.inlineCompletionsModel) {
|
||||
this.inlineCompletionsModel.showNextInlineCompletion();
|
||||
}
|
||||
this.activeInlineCompletionsModel?.showNext();
|
||||
}
|
||||
|
||||
public showPreviousInlineCompletion(): void {
|
||||
if (this.widget.model === this.inlineCompletionsModel) {
|
||||
this.inlineCompletionsModel.showPreviousInlineCompletion();
|
||||
}
|
||||
this.activeInlineCompletionsModel?.showPrevious();
|
||||
}
|
||||
|
||||
private updateModel() {
|
||||
this.widget.setModel(this.suggestWidgetAdapterModel.isActive ? this.suggestWidgetAdapterModel : this.inlineCompletionsModel);
|
||||
this.inlineCompletionsModel.setActive(this.widget.model === this.inlineCompletionsModel);
|
||||
this.widget.setModel(
|
||||
this.suggestWidgetAdapterModel.isActive
|
||||
? this.suggestWidgetAdapterModel
|
||||
: this.inlineCompletionsModel
|
||||
);
|
||||
this.inlineCompletionsModel?.setActive(this.widget.model === this.inlineCompletionsModel);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,7 +250,7 @@ export class ShowNextInlineCompletionAction extends EditorAction {
|
|||
precondition: EditorContextKeys.writable,
|
||||
kbOpts: {
|
||||
weight: 100,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow,
|
||||
primary: KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -262,7 +273,7 @@ export class ShowPreviousInlineCompletionAction extends EditorAction {
|
|||
precondition: EditorContextKeys.writable,
|
||||
kbOpts: {
|
||||
weight: 100,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow,
|
||||
primary: KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ export abstract class BaseGhostTextWidgetModel extends Disposable implements Gho
|
|||
|
||||
public get expanded() {
|
||||
if (this._expanded === undefined) {
|
||||
return this.editor.getOption(EditorOption.suggest).suggestionPreviewExpanded;
|
||||
return this.editor.getOption(EditorOption.suggest).ghostTextExpanded;
|
||||
}
|
||||
return this._expanded;
|
||||
}
|
||||
|
|
|
@ -11,10 +11,11 @@ import { IModelDecoration } from 'vs/editor/common/model';
|
|||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { GhostTextController, ShowNextInlineCompletionAction, ShowPreviousInlineCompletionAction } from 'vs/editor/contrib/inlineCompletions/ghostTextController';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewZoneData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
|
||||
export class InlineCompletionsHover implements IHoverPart {
|
||||
|
||||
constructor(
|
||||
public readonly owner: IEditorHoverParticipant<InlineCompletionsHover>,
|
||||
public readonly range: Range
|
||||
|
@ -27,15 +28,15 @@ export class InlineCompletionsHover implements IHoverPart {
|
|||
&& this.range.endColumn >= anchor.range.endColumn
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class InlineCompletionsHoverParticipant implements IEditorHoverParticipant<InlineCompletionsHover> {
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
hover: IEditorHover,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IMenuService private readonly _menuService: IMenuService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
) { }
|
||||
|
||||
suggestHoverAnchor(mouseEvent: IEditorMouseEvent): HoverAnchor | null {
|
||||
|
@ -72,17 +73,34 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan
|
|||
}
|
||||
|
||||
renderHoverParts(hoverParts: InlineCompletionsHover[], fragment: DocumentFragment, statusBar: IEditorHoverStatusBar): IDisposable {
|
||||
const menu = this._menuService.createMenu(
|
||||
MenuId.InlineCompletionsActions,
|
||||
this._contextKeyService
|
||||
);
|
||||
|
||||
statusBar.addAction({
|
||||
label: nls.localize('showPreviousInlineCompletion', "⬅️ Previous"),
|
||||
label: nls.localize('showPreviousInlineCompletion', "Previous"),
|
||||
commandId: ShowPreviousInlineCompletionAction.ID,
|
||||
run: () => this._commandService.executeCommand(ShowPreviousInlineCompletionAction.ID)
|
||||
});
|
||||
statusBar.addAction({
|
||||
label: nls.localize('showNextInlineCompletion', "Next ➡️"),
|
||||
label: nls.localize('showNextInlineCompletion', "Next"),
|
||||
commandId: ShowNextInlineCompletionAction.ID,
|
||||
run: () => this._commandService.executeCommand(ShowNextInlineCompletionAction.ID)
|
||||
});
|
||||
|
||||
for (const [_, group] of menu.getActions()) {
|
||||
for (const action of group) {
|
||||
if (action instanceof MenuItemAction) {
|
||||
statusBar.addAction({
|
||||
label: action.label,
|
||||
commandId: action.item.id,
|
||||
run: () => this._commandService.executeCommand(action.item.id)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import { InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCom
|
|||
import { BaseGhostTextWidgetModel, GhostText, GhostTextWidgetModel } from 'vs/editor/contrib/inlineCompletions/ghostTextWidget';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
export class InlineCompletionsModel extends Disposable implements GhostTextWidgetModel {
|
||||
protected readonly onDidChangeEmitter = new Emitter<void>();
|
||||
|
@ -77,6 +78,11 @@ export class InlineCompletionsModel extends Disposable implements GhostTextWidge
|
|||
}
|
||||
|
||||
private startSessionIfTriggered(): void {
|
||||
const suggestOptions = this.editor.getOption(EditorOption.suggest);
|
||||
if (!suggestOptions.showInlineCompletions) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.session && this.session.isValid) {
|
||||
return;
|
||||
}
|
||||
|
@ -105,11 +111,11 @@ export class InlineCompletionsModel extends Disposable implements GhostTextWidge
|
|||
this.session?.commitCurrentCompletion();
|
||||
}
|
||||
|
||||
public showNextInlineCompletion(): void {
|
||||
public showNext(): void {
|
||||
this.session?.showNextInlineCompletion();
|
||||
}
|
||||
|
||||
public showPreviousInlineCompletion(): void {
|
||||
public showPrevious(): void {
|
||||
this.session?.showPreviousInlineCompletion();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import { Event } from 'vs/base/common/event';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { CompletionItemInsertTextRule } from 'vs/editor/common/modes';
|
||||
|
@ -66,6 +67,11 @@ export class SuggestWidgetAdapterModel extends BaseGhostTextWidgetModel {
|
|||
}));
|
||||
}
|
||||
|
||||
private isSuggestionPreviewEnabled(): boolean {
|
||||
const suggestOptions = this.editor.getOption(EditorOption.suggest);
|
||||
return suggestOptions.showSuggestionPreview;
|
||||
}
|
||||
|
||||
private updateFromSuggestion(): void {
|
||||
const suggestController = SuggestController.get(this.editor);
|
||||
if (!suggestController) {
|
||||
|
@ -109,7 +115,7 @@ export class SuggestWidgetAdapterModel extends BaseGhostTextWidgetModel {
|
|||
|
||||
const suggestController = SuggestController.get(this.editor);
|
||||
if (suggestController) {
|
||||
if (this.minReservedLineCount >= 1) {
|
||||
if (this.minReservedLineCount >= 1 && this.isSuggestionPreviewEnabled()) {
|
||||
suggestController.forceRenderingAbove();
|
||||
} else {
|
||||
suggestController.stopForceRenderingAbove();
|
||||
|
@ -120,7 +126,9 @@ export class SuggestWidgetAdapterModel extends BaseGhostTextWidgetModel {
|
|||
}
|
||||
|
||||
public override get ghostText(): GhostText | undefined {
|
||||
return this.currentGhostText;
|
||||
return this.isSuggestionPreviewEnabled()
|
||||
? this.currentGhostText
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
src/vs/monaco.d.ts
vendored
12
src/vs/monaco.d.ts
vendored
|
@ -3845,14 +3845,18 @@ declare namespace monaco.editor {
|
|||
*/
|
||||
showStatusBar?: boolean;
|
||||
/**
|
||||
* Enable or disable the rendering of the suggestion inline.
|
||||
* Enable or disable the rendering of the suggestion preview.
|
||||
*/
|
||||
showSuggestionPreview?: boolean;
|
||||
/**
|
||||
* Enable or disable the default expansion of the suggestion preview.
|
||||
* Defaults to false.
|
||||
* Enable or disable the rendering of automatic inline completions.
|
||||
*/
|
||||
showInlineCompletions?: boolean;
|
||||
/**
|
||||
* Enable or disable the default expansion of the ghost text as used
|
||||
* by the suggestion preview or the inline completions.
|
||||
*/
|
||||
suggestionPreviewExpanded?: boolean;
|
||||
ghostTextExpanded?: boolean;
|
||||
/**
|
||||
* Show details inline with the label. Defaults to true.
|
||||
*/
|
||||
|
|
|
@ -171,6 +171,7 @@ export class MenuId {
|
|||
static readonly TerminalTabEmptyAreaContext = new MenuId('TerminalTabEmptyAreaContext');
|
||||
static readonly TerminalInlineTabContext = new MenuId('TerminalInlineTabContext');
|
||||
static readonly WebviewContext = new MenuId('WebviewContext');
|
||||
static readonly InlineCompletionsActions = new MenuId('InlineCompletionsActions');
|
||||
|
||||
readonly id: number;
|
||||
readonly _debugName: string;
|
||||
|
|
|
@ -229,7 +229,13 @@ const apiMenus: IAPIMenu[] = [
|
|||
key: 'ports/item/port/inline',
|
||||
id: MenuId.TunnelPortInline,
|
||||
description: localize('view.tunnelPortInline', "The Ports view item port inline menu")
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'editor/inlineCompletions/actions',
|
||||
id: MenuId.InlineCompletionsActions,
|
||||
description: localize('inlineCompletions.actions', "The actions shown when hovering on an inline completion"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
];
|
||||
|
||||
namespace schema {
|
||||
|
|
Loading…
Reference in a new issue