From 06f149fa137ee9b14582a7f7acb39ad512dc4022 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Mon, 28 Apr 2025 23:06:51 -0700 Subject: [PATCH] Revert "feat: syntax highlighting terminal output with Shiki (#3021)" This reverts commit 8b072ad126272e1443f512c99e1c6e790fee5dfc. --- .../src/components/chat/CommandExecution.tsx | 75 +++++++++---------- .../chat/__tests__/CommandExecution.test.tsx | 51 +++++++------ .../components/common/__mocks__/CodeBlock.tsx | 4 +- 3 files changed, 66 insertions(+), 64 deletions(-) diff --git a/webview-ui/src/components/chat/CommandExecution.tsx b/webview-ui/src/components/chat/CommandExecution.tsx index ed6c90036d6..f2977849b71 100644 --- a/webview-ui/src/components/chat/CommandExecution.tsx +++ b/webview-ui/src/components/chat/CommandExecution.tsx @@ -1,9 +1,9 @@ -import { forwardRef, useState } from "react" -import { useTranslation } from "react-i18next" +import { HTMLAttributes, forwardRef, useMemo, useState } from "react" +import { Virtuoso } from "react-virtuoso" +import { ChevronDown } from "lucide-react" import { useExtensionState } from "@src/context/ExtensionStateContext" -import { BaseTerminal } from "../../../../src/integrations/terminal/BaseTerminal" -import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock" +import { cn } from "@src/lib/utils" interface CommandExecutionProps { command: string @@ -11,48 +11,45 @@ interface CommandExecutionProps { } export const CommandExecution = forwardRef(({ command, output }, ref) => { - const { t } = useTranslation() - const { terminalShellIntegrationDisabled = false, terminalOutputLineLimit = 500 } = useExtensionState() + const { terminalShellIntegrationDisabled = false } = useExtensionState() + + // If we aren't opening the VSCode terminal for this command then we default + // to expanding the command execution output. const [isExpanded, setIsExpanded] = useState(terminalShellIntegrationDisabled) - const compressedOutput = BaseTerminal.compressTerminalOutput(output, terminalOutputLineLimit) - const onToggleExpand = () => { - setIsExpanded(!isExpanded) - } + const lines = useMemo(() => output.split("\n"), [output]) return ( - <> +
- - {output.length > 0 && ( -
-
- - {t("chat:commandOutput")} -
- {isExpanded && } -
- )} + className={cn("flex flex-row justify-between cursor-pointer active:opacity-75", { + "opacity-50": isExpanded, + })} + onClick={() => setIsExpanded(!isExpanded)}> + {command} + +
+
+ {lines[i]}} + followOutput="auto" + />
- +
) }) +type LineProps = HTMLAttributes + +const Line = ({ className, ...props }: LineProps) => { + return ( +
+ ) +} + CommandExecution.displayName = "CommandExecution" diff --git a/webview-ui/src/components/chat/__tests__/CommandExecution.test.tsx b/webview-ui/src/components/chat/__tests__/CommandExecution.test.tsx index 0c0bd0733f2..1fa5c831e33 100644 --- a/webview-ui/src/components/chat/__tests__/CommandExecution.test.tsx +++ b/webview-ui/src/components/chat/__tests__/CommandExecution.test.tsx @@ -1,18 +1,33 @@ // npx jest src/components/chat/__tests__/CommandExecution.test.tsx import React from "react" -import { render, screen, fireEvent } from "@testing-library/react" +import { render, screen } from "@testing-library/react" import { ExtensionStateContextProvider } from "@src/context/ExtensionStateContext" import { CommandExecution } from "../CommandExecution" -jest.mock("../../../components/common/CodeBlock") - jest.mock("@src/lib/utils", () => ({ cn: (...inputs: any[]) => inputs.filter(Boolean).join(" "), })) +jest.mock("lucide-react", () => ({ + ChevronDown: () =>
ChevronDown
, +})) + +jest.mock("react-virtuoso", () => ({ + Virtuoso: React.forwardRef(({ totalCount, itemContent }: any, ref: any) => ( +
+ {Array.from({ length: totalCount }).map((_, index) => ( +
+ {itemContent(index)} +
+ ))} +
+ )), + VirtuosoHandle: jest.fn(), +})) + describe("CommandExecution", () => { const renderComponent = (command: string, output: string) => { return render( @@ -25,34 +40,24 @@ describe("CommandExecution", () => { it("renders command output with virtualized list", () => { const testOutput = "Line 1\nLine 2\nLine 3" renderComponent("ls", testOutput) - const codeBlock = screen.getByTestId("mock-code-block") - expect(codeBlock).toHaveTextContent("ls") - - fireEvent.click(screen.getByText("commandOutput")) - const outputBlock = screen.getAllByTestId("mock-code-block")[1] - - expect(outputBlock).toHaveTextContent("Line 1") - expect(outputBlock).toHaveTextContent("Line 2") - expect(outputBlock).toHaveTextContent("Line 3") + expect(screen.getByTestId("virtuoso-container")).toBeInTheDocument() + expect(screen.getByText("Line 1")).toBeInTheDocument() + expect(screen.getByText("Line 2")).toBeInTheDocument() + expect(screen.getByText("Line 3")).toBeInTheDocument() }) it("handles empty output", () => { renderComponent("ls", "") - const codeBlock = screen.getByTestId("mock-code-block") - expect(codeBlock).toHaveTextContent("ls") - expect(screen.queryByText("commandOutput")).not.toBeInTheDocument() - expect(screen.queryAllByTestId("mock-code-block")).toHaveLength(1) + expect(screen.getByTestId("virtuoso-container")).toBeInTheDocument() + expect(screen.getByTestId("virtuoso-item-0")).toBeInTheDocument() + expect(screen.queryByTestId("virtuoso-item-1")).not.toBeInTheDocument() }) it("handles large output", () => { const largeOutput = Array.from({ length: 1000 }, (_, i) => `Line ${i + 1}`).join("\n") renderComponent("ls", largeOutput) - const codeBlock = screen.getByTestId("mock-code-block") - expect(codeBlock).toHaveTextContent("ls") - - fireEvent.click(screen.getByText("commandOutput")) - const outputBlock = screen.getAllByTestId("mock-code-block")[1] - expect(outputBlock).toHaveTextContent("Line 1") - expect(outputBlock).toHaveTextContent("Line 1000") + expect(screen.getByTestId("virtuoso-container")).toBeInTheDocument() + expect(screen.getByText("Line 1")).toBeInTheDocument() + expect(screen.getByText("Line 1000")).toBeInTheDocument() }) }) diff --git a/webview-ui/src/components/common/__mocks__/CodeBlock.tsx b/webview-ui/src/components/common/__mocks__/CodeBlock.tsx index 7eaa790ff22..b4d73633673 100644 --- a/webview-ui/src/components/common/__mocks__/CodeBlock.tsx +++ b/webview-ui/src/components/common/__mocks__/CodeBlock.tsx @@ -1,10 +1,10 @@ import * as React from "react" interface CodeBlockProps { - source?: string + children?: React.ReactNode language?: string } -const CodeBlock: React.FC = ({ source = "" }) =>
{source}
+const CodeBlock: React.FC = () =>
Mocked Code Block
export default CodeBlock