feat: move disableDiffVisualization to experimental settings

- Moved disableDiffVisualization from global settings to experimental settings
- Updated all references to access it through experiments instead
- Fixed test cases to include the new experiment
- Updated translations to reflect experimental nature
- Setting now properly affects ALL edit tools (write_to_file, apply_diff, insert_content, search_and_replace)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
hannesrudolph 2025-06-16 13:26:33 -06:00
parent 9e5c504c1c
commit 530c5c470f
15 changed files with 30 additions and 63 deletions

View file

@ -2,6 +2,6 @@
"roo-cline": patch
---
Add setting to disable diff visualization to prevent LSP crashes
Add experimental setting to disable diff visualization for all edit tools
Added a new setting `disableDiffVisualization` that allows users to disable the diff view when editing files. When enabled, files will open directly in the editor instead of showing a side-by-side diff view. This helps prevent Language Server Protocol (LSP) crashes that can occur with very large files, particularly affecting C# developers. The changes made by Roo are still visible in the chat window.
Added a new experimental setting `disableDiffVisualization` that allows users to disable diff visualization for all edit tools (write_to_file, apply_diff, insert_content, search_and_replace). When enabled, files will open directly in the editor instead of showing a side-by-side diff view. This helps prevent Language Server Protocol (LSP) crashes that can occur with very large files, particularly affecting C# developers. The changes made by Roo are still visible in the chat window.

View file

@ -6,7 +6,13 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
* ExperimentId
*/
export const experimentIds = ["powerSteering", "disableCompletionCommand", "marketplace", "multiFileApplyDiff"] as const
export const experimentIds = [
"powerSteering",
"disableCompletionCommand",
"marketplace",
"multiFileApplyDiff",
"disableDiffVisualization",
] as const
export const experimentIdsSchema = z.enum(experimentIds)
@ -21,6 +27,7 @@ export const experimentsSchema = z.object({
disableCompletionCommand: z.boolean().optional(),
marketplace: z.boolean().optional(),
multiFileApplyDiff: z.boolean().optional(),
disableDiffVisualization: z.boolean().optional(),
})
export type Experiments = z.infer<typeof experimentsSchema>

View file

