Merge pull request #197817 from microsoft/aiday/sparkleOnHover

Add a setting to make the sparkle appear on hover or always
This commit is contained in:
Aiday Marlen Kyzy 2023-11-10 09:20:08 +01:00 committed by GitHub
commit df2dd86b0c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 27 deletions

View file

@ -789,4 +789,4 @@
"--z-index-notebook-sticky-scroll",
"--zoom-factor"
]
}
}

View file

@ -331,14 +331,23 @@
/* gutter decoration */
.monaco-editor .glyph-margin-widgets .cgmr.codicon-inline-chat {
.monaco-editor .glyph-margin-widgets .cgmr.codicon-inline-chat-opaque,
.monaco-editor .glyph-margin-widgets .cgmr.codicon-inline-chat-transparent {
display: block;
cursor: pointer;
opacity: 0.5;
transition: opacity .2s ease-in-out;
}
.monaco-editor .glyph-margin-widgets .cgmr.codicon-inline-chat:hover {
.monaco-editor .glyph-margin-widgets .cgmr.codicon-inline-chat-opaque {
opacity: 0.5;
}
.monaco-editor .glyph-margin-widgets .cgmr.codicon-inline-chat-transparent {
opacity: 0;
}
.monaco-editor .glyph-margin-widgets .cgmr.codicon-inline-chat-opaque:hover,
.monaco-editor .glyph-margin-widgets .cgmr.codicon-inline-chat-transparent:hover {
opacity: 1;
}

View file

@ -16,7 +16,7 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co
import { DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { GutterActionsRegistry } from 'vs/workbench/contrib/codeEditor/browser/editorLineNumberMenu';
import { Action } from 'vs/base/common/actions';
import { IInlineChatService } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { IInlineChatService, ShowGutterIcon } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Iterable } from 'vs/base/common/iterator';
import { Range } from 'vs/editor/common/core/range';
@ -25,18 +25,22 @@ import { MarkdownString } from 'vs/base/common/htmlContent';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { LOCALIZED_START_INLINE_CHAT_STRING } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions';
import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
const GUTTER_INLINE_CHAT_ICON = registerIcon('inline-chat', Codicon.sparkle, localize('startInlineChatIcon', 'Icon which spawns the inline chat from the gutter'));
const GUTTER_INLINE_CHAT_OPAQUE_ICON = registerIcon('inline-chat-opaque', Codicon.sparkle, localize('startInlineChatOpaqueIcon', 'Icon which spawns the inline chat from the gutter. It is half opaque by default and becomes completely opaque on hover.'));
const GUTTER_INLINE_CHAT_TRANSPARENT_ICON = registerIcon('inline-chat-transparent', Codicon.sparkle, localize('startInlineChatTransparentIcon', 'Icon which spawns the inline chat from the gutter. It is transparent by default and becomes opaque on hover.'));
export class InlineChatDecorationsContribution extends Disposable implements IEditorContribution {
private _gutterDecorationID: string | undefined;
private _inlineChatKeybinding: string | undefined;
private readonly _localToDispose = new DisposableStore();
private readonly _gutterDecoration: IModelDecorationOptions;
private readonly _gutterDecorationOpaque: IModelDecorationOptions;
private readonly _gutterDecorationTransparent: IModelDecorationOptions;
public static readonly GUTTER_SETTING_ID = 'inlineChat.showGutterIcon';
private static readonly GUTTER_ICON_CLASSNAME = 'codicon-inline-chat';
private static readonly GUTTER_ICON_OPAQUE_CLASSNAME = 'codicon-inline-chat-opaque';
private static readonly GUTTER_ICON_TRANSPARENT_CLASSNAME = 'codicon-inline-chat-transparent';
constructor(
private readonly _editor: ICodeEditor,
@ -47,12 +51,8 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd
@IDebugService private readonly _debugService: IDebugService
) {
super();
this._gutterDecoration = ModelDecorationOptions.register({
description: 'inline-chat-decoration',
glyphMarginClassName: ThemeIcon.asClassName(GUTTER_INLINE_CHAT_ICON),
glyphMargin: { position: GlyphMarginLane.Left },
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
});
this._gutterDecorationTransparent = this._registerGutterDecoration(true);
this._gutterDecorationOpaque = this._registerGutterDecoration(false);
this._register(this._configurationService.onDidChangeConfiguration((e: IConfigurationChangeEvent) => {
if (!e.affectsConfiguration(InlineChatDecorationsContribution.GUTTER_SETTING_ID)) {
return;
@ -70,19 +70,30 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd
this._onEnablementOrModelChanged();
}
private _registerGutterDecoration(isTransparent: boolean): ModelDecorationOptions {
return ModelDecorationOptions.register({
description: 'inline-chat-decoration',
glyphMarginClassName: ThemeIcon.asClassName(isTransparent ? GUTTER_INLINE_CHAT_TRANSPARENT_ICON : GUTTER_INLINE_CHAT_OPAQUE_ICON),
glyphMargin: { position: GlyphMarginLane.Left },
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
});
}
private _updateDecorationHover(): void {
const keybinding = this._keybindingService.lookupKeybinding('inlineChat.start')?.getLabel() ?? undefined;
if (this._inlineChatKeybinding === keybinding) {
return;
}
this._inlineChatKeybinding = keybinding;
this._gutterDecoration.glyphMarginHoverMessage = new MarkdownString(keybinding ? localize('runWithKeybinding', 'Start Inline Chat [{0}]', keybinding) : LOCALIZED_START_INLINE_CHAT_STRING);
const hoverMessage = new MarkdownString(keybinding ? localize('runWithKeybinding', 'Start Inline Chat [{0}]', keybinding) : LOCALIZED_START_INLINE_CHAT_STRING);
this._gutterDecorationTransparent.glyphMarginHoverMessage = hoverMessage;
this._gutterDecorationOpaque.glyphMarginHoverMessage = hoverMessage;
}
private _onEnablementOrModelChanged(): void {
// cancels the scheduler, removes editor listeners / removes decoration
this._localToDispose.clear();
if (!this._editor.hasModel() || !this._isSettingEnabled() || !this._hasProvider()) {
if (!this._editor.hasModel() || this._showGutterIconMode() === ShowGutterIcon.Never || !this._hasProvider()) {
return;
}
const editor = this._editor;
@ -94,7 +105,12 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd
this._localToDispose.add(this._inlineChatSessionService.onWillStartSession(onInlineChatSessionChanged));
this._localToDispose.add(this._inlineChatSessionService.onDidEndSession(onInlineChatSessionChanged));
this._localToDispose.add(this._editor.onMouseDown(async (e: IEditorMouseEvent) => {
if (!e.target.element?.classList.contains(InlineChatDecorationsContribution.GUTTER_ICON_CLASSNAME)) {
const showGutterIconMode = this._showGutterIconMode();
const gutterDecorationClassName = showGutterIconMode === ShowGutterIcon.Always ?
InlineChatDecorationsContribution.GUTTER_ICON_OPAQUE_CLASSNAME :
(showGutterIconMode === ShowGutterIcon.MouseOver ?
InlineChatDecorationsContribution.GUTTER_ICON_TRANSPARENT_CLASSNAME : undefined);
if (!gutterDecorationClassName || !e.target.element?.classList.contains(gutterDecorationClassName)) {
return;
}
InlineChatController.get(this._editor)?.run();
@ -131,8 +147,8 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd
}
}
private _isSettingEnabled(): boolean {
return this._configurationService.getValue<boolean>(InlineChatDecorationsContribution.GUTTER_SETTING_ID);
private _showGutterIconMode(): ShowGutterIcon {
return this._configurationService.getValue<ShowGutterIcon>(InlineChatDecorationsContribution.GUTTER_SETTING_ID);
}
private _hasProvider(): boolean {
@ -141,7 +157,11 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd
private _addGutterDecoration(lineNumber: number) {
this._editor.changeDecorations((accessor: IModelDecorationsChangeAccessor) => {
this._gutterDecorationID = accessor.addDecoration(new Range(lineNumber, 0, lineNumber, 0), this._gutterDecoration);
const showGutterIconMode = this._showGutterIconMode();
if (showGutterIconMode === ShowGutterIcon.Never) {
return;
}
this._gutterDecorationID = accessor.addDecoration(new Range(lineNumber, 0, lineNumber, 0), showGutterIconMode === ShowGutterIcon.Always ? this._gutterDecorationOpaque : this._gutterDecorationTransparent);
});
}
@ -170,12 +190,12 @@ GutterActionsRegistry.registerGutterActionsGenerator(({ lineNumber, editor, acce
if (noProviders) {
return;
}
const configurationService = accessor.get(IConfigurationService);
const preferencesService = accessor.get(IPreferencesService);
result.push(new Action(
'inlineChat.toggleShowGutterIcon',
localize('toggleShowGutterIcon', "Toggle Inline Chat Icon"),
'inlineChat.configureShowGutterIcon',
localize('configureShowGutterIcon', "Configure Inline Chat Icon"),
undefined,
true,
() => { configurationService.updateValue(InlineChatDecorationsContribution.GUTTER_SETTING_ID, !configurationService.getValue<boolean>(InlineChatDecorationsContribution.GUTTER_SETTING_ID)); }
() => { preferencesService.openUserSettings({ query: 'inlineChat.showGutterIcon' }); }
));
});

View file

@ -186,6 +186,12 @@ export const enum EditMode {
Preview = 'preview'
}
export const enum ShowGutterIcon {
Always = 'always',
MouseOver = 'mouseover',
Never = 'never'
}
Registry.as<IConfigurationMigrationRegistry>(ExtensionsMigration.ConfigurationMigration).registerConfigurationMigrations(
[{
key: 'interactiveEditor.editMode', migrateFn: (value: any) => {
@ -214,9 +220,15 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
type: 'boolean'
},
'inlineChat.showGutterIcon': {
description: localize('showGutterIcon', "Show/hide a gutter icon for spawning inline chat on empty lines."),
default: false,
type: 'boolean'
description: localize('showGutterIcon', "Controls when the gutter icon for spawning inline chat is shown."),
default: ShowGutterIcon.Always,
type: 'string',
enum: [ShowGutterIcon.Always, ShowGutterIcon.MouseOver, ShowGutterIcon.Never],
markdownEnumDescriptions: [
localize('showGutterIcon.always', "Always show the gutter icon."),
localize('showGutterIcon.mouseover', "Show the gutter icon when the mouse is over the icon."),
localize('showGutterIcon.never', "Never show the gutter icon."),
]
}
}
});