mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 00:09:30 +01:00
fix: remove backslash escaping from terminal command labels (#303856)
* fix: remove escapeMarkdownSyntaxTokens from terminal command labels Fixes #303844 The command text in ChatTerminalThinkingCollapsibleWrapper was being escaped with escapeMarkdownSyntaxTokens(), which adds backslashes before chars like - * # etc. This is unnecessary because the text is always rendered inside markdown code spans or via .textContent, both of which treat content as literal. Also adds a component fixture for the terminal collapsible wrapper to enable visual regression testing of command label rendering. * fix: use DOM nodes instead of MarkdownString for sandbox command labels Addresses review feedback: commands containing backticks (common in PowerShell) would break the inline-code markdown spans. Now both sandbox and non-sandbox paths use text nodes + <code> elements with .textContent, which is always safe for arbitrary command text. Also adds fixture cases for backtick-containing commands to catch this class of issue. * fix: remove colons from fixture names to fix CI artifact paths * add screenshot baselines for terminal collapsible fixtures
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
import { h } from '../../../../../../../base/browser/dom.js';
|
||||
import { ActionBar } from '../../../../../../../base/browser/ui/actionbar/actionbar.js';
|
||||
import { escapeMarkdownSyntaxTokens, isMarkdownString, MarkdownString } from '../../../../../../../base/common/htmlContent.js';
|
||||
import { isMarkdownString, MarkdownString } from '../../../../../../../base/common/htmlContent.js';
|
||||
import { IConfigurationService } from '../../../../../../../platform/configuration/common/configuration.js';
|
||||
import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { ChatConfiguration } from '../../../../common/constants.js';
|
||||
@@ -1624,7 +1624,7 @@ export class ContinueInBackgroundAction extends Action implements IAction {
|
||||
}
|
||||
}
|
||||
|
||||
class ChatTerminalThinkingCollapsibleWrapper extends ChatCollapsibleContentPart {
|
||||
export class ChatTerminalThinkingCollapsibleWrapper extends ChatCollapsibleContentPart {
|
||||
private readonly _terminalContentElement: HTMLElement;
|
||||
private readonly _commandText: string;
|
||||
private readonly _isSandboxWrapped: boolean;
|
||||
@@ -1640,11 +1640,13 @@ class ChatTerminalThinkingCollapsibleWrapper extends ChatCollapsibleContentPart
|
||||
@IHoverService hoverService: IHoverService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
const title = isComplete ? `Ran \`${escapeMarkdownSyntaxTokens(commandText)}\`` : `Running \`${escapeMarkdownSyntaxTokens(commandText)}\``;
|
||||
const title = isComplete
|
||||
? localize('chat.terminal.ran.plain', "Ran {0}", commandText)
|
||||
: localize('chat.terminal.running.plain', "Running {0}", commandText);
|
||||
super(title, context, undefined, hoverService, configurationService);
|
||||
|
||||
this._terminalContentElement = contentElement;
|
||||
this._commandText = escapeMarkdownSyntaxTokens(commandText);
|
||||
this._commandText = commandText;
|
||||
this._isSandboxWrapped = isSandboxWrapped;
|
||||
this._isComplete = isComplete;
|
||||
|
||||
@@ -1663,16 +1665,22 @@ class ChatTerminalThinkingCollapsibleWrapper extends ChatCollapsibleContentPart
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._isSandboxWrapped) {
|
||||
this._collapseButton.label = new MarkdownString(this._isComplete
|
||||
? localize('chat.terminal.ranInSandbox', "Ran `{0}` in sandbox", this._commandText)
|
||||
: localize('chat.terminal.runningInSandbox', "Running `{0}` in sandbox", this._commandText));
|
||||
return;
|
||||
}
|
||||
|
||||
const labelElement = this._collapseButton.labelElement;
|
||||
labelElement.textContent = '';
|
||||
|
||||
if (this._isSandboxWrapped) {
|
||||
const prefixText = this._isComplete
|
||||
? localize('chat.terminal.ranInSandbox.prefix', "Ran ")
|
||||
: localize('chat.terminal.runningInSandbox.prefix', "Running ");
|
||||
const suffixText = localize('chat.terminal.sandbox.suffix', " in sandbox");
|
||||
labelElement.appendChild(document.createTextNode(prefixText));
|
||||
const codeElement = document.createElement('code');
|
||||
codeElement.textContent = this._commandText;
|
||||
labelElement.appendChild(codeElement);
|
||||
labelElement.appendChild(document.createTextNode(suffixText));
|
||||
return;
|
||||
}
|
||||
|
||||
const prefixText = this._isComplete
|
||||
? localize('chat.terminal.ran.prefix', "Ran ")
|
||||
: localize('chat.terminal.running.prefix', "Running ");
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from '../../../../base/browser/dom.js';
|
||||
import { Event } from '../../../../base/common/event.js';
|
||||
import { observableValue } from '../../../../base/common/observable.js';
|
||||
import { mock, upcastPartial } from '../../../../base/test/common/mock.js';
|
||||
import type { IChatContentPartRenderContext, InlineTextModelCollection } from '../../../contrib/chat/browser/widget/chatContentParts/chatContentParts.js';
|
||||
import type { IChatResponseViewModel } from '../../../contrib/chat/common/model/chatViewModel.js';
|
||||
import { ChatTerminalThinkingCollapsibleWrapper } from '../../../contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.js';
|
||||
import { ComponentFixtureContext, createEditorServices, defineComponentFixture, defineThemedFixtureGroup } from './fixtureUtils.js';
|
||||
|
||||
import '../../../contrib/chat/browser/widget/media/chat.css';
|
||||
|
||||
function createMockContext(): IChatContentPartRenderContext {
|
||||
return {
|
||||
element: new class extends mock<IChatResponseViewModel>() { }(),
|
||||
elementIndex: 0,
|
||||
container: document.createElement('div'),
|
||||
content: [],
|
||||
contentIndex: 0,
|
||||
editorPool: undefined!,
|
||||
codeBlockStartIndex: 0,
|
||||
treeStartIndex: 0,
|
||||
diffEditorPool: undefined!,
|
||||
codeBlockModelCollection: undefined!,
|
||||
currentWidth: observableValue('currentWidth', 400),
|
||||
onDidChangeVisibility: Event.None,
|
||||
inlineTextModels: upcastPartial<InlineTextModelCollection>({}),
|
||||
};
|
||||
}
|
||||
|
||||
function renderCollapsible(context: ComponentFixtureContext, commandText: string, isSandboxWrapped: boolean, isComplete: boolean): void {
|
||||
const { container, disposableStore } = context;
|
||||
|
||||
const instantiationService = createEditorServices(disposableStore, {
|
||||
colorTheme: context.theme,
|
||||
});
|
||||
|
||||
container.style.width = '500px';
|
||||
container.style.padding = '8px';
|
||||
container.classList.add('monaco-workbench');
|
||||
|
||||
const session = dom.$('.interactive-session');
|
||||
container.appendChild(session);
|
||||
|
||||
const contentElement = dom.$('.chat-terminal-output-placeholder');
|
||||
contentElement.textContent = '(terminal output would appear here)';
|
||||
contentElement.style.padding = '8px';
|
||||
contentElement.style.color = 'var(--vscode-descriptionForeground)';
|
||||
|
||||
const wrapper = disposableStore.add(instantiationService.createInstance(
|
||||
ChatTerminalThinkingCollapsibleWrapper,
|
||||
commandText,
|
||||
isSandboxWrapped,
|
||||
contentElement,
|
||||
createMockContext(),
|
||||
false,
|
||||
isComplete,
|
||||
));
|
||||
|
||||
session.appendChild(wrapper.domNode);
|
||||
}
|
||||
|
||||
export default defineThemedFixtureGroup({ path: 'chat/terminalCollapsible/' }, {
|
||||
'Ran - simple command': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'ls -lh', false, true),
|
||||
}),
|
||||
'Running - simple command': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'ls -lh', false, false),
|
||||
}),
|
||||
'Ran sandbox - simple command': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'ls -lh', true, true),
|
||||
}),
|
||||
'Running sandbox - simple command': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'ls -lh', true, false),
|
||||
}),
|
||||
'Ran - special chars': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'grep -rn "hello" ./src --include="*.ts"', false, true),
|
||||
}),
|
||||
'Ran sandbox - special chars': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'grep -rn "hello" ./src --include="*.ts"', true, true),
|
||||
}),
|
||||
'Ran - backticks': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'echo `date` && echo `hostname`', false, true),
|
||||
}),
|
||||
'Ran sandbox - backticks': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'echo `date` && echo `hostname`', true, true),
|
||||
}),
|
||||
'Ran sandbox - powershell backticks': defineComponentFixture({
|
||||
render: ctx => renderCollapsible(ctx, 'Get-Process | Where-Object {$_.Name -eq `"notepad`"}', true, true),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0ae8dab0fa799afaadf186c93436bca9ac39c36b99b8f9846ed0d7284494be93
|
||||
size 2288
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:89f27caedd1e45d22e90243691bcc0153195d121dc2c2bc58be4a1d50ace828f
|
||||
size 2229
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:793aa216b116d5b028adf16db5e8780f7b844bfbdd38657f943c6427de3e320b
|
||||
size 945
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6500291e4787ee83dbe70cf44c1b6cb0c5c96f4311cf865cba26ce86a92581b4
|
||||
size 938
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4e88d70bf076040be9311a96a39717699e59e2bd86a9b2ca7e32b2424bfbf273
|
||||
size 2442
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f267b5737a1cbfeddd000065fa66735c058cdaa090b60445f583db282eb4787d
|
||||
size 2359
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ae563a7c9f939d3fefa4a30e91e1a514ae0de7b881813827110b62def6bd05ef
|
||||
size 3061
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1d87074216734a0e6a15683060271590bb6ec594dca7ca84c6ae3b0180df2a66
|
||||
size 2988
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dce5773e95961f082c034816bc63fbf79b8cedecaec9750840fe483d4855f32f
|
||||
size 4476
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3a05a111b62dbdd2f355f31f08a2c5c6d3731a1ce9607d95e66c42e092a1f099
|
||||
size 4354
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:eda8e2a9eeb0b53fee694039861e868d40b711c4f26b95748605f0a14a8a7811
|
||||
size 1759
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d71509185136eced7961758fde339f267f38966a4e35939c8fa11fde64e641e2
|
||||
size 1757
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a2a089a8a290b9ccb52d42cdd987ca8ccccdbb0a47394e82f0d6e40b447663a7
|
||||
size 3156
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:927527224e48e702d81b44beedc1a2395c638dbaaf952d1a1c926d576d2bf15f
|
||||
size 3152
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3647c8c57337952dd845f9151b3ddfbb074857e4036d76dd6c7d7beda734c676
|
||||
size 1170
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c0008e3885e2e6bf569757ace983c635feb9da86d470f2455e4a6884d7d22b6e
|
||||
size 1139
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a1f340aaeaa112ab63fa6a6048fc76618097114ffbf9ada8a4e8f7736df5c5dd
|
||||
size 1932
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f3a5c44027dd8659083344aa7f15acd02d2e59d53913b4ca58e5345204470f0d
|
||||
size 1893
|
||||
Reference in New Issue
Block a user