Skip to content

Commit c0699e2

Browse files
authored
Joh/sweet-crawdad (#234851)
* fix empty line hint * no ellipsis on hints * add `renderDetectedCommandsWithRequest` option and use it for inline chat fyi @roblourens
1 parent 449e2e0 commit c0699e2

File tree

8 files changed

+98
-47
lines changed

8 files changed

+98
-47
lines changed

Diff for: src/vs/workbench/contrib/chat/browser/chat.ts

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export interface IChatListItemRendererOptions {
111111
readonly noPadding?: boolean;
112112
readonly editableCodeBlock?: boolean;
113113
readonly renderCodeBlockPills?: boolean;
114+
readonly renderDetectedCommandsWithRequest?: boolean;
114115
readonly renderTextEditsAsSummary?: (uri: URI) => boolean;
115116
}
116117

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
7+
import { getDefaultHoverDelegate } from '../../../../../base/browser/ui/hover/hoverDelegateFactory.js';
8+
import { Disposable } from '../../../../../base/common/lifecycle.js';
9+
import { IHoverService } from '../../../../../platform/hover/browser/hover.js';
10+
import { IChatAgentCommand } from '../../common/chatAgents.js';
11+
import { chatSubcommandLeader } from '../../common/chatParserTypes.js';
12+
import { IChatRendererContent } from '../../common/chatViewModel.js';
13+
import { ChatTreeItem } from '../chat.js';
14+
import { IChatContentPart } from './chatContentParts.js';
15+
import { renderIcon } from '../../../../../base/browser/ui/iconLabel/iconLabels.js';
16+
import { Codicon } from '../../../../../base/common/codicons.js';
17+
import { addDisposableListener } from '../../../../../base/browser/dom.js';
18+
import { localize } from '../../../../../nls.js';
19+
20+
21+
export class ChatAgentCommandContentPart extends Disposable implements IChatContentPart {
22+
23+
readonly domNode: HTMLElement = document.createElement('span');
24+
25+
constructor(
26+
cmd: IChatAgentCommand,
27+
onClick: () => void,
28+
@IHoverService private readonly _hoverService: IHoverService,
29+
) {
30+
super();
31+
this._store.add(this._hoverService.setupManagedHover(getDefaultHoverDelegate('element'), this.domNode, localize('rerun', "Detected command. Select to rerun without {0}{1}", chatSubcommandLeader, cmd.name)));
32+
this.domNode.classList.add('chat-agent-command');
33+
this.domNode.innerText = chatSubcommandLeader + cmd.name;
34+
this.domNode.setAttribute('aria-label', cmd.name);
35+
this.domNode.setAttribute('role', 'button');
36+
37+
this.domNode.appendChild(renderIcon(Codicon.close));
38+
39+
this._store.add(addDisposableListener(this.domNode, 'click', onClick));
40+
}
41+
42+
hasSameContent(other: IChatRendererContent, followingContent: IChatRendererContent[], element: ChatTreeItem): boolean {
43+
return false;
44+
}
45+
}

Diff for: src/vs/workbench/contrib/chat/browser/chatListRenderer.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { CodeBlockModelCollection } from '../common/codeBlockModelCollection.js'
5353
import { MarkUnhelpfulActionId } from './actions/chatTitleActions.js';
5454
import { ChatTreeItem, IChatCodeBlockInfo, IChatFileTreeInfo, IChatListItemRendererOptions, IChatWidgetService } from './chat.js';
5555
import { ChatAgentHover, getChatAgentHoverOptions } from './chatAgentHover.js';
56+
import { ChatAgentCommandContentPart } from './chatContentParts/chatAgentCommandContentPart.js';
5657
import { ChatAttachmentsContentPart } from './chatContentParts/chatAttachmentsContentPart.js';
5758
import { ChatCodeCitationContentPart } from './chatContentParts/chatCodeCitationContentPart.js';
5859
import { ChatCommandButtonContentPart } from './chatContentParts/chatCommandContentPart.js';
@@ -123,8 +124,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
123124
protected readonly _onDidClickFollowup = this._register(new Emitter<IChatFollowup>());
124125
readonly onDidClickFollowup: Event<IChatFollowup> = this._onDidClickFollowup.event;
125126

126-
private readonly _onDidClickRerunWithAgentOrCommandDetection = new Emitter<IChatResponseViewModel>();
127-
readonly onDidClickRerunWithAgentOrCommandDetection: Event<IChatResponseViewModel> = this._onDidClickRerunWithAgentOrCommandDetection.event;
127+
private readonly _onDidClickRerunWithAgentOrCommandDetection = new Emitter<{ sessionId: string; requestId: string }>();
128+
readonly onDidClickRerunWithAgentOrCommandDetection: Event<{ sessionId: string; requestId: string }> = this._onDidClickRerunWithAgentOrCommandDetection.event;
128129

129130
protected readonly _onDidChangeItemHeight = this._register(new Emitter<IItemHeightChangeParams>());
130131
readonly onDidChangeItemHeight: Event<IItemHeightChangeParams> = this._onDidChangeItemHeight.event;
@@ -531,6 +532,17 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
531532
};
532533
const newPart = this.renderChatContentPart(data, templateData, context);
533534
if (newPart) {
535+
536+
if (this.rendererOptions.renderDetectedCommandsWithRequest
537+
&& isRequestVM(element) && element.agentOrSlashCommandDetected && element.slashCommand
538+
&& data.kind === 'markdownContent' // TODO this is fishy but I didn't find a better way to render on the same inline as the MD request part
539+
) {
540+
newPart.domNode.style.display = 'inline-flex';
541+
const cmdPart = this.instantiationService.createInstance(ChatAgentCommandContentPart, element.slashCommand, () => this._onDidClickRerunWithAgentOrCommandDetection.fire({ sessionId: element.sessionId, requestId: element.id }));
542+
templateData.value.appendChild(cmdPart.domNode);
543+
parts.push(cmdPart);
544+
}
545+
534546
templateData.value.appendChild(newPart.domNode);
535547
parts.push(newPart);
536548
}