@ -83,7 +83,6 @@ export const globalSettingsSchema = z.object({
rateLimitSeconds: z.number().optional(),
diffEnabled: z.boolean().optional(),
disableDiffVisualization: z.boolean().optional(),
fuzzyMatchThreshold: z.number().optional(),
experiments: experimentsSchema.optional(),
@ -212,7 +211,6 @@ export const EVALS_SETTINGS: RooCodeSettings = {
terminalShellIntegrationDisabled: true,
diffEnabled: true,
disableDiffVisualization: false,
fuzzyMatchThreshold: 1,
enableCheckpoints: false,

View file

@ -529,12 +529,13 @@ export class ClineProvider
apiConfiguration,
organizationAllowList,
diffEnabled: enableDiff,
disableDiffVisualization,
enableCheckpoints,
fuzzyMatchThreshold,
experiments,
} = await this.getState()
const disableDiffVisualization = experiments.disableDiffVisualization ?? false
if (!ProfileValidator.isProfileAllowed(apiConfiguration, organizationAllowList)) {
throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist"))
}
@ -571,12 +572,13 @@ export class ClineProvider
const {
apiConfiguration,
diffEnabled: enableDiff,
disableDiffVisualization,
enableCheckpoints,
fuzzyMatchThreshold,
experiments,
} = await this.getState()
const disableDiffVisualization = experiments.disableDiffVisualization ?? false
const cline = new Task({
provider: this,
apiConfiguration,
@ -1550,7 +1552,6 @@ export class ClineProvider
ttsEnabled: stateValues.ttsEnabled ?? false,
ttsSpeed: stateValues.ttsSpeed ?? 1.0,
diffEnabled: stateValues.diffEnabled ?? true,
disableDiffVisualization: stateValues.disableDiffVisualization ?? false,
enableCheckpoints: stateValues.enableCheckpoints ?? true,
soundVolume: stateValues.soundVolume,
browserViewportSize: stateValues.browserViewportSize ?? "900x600",

View file

@ -645,11 +645,6 @@ export const webviewMessageHandler = async (
await updateGlobalState("diffEnabled", diffEnabled)
await provider.postStateToWebview()
break
case "disableDiffVisualization":
const disableDiffVisualization = message.bool ?? false
await updateGlobalState("disableDiffVisualization", disableDiffVisualization)
await provider.postStateToWebview()
break
case "enableCheckpoints":
const enableCheckpoints = message.bool ?? true
await updateGlobalState("enableCheckpoints", enableCheckpoints)

View file

@ -188,7 +188,6 @@ export type ExtensionState = Pick<
| "terminalZdotdir"
| "terminalCompressProgressBar"
| "diffEnabled"
| "disableDiffVisualization"
| "fuzzyMatchThreshold"
// | "experiments" // Optional in GlobalSettings, required here.
| "language"

View file

@ -82,7 +82,6 @@ export interface WebviewMessage {
| "ttsSpeed"
| "soundVolume"
| "diffEnabled"
| "disableDiffVisualization"
| "enableCheckpoints"
| "browserViewportSize"
| "screenshotQuality"

View file

@ -30,6 +30,7 @@ describe("experiments", () => {
marketplace: false,
disableCompletionCommand: false,
multiFileApplyDiff: false,
disableDiffVisualization: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
@ -40,6 +41,7 @@ describe("experiments", () => {
marketplace: false,
disableCompletionCommand: false,
multiFileApplyDiff: false,
disableDiffVisualization: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
})
@ -50,6 +52,7 @@ describe("experiments", () => {
marketplace: false,
disableCompletionCommand: false,
multiFileApplyDiff: false,
disableDiffVisualization: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
@ -70,6 +73,7 @@ describe("experiments", () => {
marketplace: false,
disableCompletionCommand: false,
multiFileApplyDiff: false,
disableDiffVisualization: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.MARKETPLACE)).toBe(false)
})
@ -80,6 +84,7 @@ describe("experiments", () => {
marketplace: true,
disableCompletionCommand: false,
multiFileApplyDiff: false,
disableDiffVisualization: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.MARKETPLACE)).toBe(true)
})

View file

@ -5,6 +5,7 @@ export const EXPERIMENT_IDS = {
MULTI_FILE_APPLY_DIFF: "multiFileApplyDiff",
DISABLE_COMPLETION_COMMAND: "disableCompletionCommand",
POWER_STEERING: "powerSteering",
DISABLE_DIFF_VISUALIZATION: "disableDiffVisualization",
} as const satisfies Record<string, ExperimentId>
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@ -20,6 +21,7 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
MULTI_FILE_APPLY_DIFF: { enabled: false },
DISABLE_COMPLETION_COMMAND: { enabled: false },
POWER_STEERING: { enabled: false },
DISABLE_DIFF_VISUALIZATION: { enabled: false },
}
export const experimentDefault = Object.fromEntries(

View file

@ -73,7 +73,7 @@ const ApiOptions = ({
setErrorMessage,
}: ApiOptionsProps) => {
const { t } = useAppTranslation()
const { organizationAllowList, disableDiffVisualization, setDisableDiffVisualization } = useExtensionState()
const { organizationAllowList } = useExtensionState()
const [customHeaders, setCustomHeaders] = useState<[string, string][]>(() => {
const headers = apiConfiguration?.openAiHeaders || {}
@ -476,19 +476,8 @@ const ApiOptions = ({
<>
<DiffSettingsControl
diffEnabled={apiConfiguration.diffEnabled}
disableDiffVisualization={disableDiffVisualization}
fuzzyMatchThreshold={apiConfiguration.fuzzyMatchThreshold}
onChange={(field, value) => {
if (field === "disableDiffVisualization") {
setDisableDiffVisualization(value)
vscode.postMessage({
type: "disableDiffVisualization",
bool: value,
})
} else {
setApiConfigurationField(field, value)
}
}}
onChange={(field, value) => setApiConfigurationField(field, value)}
/>
<TemperatureControl
value={apiConfiguration.modelTemperature}

View file

@ -5,14 +5,12 @@ import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
interface DiffSettingsControlProps {
diffEnabled?: boolean
disableDiffVisualization?: boolean
fuzzyMatchThreshold?: number
onChange: (field: "diffEnabled" | "disableDiffVisualization" | "fuzzyMatchThreshold", value: any) => void
onChange: (field: "diffEnabled" | "fuzzyMatchThreshold", value: any) => void
}
export const DiffSettingsControl: React.FC<DiffSettingsControlProps> = ({
diffEnabled = true,
disableDiffVisualization = false,
fuzzyMatchThreshold = 1.0,
onChange,
}) => {
@ -25,13 +23,6 @@ export const DiffSettingsControl: React.FC<DiffSettingsControlProps> = ({
[onChange],
)
const handleDisableDiffVisualizationChange = useCallback(
(e: any) => {
onChange("disableDiffVisualization", e.target.checked)
},
[onChange],
)
const handleThresholdChange = useCallback(
(newValue: number[]) => {
onChange("fuzzyMatchThreshold", newValue[0])
@ -70,19 +61,6 @@ export const DiffSettingsControl: React.FC<DiffSettingsControlProps> = ({
{t("settings:advanced.diff.matchPrecision.description")}
</div>
</div>
<div>
<VSCodeCheckbox
checked={disableDiffVisualization}
onChange={handleDisableDiffVisualizationChange}>
<span className="font-medium">
{t("settings:advanced.diff.disableVisualization.label")}
</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm">
{t("settings:advanced.diff.disableVisualization.description")}
</div>
</div>
</div>
)}
</div>

View file

@ -139,7 +139,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
browserViewportSize,
enableCheckpoints,
diffEnabled,
disableDiffVisualization,
experiments,
fuzzyMatchThreshold,
maxOpenTabsContext,
@ -281,7 +280,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
vscode.postMessage({ type: "ttsSpeed", value: ttsSpeed })
vscode.postMessage({ type: "soundVolume", value: soundVolume })
vscode.postMessage({ type: "diffEnabled", bool: diffEnabled })
vscode.postMessage({ type: "disableDiffVisualization", bool: disableDiffVisualization })
vscode.postMessage({ type: "enableCheckpoints", bool: enableCheckpoints })
vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize })
vscode.postMessage({ type: "remoteBrowserHost", text: remoteBrowserHost })

View file

@ -68,7 +68,6 @@ export interface ExtensionStateContextType extends ExtensionState {
setTtsEnabled: (value: boolean) => void
setTtsSpeed: (value: number) => void
setDiffEnabled: (value: boolean) => void
setDisableDiffVisualization: (value: boolean) => void
setEnableCheckpoints: (value: boolean) => void
setBrowserViewportSize: (value: string) => void
setFuzzyMatchThreshold: (value: number) => void
@ -155,7 +154,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
ttsEnabled: false,
ttsSpeed: 1.0,
diffEnabled: false,
disableDiffVisualization: false,
enableCheckpoints: true,
fuzzyMatchThreshold: 1.0,
language: "en", // Default language code
@ -344,8 +342,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setTtsEnabled: (value) => setState((prevState) => ({ ...prevState, ttsEnabled: value })),
setTtsSpeed: (value) => setState((prevState) => ({ ...prevState, ttsSpeed: value })),
setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })),
setDisableDiffVisualization: (value) =>
setState((prevState) => ({ ...prevState, disableDiffVisualization: value })),
setEnableCheckpoints: (value) => setState((prevState) => ({ ...prevState, enableCheckpoints: value })),
setBrowserViewportSize: (value: string) =>
setState((prevState) => ({ ...prevState, browserViewportSize: value })),

View file

@ -223,9 +223,9 @@ describe("mergeExtensionState", () => {
experiments: {
powerSteering: true,
marketplace: false,
concurrentFileReads: true,
disableCompletionCommand: false,
multiFileApplyDiff: true,
disableDiffVisualization: false,
} as Record<ExperimentId, boolean>,
}
@ -239,9 +239,9 @@ describe("mergeExtensionState", () => {
expect(result.experiments).toEqual({
powerSteering: true,
marketplace: false,
concurrentFileReads: true,
disableCompletionCommand: false,
multiFileApplyDiff: true,
disableDiffVisualization: false,
})
})
})

View file

@ -469,10 +469,6 @@
"matchPrecision": {
"label": "Match precision",
"description": "This slider controls how precisely code sections must match when applying diffs. Lower values allow more flexible matching but increase the risk of incorrect replacements. Use values below 100% with extreme caution."
},
"disableVisualization": {
"label": "Disable diff visualization",
"description": "When enabled, files will open directly in the editor instead of showing a diff view. This can prevent LSP crashes with very large files. You can still see the changes in the chat window."
}
}
},
@ -512,6 +508,10 @@
"MULTI_FILE_APPLY_DIFF": {
"name": "Enable concurrent file edits",
"description": "When enabled, Roo can edit multiple files in a single request. When disabled, Roo must edit files one at a time. Disabling this can help when working with less capable models or when you want more control over file modifications."
},
"DISABLE_DIFF_VISUALIZATION": {
"name": "Disable diff visualization for all edit tools",
"description": "When enabled, files will open directly in the editor instead of showing a diff view for all edit tools (write_to_file, apply_diff, insert_content, search_and_replace). This helps prevent LSP crashes with very large files. Changes are still visible in the chat window."
}
},
"promptCaching": {