Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
3d39f2c
feat(diffView): add autoFocus setting for diff view tab behavior
felixAnhalt Apr 25, 2025
53eb305
feat(diffViewWebUI): add diffViewAutoFocus setting and control
felixAnhalt Apr 25, 2025
a1831be
feat(diffView): add autoFocus labels and descriptions for multiple la…
felixAnhalt Apr 25, 2025
5ba577d
refactor(diffView): re-add autoFocus labels and descriptions from mul…
felixAnhalt Apr 25, 2025
742cc69
feat(diffView): implement diffViewAutoFocus setting and update relate…
felixAnhalt Apr 25, 2025
629fbad
feat(diffView): update diff editor behavior to use rightUri and enhan…
felixAnhalt Apr 25, 2025
4d0e9e0
feat(diffView): refactor diff editor opening logic and improve autoFo…
felixAnhalt Apr 25, 2025
dab08ab
feat(changeset): add changeset
felixAnhalt Apr 25, 2025
84dc391
test(diffView): add mock for createTextEditorDecorationType and verif…
felixAnhalt Apr 25, 2025
04889b7
test(diffView): add mock for createTextEditorDecorationType and verif…
felixAnhalt Apr 25, 2025
5f86d92
feat(diffView): enhance autoFocus handling for diff view and improve …
felixAnhalt Apr 26, 2025
8705104
feat(diffView): streamline editor fetching un open diff editor
felixAnhalt Apr 27, 2025
936fec1
feat(diffView): enhance diff editor opening to respect view column se…
felixAnhalt Apr 30, 2025
8644484
feat(diffView): update view column handling to default to Active for …
felixAnhalt May 1, 2025
151452a
feat(diffView): update default view column to Active for diff editor …
felixAnhalt May 1, 2025
6fb64b1
feat(diffView): implement focus handling for previous editor when aut…
felixAnhalt May 1, 2025
6be9b12
feat(diffView): refactor openDiffEditor to use default view column an…
felixAnhalt May 2, 2025
5fb1ec7
feat(diffView): finalize view col and re-focus behavior
felixAnhalt May 4, 2025
e19bbad
feat(diffView): restore functionality to open the finished edited fil…
felixAnhalt May 4, 2025
6b2e8a4
feat(diffView): add autoCloseRooTabs setting to manage automatic clos…
felixAnhalt May 4, 2025
aa87e05
fix(diffView): set default view column to -1 for proper initialization
felixAnhalt May 4, 2025
1d2c965
fix(diffView): remove unnecessary check for autoCloseRooTabs in close…
felixAnhalt May 4, 2025
fedddae
feat(diffView): add autoCloseTabs setting to manage tab closure behavior
felixAnhalt May 4, 2025
c0a5673
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 4, 2025
1426735
feat(diffView): enhance auto-focus behavior and manage user interactions
felixAnhalt May 6, 2025
d1d4eec
Merge branch 'RooVetGit:main' into feature/2122-editor-focus
felixAnhalt May 6, 2025
0e21dd4
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 9, 2025
ee10d10
feat(settings): streamline JSON structure and add auto-focus and auto…
felixAnhalt May 9, 2025
927760c
feat(diffView): refactor diff view reset methods to include listeners…
felixAnhalt May 9, 2025
945666b
feat(diffView): fix diffViewProvider config tests (ugly for now)
felixAnhalt May 9, 2025
185626c
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 11, 2025
7629191
feat(view): enhance view column handling for multiple windows and Web…
felixAnhalt May 11, 2025
61a8f55
Merge branch 'main' of https://github.com/RooVetGit/Roo-Code into edi…
seedlord May 13, 2025
f4ab14d
fix: comment out tab closing logic in DiffViewProvider
seedlord May 13, 2025
bff8aaf
Merge branch 'main' of https://github.com/RooVetGit/Roo-Code into edi…
seedlord May 13, 2025
05be241
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 13, 2025
f371001
Merge branch 'feature/2122-editor-focus' of https://github.com/felixa…
seedlord May 14, 2025
31d8a77
Merge branch 'main' of https://github.com/RooVetGit/Roo-Code into edi…
seedlord May 14, 2025
4560403
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 14, 2025
4799865
fix(exports): add exports/types.ts change leftovers from merge
felixAnhalt May 14, 2025
36ab8ad
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 15, 2025
f2627fe
fix(settings): update autoFocus description for clarity and consistency
felixAnhalt May 15, 2025
9316be6
Merge branch 'feature/2122-editor-focus' of https://github.com/felixa…
seedlord May 17, 2025
92647f7
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 17, 2025
a5ceddc
fix(diff-view): streamline tab closing logic in DiffViewProvider
felixAnhalt May 17, 2025
ef726e8
Merge branch 'feature/2122-editor-focus' of https://github.com/felixa…
seedlord May 17, 2025
92a623d
Merge branch 'main' of https://github.com/RooVetGit/Roo-Code into edi…
seedlord May 18, 2025
3d7e844
Add 'autoCloseAllRooTabs' setting to enhance tab management functiona…
seedlord May 19, 2025
f6b9efb
Refine diff editor behavior for new files and improve focus handling
seedlord May 19, 2025
86a721e
Refactor diff editor settings handling to dynamically read configurat…
seedlord May 19, 2025
2047067
Merge branch 'RooVetGit:main' into editorfocuspr/felixAnhalt/2955
seedlord May 20, 2025
c3e4794
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 20, 2025
ef84af7
refactor(diff): simplify scrolling behavior by removing autoFocus checks
seedlord May 20, 2025
31a0556
Merge branch 'main' of https://github.com/RooVetGit/Roo-Code into edi…
seedlord May 20, 2025
0e4e21a
Merge branch 'feature/2122-editor-focus' of https://github.com/felixa…
seedlord May 20, 2025
0fc0234
Merge branch 'main' into feature/2122-editor-focus
felixAnhalt May 21, 2025
542c7fb
Merge remote-tracking branch 'seedlord/editorfocuspr/felixAnhalt/2955…
felixAnhalt May 21, 2025
45249e1
refactor(diff): enhance tab closing logic and settings management in …
felixAnhalt May 21, 2025
e239ed0
feat(settings): add 'autoCloseAll' option to enhance tab management
felixAnhalt May 21, 2025
829cfe3
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 23, 2025
ff13aa2
feat(lockfile): add lockfile etc
felixAnhalt May 25, 2025
e189a0f
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 25, 2025
a834171
feat(diff): enhance focus behavior and tab management after diff oper…
felixAnhalt May 25, 2025
bf81452
feat(diff): implement focus behavior for the last tab in active group…
felixAnhalt May 25, 2025
e290d49
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 27, 2025
8c48cb8
feat(settings): add new options for diff view auto focus and tab mana…
felixAnhalt May 27, 2025
43dc92f
feat(settings): add options for diff view auto focus and tab management
felixAnhalt May 27, 2025
883f3e9
feat(settings): add options for diff view auto focus and tab management
felixAnhalt May 27, 2025
e5a75ee
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt May 31, 2025
e8b93c3
feat(writeToFileTool): update diff view provider to use view column a…
felixAnhalt May 31, 2025
b7a3220
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt Jun 7, 2025
2f9b748
Apply suggestions from code review
felixAnhalt Jun 7, 2025
0406afa
fix(ClineProvider): use tabPanelId to ref tab panels
felixAnhalt Jun 7, 2025
cdab9aa
refactor(SettingsView, ExtensionStateContext): streamline state updat…
felixAnhalt Jun 7, 2025
1a32c82
chore(cleanup): rem random linter changes etc
felixAnhalt Jun 7, 2025
5f05cce
feat(UserInteractionProvider): implement user interaction handling fo…
felixAnhalt Jun 7, 2025
0e03f5f
feat(PostDiffViewBehaviorUtils): add utility for managing post-diff b…
felixAnhalt Jun 7, 2025
f9b254f
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt Jun 9, 2025
8069fe9
chore(linting): rem out of scope linting
felixAnhalt Jun 9, 2025
08749da
chore(linting): rem out of scope linting
felixAnhalt Jun 9, 2025
f8455e3
chore(linting): rem out of scope linting
felixAnhalt Jun 9, 2025
6aa788a
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt Jun 9, 2025
af3b00d
chore(linting): rem out of scope linting
felixAnhalt Jun 9, 2025
f797f9d
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt Jun 10, 2025
0f7cad1
Merge remote-tracking branch 'origin/main' into feature/2122-editor-f…
felixAnhalt Jun 28, 2025
fae5f31
refactor(tests): update test mocks and improve diff view handling
felixAnhalt Jun 28, 2025
8c33c81
chore(linting): rem linter again
felixAnhalt Jun 28, 2025
ba19395
chore(linting): rem linter again
felixAnhalt Jun 28, 2025
7ecb74e
feat(file-editing): add file-based editing options and settings (#2)
felixAnhalt Jun 30, 2025
fd82dc5
Merge remote-tracking branch 'refs/remotes/origin/main' into feature/…
felixAnhalt Jun 30, 2025
9c94994
chore(mcphub): rem old jest test
felixAnhalt Jun 30, 2025
1379392
Merge remote-tracking branch 'refs/remotes/origin/main' into feature/…
felixAnhalt Jul 5, 2025
0569d7f
feat(settings): clean settings logic and use meaningful icons
felixAnhalt Jul 5, 2025
73f7239
feat(editor): update diff view auto focus settings and related options
felixAnhalt Jul 5, 2025
8481380
feat(editor): rename diffViewProvider to editingProvider for consistency
felixAnhalt Jul 5, 2025
2bac5e1
chore(linting): rem useless linter
felixAnhalt Jul 5, 2025
df54ea2
chore(linting): rem useless linter
felixAnhalt Jul 5, 2025
b4f213e
feat(editor): rename diffViewProvider to editingProvider in tests and…
felixAnhalt Jul 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/large-snakes-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"roo-cline": patch
---

Added an editable setting to allow users to preserve focus on the tab they are in while roo code is opening new tabs to do what it has to do.
11 changes: 11 additions & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,16 @@ export const globalSettingsSchema = z.object({
terminalCompressProgressBar: z.boolean().optional(),

rateLimitSeconds: z.number().optional(),

diffEnabled: z.boolean().optional(),
diffViewAutoFocus: z.boolean().optional(),
autoCloseRooTabs: z.boolean().optional(),
autoCloseAllRooTabs: z.boolean().optional(),
fileBasedEditing: z.boolean().optional(),
openTabsInCorrectGroup: z.boolean().optional(),
openTabsAtEndOfList: z.boolean().optional(),
fuzzyMatchThreshold: z.number().optional(),

experiments: experimentsSchema.optional(),

codebaseIndexModels: codebaseIndexModelsSchema.optional(),
Expand Down Expand Up @@ -220,6 +228,9 @@ export const EVALS_SETTINGS: RooCodeSettings = {
terminalShellIntegrationDisabled: true,

diffEnabled: true,
fileBasedEditing: false,
openTabsInCorrectGroup: false,
openTabsAtEndOfList: false,
fuzzyMatchThreshold: 1,

enableCheckpoints: false,
Expand Down
2 changes: 0 additions & 2 deletions packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ export type ProviderSettingsEntry = z.infer<typeof providerSettingsEntrySchema>

const baseProviderSettingsSchema = z.object({
includeMaxTokens: z.boolean().optional(),
diffEnabled: z.boolean().optional(),
fuzzyMatchThreshold: z.number().optional(),
modelTemperature: z.number().nullish(),
rateLimitSeconds: z.number().optional(),

Expand Down
41 changes: 0 additions & 41 deletions src/core/config/ProviderSettingsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,6 @@ export class ProviderSettingsManager {
isDirty = true
}

if (!providerProfiles.migrations.diffSettingsMigrated) {
await this.migrateDiffSettings(providerProfiles)
providerProfiles.migrations.diffSettingsMigrated = true
isDirty = true
}

if (!providerProfiles.migrations.openAiHeadersMigrated) {
await this.migrateOpenAiHeaders(providerProfiles)
providerProfiles.migrations.openAiHeadersMigrated = true
Expand Down Expand Up @@ -169,41 +163,6 @@ export class ProviderSettingsManager {
}
}

private async migrateDiffSettings(providerProfiles: ProviderProfiles) {
try {
let diffEnabled: boolean | undefined
let fuzzyMatchThreshold: number | undefined

try {
diffEnabled = await this.context.globalState.get<boolean>("diffEnabled")
fuzzyMatchThreshold = await this.context.globalState.get<number>("fuzzyMatchThreshold")
} catch (error) {
console.error("[MigrateDiffSettings] Error getting global diff settings:", error)
}

if (diffEnabled === undefined) {
// Failed to get the existing value, use the default.
diffEnabled = true
}

if (fuzzyMatchThreshold === undefined) {
// Failed to get the existing value, use the default.
fuzzyMatchThreshold = 1.0
}

for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
if (apiConfig.diffEnabled === undefined) {
apiConfig.diffEnabled = diffEnabled
}
if (apiConfig.fuzzyMatchThreshold === undefined) {
apiConfig.fuzzyMatchThreshold = fuzzyMatchThreshold
}
}
} catch (error) {
console.error(`[MigrateDiffSettings] Failed to migrate diff settings:`, error)
}
}

private async migrateOpenAiHeaders(providerProfiles: ProviderProfiles) {
try {
for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
Expand Down
27 changes: 18 additions & 9 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ import { McpServerManager } from "../../services/mcp/McpServerManager"
import { RepoPerTaskCheckpointService } from "../../services/checkpoints"

// integrations
import { DiffViewProvider } from "../../integrations/editor/DiffViewProvider"
import { IEditingProvider } from "../../integrations/editor/IEditingProvider"
import { EditingProviderFactory } from "../../integrations/editor/EditingProviderFactory"
import { findToolName, formatContentBlockToMarkdown } from "../../integrations/misc/export-markdown"
import { RooTerminalProcess } from "../../integrations/terminal/types"
import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry"
Expand Down Expand Up @@ -165,7 +166,7 @@ export class Task extends EventEmitter<ClineEvents> {
browserSession: BrowserSession

// Editing
diffViewProvider: DiffViewProvider
editingProvider: IEditingProvider
diffStrategy?: DiffStrategy
diffEnabled: boolean = false
fuzzyMatchThreshold: number
Expand Down Expand Up @@ -253,7 +254,7 @@ export class Task extends EventEmitter<ClineEvents> {
this.consecutiveMistakeLimit = consecutiveMistakeLimit
this.providerRef = new WeakRef(provider)
this.globalStoragePath = provider.context.globalStorageUri.fsPath
this.diffViewProvider = new DiffViewProvider(this.cwd)
this.editingProvider = EditingProviderFactory.createEditingProvider(this.cwd)
this.enableCheckpoints = enableCheckpoints

this.rootTask = rootTask
Expand Down Expand Up @@ -754,6 +755,7 @@ export class Task extends EventEmitter<ClineEvents> {

public async resumePausedTask(lastMessage: string) {
// Release this Cline instance from paused state.
this.editingProvider = EditingProviderFactory.createEditingProvider(this.cwd)
this.isPaused = false
this.emit("taskUnpaused")

Expand Down Expand Up @@ -1058,8 +1060,8 @@ export class Task extends EventEmitter<ClineEvents> {

try {
// If we're not streaming then `abortStream` won't be called
if (this.isStreaming && this.diffViewProvider.isEditing) {
this.diffViewProvider.revertChanges().catch(console.error)
if (this.isStreaming && this.editingProvider.isEditing) {
this.editingProvider.revertChanges().catch(console.error)
}
} catch (error) {
console.error("Error reverting diff changes:", error)
Expand Down Expand Up @@ -1113,6 +1115,9 @@ export class Task extends EventEmitter<ClineEvents> {
private async initiateTaskLoop(userContent: Anthropic.Messages.ContentBlockParam[]): Promise<void> {
// Kicks off the checkpoints initialization process in the background.
getCheckpointService(this)
// Lets track if the user is interacting with the editor after we start our task loop.
this.editingProvider.initialize()
this.editingProvider.disableAutoFocusAfterUserInteraction?.()

let nextUserContent = userContent
let includeFileDetails = true
Expand Down Expand Up @@ -1153,6 +1158,8 @@ export class Task extends EventEmitter<ClineEvents> {
throw new Error(`[RooCode#recursivelyMakeRooRequests] task ${this.taskId}.${this.instanceId} aborted`)
}

this.editingProvider = EditingProviderFactory.resetAndCreateNewEditingProvider(this.cwd, this.editingProvider)

if (this.consecutiveMistakeCount >= this.consecutiveMistakeLimit) {
const { response, text, images } = await this.ask(
"mistake_limit_reached",
Expand Down Expand Up @@ -1280,8 +1287,8 @@ export class Task extends EventEmitter<ClineEvents> {
}

const abortStream = async (cancelReason: ClineApiReqCancelReason, streamingFailedMessage?: string) => {
if (this.diffViewProvider.isEditing) {
await this.diffViewProvider.revertChanges() // closes diff view
if (this.editingProvider.isEditing) {
await this.editingProvider.revertChanges() // closes diff view
}

// if last message is a partial we need to update and save it
Expand Down Expand Up @@ -1333,7 +1340,7 @@ export class Task extends EventEmitter<ClineEvents> {
this.presentAssistantMessageLocked = false
this.presentAssistantMessageHasPendingUpdates = false

await this.diffViewProvider.reset()
await this.editingProvider.reset()

// Yields only if the first chunk is successful, otherwise will
// allow the user to retry the request (most likely due to rate
Expand Down Expand Up @@ -1716,7 +1723,9 @@ export class Task extends EventEmitter<ClineEvents> {

const contextWindow = modelInfo.contextWindow

const currentProfileId = state?.listApiConfigMeta.find((profile) => profile.name === state?.currentApiConfigName)?.id ?? "default";
const currentProfileId =
state?.listApiConfigMeta.find((profile) => profile.name === state?.currentApiConfigName)?.id ??
"default"

const truncateResult = await truncateConversationIfNeeded({
messages: this.apiConversationHistory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe("applyDiffTool experiment routing", () => {
applyDiff: vi.fn(),
getProgressStatus: vi.fn(),
},
diffViewProvider: {
editingProvider: {
reset: vi.fn(),
},
api: {
Expand Down
55 changes: 31 additions & 24 deletions src/core/tools/__tests__/writeToFileTool.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ vi.mock("../../ignore/RooIgnoreController", () => ({
},
}))

const MOCK_VIEW_COLUMN = 1

describe("writeToFileTool", () => {
// Test data
const testFilePath = "test/file.txt"
Expand All @@ -112,7 +114,6 @@ describe("writeToFileTool", () => {
const mockCline: any = {}
let mockAskApproval: ReturnType<typeof vi.fn>
let mockHandleError: ReturnType<typeof vi.fn>
let mockPushToolResult: ReturnType<typeof vi.fn>
let mockRemoveClosingTag: ReturnType<typeof vi.fn>
let toolResult: ToolResponse | undefined

Expand All @@ -135,7 +136,7 @@ describe("writeToFileTool", () => {
mockCline.rooIgnoreController = {
validateAccess: vi.fn().mockReturnValue(true),
}
mockCline.diffViewProvider = {
mockCline.editingProvider = {
editType: undefined,
isEditing: false,
originalContent: "",
Expand Down Expand Up @@ -168,6 +169,7 @@ describe("writeToFileTool", () => {
}
return "Tool result message"
}),
resetWithListeners: vi.fn().mockResolvedValue(undefined),
}
mockCline.api = {
getModel: vi.fn().mockReturnValue({ id: "claude-3" }),
Expand All @@ -179,6 +181,11 @@ describe("writeToFileTool", () => {
mockCline.ask = vi.fn().mockResolvedValue(undefined)
mockCline.recordToolError = vi.fn()
mockCline.sayAndCreateMissingParamError = vi.fn().mockResolvedValue("Missing param error")
mockCline.providerRef = {
deref: vi.fn().mockReturnValue({
getViewColumn: vi.fn().mockReturnValue(MOCK_VIEW_COLUMN),
}),
}

mockAskApproval = vi.fn().mockResolvedValue(true)
mockHandleError = vi.fn().mockResolvedValue(undefined)
Expand Down Expand Up @@ -238,7 +245,7 @@ describe("writeToFileTool", () => {
await executeWriteFileTool({}, { accessAllowed: true })

expect(mockCline.rooIgnoreController.validateAccess).toHaveBeenCalledWith(testFilePath)
expect(mockCline.diffViewProvider.open).toHaveBeenCalledWith(testFilePath)
expect(mockCline.editingProvider.open).toHaveBeenCalledWith(testFilePath, MOCK_VIEW_COLUMN)
})
})

Expand All @@ -247,18 +254,18 @@ describe("writeToFileTool", () => {
await executeWriteFileTool({}, { fileExists: true })

expect(mockedFileExistsAtPath).toHaveBeenCalledWith(absoluteFilePath)
expect(mockCline.diffViewProvider.editType).toBe("modify")
expect(mockCline.editingProvider.editType).toBe("modify")
})

it.skipIf(process.platform === "win32")("detects new file and sets editType to create", async () => {
await executeWriteFileTool({}, { fileExists: false })

expect(mockedFileExistsAtPath).toHaveBeenCalledWith(absoluteFilePath)
expect(mockCline.diffViewProvider.editType).toBe("create")
expect(mockCline.editingProvider.editType).toBe("create")
})

it("uses cached editType without filesystem check", async () => {
mockCline.diffViewProvider.editType = "modify"
mockCline.editingProvider.editType = "modify"

await executeWriteFileTool({})

Expand All @@ -270,13 +277,13 @@ describe("writeToFileTool", () => {
it("removes markdown code block markers from content", async () => {
await executeWriteFileTool({ content: testContentWithMarkdown })

expect(mockCline.diffViewProvider.update).toHaveBeenCalledWith("Line 1\nLine 2", true)
expect(mockCline.editingProvider.update).toHaveBeenCalledWith("Line 1\nLine 2", true)
})

it("passes through empty content unchanged", async () => {
await executeWriteFileTool({ content: "" })

expect(mockCline.diffViewProvider.update).toHaveBeenCalledWith("", true)
expect(mockCline.editingProvider.update).toHaveBeenCalledWith("", true)
})

it("unescapes HTML entities for non-Claude models", async () => {
Expand Down Expand Up @@ -304,7 +311,7 @@ describe("writeToFileTool", () => {

expect(mockedEveryLineHasLineNumbers).toHaveBeenCalledWith(contentWithLineNumbers)
expect(mockedStripLineNumbers).toHaveBeenCalledWith(contentWithLineNumbers)
expect(mockCline.diffViewProvider.update).toHaveBeenCalledWith("line one\nline two", true)
expect(mockCline.editingProvider.update).toHaveBeenCalledWith("line one\nline two", true)
})
})

Expand All @@ -313,10 +320,10 @@ describe("writeToFileTool", () => {
await executeWriteFileTool({}, { fileExists: false })

expect(mockCline.consecutiveMistakeCount).toBe(0)
expect(mockCline.diffViewProvider.open).toHaveBeenCalledWith(testFilePath)
expect(mockCline.diffViewProvider.update).toHaveBeenCalledWith(testContent, true)
expect(mockCline.editingProvider.open).toHaveBeenCalledWith(testFilePath, MOCK_VIEW_COLUMN)
expect(mockCline.editingProvider.update).toHaveBeenCalledWith(testContent, true)
expect(mockAskApproval).toHaveBeenCalled()
expect(mockCline.diffViewProvider.saveChanges).toHaveBeenCalled()
expect(mockCline.editingProvider.saveChanges).toHaveBeenCalled()
expect(mockCline.fileContextTracker.trackFileContext).toHaveBeenCalledWith(testFilePath, "roo_edited")
expect(mockCline.didEditFile).toBe(true)
})
Expand All @@ -341,21 +348,21 @@ describe("writeToFileTool", () => {
it("returns early when path is missing in partial block", async () => {
await executeWriteFileTool({ path: undefined }, { isPartial: true })

expect(mockCline.diffViewProvider.open).not.toHaveBeenCalled()
expect(mockCline.editingProvider.open).not.toHaveBeenCalled()
})

it("returns early when content is undefined in partial block", async () => {
await executeWriteFileTool({ content: undefined }, { isPartial: true })

expect(mockCline.diffViewProvider.open).not.toHaveBeenCalled()
expect(mockCline.editingProvider.open).not.toHaveBeenCalled()
})

it("streams content updates during partial execution", async () => {
await executeWriteFileTool({}, { isPartial: true })

expect(mockCline.ask).toHaveBeenCalled()
expect(mockCline.diffViewProvider.open).toHaveBeenCalledWith(testFilePath)
expect(mockCline.diffViewProvider.update).toHaveBeenCalledWith(testContent, false)
expect(mockCline.editingProvider.open).toHaveBeenCalledWith(testFilePath, MOCK_VIEW_COLUMN)
expect(mockCline.editingProvider.update).toHaveBeenCalledWith(testContent, false)
})
})

Expand All @@ -365,19 +372,19 @@ describe("writeToFileTool", () => {

await executeWriteFileTool({})

expect(mockCline.diffViewProvider.revertChanges).toHaveBeenCalled()
expect(mockCline.diffViewProvider.saveChanges).not.toHaveBeenCalled()
expect(mockCline.editingProvider.revertChanges).toHaveBeenCalled()
expect(mockCline.editingProvider.saveChanges).not.toHaveBeenCalled()
})

it("reports user edits with diff feedback", async () => {
const userEditsValue = "- old line\n+ new line"
mockCline.diffViewProvider.saveChanges.mockResolvedValue({
mockCline.editingProvider.saveChanges.mockResolvedValue({
newProblemsMessage: " with warnings",
userEdits: userEditsValue,
finalContent: "modified content",
})
// Manually set the property on the mock instance because the original saveChanges is not called
mockCline.diffViewProvider.userEdits = userEditsValue
mockCline.editingProvider.userEdits = userEditsValue

await executeWriteFileTool({}, { fileExists: true })

Expand All @@ -390,21 +397,21 @@ describe("writeToFileTool", () => {

describe("error handling", () => {
it("handles general file operation errors", async () => {
mockCline.diffViewProvider.open.mockRejectedValue(new Error("General error"))
mockCline.editingProvider.open.mockRejectedValue(new Error("General error"))

await executeWriteFileTool({})

expect(mockHandleError).toHaveBeenCalledWith("writing file", expect.any(Error))
expect(mockCline.diffViewProvider.reset).toHaveBeenCalled()
expect(mockCline.editingProvider.resetWithListeners).toHaveBeenCalled()
})

it("handles partial streaming errors", async () => {
mockCline.diffViewProvider.open.mockRejectedValue(new Error("Open failed"))
mockCline.editingProvider.open.mockRejectedValue(new Error("Open failed"))

await executeWriteFileTool({}, { isPartial: true })

expect(mockHandleError).toHaveBeenCalledWith("writing file", expect.any(Error))
expect(mockCline.diffViewProvider.reset).toHaveBeenCalled()
expect(mockCline.editingProvider.resetWithListeners).toHaveBeenCalled()
})
})
})
Loading