33 * GPL-3.0-only. See LICENSE.md in the project root for license details.
44 */
55
6- import { Disposable , Uri , WorkspaceFolder , WorkspaceFoldersChangeEvent , window } from 'vscode'
7- import { FSWatcher , existsSync , watch } from 'fs'
6+ import { Disposable , WorkspaceFolder , WorkspaceFoldersChangeEvent , window } from 'vscode'
7+ import { existsSync , WatchEventType } from 'fs'
8+ import { PathWatcher , WatcherCallback } from './Foundation/PathWatcher'
89import { join } from 'path'
910
10- type CallbackFunction = ( event : Uri ) => void
11+ type RepoWatcherCallback = ( event : WatchEventType , filename : string ) => void
1112
1213// https://github.com/Microsoft/vscode/issues/3025
1314export default class implements Disposable {
14- private callback : CallbackFunction
15- private watchers : Map < string , FSWatcher > = new Map ( ) as Map < string , FSWatcher >
15+ private callback : RepoWatcherCallback
16+ private watchers : Map < string , PathWatcher [ ] > = new Map ( ) as Map < string , PathWatcher [ ] >
1617
1718 /**
1819 * Creates a new watcher.
1920 *
2021 * @param repos the repositories to watch
2122 * @param callback the callback to run when detecting changes
2223 */
23- constructor ( repos : string [ ] , callback : CallbackFunction ) {
24+ constructor ( repos : string [ ] , callback : RepoWatcherCallback ) {
2425 this . callback = callback
25- repos . forEach ( ( directory ) => { this . registerProjectWatcher ( directory ) } )
26+ repos . forEach ( ( directory ) => { this . registerProjectWatchers ( directory ) } )
2627 }
2728
2829 /**
@@ -33,67 +34,77 @@ export default class implements Disposable {
3334 public configure ( directoryChanges : WorkspaceFoldersChangeEvent ) : void {
3435 directoryChanges . added . forEach ( ( changedDirectory : WorkspaceFolder ) => {
3536 const directory = changedDirectory . uri . fsPath
36- this . registerProjectWatcher ( directory )
37+ this . registerProjectWatchers ( directory )
3738 } )
3839
3940 directoryChanges . removed . forEach ( ( changedDirectory : WorkspaceFolder ) => {
4041 const directory = changedDirectory . uri . fsPath
41- this . removeProjectWatcher ( directory )
42+ this . removeProjectWatchers ( directory )
4243 } )
4344 }
4445
4546 /**
4647 * Disposes this object.
48+ * @see Disposable.dispose()
4749 */
4850 public dispose ( ) : void {
4951 for ( const path of this . watchers . keys ( ) ) {
50- this . removeProjectWatcher ( path )
52+ this . removeProjectWatchers ( path )
5153 }
5254 }
5355
5456 /**
55- * Registers a new project directory watcher .
57+ * Registers the project directory watchers .
5658 *
5759 * @param projectPath the directory path
5860 */
59- private registerProjectWatcher ( projectPath : string ) : void {
61+ private registerProjectWatchers ( projectPath : string ) : void {
6062 global . dbg ( `[FSWatch] Watch ${ projectPath } ...` )
61- if ( this . watchers . has ( projectPath ) ) {
63+ const pathToMonitor = join ( projectPath , '.git' , 'refs' )
64+ if ( ! existsSync ( pathToMonitor ) ) {
6265 return
6366 }
6467
65- const pathToMonitor = join ( projectPath , '.git' , 'refs' )
68+ this . registerPathWatcher ( pathToMonitor , projectPath )
69+ }
6670
67- if ( ! existsSync ( pathToMonitor ) ) {
71+ /**
72+ * Creates a FS Watcher for a directory.
73+ * @param pathToMonitor the path to monitor
74+ * @param projectPath the path to use in the callback
75+ */
76+ private registerPathWatcher ( pathToMonitor : string , projectPath : string ) {
77+ const watchers = this . watchers . get ( projectPath )
78+ ?? this . watchers . set ( projectPath , [ ] ) . get ( projectPath ) ! // eslint-disable-line @typescript-eslint/no-non-null-assertion
79+
80+ if ( watchers . length && watchers . find ( ( watcher ) => watcher . for ( pathToMonitor ) ) ) {
6881 return
6982 }
7083
7184 try {
72- const watcher = watch ( pathToMonitor , ( event : string , filename ) => {
85+ // Create the watcher callback which will review if a reaction is needed
86+ // based on the changed (stash) file.
87+ const callback : WatcherCallback = ( event , filename ) => {
7388 if ( filename ?. includes ( 'stash' ) ) {
74- this . callback ( Uri . file ( projectPath ) )
89+ this . callback ( event , projectPath )
7590 }
76- } )
91+ }
7792
78- this . watchers . set ( projectPath , watcher )
93+ watchers . push ( PathWatcher . watch ( pathToMonitor , callback ) )
7994 }
8095 catch ( error ) {
96+ const msg = `Unable to create a stashes monitor for ${ pathToMonitor } .`
97+ + ' This may happen on NFS or if the path is a link.'
98+ + ' See the console for details'
99+ console . error ( msg )
81100 console . error ( error )
82- void window . showErrorMessage ( `Unable to a create a stashes monitor for
83- ${ projectPath } . This may happen on NFS or if the path is a link` )
101+ void window . showErrorMessage ( msg )
84102 }
85103 }
86104
87- /**
88- * Removes an active project directory watcher.
89- *
90- * @param path the directory path
91- */
92- private removeProjectWatcher ( path : string ) : void {
93- if ( this . watchers . has ( path ) ) {
94- global . dbg ( `[FSWatch] Stop watching ${ path } ...` )
95- this . watchers . get ( path ) ?. close ( )
96- this . watchers . delete ( path )
97- }
105+ private removeProjectWatchers ( path : string ) : void {
106+ global . dbg ( `[FSWatch] Stop watching ${ path } ...` )
107+ this . watchers . get ( path ) ?. forEach ( ( watcher ) => { watcher . dispose ( ) } )
108+ this . watchers . delete ( path )
98109 }
99110}
0 commit comments