@@ -5,7 +5,7 @@ import * as fs from 'fs'
55import { TreeDataProvider , TreeItem , TreeItemCollapsibleState ,
66 Uri , Disposable , EventEmitter , TextDocumentShowOptions ,
77 QuickPickItem , ProgressLocation , Memento , OutputChannel ,
8- workspace , commands , window , env , WorkspaceFoldersChangeEvent , TreeView , ThemeIcon , TreeItemCheckboxState , TreeCheckboxChangeEvent , authentication } from 'vscode'
8+ workspace , commands , window , env , WorkspaceFoldersChangeEvent , TreeView , ThemeIcon , TreeItemCheckboxState , TreeCheckboxChangeEvent , authentication , TextEditor } from 'vscode'
99import { NAMESPACE } from './constants'
1010import { Repository , Git } from './git/git'
1111import { Ref , RefType } from './git/api/git'
@@ -114,6 +114,12 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
114114 private _onDidChangeTreeData = new EventEmitter < Element | void > ( ) ;
115115 readonly onDidChangeTreeData = this . _onDidChangeTreeData . event ;
116116
117+ private fireTreeDataChange ( ) {
118+ this . parentMap . clear ( ) ;
119+ this . elementMap . clear ( ) ;
120+ this . _onDidChangeTreeData . fire ( ) ;
121+ }
122+
117123 // Configuration options
118124 private treeRootIsRepo : boolean ;
119125 private includeFilesOutsideWorkspaceFolderRoot : boolean ;
@@ -132,6 +138,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
132138 private omitUntrackedFiles : boolean ;
133139 private omitUnstagedChanges : boolean ;
134140 private sortOrder : SortOrder ;
141+ private autoReveal : boolean ;
135142
136143 // Dynamic options
137144 private repository : Repository | undefined ;
@@ -163,6 +170,8 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
163170 private treeView : TreeView < Element > ;
164171 private isPaused : boolean ;
165172 private checkboxStates : Map < string , CheckboxStateInfo > = new Map < string , CheckboxStateInfo > ( ) ;
173+ private parentMap : Map < string , Element > = new Map ( ) ;
174+ private elementMap : Map < string , FileElement > = new Map ( ) ;
166175
167176 // Other
168177 private readonly disposables : Disposable [ ] = [ ] ;
@@ -215,6 +224,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
215224 this . disposables . push ( onRelevantWorkspaceChange ( this . handleWorkspaceChange , this ) ) ;
216225
217226 this . disposables . push ( treeView . onDidChangeCheckboxState ( this . handleChangeCheckboxState , this ) ) ;
227+ this . disposables . push ( window . onDidChangeActiveTextEditor ( this . handleActiveEditorChange , this ) ) ;
218228 }
219229
220230 async setRepository ( repositoryRoot : string ) {
@@ -262,7 +272,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
262272
263273 async unsetRepository ( ) {
264274 this . repository = undefined ;
265- this . _onDidChangeTreeData . fire ( ) ;
275+ this . fireTreeDataChange ( ) ;
266276 this . log ( 'No repository selected' ) ;
267277
268278 this . updateTreeTitle ( ) ;
@@ -282,7 +292,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
282292 this . checkboxStates . clear ( ) ;
283293 this . searchFilter = undefined ;
284294 this . updateFilterContext ( ) ;
285- this . _onDidChangeTreeData . fire ( ) ;
295+ this . fireTreeDataChange ( ) ;
286296 }
287297
288298 async promptChangeRepository ( ) {
@@ -358,6 +368,22 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
358368 }
359369 }
360370
371+ private handleActiveEditorChange ( editor : TextEditor | undefined ) {
372+ if ( ! this . autoReveal || ! editor || ! this . treeView . visible ) {
373+ return ;
374+ }
375+ const uri = editor . document . uri ;
376+ if ( uri . scheme !== 'file' ) {
377+ return ;
378+ }
379+ const fileElement = this . elementMap . get ( uri . fsPath ) ;
380+ if ( fileElement ) {
381+ this . treeView . reveal ( fileElement , { select : true , focus : false } ) . then ( undefined , ( ) => {
382+ // Element may not be in the tree (e.g. not yet expanded), ignore
383+ } ) ;
384+ }
385+ }
386+
361387 private log ( msg : string , error : Error | undefined = undefined ) {
362388 if ( error ) {
363389 console . warn ( msg , error ) ;
@@ -394,6 +420,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
394420 this . omitUntrackedFiles = config . get < boolean > ( 'omitUntrackedFiles' , false ) ;
395421 this . omitUnstagedChanges = config . get < boolean > ( 'omitUnstagedChanges' , false ) ;
396422 this . sortOrder = config . get < SortOrder > ( 'sortOrder' , 'path' ) ;
423+ this . autoReveal = config . get < boolean > ( 'autoReveal' , true ) ;
397424 }
398425
399426 private async getStoredBaseRef ( ) : Promise < string | undefined > {
@@ -442,6 +469,11 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
442469 return toTreeItem ( element , this . openChangesOnSelect , this . iconsMinimal , this . showCollapsed , this . viewAsList , checkboxState , this . asAbsolutePath ) ;
443470 }
444471
472+ getParent ( element : Element ) : Element | undefined {
473+ const id = getElementId ( element ) ;
474+ return this . parentMap . get ( id ) ;
475+ }
476+
445477 private computeFolderCheckboxState ( folder : FolderElement ) : TreeItemCheckboxState {
446478 // Check if user explicitly set state on this folder
447479 const explicitState = this . checkboxStates . get ( folder . dstAbsPath ) ;
@@ -490,20 +522,35 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
490522 this . filesInsideTreeRoot . size > 0 ||
491523 ( this . includeFilesOutsideWorkspaceFolderRoot && this . filesOutsideTreeRoot . size > 0 ) ;
492524
493- return [ new RefElement ( this . repoRoot , this . baseRef , hasFiles ) ] ;
525+ const children = [ new RefElement ( this . repoRoot , this . baseRef , hasFiles ) ] ;
526+ // RefElement is the root, no parent to record
527+ return children ;
494528 } else if ( element instanceof RefElement ) {
495529 const entries : Element [ ] = [ ] ;
496530 if ( this . includeFilesOutsideWorkspaceFolderRoot && this . filesOutsideTreeRoot . size > 0 ) {
497531 entries . push ( new RepoRootElement ( this . repoRoot ) ) ;
498532 }
499- return entries . concat ( this . getFileSystemEntries ( this . treeRoot , false ) ) ;
533+ const children = entries . concat ( this . getFileSystemEntries ( this . treeRoot , false ) ) ;
534+ this . recordParents ( element , children ) ;
535+ return children ;
500536 } else if ( element instanceof FolderElement ) {
501- return this . getFileSystemEntries ( element . dstAbsPath , element . useFilesOutsideTreeRoot ) ;
537+ const children = this . getFileSystemEntries ( element . dstAbsPath , element . useFilesOutsideTreeRoot ) ;
538+ this . recordParents ( element , children ) ;
539+ return children ;
502540 }
503541 assert . fail ( "unsupported element type" ) ;
504542 return [ ] ;
505543 }
506544
545+ private recordParents ( parent : Element , children : Element [ ] ) {
546+ for ( const child of children ) {
547+ this . parentMap . set ( getElementId ( child ) , parent ) ;
548+ if ( child instanceof FileElement ) {
549+ this . elementMap . set ( child . dstAbsPath , child ) ;
550+ }
551+ }
552+ }
553+
507554 private async updateRefs ( baseRef ?: string ) : Promise < void >
508555 {
509556 this . log ( 'Updating refs' ) ;
@@ -617,7 +664,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
617664 this . filesInsideTreeRoot = new Map ( ) ;
618665 this . filesOutsideTreeRoot = new Map ( ) ;
619666 if ( fireChangeEvents ) {
620- this . _onDidChangeTreeData . fire ( ) ;
667+ this . fireTreeDataChange ( ) ;
621668 }
622669 return ;
623670 }
@@ -686,7 +733,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
686733
687734 // Fire tree refresh to update checkbox UI
688735 if ( actualPathsToReset . length > 0 ) {
689- this . _onDidChangeTreeData . fire ( ) ;
736+ this . fireTreeDataChange ( ) ;
690737 }
691738 }
692739
@@ -752,7 +799,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
752799
753800 if ( fireChangeEvents && ( treeHasChanged || needsRefreshForSorting ) ) {
754801 this . log ( 'Refreshing tree' )
755- this . _onDidChangeTreeData . fire ( ) ;
802+ this . fireTreeDataChange ( ) ;
756803 }
757804 }
758805
@@ -882,7 +929,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
882929 this . filesOutsideTreeRoot = new Map ( ) ;
883930 }
884931 }
885- this . _onDidChangeTreeData . fire ( ) ;
932+ this . fireTreeDataChange ( ) ;
886933 }
887934 }
888935
@@ -1346,7 +1393,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
13461393 this . filesOutsideTreeRoot = new Map ( ) ;
13471394 }
13481395 this . log ( 'Refreshing tree' ) ;
1349- this . _onDidChangeTreeData . fire ( ) ;
1396+ this . fireTreeDataChange ( ) ;
13501397 } ) ;
13511398 }
13521399
@@ -1521,7 +1568,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
15211568 await this . updateRefs ( originBaseRef ) ;
15221569 await this . updateDiff ( false ) ;
15231570 this . log ( 'Refreshing tree' ) ;
1524- this . _onDidChangeTreeData . fire ( ) ;
1571+ this . fireTreeDataChange ( ) ;
15251572 window . showInformationMessage ( `Now comparing PR #${ prNumber } : ${ pr . title } ` ) ;
15261573 } catch ( e : any ) {
15271574 let msg = 'Failed to update comparison base' ;
@@ -1575,7 +1622,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
15751622 this . viewAsList = viewAsList ;
15761623 commands . executeCommand ( 'setContext' , NAMESPACE + '.viewAsList' , viewAsList ) ;
15771624 this . log ( 'Refreshing tree' ) ;
1578- this . _onDidChangeTreeData . fire ( ) ;
1625+ this . fireTreeDataChange ( ) ;
15791626 }
15801627
15811628 async sortByName ( ) {
@@ -1623,7 +1670,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
16231670 this . updateTreeTitle ( ) ;
16241671 this . updateFilterContext ( ) ;
16251672 this . log ( this . searchFilter ? `Filtering files by: ${ this . searchFilter } ` : 'Cleared file filter' ) ;
1626- this . _onDidChangeTreeData . fire ( ) ;
1673+ this . fireTreeDataChange ( ) ;
16271674 }
16281675
16291676 clearFilter ( ) {
@@ -1634,7 +1681,7 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
16341681 this . updateTreeTitle ( ) ;
16351682 this . updateFilterContext ( ) ;
16361683 this . log ( 'Cleared file filter' ) ;
1637- this . _onDidChangeTreeData . fire ( ) ;
1684+ this . fireTreeDataChange ( ) ;
16381685 }
16391686
16401687 private updateFilterContext ( ) {
@@ -1715,6 +1762,16 @@ export class GitTreeCompareProvider implements TreeDataProvider<Element>, Dispos
17151762 }
17161763}
17171764
1765+ function getElementId ( element : Element ) : string {
1766+ if ( element instanceof RefElement ) {
1767+ return 'ref' ;
1768+ } else if ( element instanceof RepoRootElement ) {
1769+ return 'root' ;
1770+ } else {
1771+ return element . dstAbsPath ;
1772+ }
1773+ }
1774+
17181775function toTreeItem ( element : Element , openChangesOnSelect : boolean , iconsMinimal : boolean ,
17191776 showCollapsed : boolean , viewAsList : boolean ,
17201777 checkboxState : TreeItemCheckboxState | undefined ,
0 commit comments