@@ -13,10 +13,17 @@ import { ClineSayTool } from "../../shared/ExtensionMessage"
1313import { Task } from "../../core/task/Task"
1414
1515import { DecorationController } from "./DecorationController"
16+ import { PostDiffViewBehaviorUtils } from "./PostDiffViewBehaviorUtils"
17+ import { TextDocument , TextDocumentShowOptions } from "vscode"
1618
1719export const DIFF_VIEW_URI_SCHEME = "cline-diff"
1820export const DIFF_VIEW_LABEL_CHANGES = "Original ↔ Roo's Changes"
1921
22+ interface DiffSettings {
23+ autoCloseRooTabs : boolean
24+ autoCloseAllRooTabs : boolean
25+ }
26+
2027// TODO: https://github.com/cline/cline/pull/3354
2128export class DiffViewProvider {
2229 // Properties to store the results of saveChanges
@@ -34,15 +41,88 @@ export class DiffViewProvider {
3441 private activeLineController ?: DecorationController
3542 private streamedLines : string [ ] = [ ]
3643 private preDiagnostics : [ vscode . Uri , vscode . Diagnostic [ ] ] [ ] = [ ]
44+ private rooOpenedTabs : Set < string > = new Set ( )
45+ private autoCloseAllRooTabs : boolean = false // Added new setting
46+ private preDiffActiveEditor ?: vscode . TextEditor // Store active editor before diff operation
47+ private postDiffBehaviorUtils : PostDiffViewBehaviorUtils
48+
49+ constructor ( private cwd : string ) {
50+ // Initialize PostDiffviewBehaviorUtils with initial context
51+ this . postDiffBehaviorUtils = new PostDiffViewBehaviorUtils ( {
52+ relPath : this . relPath ,
53+ editType : this . editType ,
54+ documentWasOpen : this . documentWasOpen ,
55+ cwd : this . cwd ,
56+ rooOpenedTabs : this . rooOpenedTabs ,
57+ preDiffActiveEditor : this . preDiffActiveEditor ,
58+ autoCloseAllRooTabs : this . autoCloseAllRooTabs ,
59+ } )
60+ }
3761
38- constructor ( private cwd : string ) { }
62+ public initialize ( ) {
63+ const settings = this . _readDiffSettings ( )
64+ this . autoCloseAllRooTabs = settings . autoCloseAllRooTabs
65+ // Track currently visible editors and active editor for focus restoration and tab cleanup
66+ this . rooOpenedTabs . clear ( )
67+
68+ // Update PostDiffviewBehaviorUtils context with latest values
69+ this . postDiffBehaviorUtils . updateContext ( {
70+ autoCloseAllRooTabs : this . autoCloseAllRooTabs ,
71+ } )
72+ }
73+
74+ private _readDiffSettings ( ) : DiffSettings {
75+ const config = vscode . workspace . getConfiguration ( "roo-cline" )
76+ const autoCloseRooTabs = config . get < boolean > ( "autoCloseRooTabs" , false )
77+ const autoCloseAllRooTabs = config . get < boolean > ( "autoCloseAllRooTabs" , false )
78+ return { autoCloseRooTabs, autoCloseAllRooTabs }
79+ }
80+
81+ private async showTextDocumentSafe ( {
82+ uri,
83+ textDocument,
84+ options,
85+ } : {
86+ uri ?: vscode . Uri
87+ textDocument ?: TextDocument
88+ options ?: TextDocumentShowOptions
89+ } ) {
90+ // If the uri is already open, we want to focus it
91+ if ( uri ) {
92+ const editor = await vscode . window . showTextDocument ( uri , options )
93+ return editor
94+ }
95+ // If the textDocument is already open, we want to focus it
96+ if ( textDocument ) {
97+ const editor = await vscode . window . showTextDocument ( textDocument , options )
98+ return editor
99+ }
100+ // If the textDocument is not open and not able to be opened, we just reset the suppressInteractionFlag
101+ return null
102+ }
39103
40104 async open ( relPath : string ) : Promise < void > {
105+ this . preDiffActiveEditor = vscode . window . activeTextEditor
106+
41107 this . relPath = relPath
42108 const fileExists = this . editType === "modify"
43109 const absolutePath = path . resolve ( this . cwd , relPath )
44110 this . isEditing = true
45111
112+ // Update PostDiffviewBehaviorUtils context with current state
113+ this . postDiffBehaviorUtils . updateContext ( {
114+ relPath : relPath ,
115+ editType : this . editType ,
116+ documentWasOpen : this . documentWasOpen ,
117+ preDiffActiveEditor : this . preDiffActiveEditor ,
118+ } )
119+
120+ // Track the URI of the actual file that will be part of the diff.
121+ // This ensures that if VS Code opens a tab for it during vscode.diff,
122+ // we can identify it as a "Roo-opened" tab for cleanup.
123+ const fileUriForDiff = vscode . Uri . file ( absolutePath )
124+ this . rooOpenedTabs . add ( fileUriForDiff . toString ( ) )
125+
46126 // If the file is already open, ensure it's not dirty before getting its
47127 // contents.
48128 if ( fileExists ) {
@@ -76,22 +156,18 @@ export class DiffViewProvider {
76156
77157 // If the file was already open, close it (must happen after showing the
78158 // diff view since if it's the only tab the column will close).
79- this . documentWasOpen = false
80-
81- // Close the tab if it's open (it's already saved above).
82- const tabs = vscode . window . tabGroups . all
83- . map ( ( tg ) => tg . tabs )
84- . flat ( )
85- . filter (
86- ( tab ) => tab . input instanceof vscode . TabInputText && arePathsEqual ( tab . input . uri . fsPath , absolutePath ) ,
87- )
88-
89- for ( const tab of tabs ) {
90- if ( ! tab . isDirty ) {
91- await vscode . window . tabGroups . close ( tab )
92- }
93- this . documentWasOpen = true
94- }
159+ this . documentWasOpen =
160+ vscode . window . tabGroups . all
161+ . map ( ( tg ) => tg . tabs )
162+ . flat ( )
163+ . filter (
164+ ( tab ) =>
165+ tab . input instanceof vscode . TabInputText && arePathsEqual ( tab . input . uri . fsPath , absolutePath ) ,
166+ ) . length > 0
167+
168+ this . postDiffBehaviorUtils . updateContext ( {
169+ documentWasOpen : this . documentWasOpen ,
170+ } )
95171
96172 this . activeDiffEditor = await this . openDiffEditor ( )
97173 this . fadedOverlayController = new DecorationController ( "fadedOverlay" , this . activeDiffEditor )
@@ -102,6 +178,17 @@ export class DiffViewProvider {
102178 this . streamedLines = [ ]
103179 }
104180
181+ /**
182+ * Opens a file editor and tracks it as opened by Roo if not already open.
183+ */
184+ private async showAndTrackEditor ( uri : vscode . Uri , options : vscode . TextDocumentShowOptions = { } ) {
185+ const editor = await this . showTextDocumentSafe ( { uri, options } )
186+ // Always track tabs opened by Roo, regardless of autoCloseTabs setting or if the document was already open.
187+ // The decision to close will be made in closeAllRooOpenedViews based on settings.
188+ this . rooOpenedTabs . add ( uri . toString ( ) )
189+ return editor
190+ }
191+
105192 async update ( accumulatedContent : string , isFinal : boolean ) {
106193 if ( ! this . relPath || ! this . activeLineController || ! this . fadedOverlayController ) {
107194 throw new Error ( "Required values not set" )
@@ -197,8 +284,27 @@ export class DiffViewProvider {
197284 }
198285
199286 await vscode . window . showTextDocument ( vscode . Uri . file ( absolutePath ) , { preview : false , preserveFocus : true } )
200- await this . closeAllDiffViews ( )
201287
288+ await this . postDiffBehaviorUtils . closeAllRooOpenedViews ( this . _readDiffSettings ( ) )
289+
290+ // Implement post-diff focus behavior
291+ await this . postDiffBehaviorUtils . handlePostDiffFocus ( )
292+
293+ // If no auto-close settings are enabled and the document was not open before,
294+ // open the file after the diff is complete.
295+
296+ const settings = this . _readDiffSettings ( ) // Dynamically read settings
297+
298+ // If no auto-close settings are enabled and the document was not open before OR it's a new file,
299+ // open the file after the diff is complete.
300+ if (
301+ ! settings . autoCloseRooTabs &&
302+ ! settings . autoCloseAllRooTabs &&
303+ ( this . editType === "create" || ! this . documentWasOpen )
304+ ) {
305+ const absolutePath = path . resolve ( this . cwd , this . relPath ! )
306+ await this . showAndTrackEditor ( vscode . Uri . file ( absolutePath ) , { preview : false , preserveFocus : true } )
307+ }
202308 // Getting diagnostics before and after the file edit is a better approach than
203309 // automatically tracking problems in real-time. This method ensures we only
204310 // report new problems that are a direct result of this specific edit.
@@ -344,7 +450,7 @@ export class DiffViewProvider {
344450 await updatedDocument . save ( )
345451 }
346452
347- await this . closeAllDiffViews ( )
453+ await this . postDiffBehaviorUtils . closeAllRooOpenedViews ( this . _readDiffSettings ( ) )
348454 await fs . unlink ( absolutePath )
349455
350456 // Remove only the directories we created, in reverse order.
@@ -375,47 +481,19 @@ export class DiffViewProvider {
375481 } )
376482 }
377483
378- await this . closeAllDiffViews ( )
484+ await this . postDiffBehaviorUtils . closeAllRooOpenedViews ( this . _readDiffSettings ( ) )
379485 }
380486
487+ // Implement post-diff focus behavior
488+ await this . postDiffBehaviorUtils . handlePostDiffFocus ( )
489+
381490 // Edit is done.
382491 await this . reset ( )
383492 }
384493
385- private async closeAllDiffViews ( ) : Promise < void > {
386- const closeOps = vscode . window . tabGroups . all
387- . flatMap ( ( group ) => group . tabs )
388- . filter ( ( tab ) => {
389- // Check for standard diff views with our URI scheme
390- if (
391- tab . input instanceof vscode . TabInputTextDiff &&
392- tab . input . original . scheme === DIFF_VIEW_URI_SCHEME &&
393- ! tab . isDirty
394- ) {
395- return true
396- }
397-
398- // Also check by tab label for our specific diff views
399- // This catches cases where the diff view might be created differently
400- // when files are pre-opened as text documents
401- if ( tab . label . includes ( DIFF_VIEW_LABEL_CHANGES ) && ! tab . isDirty ) {
402- return true
403- }
404-
405- return false
406- } )
407- . map ( ( tab ) =>
408- vscode . window . tabGroups . close ( tab ) . then (
409- ( ) => undefined ,
410- ( err ) => {
411- console . error ( `Failed to close diff tab ${ tab . label } ` , err )
412- } ,
413- ) ,
414- )
415-
416- await Promise . all ( closeOps )
417- }
418-
494+ /**
495+ * Opens the diff editor
496+ */
419497 private async openDiffEditor ( ) : Promise < vscode . TextEditor > {
420498 if ( ! this . relPath ) {
421499 throw new Error (
@@ -580,7 +658,13 @@ export class DiffViewProvider {
580658 }
581659
582660 async reset ( ) : Promise < void > {
583- await this . closeAllDiffViews ( )
661+ // Ensure any diff views opened by this provider are closed to release
662+ // memory.
663+ try {
664+ await this . postDiffBehaviorUtils . closeAllRooOpenedViews ( this . _readDiffSettings ( ) )
665+ } catch ( error ) {
666+ console . error ( "Error closing diff views" , error )
667+ }
584668 this . editType = undefined
585669 this . isEditing = false
586670 this . originalContent = undefined
@@ -591,5 +675,7 @@ export class DiffViewProvider {
591675 this . activeLineController = undefined
592676 this . streamedLines = [ ]
593677 this . preDiagnostics = [ ]
678+ this . rooOpenedTabs . clear ( )
679+
594680 }
595681}
0 commit comments