mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: preserve pinned tab state when editing files
When RooCode edits a file, it closes the existing tab and opens a diff editor. After accepting or rejecting changes, the file is reopened but the pinned state is lost. This fix captures the tab isPinned state before closing, then restores it after reopening via workbench.action.pinEditor command in both saveChanges and revertChanges flows. Closes #11326
This commit is contained in:
parent
99a2e3b1ed
commit
e444c553bc
2 changed files with 224 additions and 1 deletions
|
|
@ -28,6 +28,7 @@ export class DiffViewProvider {
|
|||
originalContent: string | undefined
|
||||
private createdDirs: string[] = []
|
||||
private documentWasOpen = false
|
||||
private documentWasPinned = false
|
||||
private relPath?: string
|
||||
private newContent?: string
|
||||
private activeDiffEditor?: vscode.TextEditor
|
||||
|
|
@ -84,6 +85,7 @@ export class DiffViewProvider {
|
|||
// If the file was already open, close it (must happen after showing the
|
||||
// diff view since if it's the only tab the column will close).
|
||||
this.documentWasOpen = false
|
||||
this.documentWasPinned = false
|
||||
|
||||
// Close the tab if it's open (it's already saved above).
|
||||
const tabs = vscode.window.tabGroups.all
|
||||
|
|
@ -97,6 +99,9 @@ export class DiffViewProvider {
|
|||
)
|
||||
|
||||
for (const tab of tabs) {
|
||||
if (tab.isPinned) {
|
||||
this.documentWasPinned = true
|
||||
}
|
||||
if (!tab.isDirty) {
|
||||
await vscode.window.tabGroups.close(tab)
|
||||
}
|
||||
|
|
@ -210,6 +215,12 @@ export class DiffViewProvider {
|
|||
}
|
||||
|
||||
await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, preserveFocus: true })
|
||||
|
||||
// Restore pinned state if the tab was pinned before editing.
|
||||
if (this.documentWasPinned) {
|
||||
await vscode.commands.executeCommand("workbench.action.pinEditor")
|
||||
}
|
||||
|
||||
await this.closeAllDiffViews()
|
||||
|
||||
// Getting diagnostics before and after the file edit is a better approach than
|
||||
|
|
@ -401,6 +412,11 @@ export class DiffViewProvider {
|
|||
preview: false,
|
||||
preserveFocus: true,
|
||||
})
|
||||
|
||||
// Restore pinned state if the tab was pinned before editing.
|
||||
if (this.documentWasPinned) {
|
||||
await vscode.commands.executeCommand("workbench.action.pinEditor")
|
||||
}
|
||||
}
|
||||
|
||||
await this.closeAllDiffViews()
|
||||
|
|
@ -619,6 +635,7 @@ export class DiffViewProvider {
|
|||
this.originalContent = undefined
|
||||
this.createdDirs = []
|
||||
this.documentWasOpen = false
|
||||
this.documentWasPinned = false
|
||||
this.activeDiffEditor = undefined
|
||||
this.fadedOverlayController = undefined
|
||||
this.activeLineController = undefined
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ vi.mock("vscode", () => ({
|
|||
visibleTextEditors: [],
|
||||
},
|
||||
commands: {
|
||||
executeCommand: vi.fn(),
|
||||
executeCommand: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
languages: {
|
||||
getDiagnostics: vi.fn(() => []),
|
||||
|
|
@ -84,6 +84,7 @@ vi.mock("vscode", () => ({
|
|||
TextEditorRevealType: {
|
||||
InCenter: 2,
|
||||
},
|
||||
TabInputText: class TabInputText {},
|
||||
TabInputTextDiff: class TabInputTextDiff {},
|
||||
Uri: {
|
||||
file: vi.fn((path) => ({ fsPath: path })),
|
||||
|
|
@ -517,4 +518,209 @@ describe("DiffViewProvider", () => {
|
|||
expect(vscode.languages.getDiagnostics).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("pinned tab preservation", () => {
|
||||
describe("open method - pin state capture", () => {
|
||||
it("should capture pinned state when closing a pinned tab", async () => {
|
||||
const absolutePath = `${mockCwd}/test.txt`
|
||||
|
||||
// Setup a pinned tab
|
||||
const pinnedTab = {
|
||||
input: { uri: { scheme: "file", fsPath: absolutePath } },
|
||||
isDirty: false,
|
||||
isPinned: true,
|
||||
}
|
||||
|
||||
// Make the tab look like a TabInputText instance
|
||||
Object.setPrototypeOf(pinnedTab.input, vscode.TabInputText?.prototype ?? {})
|
||||
|
||||
Object.defineProperty(vscode.window.tabGroups, "all", {
|
||||
get: () => [{ tabs: [pinnedTab] }],
|
||||
configurable: true,
|
||||
})
|
||||
|
||||
vi.mocked(vscode.window.tabGroups.close).mockResolvedValue(true as any)
|
||||
|
||||
// Mock for openDiffEditor
|
||||
const mockEditor = {
|
||||
document: {
|
||||
uri: { fsPath: absolutePath, scheme: "file" },
|
||||
getText: vi.fn().mockReturnValue(""),
|
||||
lineCount: 0,
|
||||
},
|
||||
selection: { active: { line: 0, character: 0 }, anchor: { line: 0, character: 0 } },
|
||||
edit: vi.fn().mockResolvedValue(true),
|
||||
revealRange: vi.fn(),
|
||||
}
|
||||
|
||||
vi.mocked(vscode.window.showTextDocument).mockResolvedValue(mockEditor as any)
|
||||
|
||||
vi.mocked(vscode.workspace.onDidOpenTextDocument).mockImplementation((callback) => {
|
||||
setTimeout(() => {
|
||||
callback({ uri: { fsPath: absolutePath, scheme: "file" } } as any)
|
||||
}, 0)
|
||||
return { dispose: vi.fn() }
|
||||
})
|
||||
|
||||
vi.mocked(vscode.window).visibleTextEditors = [mockEditor as any]
|
||||
;(diffViewProvider as any).editType = "modify"
|
||||
|
||||
await diffViewProvider.open("test.txt")
|
||||
|
||||
expect((diffViewProvider as any).documentWasPinned).toBe(true)
|
||||
expect((diffViewProvider as any).documentWasOpen).toBe(true)
|
||||
})
|
||||
|
||||
it("should not set pinned state when closing an unpinned tab", async () => {
|
||||
const absolutePath = `${mockCwd}/test.txt`
|
||||
|
||||
const unpinnedTab = {
|
||||
input: { uri: { scheme: "file", fsPath: absolutePath } },
|
||||
isDirty: false,
|
||||
isPinned: false,
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(unpinnedTab.input, vscode.TabInputText?.prototype ?? {})
|
||||
|
||||
Object.defineProperty(vscode.window.tabGroups, "all", {
|
||||
get: () => [{ tabs: [unpinnedTab] }],
|
||||
configurable: true,
|
||||
})
|
||||
|
||||
vi.mocked(vscode.window.tabGroups.close).mockResolvedValue(true as any)
|
||||
|
||||
const mockEditor = {
|
||||
document: {
|
||||
uri: { fsPath: absolutePath, scheme: "file" },
|
||||
getText: vi.fn().mockReturnValue(""),
|
||||
lineCount: 0,
|
||||
},
|
||||
selection: { active: { line: 0, character: 0 }, anchor: { line: 0, character: 0 } },
|
||||
edit: vi.fn().mockResolvedValue(true),
|
||||
revealRange: vi.fn(),
|
||||
}
|
||||
|
||||
vi.mocked(vscode.window.showTextDocument).mockResolvedValue(mockEditor as any)
|
||||
|
||||
vi.mocked(vscode.workspace.onDidOpenTextDocument).mockImplementation((callback) => {
|
||||
setTimeout(() => {
|
||||
callback({ uri: { fsPath: absolutePath, scheme: "file" } } as any)
|
||||
}, 0)
|
||||
return { dispose: vi.fn() }
|
||||
})
|
||||
|
||||
vi.mocked(vscode.window).visibleTextEditors = [mockEditor as any]
|
||||
;(diffViewProvider as any).editType = "modify"
|
||||
|
||||
await diffViewProvider.open("test.txt")
|
||||
|
||||
expect((diffViewProvider as any).documentWasPinned).toBe(false)
|
||||
expect((diffViewProvider as any).documentWasOpen).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("saveChanges - pin state restoration", () => {
|
||||
beforeEach(() => {
|
||||
;(diffViewProvider as any).relPath = "test.ts"
|
||||
;(diffViewProvider as any).newContent = "new content"
|
||||
;(diffViewProvider as any).activeDiffEditor = {
|
||||
document: {
|
||||
getText: vi.fn().mockReturnValue("new content"),
|
||||
isDirty: false,
|
||||
save: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
}
|
||||
;(diffViewProvider as any).preDiagnostics = []
|
||||
;(diffViewProvider as any).closeAllDiffViews = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
vi.mocked(vscode.window.showTextDocument).mockResolvedValue({} as any)
|
||||
vi.mocked(vscode.languages.getDiagnostics).mockReturnValue([])
|
||||
vi.mocked(vscode.commands.executeCommand).mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
it("should pin the editor after saving when documentWasPinned is true", async () => {
|
||||
;(diffViewProvider as any).documentWasPinned = true
|
||||
|
||||
await diffViewProvider.saveChanges(false)
|
||||
|
||||
expect(vscode.commands.executeCommand).toHaveBeenCalledWith("workbench.action.pinEditor")
|
||||
})
|
||||
|
||||
it("should not pin the editor after saving when documentWasPinned is false", async () => {
|
||||
;(diffViewProvider as any).documentWasPinned = false
|
||||
|
||||
await diffViewProvider.saveChanges(false)
|
||||
|
||||
expect(vscode.commands.executeCommand).not.toHaveBeenCalledWith("workbench.action.pinEditor")
|
||||
})
|
||||
})
|
||||
|
||||
describe("revertChanges - pin state restoration", () => {
|
||||
beforeEach(() => {
|
||||
;(diffViewProvider as any).relPath = "test.ts"
|
||||
;(diffViewProvider as any).newContent = "new content"
|
||||
;(diffViewProvider as any).originalContent = "original content"
|
||||
;(diffViewProvider as any).editType = "modify"
|
||||
;(diffViewProvider as any).activeDiffEditor = {
|
||||
document: {
|
||||
uri: { fsPath: `${mockCwd}/test.ts` },
|
||||
getText: vi.fn().mockReturnValue("new content"),
|
||||
positionAt: vi.fn().mockReturnValue({ line: 0, character: 0 }),
|
||||
isDirty: false,
|
||||
save: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
}
|
||||
|
||||
vi.mocked(vscode.workspace.applyEdit).mockResolvedValue(true)
|
||||
vi.mocked(vscode.window.showTextDocument).mockResolvedValue({} as any)
|
||||
vi.mocked(vscode.commands.executeCommand).mockResolvedValue(undefined)
|
||||
|
||||
// Mock closeAllDiffViews
|
||||
;(diffViewProvider as any).closeAllDiffViews = vi.fn().mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
it("should pin the editor after reverting when documentWasPinned is true", async () => {
|
||||
;(diffViewProvider as any).documentWasOpen = true
|
||||
;(diffViewProvider as any).documentWasPinned = true
|
||||
|
||||
await diffViewProvider.revertChanges()
|
||||
|
||||
expect(vscode.commands.executeCommand).toHaveBeenCalledWith("workbench.action.pinEditor")
|
||||
})
|
||||
|
||||
it("should not pin the editor after reverting when documentWasPinned is false", async () => {
|
||||
;(diffViewProvider as any).documentWasOpen = true
|
||||
;(diffViewProvider as any).documentWasPinned = false
|
||||
|
||||
await diffViewProvider.revertChanges()
|
||||
|
||||
expect(vscode.commands.executeCommand).not.toHaveBeenCalledWith("workbench.action.pinEditor")
|
||||
})
|
||||
|
||||
it("should not pin the editor when document was not open", async () => {
|
||||
;(diffViewProvider as any).documentWasOpen = false
|
||||
;(diffViewProvider as any).documentWasPinned = true
|
||||
|
||||
await diffViewProvider.revertChanges()
|
||||
|
||||
expect(vscode.commands.executeCommand).not.toHaveBeenCalledWith("workbench.action.pinEditor")
|
||||
})
|
||||
})
|
||||
|
||||
describe("reset - pin state cleanup", () => {
|
||||
it("should reset documentWasPinned to false", async () => {
|
||||
;(diffViewProvider as any).documentWasPinned = true
|
||||
|
||||
// Mock tabGroups.all to return empty for closeAllDiffViews
|
||||
Object.defineProperty(vscode.window.tabGroups, "all", {
|
||||
get: () => [],
|
||||
configurable: true,
|
||||
})
|
||||
|
||||
await (diffViewProvider as any).reset()
|
||||
|
||||
expect((diffViewProvider as any).documentWasPinned).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue