From f62ffbb85b30a7a0af94f018e7d845bd60a3180b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 12 Nov 2025 16:44:34 +0100 Subject: [PATCH] Remove NLS inline chat hint (#276935) fixes https://github.com/microsoft/vscode/issues/271279 fixes https://github.com/microsoft/vscode/issues/276927 --- .../lib/stylelint/vscode-known-variables.json | 1 - .../browser/inlineChat.contribution.ts | 7 - .../browser/inlineChatCurrentLine.ts | 366 ------------------ .../inlineChat/browser/media/inlineChat.css | 13 - .../contrib/inlineChat/common/inlineChat.ts | 14 - 5 files changed, 401 deletions(-) delete mode 100644 src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index fdbda21e666..dbb93def682 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -900,7 +900,6 @@ "--dropdown-padding-bottom", "--dropdown-padding-top", "--inline-chat-frame-progress", - "--inline-chat-hint-progress", "--insert-border-color", "--last-tab-margin-right", "--monaco-monospace-font", diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index 27b562a58a0..a8e43324fbb 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -22,7 +22,6 @@ import { localize } from '../../../../nls.js'; import { ChatContextKeys } from '../../chat/common/chatContextKeys.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { InlineChatAccessibilityHelp } from './inlineChatAccessibilityHelp.js'; -import { InlineChatExpandLineAction, InlineChatHintsController, HideInlineChatHintAction, ShowInlineChatHintAction } from './inlineChatCurrentLine.js'; registerEditorContribution(InlineChatController2.ID, InlineChatController2, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors registerEditorContribution(INLINE_CHAT_ID, InlineChatController1, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors @@ -35,12 +34,6 @@ registerAction2(InlineChatActions.UndoAndCloseSessionAction2); registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed); - -registerAction2(InlineChatExpandLineAction); -registerAction2(ShowInlineChatHintAction); -registerAction2(HideInlineChatHintAction); -registerEditorContribution(InlineChatHintsController.ID, InlineChatHintsController, EditorContributionInstantiation.Eventually); - // --- MENU special --- const editActionMenuItem: IMenuItem = { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts deleted file mode 100644 index fce058db20d..00000000000 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts +++ /dev/null @@ -1,366 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { stringValue } from '../../../../base/browser/cssValue.js'; -import { createStyleSheet2 } from '../../../../base/browser/domStylesheets.js'; -import { IMouseEvent } from '../../../../base/browser/mouseEvent.js'; -import { toAction } from '../../../../base/common/actions.js'; -import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; -import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { autorun, derived, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; -import { isEqual } from '../../../../base/common/resources.js'; -import { URI } from '../../../../base/common/uri.js'; -import { ICodeEditor, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; -import { EditorAction2, ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { observableCodeEditor } from '../../../../editor/browser/observableCodeEditor.js'; -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { IEditorContribution } from '../../../../editor/common/editorCommon.js'; -import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { StandardTokenType } from '../../../../editor/common/encodedTokenAttributes.js'; -import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; -import { IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/model.js'; -import { IMarkerDecorationsService } from '../../../../editor/common/services/markerDecorations.js'; -import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js'; -import { localize, localize2 } from '../../../../nls.js'; -import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; -import { IChatAgentService } from '../../chat/common/chatAgents.js'; -import { ChatAgentLocation } from '../../chat/common/constants.js'; -import { AGENT_FILE_EXTENSION, LEGACY_MODE_FILE_EXTENSION } from '../../chat/common/promptSyntax/config/promptFileLocations.js'; -import { INSTRUCTIONS_LANGUAGE_ID, PROMPT_LANGUAGE_ID } from '../../chat/common/promptSyntax/promptTypes.js'; -import { ACTION_START, CTX_INLINE_CHAT_V1_ENABLED, CTX_INLINE_CHAT_VISIBLE, InlineChatConfigKeys } from '../common/inlineChat.js'; -import { AbstractInline1ChatAction } from './inlineChatActions.js'; -import { InlineChatController } from './inlineChatController.js'; -import './media/inlineChat.css'; - -/** - * Set of language IDs where inline chat hints should not be shown. - */ -const IGNORED_LANGUAGE_IDS = new Set([ - PLAINTEXT_LANGUAGE_ID, - 'markdown', - 'search-result', - INSTRUCTIONS_LANGUAGE_ID, - PROMPT_LANGUAGE_ID, - LEGACY_MODE_FILE_EXTENSION, - AGENT_FILE_EXTENSION -]); - -export const CTX_INLINE_CHAT_SHOWING_HINT = new RawContextKey('inlineChatShowingHint', false, localize('inlineChatShowingHint', "Whether inline chat shows a contextual hint")); - -const _inlineChatActionId = 'inlineChat.startWithCurrentLine'; - -export class InlineChatExpandLineAction extends EditorAction2 { - - constructor() { - super({ - id: _inlineChatActionId, - category: AbstractInline1ChatAction.category, - title: localize2('startWithCurrentLine', "Start in Editor with Current Line"), - f1: true, - precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE.negate(), CTX_INLINE_CHAT_V1_ENABLED, EditorContextKeys.writable), - keybinding: [{ - when: CTX_INLINE_CHAT_SHOWING_HINT, - weight: KeybindingWeight.WorkbenchContrib + 1, - primary: KeyMod.CtrlCmd | KeyCode.KeyI - }, { - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyI), - }] - }); - } - - override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor) { - const ctrl = InlineChatController.get(editor); - if (!ctrl || !editor.hasModel()) { - return; - } - - const model = editor.getModel(); - const lineNumber = editor.getSelection().positionLineNumber; - const lineContent = model.getLineContent(lineNumber); - - const startColumn = model.getLineFirstNonWhitespaceColumn(lineNumber); - const endColumn = model.getLineMaxColumn(lineNumber); - - // clear the line - let undoEdits: IValidEditOperation[] = []; - model.pushEditOperations(null, [EditOperation.replace(new Range(lineNumber, startColumn, lineNumber, endColumn), '')], (edits) => { - undoEdits = edits; - return null; - }); - - // trigger chat - const accepted = await ctrl.run({ - autoSend: true, - message: lineContent.trim(), - position: new Position(lineNumber, startColumn) - }); - - if (!accepted) { - model.pushEditOperations(null, undoEdits, () => null); - } - } -} - -export class ShowInlineChatHintAction extends EditorAction2 { - - constructor() { - super({ - id: 'inlineChat.showHint', - category: AbstractInline1ChatAction.category, - title: localize2('showHint', "Show Inline Chat Hint"), - f1: false, - precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE.negate(), CTX_INLINE_CHAT_V1_ENABLED, EditorContextKeys.writable), - }); - } - - override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ...args: [uri: URI, position: IPosition, ...rest: unknown[]]) { - if (!editor.hasModel()) { - return; - } - - const ctrl = InlineChatHintsController.get(editor); - if (!ctrl) { - return; - } - - const [uri, position] = args; - if (!URI.isUri(uri) || !Position.isIPosition(position)) { - ctrl.hide(); - return; - } - - const model = editor.getModel(); - if (!isEqual(model.uri, uri)) { - ctrl.hide(); - return; - } - - model.tokenization.forceTokenization(position.lineNumber); - const tokens = model.tokenization.getLineTokens(position.lineNumber); - - let totalLength = 0; - let specialLength = 0; - let lastTokenType: StandardTokenType | undefined; - - tokens.forEach(idx => { - const tokenType = tokens.getStandardTokenType(idx); - const startOffset = tokens.getStartOffset(idx); - const endOffset = tokens.getEndOffset(idx); - totalLength += endOffset - startOffset; - - if (tokenType !== StandardTokenType.Other) { - specialLength += endOffset - startOffset; - } - lastTokenType = tokenType; - }); - - if (specialLength / totalLength > 0.25) { - ctrl.hide(); - return; - } - if (lastTokenType === StandardTokenType.Comment) { - ctrl.hide(); - return; - } - ctrl.show(); - } -} - -export class InlineChatHintsController extends Disposable implements IEditorContribution { - - public static readonly ID = 'editor.contrib.inlineChatHints'; - - static get(editor: ICodeEditor): InlineChatHintsController | null { - return editor.getContribution(InlineChatHintsController.ID); - } - - private readonly _editor: ICodeEditor; - private readonly _ctxShowingHint: IContextKey; - private readonly _visibilityObs = observableValue(this, false); - - constructor( - editor: ICodeEditor, - @IContextKeyService contextKeyService: IContextKeyService, - @ICommandService commandService: ICommandService, - @IKeybindingService keybindingService: IKeybindingService, - @IChatAgentService chatAgentService: IChatAgentService, - @IMarkerDecorationsService markerDecorationService: IMarkerDecorationsService, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IConfigurationService private readonly _configurationService: IConfigurationService - ) { - super(); - this._editor = editor; - this._ctxShowingHint = CTX_INLINE_CHAT_SHOWING_HINT.bindTo(contextKeyService); - - const ghostCtrl = InlineCompletionsController.get(editor); - - this._store.add(commandService.onWillExecuteCommand(e => { - if (e.commandId === _inlineChatActionId || e.commandId === ACTION_START) { - this.hide(); - } - })); - - this._store.add(this._editor.onMouseDown(e => { - if (e.target.type !== MouseTargetType.CONTENT_TEXT) { - return; - } - if (!e.target.element?.classList.contains('inline-chat-hint-text')) { - return; - } - if (e.event.leftButton) { - commandService.executeCommand(_inlineChatActionId); - this.hide(); - } else if (e.event.rightButton) { - e.event.preventDefault(); - this._showContextMenu(e.event, e.target.element?.classList.contains('whitespace') - ? InlineChatConfigKeys.LineEmptyHint - : InlineChatConfigKeys.LineNLHint - ); - } - })); - - const markerSuppression = this._store.add(new MutableDisposable()); - const decos = this._editor.createDecorationsCollection(); - - const editorObs = observableCodeEditor(editor); - const keyObs = observableFromEvent(keybindingService.onDidUpdateKeybindings, _ => keybindingService.lookupKeybinding(ACTION_START)?.getLabel()); - const configHintEmpty = observableConfigValue(InlineChatConfigKeys.LineEmptyHint, false, this._configurationService); - const configHintNL = observableConfigValue(InlineChatConfigKeys.LineNLHint, false, this._configurationService); - - const showDataObs = derived((r) => { - const ghostState = ghostCtrl?.model.read(r)?.state.read(r); - - const textFocus = editorObs.isTextFocused.read(r); - const position = editorObs.cursorPosition.read(r); - const model = editorObs.model.read(r); - - const kb = keyObs.read(r); - - if (ghostState !== undefined || !kb || !position || !model || !textFocus) { - return undefined; - } - - if (IGNORED_LANGUAGE_IDS.has(model.getLanguageId())) { - return undefined; - } - - editorObs.versionId.read(r); - - const visible = this._visibilityObs.read(r); - const isEol = model.getLineMaxColumn(position.lineNumber) === position.column; - const isWhitespace = model.getLineLastNonWhitespaceColumn(position.lineNumber) === 0 && model.getValueLength() > 0 && position.column > 1; - - if (isWhitespace) { - return configHintEmpty.read(r) - ? { isEol, isWhitespace, kb, position, model } - : undefined; - } - - if (visible && isEol && configHintNL.read(r)) { - return { isEol, isWhitespace, kb, position, model }; - } - - return undefined; - }); - - const style = createStyleSheet2(); - this._store.add(style); - - this._store.add(autorun(r => { - - const showData = showDataObs.read(r); - if (!showData) { - decos.clear(); - markerSuppression.clear(); - this._ctxShowingHint.reset(); - return; - } - - const agentName = chatAgentService.getDefaultAgent(ChatAgentLocation.EditorInline)?.name ?? localize('defaultTitle', "Chat"); - const { position, isEol, isWhitespace, kb, model } = showData; - - const inlineClassName: string[] = ['a' /*HACK but sorts as we want*/, 'inline-chat-hint', 'inline-chat-hint-text']; - let content: string; - if (isWhitespace) { - content = '\u00a0' + localize('title2', "{0} to edit with {1}", kb, agentName); - } else if (isEol) { - content = '\u00a0' + localize('title1', "{0} to continue with {1}", kb, agentName); - } else { - content = '\u200a' + kb + '\u200a'; - inlineClassName.push('embedded'); - } - - style.setStyle(`.inline-chat-hint-text::after { content: ${stringValue(content)} }`); - if (isWhitespace) { - inlineClassName.push('whitespace'); - } - - this._ctxShowingHint.set(true); - - decos.set([{ - range: Range.fromPositions(position), - options: { - description: 'inline-chat-hint-line', - showIfCollapsed: true, - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - afterContentClassName: inlineClassName.join(' '), - } - }]); - - markerSuppression.value = markerDecorationService.addMarkerSuppression(model.uri, model.validateRange(new Range(position.lineNumber, 1, position.lineNumber, Number.MAX_SAFE_INTEGER))); - })); - } - - private _showContextMenu(event: IMouseEvent, setting: string): void { - this._contextMenuService.showContextMenu({ - getAnchor: () => ({ x: event.posx, y: event.posy }), - getActions: () => [ - toAction({ - id: 'inlineChat.disableHint', - label: localize('disableHint', "Disable Inline Chat Hint"), - run: async () => { - await this._configurationService.updateValue(setting, false); - } - }) - ] - }); - } - - show(): void { - this._visibilityObs.set(true, undefined); - } - - hide(): void { - this._visibilityObs.set(false, undefined); - } -} - -export class HideInlineChatHintAction extends EditorAction2 { - - constructor() { - super({ - id: 'inlineChat.hideHint', - title: localize2('hideHint', "Hide Inline Chat Hint"), - precondition: CTX_INLINE_CHAT_SHOWING_HINT, - keybinding: { - weight: KeybindingWeight.EditorContrib - 10, - primary: KeyCode.Escape - } - }); - } - - override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { - InlineChatHintsController.get(editor)?.hide(); - } -} diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index fdb73019c9f..2e3fdadcd45 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -410,16 +410,3 @@ .monaco-workbench .inline-chat .chat-attached-context { padding: 3px 0px; } - - -/* HINT */ - -.monaco-workbench .monaco-editor .inline-chat-hint { - cursor: pointer; - color: var(--vscode-editorGhostText-foreground); -} - -.monaco-workbench .monaco-editor .inline-chat-hint.embedded { - border: 1px solid var(--vscode-editorSuggestWidget-border); - border-radius: 3px; -} diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 64d678e8fea..6517d062eaa 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -18,8 +18,6 @@ export const enum InlineChatConfigKeys { StartWithOverlayWidget = 'inlineChat.startWithOverlayWidget', HoldToSpeech = 'inlineChat.holdToSpeech', AccessibleDiffView = 'inlineChat.accessibleDiffView', - LineEmptyHint = 'inlineChat.lineEmptyHint', - LineNLHint = 'inlineChat.lineNaturalLanguageHint', EnableV2 = 'inlineChat.enableV2', notebookAgent = 'inlineChat.notebookAgent', } @@ -48,18 +46,6 @@ Registry.as(Extensions.Configuration).registerConfigurat localize('accessibleDiffView.off', "The accessible diff viewer is never enabled."), ], }, - [InlineChatConfigKeys.LineEmptyHint]: { - description: localize('emptyLineHint', "Whether empty lines show a hint to generate code with inline chat."), - default: false, - type: 'boolean', - tags: ['experimental'], - }, - [InlineChatConfigKeys.LineNLHint]: { - markdownDescription: localize('lineSuffixHint', "Whether lines that are dominated by natural language or pseudo code show a hint to continue with inline chat. For instance, `class Person with name and hobbies` would show a hint to continue with chat."), - default: true, - type: 'boolean', - tags: ['experimental'], - }, [InlineChatConfigKeys.EnableV2]: { description: localize('enableV2', "Whether to use the next version of inline chat."), default: false,