4
4
import * as fs from 'fs' ;
5
5
import * as path from 'path' ;
6
6
import { Uri } from 'vscode' ;
7
- import { FileChangeType } from '../../../../common/platform/fileSystemWatcher' ;
7
+ import { FileChangeType , watchLocationForPattern } from '../../../../common/platform/fileSystemWatcher' ;
8
8
import { sleep } from '../../../../common/utils/async' ;
9
9
import { traceError , traceVerbose } from '../../../../logging' ;
10
10
import { getEnvironmentDirFromPath } from '../../../common/commonUtils' ;
@@ -47,6 +47,33 @@ function checkDirWatchable(dirname: string): DirUnwatchableReason {
47
47
return undefined ;
48
48
}
49
49
50
+ type LocationWatchOptions = {
51
+ /**
52
+ * Glob which represents basename of the executable or directory to watch.
53
+ */
54
+ baseGlob ?: string ;
55
+ /**
56
+ * Time to wait before handling an environment-created event.
57
+ */
58
+ delayOnCreated ?: number ; // milliseconds
59
+ /**
60
+ * Location affected by the event. If not provided, a default search location is used.
61
+ */
62
+ searchLocation ?: string ;
63
+ /**
64
+ * The Python env structure to watch.
65
+ */
66
+ envStructure ?: PythonEnvStructure ;
67
+ } ;
68
+
69
+ type FileWatchOptions = {
70
+ /**
71
+ * If the provided root is a file instead. In this case the file is directly watched instead for
72
+ * looking for python binaries inside a root.
73
+ */
74
+ isFile : boolean ;
75
+ } ;
76
+
50
77
/**
51
78
* The base for Python envs locators who watch the file system.
52
79
* Most low-level locators should be using this.
@@ -63,24 +90,7 @@ export abstract class FSWatchingLocator<I = PythonEnvInfo> extends LazyResourceB
63
90
* Returns the kind of environment specific to locator given the path to executable.
64
91
*/
65
92
private readonly getKind : ( executable : string ) => Promise < PythonEnvKind > ,
66
- private readonly opts : {
67
- /**
68
- * Glob which represents basename of the executable or directory to watch.
69
- */
70
- baseGlob ?: string ;
71
- /**
72
- * Time to wait before handling an environment-created event.
73
- */
74
- delayOnCreated ?: number ; // milliseconds
75
- /**
76
- * Location affected by the event. If not provided, a default search location is used.
77
- */
78
- searchLocation ?: string ;
79
- /**
80
- * The Python env structure to watch.
81
- */
82
- envStructure ?: PythonEnvStructure ;
83
- } = { } ,
93
+ private readonly creationOptions : LocationWatchOptions | FileWatchOptions = { } ,
84
94
private readonly watcherKind : FSWatcherKind = FSWatcherKind . Global ,
85
95
) {
86
96
super ( ) ;
@@ -89,8 +99,8 @@ export abstract class FSWatchingLocator<I = PythonEnvInfo> extends LazyResourceB
89
99
90
100
protected async initWatchers ( ) : Promise < void > {
91
101
// Enable all workspace watchers.
92
- if ( this . watcherKind === FSWatcherKind . Global ) {
93
- // Do not allow global watchers for now
102
+ if ( this . watcherKind === FSWatcherKind . Global && ! isWatchingAFile ( this . creationOptions ) ) {
103
+ // Do not allow global location watchers for now.
94
104
return ;
95
105
}
96
106
@@ -102,6 +112,9 @@ export abstract class FSWatchingLocator<I = PythonEnvInfo> extends LazyResourceB
102
112
roots = [ roots ] ;
103
113
}
104
114
const promises = roots . map ( async ( root ) => {
115
+ if ( isWatchingAFile ( this . creationOptions ) ) {
116
+ return root ;
117
+ }
105
118
// Note that we only check the root dir. Any directories
106
119
// that might be watched due to a glob are not checked.
107
120
const unwatchable = await checkDirWatchable ( root ) ;
@@ -116,12 +129,23 @@ export abstract class FSWatchingLocator<I = PythonEnvInfo> extends LazyResourceB
116
129
}
117
130
118
131
private startWatchers ( root : string ) : void {
132
+ const opts = this . creationOptions ;
133
+ if ( isWatchingAFile ( opts ) ) {
134
+ traceVerbose ( 'Start watching file for changes' , root ) ;
135
+ this . disposables . push (
136
+ watchLocationForPattern ( path . dirname ( root ) , path . basename ( root ) , ( ) => {
137
+ traceVerbose ( 'Detected change in file: ' , root , 'initiating a refresh' ) ;
138
+ this . emitter . fire ( { } ) ;
139
+ } ) ,
140
+ ) ;
141
+ return ;
142
+ }
119
143
const callback = async ( type : FileChangeType , executable : string ) => {
120
144
if ( type === FileChangeType . Created ) {
121
- if ( this . opts . delayOnCreated !== undefined ) {
145
+ if ( opts . delayOnCreated !== undefined ) {
122
146
// Note detecting kind of env depends on the file structure around the
123
147
// executable, so we need to wait before attempting to detect it.
124
- await sleep ( this . opts . delayOnCreated ) ;
148
+ await sleep ( opts . delayOnCreated ) ;
125
149
}
126
150
}
127
151
// Fetching kind after deletion normally fails because the file structure around the
@@ -135,20 +159,22 @@ export abstract class FSWatchingLocator<I = PythonEnvInfo> extends LazyResourceB
135
159
// |__ env
136
160
// |__ bin or Scripts
137
161
// |__ python <--- executable
138
- const searchLocation = Uri . file (
139
- this . opts . searchLocation ?? path . dirname ( getEnvironmentDirFromPath ( executable ) ) ,
140
- ) ;
162
+ const searchLocation = Uri . file ( opts . searchLocation ?? path . dirname ( getEnvironmentDirFromPath ( executable ) ) ) ;
141
163
traceVerbose ( 'Fired event ' , JSON . stringify ( { type, kind, searchLocation } ) , 'from locator' ) ;
142
164
this . emitter . fire ( { type, kind, searchLocation } ) ;
143
165
} ;
144
166
145
167
const globs = resolvePythonExeGlobs (
146
- this . opts . baseGlob ,
168
+ opts . baseGlob ,
147
169
// The structure determines which globs are returned.
148
- this . opts . envStructure ,
170
+ opts . envStructure ,
149
171
) ;
150
172
traceVerbose ( 'Start watching root' , root , 'for globs' , JSON . stringify ( globs ) ) ;
151
173
const watchers = globs . map ( ( g ) => watchLocationForPythonBinaries ( root , callback , g ) ) ;
152
174
this . disposables . push ( ...watchers ) ;
153
175
}
154
176
}
177
+
178
+ function isWatchingAFile ( options : LocationWatchOptions | FileWatchOptions ) : options is FileWatchOptions {
179
+ return 'isFile' in options && options . isFile ;
180
+ }
0 commit comments