@@ -137,7 +137,7 @@ export class CppProperties {
137137 private configFileWatcher : vscode . FileSystemWatcher | null = null ;
138138 private configFileWatcherFallbackTime : Date = new Date ( ) ; // Used when file watching fails.
139139 private compileCommandsFile : vscode . Uri | undefined | null = undefined ;
140- private compileCommandsFileWatchers : fs . FSWatcher [ ] = [ ] ;
140+ private compileCommandsFileWatcher : fs . FSWatcher | undefined = undefined ;
141141 private compileCommandsFileWatcherFallbackTime : Date = new Date ( ) ; // Used when file watching fails.
142142 private defaultCompilerPath : string | null = null ;
143143 private knownCompilers ?: KnownCompiler [ ] ;
@@ -308,16 +308,19 @@ export class CppProperties {
308308
309309 private onConfigurationsChanged ( ) : void {
310310 if ( this . Configurations ) {
311+ console . log ( 'onConfigurationsChanged' ) ;
311312 this . configurationsChanged . fire ( this ) ;
312313 }
313314 }
314315
315316 private onSelectionChanged ( ) : void {
317+ console . log ( 'onSelectionChanged ' , this . CurrentConfigurationIndex ) ;
316318 this . selectionChanged . fire ( this . CurrentConfigurationIndex ) ;
317319 void this . handleSquiggles ( ) . catch ( logAndReturn . undefined ) ;
318320 }
319321
320- private onCompileCommandsChanged ( path : string ) : void {
322+ private onCompileCommandsChanged ( path : string , caller : string ) : void {
323+ console . log ( 'onCompileCommandsChanged ' , path , caller ) ;
321324 this . compileCommandsChanged . fire ( path ) ;
322325 }
323326
@@ -1104,53 +1107,48 @@ export class CppProperties {
11041107 }
11051108 }
11061109
1107- this . updateCompileCommandsFileWatchers ( ) ;
1110+
1111+ this . updateCompileCommandsFileWatcher ( ) ;
11081112 if ( ! this . configurationIncomplete ) {
11091113 this . onConfigurationsChanged ( ) ;
11101114 }
11111115 }
11121116
11131117 private compileCommandsFileWatcherTimer ?: NodeJS . Timeout ;
1114- private compileCommandsFileWatcherFiles : Set < string > = new Set < string > ( ) ;
11151118
11161119 // Dispose existing and loop through cpp and populate with each file (exists or not) as you go.
11171120 // paths are expected to have variables resolved already
1118- public updateCompileCommandsFileWatchers ( ) : void {
1121+ public updateCompileCommandsFileWatcher ( ) : void {
1122+ // close the existing watcher if it exists
1123+ this . compileCommandsFileWatcher ?. close ( ) ;
1124+ this . compileCommandsFileWatcher = undefined ;
1125+
1126+ // check if the current configuration is using a configuration provider (e.g `CMake Tools`)
1127+ // if so, avoid setting up a `compile_commands.json` file watcher to avoid unnessary parsing
1128+ // by the language server
1129+ if ( this . CurrentConfiguration ?. configurationProvider ) {
1130+ return ;
1131+ }
1132+
11191133 if ( this . configurationJson ) {
1120- this . compileCommandsFileWatchers . forEach ( ( watcher : fs . FSWatcher ) => watcher . close ( ) ) ;
1121- this . compileCommandsFileWatchers = [ ] ; // reset it
1122- const filePaths : Set < string > = new Set < string > ( ) ;
1123- this . configurationJson . configurations . forEach ( c => {
1124- if ( c . compileCommands ) {
1125- const fileSystemCompileCommandsPath : string = this . resolvePath ( c . compileCommands ) ;
1126- if ( fs . existsSync ( fileSystemCompileCommandsPath ) ) {
1127- filePaths . add ( fileSystemCompileCommandsPath ) ;
1128- }
1129- }
1130- } ) ;
1134+ const path : string = this . resolvePath ( this . CurrentConfiguration ?. compileCommands ) ;
11311135 try {
1132- filePaths . forEach ( ( path : string ) => {
1133- this . compileCommandsFileWatchers . push ( fs . watch ( path , ( ) => {
1134- // Wait 1 second after a change to allow time for the write to finish.
1135- if ( this . compileCommandsFileWatcherTimer ) {
1136- clearInterval ( this . compileCommandsFileWatcherTimer ) ;
1137- }
1138- this . compileCommandsFileWatcherFiles . add ( path ) ;
1139- this . compileCommandsFileWatcherTimer = setTimeout ( ( ) => {
1140- this . compileCommandsFileWatcherFiles . forEach ( ( path : string ) => {
1141- this . onCompileCommandsChanged ( path ) ;
1142- } ) ;
1143- if ( this . compileCommandsFileWatcherTimer ) {
1144- clearInterval ( this . compileCommandsFileWatcherTimer ) ;
1145- }
1146- this . compileCommandsFileWatcherFiles . clear ( ) ;
1147- this . compileCommandsFileWatcherTimer = undefined ;
1148- } , 1000 ) ;
1149- } ) ) ;
1150- } ) ;
1151- } catch ( e ) {
1152- // The file watcher limit is hit.
1153- // TODO: Check if the compile commands file has a higher timestamp during the interval timer.
1136+ this . compileCommandsFileWatcher = fs . watch ( path , ( ) => {
1137+ // Wait 1 second after a change to allow time for the write to finish.
1138+ clearInterval ( this . compileCommandsFileWatcherTimer ) ;
1139+ this . compileCommandsFileWatcherTimer = setTimeout ( ( ) => {
1140+ this . onCompileCommandsChanged ( path , "file watcher" ) ;
1141+ clearInterval ( this . compileCommandsFileWatcherTimer ) ;
1142+ this . compileCommandsFileWatcherTimer = undefined ;
1143+ } , 1000 ) ;
1144+ } )
1145+ }
1146+ catch ( e : any ) {
1147+ // either file not created or too many watchers
1148+ // rely on polling until the file is created
1149+ // then, file watching will be attempted again
1150+ this . compileCommandsFileWatcher ?. close ( ) ;
1151+ this . compileCommandsFileWatcher = undefined ;
11541152 }
11551153 }
11561154 }
@@ -2300,34 +2298,56 @@ export class CppProperties {
23002298 } ) ;
23012299 }
23022300
2303- public checkCompileCommands ( ) : void {
2301+ /**
2302+ * Manually check for changes in the compileCommands file.
2303+ *
2304+ * NOTE: The check is skipped on any of the following terms:
2305+ * - There is an active `compile_commands.json` file watcher
2306+ * - The `configurationProvider` property is set and the `force` parameter is not set to true
2307+ * - The `compileCommands` property is not set
2308+ * @param bypassConfigurationProvider bypass the `ConfigurationProvider` is set condition
2309+ */
2310+ public checkCompileCommands ( bypassConfigurationProvider : boolean = false ) : void {
2311+ // if the file watcher is active, we don't need to check here for changes
2312+ if ( this . compileCommandsFileWatcher ) {
2313+ return ;
2314+ }
2315+ // configuration provider didn't fail to provide a configuration
2316+ if ( this . CurrentConfiguration ?. configurationProvider && ! bypassConfigurationProvider ) {
2317+ return ;
2318+ }
23042319 // Check for changes in case of file watcher failure.
23052320 const compileCommands : string | undefined = this . CurrentConfiguration ?. compileCommands ;
2306- if ( ! compileCommands ) {
2321+ if ( compileCommands === undefined ) {
23072322 return ;
23082323 }
2324+
23092325 const compileCommandsFile : string | undefined = this . resolvePath ( compileCommands ) ;
23102326 fs . stat ( compileCommandsFile , ( err , stats ) => {
23112327 if ( err ) {
23122328 if ( err . code === "ENOENT" && this . compileCommandsFile ) {
2313- this . compileCommandsFileWatchers = [ ] ; // reset file watchers
2314- this . onCompileCommandsChanged ( compileCommandsFile ) ;
2329+ this . onCompileCommandsChanged ( compileCommandsFile , "periodic checker - file deleted" ) ;
23152330 this . compileCommandsFile = null ; // File deleted
23162331 }
23172332 } else if ( stats . mtime > this . compileCommandsFileWatcherFallbackTime ) {
23182333 this . compileCommandsFileWatcherFallbackTime = new Date ( ) ;
2319- this . onCompileCommandsChanged ( compileCommandsFile ) ;
2334+ this . onCompileCommandsChanged ( compileCommandsFile , "periodic checker - file created" ) ;
23202335 this . compileCommandsFile = vscode . Uri . file ( compileCommandsFile ) ; // File created.
23212336 }
23222337 } ) ;
2338+
2339+ // if the compileCommands file is set (and not using a configuration provider), try to watch it
2340+ if ( this . compileCommandsFile ) {
2341+ this . updateCompileCommandsFileWatcher ( ) ;
2342+ }
23232343 }
23242344
23252345 dispose ( ) : void {
23262346 this . disposables . forEach ( ( d ) => d . dispose ( ) ) ;
23272347 this . disposables = [ ] ;
23282348
2329- this . compileCommandsFileWatchers . forEach ( ( watcher : fs . FSWatcher ) => watcher . close ( ) ) ;
2330- this . compileCommandsFileWatchers = [ ] ; // reset it
2349+ this . compileCommandsFileWatcher ?. close ( ) ;
2350+ this . compileCommandsFileWatcher = undefined ;
23312351
23322352 this . diagnosticCollection . dispose ( ) ;
23332353 }
0 commit comments