Diff for: src/vs/workbench/contrib/chat/browser/media/chat.css

+17
Original file line numberDiff line numberDiff line change
@@ -1123,12 +1123,29 @@ have to be updated for changes to the rules above, or to support more deeply nes
11231123
color: var(--vscode-chat-slashCommandForeground);
11241124
}
11251125

1126+
1127+
.interactive-item-container .chat-agent-command,
11261128
.interactive-item-container .chat-resource-widget,
11271129
.interactive-item-container .chat-agent-widget .monaco-button {
11281130
border-radius: 4px;
11291131
padding: 1px 3px;
11301132
}
11311133

1134+
.interactive-item-container .chat-agent-command {
1135+
background-color: var(--vscode-chat-slashCommandBackground);
1136+
color: var(--vscode-chat-slashCommandForeground);
1137+
cursor: pointer;
1138+
display: inline-flex;
1139+
align-items: center;
1140+
margin-right: 0.5ch;
1141+
padding: 1px 1px 1px 3px;
1142+
}
1143+
1144+
.interactive-item-container .chat-agent-command > .codicon[class*='codicon-'] {
1145+
color: var(--vscode-chat-slashCommandForeground);
1146+
font-size: 14px;
1147+
}
1148+
11321149
.interactive-item-container .chat-agent-widget .monaco-text-button {
11331150
display: inline;
11341151
border: none;

Diff for: src/vs/workbench/contrib/chat/common/chatViewModel.ts

+10
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ export interface IChatRequestViewModel {
7777
readonly isHidden: boolean;
7878
readonly isComplete: boolean;
7979
readonly isCompleteAddedRequest: boolean;
80+
readonly slashCommand: IChatAgentCommand | undefined;
81+
readonly agentOrSlashCommandDetected: boolean;
8082
}
8183

8284
export interface IChatResponseMarkdownRenderData {
@@ -387,6 +389,14 @@ export class ChatRequestViewModel implements IChatRequestViewModel {
387389
return this._model.isHidden;
388390
}
389391

392+
get slashCommand(): IChatAgentCommand | undefined {
393+
return this._model.response?.slashCommand;
394+
}
395+
396+
get agentOrSlashCommandDetected(): boolean {
397+
return this._model.response?.agentOrSlashCommandDetected ?? false;
398+
}
399+
390400
currentRenderedHeight: number | undefined;
391401

392402
constructor(

Diff for: src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,17 @@ export class InlineChatHintsController extends Disposable implements IEditorCont
235235
const isEol = model.getLineMaxColumn(position.lineNumber) === position.column;
236236
const isWhitespace = model.getLineLastNonWhitespaceColumn(position.lineNumber) === 0 && model.getValueLength() > 0 && position.column > 1;
237237

238-
if (isWhitespace && !_configurationService.getValue(InlineChatConfigKeys.LineEmptyHint)) {
239-
return undefined;
238+
if (isWhitespace) {
239+
return _configurationService.getValue(InlineChatConfigKeys.LineEmptyHint)
240+
? { isEol, isWhitespace, kb, position, model }
241+
: undefined;
240242
}
241243

242-
if (!visible || !isEol || !_configurationService.getValue(InlineChatConfigKeys.LineSuffixHint)) {
243-
return undefined;
244+
if (visible && isEol && _configurationService.getValue(InlineChatConfigKeys.LineSuffixHint)) {
245+
return { isEol, isWhitespace, kb, position, model };
244246
}
245247

246-
return { isEol, isWhitespace, kb, position, model };
248+
return undefined;
247249
});
248250

249251
this._store.add(autorun(r => {
@@ -262,9 +264,9 @@ export class InlineChatHintsController extends Disposable implements IEditorCont
262264
const inlineClassName: string[] = ['inline-chat-hint'];
263265
let content: string;
264266
if (isWhitespace) {
265-
content = '\u00a0' + localize('title2', "{0} to edit with {1}...", kb, agentName);
267+
content = '\u00a0' + localize('title2', "{0} to edit with {1}", kb, agentName);
266268
} else if (isEol) {
267-
content = '\u00a0' + localize('title1', "{0} to continue with {1}...", kb, agentName);
269+
content = '\u00a0' + localize('title1', "{0} to continue with {1}", kb, agentName);
268270
} else {
269271
content = '\u200a' + kb + '\u200a';
270272
inlineClassName.push('embedded');

Diff for: src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts

+1-38
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { $, Dimension, getActiveElement, getTotalHeight, h, reset, trackFocus } from '../../../../base/browser/dom.js';
7-
import { renderFormattedText } from '../../../../base/browser/formattedTextRenderer.js';
87
import { IActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js';
98
import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js';
109
import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js';
@@ -49,8 +48,7 @@ import { ChatWidget, IChatViewState, IChatWidgetLocationOptions } from '../../ch
4948
import { chatRequestBackground } from '../../chat/common/chatColors.js';
5049
import { ChatContextKeys } from '../../chat/common/chatContextKeys.js';
5150
import { IChatModel } from '../../chat/common/chatModel.js';
52-
import { chatSubcommandLeader } from '../../chat/common/chatParserTypes.js';
53-
import { ChatAgentVoteDirection, IChatSendRequestOptions, IChatService } from '../../chat/common/chatService.js';
51+
import { ChatAgentVoteDirection, IChatService } from '../../chat/common/chatService.js';
5452
import { isResponseVM } from '../../chat/common/chatViewModel.js';
5553
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_FOCUSED, inlineChatBackground, inlineChatForeground } from '../common/inlineChat.js';
5654
import { HunkInformation, Session } from './inlineChatSession.js';
@@ -90,7 +88,6 @@ export class InlineChatWidget {
9088
h('div.status@status', [
9189
h('div.label.info.hidden@infoLabel'),
9290
h('div.actions.hidden@toolbar1'),
93-
h('div.rerun@rerun'),
9491
h('div.label.status.hidden@statusLabel'),
9592
h('div.actions.secondary.hidden@toolbar2'),
9693
]),
@@ -201,8 +198,6 @@ export class InlineChatWidget {
201198
ctxResponseSupportIssues.reset();
202199
}));
203200

204-
const detectedAgentCmdStore = viewModelStore.add(new DisposableStore());
205-
206201
viewModelStore.add(viewModel.onDidChange(() => {
207202

208203
this._requestInProgress.set(viewModel.requestInProgress, undefined);
@@ -216,37 +211,6 @@ export class InlineChatWidget {
216211
ctxResponseErrorFiltered.set((!!(isResponseVM(last) && last.errorDetails?.responseIsFiltered)));
217212
ctxResponseSupportIssues.set(isResponseVM(last) && (last.agent?.metadata.supportIssueReporting ?? false));
218213

219-
if (isResponseVM(last) && last.agentOrSlashCommandDetected && last.slashCommand) {
220-
this._elements.rerun.innerText = last.slashCommand.name;
221-
this._elements.rerun.title = last.slashCommand.description;
222-
223-
const msg = localize('usedAgentSlashCommand', "``{0}`` [[(rerun without)]]", `${chatSubcommandLeader}${last.slashCommand.name}`);
224-
225-
reset(this._elements.rerun, renderFormattedText(msg, {
226-
className: 'agentOrSlashCommandDetected',
227-
inline: true,
228-
renderCodeSegments: true,
229-
actionHandler: {
230-
disposables: detectedAgentCmdStore,
231-
callback: (content) => {
232-
const request = this._chatService.getSession(last.sessionId)?.getRequests().find(candidate => candidate.id === last.requestId);
233-
if (request) {
234-
const options: IChatSendRequestOptions = {
235-
noCommandDetection: true,
236-
attempt: request.attempt + 1,
237-
location: location.location,
238-
userSelectedModelId: this.chatWidget.input.currentLanguageModel
239-
};
240-
this._chatService.resendRequest(request, options);
241-
}
242-
},
243-
}
244-
}));
245-
246-
} else {
247-
reset(this._elements.rerun);
248-
}
249-
250214
this._onDidChangeHeight.fire();
251215
}));
252216
this._onDidChangeHeight.fire();
@@ -515,7 +479,6 @@ export class InlineChatWidget {
515479
this._chatWidget.saveState();
516480

517481
reset(this._elements.statusLabel);
518-
reset(this._elements.rerun);
519482
this._elements.statusLabel.classList.toggle('hidden', true);
520483
this._elements.toolbar1.classList.add('hidden');
521484
this._elements.toolbar2.classList.add('hidden');

Diff for: src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export class InlineChatZoneWidget extends ZoneWidget {
8989
return isEqual(uri, editor.getModel()?.uri)
9090
&& configurationService.getValue<EditMode>(InlineChatConfigKeys.Mode) === EditMode.Live;
9191
},
92+
renderDetectedCommandsWithRequest: true,
9293
}
9394
}
9495
});

0 commit comments

Comments
 (0)