Преглед изворни кода

Fix: Missing or inconsistent syntax highlighting across UI components (#3656)

* fix: Missing or inconsistent syntax highlighting across UI components

- Change file listings to use 'shellsession' for terminal-like highlighting
- Use 'markdown' for code definitions and instructions
- Add file extension-based language detection for new files
- Ensure consistent 'diff' highlighting for all diff content
- Use 'xml' language for error messages
- Make language property required in CodeAccordian
- Set default fallback to 'txt' instead of undefined

Fixes: #3655
Signed-off-by: Eric Wheeler <[email protected]>

* chore: make language property required in CodeBlock

- Updated CodeBlockProps interface to make language property required
- Updated mock implementation to match the interface change
- Ensured CodeAccordian always provides a fallback language value

Signed-off-by: Eric Wheeler <[email protected]>

---------

Signed-off-by: Eric Wheeler <[email protected]>
Co-authored-by: Eric Wheeler <[email protected]>
KJ7LNW пре 7 месеци
родитељ
комит
9317765fb1

+ 9 - 4
webview-ui/src/components/chat/ChatRow.tsx

@@ -13,6 +13,7 @@ import { useExtensionState } from "@src/context/ExtensionStateContext"
 import { findMatchingResourceOrTemplate } from "@src/utils/mcp"
 import { vscode } from "@src/utils/vscode"
 import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
+import { getLanguageFromPath } from "@src/utils/getLanguageFromPath"
 import { Button } from "@src/components/ui"
 
 import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock"
@@ -293,7 +294,7 @@ export const ChatRowContent = ({
 						<CodeAccordian
 							path={tool.path}
 							code={tool.content ?? tool.diff}
-							language={tool.tool === "appliedDiff" ? "diff" : undefined}
+							language="diff"
 							progressStatus={message.progressStatus}
 							isLoading={message.partial}
 							isExpanded={isExpanded}
@@ -341,6 +342,7 @@ export const ChatRowContent = ({
 						<CodeAccordian
 							path={tool.path}
 							code={tool.diff}
+							language="diff"
 							progressStatus={message.progressStatus}
 							isLoading={message.partial}
 							isExpanded={isExpanded}
@@ -358,6 +360,7 @@ export const ChatRowContent = ({
 						<CodeAccordian
 							path={tool.path}
 							code={tool.content}
+							language={getLanguageFromPath(tool.path || "") || "log"}
 							isLoading={message.partial}
 							isExpanded={isExpanded}
 							onToggleExpand={onToggleExpand}
@@ -403,6 +406,7 @@ export const ChatRowContent = ({
 						</div>
 						<CodeAccordian
 							code={tool.content}
+							language="markdown"
 							isLoading={message.partial}
 							isExpanded={isExpanded}
 							onToggleExpand={onToggleExpand}
@@ -443,7 +447,7 @@ export const ChatRowContent = ({
 						<CodeAccordian
 							path={tool.path}
 							code={tool.content}
-							language="shell-session"
+							language="shellsession"
 							isExpanded={isExpanded}
 							onToggleExpand={onToggleExpand}
 						/>
@@ -463,6 +467,7 @@ export const ChatRowContent = ({
 						<CodeAccordian
 							path={tool.path}
 							code={tool.content}
+							language="markdown"
 							isExpanded={isExpanded}
 							onToggleExpand={onToggleExpand}
 						/>
@@ -492,7 +497,7 @@ export const ChatRowContent = ({
 						<CodeAccordian
 							path={tool.path! + (tool.filePattern ? `/(${tool.filePattern})` : "")}
 							code={tool.content}
-							language="log"
+							language="shellsession"
 							isExpanded={isExpanded}
 							onToggleExpand={onToggleExpand}
 						/>
@@ -712,7 +717,7 @@ export const ChatRowContent = ({
 											backgroundColor: "var(--vscode-editor-background)",
 											borderTop: "none",
 										}}>
-										<CodeBlock source={`${"```"}plaintext\n${message.text || ""}\n${"```"}`} />
+										<CodeBlock source={message.text || ""} language="xml" />
 									</div>
 								)}
 							</div>

+ 2 - 2
webview-ui/src/components/common/CodeAccordian.tsx

@@ -11,7 +11,7 @@ import CodeBlock from "./CodeBlock"
 interface CodeAccordianProps {
 	path?: string
 	code?: string
-	language?: string | undefined
+	language: string
 	progressStatus?: ToolProgressStatus
 	isLoading?: boolean
 	isExpanded: boolean
@@ -29,7 +29,7 @@ const CodeAccordian = ({
 	isFeedback,
 	onToggleExpand,
 }: CodeAccordianProps) => {
-	const inferredLanguage = useMemo(() => language ?? (path ? getLanguageFromPath(path) : undefined), [path, language])
+	const inferredLanguage = useMemo(() => language ?? (path ? getLanguageFromPath(path) : "txt"), [path, language])
 	const source = useMemo(() => code.trim(), [code])
 	const hasHeader = Boolean(path || isFeedback)
 

+ 42 - 51
webview-ui/src/components/common/CodeBlock.tsx

@@ -30,7 +30,7 @@ minWidth: "max-content",
 interface CodeBlockProps {
 	source?: string
 	rawSource?: string // Add rawSource prop for copying raw text
-	language?: string
+	language: string
 	preStyle?: React.CSSProperties
 	initialWordWrap?: boolean
 	collapsedHeight?: number
@@ -637,57 +637,48 @@ const CodeBlock = memo(
 						ref={copyButtonWrapperRef}
 						onMouseOver={() => updateCodeBlockButtonPosition()}
 						style={{ gap: 0 }}>
-						{language && (
-							<LanguageSelect
-								value={currentLanguage}
-								style={{
-									width: `calc(${currentLanguage?.length || 0}ch + 9px)`,
-								}}
-								onClick={(e) => {
-									e.currentTarget.focus()
-								}}
-								onChange={(e) => {
-									const newLang = normalizeLanguage(e.target.value)
-									userChangedLanguageRef.current = true
-									setCurrentLanguage(newLang)
-									if (onLanguageChange) {
-										onLanguageChange(newLang)
-									}
-								}}>
-								{
-									// Display original language at top of list for quick selection
-									language && (
-										<option
-											value={normalizeLanguage(language)}
-											style={{ fontWeight: "bold", textAlign: "left", fontSize: "1.2em" }}>
-											{normalizeLanguage(language)}
-										</option>
-									)
+						<LanguageSelect
+							value={currentLanguage}
+							style={{
+								width: `calc(${currentLanguage?.length || 0}ch + 9px)`,
+							}}
+							onClick={(e) => {
+								e.currentTarget.focus()
+							}}
+							onChange={(e) => {
+								const newLang = normalizeLanguage(e.target.value)
+								userChangedLanguageRef.current = true
+								setCurrentLanguage(newLang)
+								if (onLanguageChange) {
+									onLanguageChange(newLang)
 								}
-								{
-									// Display all available languages in alphabetical order
-									Object.keys(bundledLanguages)
-										.sort()
-										.map((lang) => {
-											const normalizedLang = normalizeLanguage(lang)
-											return (
-												<option
-													key={normalizedLang}
-													value={normalizedLang}
-													style={{
-														fontWeight:
-															normalizedLang === currentLanguage ? "bold" : "normal",
-														textAlign: "left",
-														fontSize:
-															normalizedLang === currentLanguage ? "1.2em" : "inherit",
-													}}>
-													{normalizedLang}
-												</option>
-											)
-										})
-								}
-							</LanguageSelect>
-						)}
+							}}>
+							<option
+								value={normalizeLanguage(language)}
+								style={{ fontWeight: "bold", textAlign: "left", fontSize: "1.2em" }}>
+								{normalizeLanguage(language)}
+							</option>
+							{
+								// Display all available languages in alphabetical order
+								Object.keys(bundledLanguages)
+									.sort()
+									.map((lang) => {
+										const normalizedLang = normalizeLanguage(lang)
+										return (
+											<option
+												key={normalizedLang}
+												value={normalizedLang}
+												style={{
+													fontWeight: normalizedLang === currentLanguage ? "bold" : "normal",
+													textAlign: "left",
+													fontSize: normalizedLang === currentLanguage ? "1.2em" : "inherit",
+												}}>
+												{normalizedLang}
+											</option>
+										)
+									})
+							}
+						</LanguageSelect>
 						{showCollapseButton && (
 							<CodeBlockButton
 								onClick={() => {

+ 1 - 1
webview-ui/src/components/common/__mocks__/CodeBlock.tsx

@@ -2,7 +2,7 @@ import * as React from "react"
 
 interface CodeBlockProps {
 	children?: React.ReactNode
-	language?: string
+	language: string
 }
 
 const CodeBlock: React.FC<CodeBlockProps> = () => <div data-testid="mock-code-block">Mocked Code Block</div>