Johannes Rieken
2025-11-12 16:44:34 +01:00
committed by GitHub
parent 9a4e259c79
commit f62ffbb85b
5 changed files with 0 additions and 401 deletions

View File

@@ -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",

View File

@@ -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 = {

View File

@@ -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<boolean>('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>(InlineChatHintsController.ID);
}
private readonly _editor: ICodeEditor;
private readonly _ctxShowingHint: IContextKey<boolean>;
private readonly _visibilityObs = observableValue<boolean>(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<void> {
InlineChatHintsController.get(editor)?.hide();
}
}

View File

@@ -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;
}

View File

@@ -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<IConfigurationRegistry>(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,