@@ -136,9 +136,10 @@ export class CppProperties {
136136 private currentConfigurationIndex : PersistentFolderState < number > | undefined ;
137137 private configFileWatcher : vscode . FileSystemWatcher | null = null ;
138138 private configFileWatcherFallbackTime : Date = new Date ( ) ; // Used when file watching fails.
139- private compileCommandsFile : vscode . Uri | undefined | null = undefined ;
139+ private compileCommandsFile : string | undefined = undefined ;
140140 private compileCommandsFileWatcher : fs . FSWatcher | undefined = undefined ;
141141 private compileCommandsFileWatcherFallbackTime : Date = new Date ( ) ; // Used when file watching fails.
142+ private configurationProviderFailedToProvide : boolean = false ;
142143 private defaultCompilerPath : string | null = null ;
143144 private knownCompilers ?: KnownCompiler [ ] ;
144145 private defaultCStandard : string | null = null ;
@@ -319,8 +320,7 @@ export class CppProperties {
319320 void this . handleSquiggles ( ) . catch ( logAndReturn . undefined ) ;
320321 }
321322
322- private onCompileCommandsChanged ( path : string , caller : string ) : void {
323- console . log ( 'onCompileCommandsChanged ' , path , caller ) ;
323+ private onCompileCommandsChanged ( path : string ) : void {
324324 this . compileCommandsChanged . fire ( path ) ;
325325 }
326326
@@ -1107,7 +1107,7 @@ export class CppProperties {
11071107 }
11081108 }
11091109
1110-
1110+ this . configurationProviderFailedToProvide = false ;
11111111 this . updateCompileCommandsFileWatcher ( ) ;
11121112 if ( ! this . configurationIncomplete ) {
11131113 this . onConfigurationsChanged ( ) ;
@@ -1119,38 +1119,51 @@ export class CppProperties {
11191119 // Dispose existing and loop through cpp and populate with each file (exists or not) as you go.
11201120 // paths are expected to have variables resolved already
11211121 public updateCompileCommandsFileWatcher ( ) : void {
1122+ console . log ( "updateCompileCommandsFileWatcher called" ) ;
11221123 // close the existing watcher if it exists
11231124 this . compileCommandsFileWatcher ?. close ( ) ;
11241125 this . compileCommandsFileWatcher = undefined ;
11251126
11261127 // check if the current configuration is using a configuration provider (e.g `CMake Tools`)
11271128 // if so, avoid setting up a `compile_commands.json` file watcher to avoid unnessary parsing
11281129 // by the language server
1129- if ( this . CurrentConfiguration ?. configurationProvider ) {
1130+ if ( this . CurrentConfiguration ?. configurationProvider && ! this . configurationProviderFailedToProvide ) {
1131+ console . log ( "Skipping compile_commands.json file watcher setup as configuration provider is set" ) ;
11301132 return ;
11311133 }
11321134
1133- if ( this . configurationJson ) {
1134- const path : string = this . resolvePath ( this . CurrentConfiguration ?. compileCommands ) ;
1135- try {
1136- this . compileCommandsFileWatcher = fs . watch ( path , ( ) => {
1137- // Wait 1 second after a change to allow time for the write to finish.
1135+ const path : string = this . resolvePath ( this . CurrentConfiguration ?. compileCommands ) ;
1136+ try {
1137+ this . compileCommandsFileWatcher = fs . watch ( path , ( eventType : fs . WatchEventType , filename : string | null ) => {
1138+ // Wait 1 second after a change to allow time for the write to finish.
1139+ clearInterval ( this . compileCommandsFileWatcherTimer ) ;
1140+ this . compileCommandsFileWatcherTimer = setTimeout ( ( ) => {
1141+ console . log ( "file watcher triggered for compile_commands.json" ) ;
1142+ this . onCompileCommandsChanged ( path ) ;
11381143 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 ;
1152- }
1144+ this . compileCommandsFileWatcherTimer = undefined ;
1145+
1146+ // if the file was deleted/renamed,
1147+ // linux based systems lose track of the file (inode deleted)
1148+ // we need to close the watcher and wait until file is created again
1149+ if ( eventType === "rename" ) {
1150+ console . log ( "compile_commands.json was renamed or deleted, closing watcher" ) ;
1151+ this . compileCommandsFileWatcher ?. close ( ) ;
1152+ this . compileCommandsFileWatcher = undefined ;
1153+ this . compileCommandsFile = undefined ;
1154+ }
1155+ } , 1000 ) ;
1156+ } )
1157+ }
1158+ catch ( e : any ) {
1159+ // either file not created or too many watchers
1160+ // rely on polling until the file is created
1161+ // then, file watching will be attempted again
1162+ console . log ( "failed to watch compile_commands.json" , e ) ;
1163+ this . compileCommandsFileWatcher ?. close ( ) ;
1164+ this . compileCommandsFileWatcher = undefined ;
11531165 }
1166+ console . log ( "file watcher finished" ) ;
11541167 }
11551168
11561169 // onBeforeOpen will be called after c_cpp_properties.json have been created (if it did not exist), but before the document is opened.
@@ -2298,48 +2311,72 @@ export class CppProperties {
22982311 } ) ;
22992312 }
23002313
2314+
2315+ /**
2316+ * if `configurationProvider` is set, we don't watch for changes in the `compileCommands` file.
2317+ * calling this function means we need to start checking it.
2318+ */
2319+ public configurationProviderFailed ( ) : void {
2320+ this . configurationProviderFailedToProvide = true ;
2321+ }
2322+
23012323 /**
23022324 * Manually check for changes in the compileCommands file.
23032325 *
23042326 * NOTE: The check is skipped on any of the following terms:
23052327 * - There is an active `compile_commands.json` file watcher
2306- * - The `configurationProvider` property is set and the `force` parameter is not set to true
2328+ * - The `configurationProvider` property is set and `configurationProviderFailed()` was not called
23072329 * - The `compileCommands` property is not set
2308- * @param bypassConfigurationProvider bypass the `ConfigurationProvider` is set condition
23092330 */
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 ) {
2331+ public checkCompileCommands ( ) : void {
2332+ if ( this . compileCommandsFileWatcher !== undefined ) {
2333+ console . log ( "Skipping check for compileCommands file changes, file watcher is active." ) ;
23132334 return ;
23142335 }
2315- // configuration provider didn't fail to provide a configuration
2316- if ( this . CurrentConfiguration ?. configurationProvider && ! bypassConfigurationProvider ) {
2336+ if ( this . CurrentConfiguration ?. configurationProvider && ! this . configurationProviderFailedToProvide ) {
2337+ console . log ( "Skipping check for compileCommands file changes, configuration provider is set and not forced." ) ;
23172338 return ;
23182339 }
2319- // Check for changes in case of file watcher failure.
23202340 const compileCommands : string | undefined = this . CurrentConfiguration ?. compileCommands ;
23212341 if ( compileCommands === undefined ) {
2342+ console . log ( "Skipping check for compileCommands file changes, compileCommands is not set." ) ;
23222343 return ;
23232344 }
23242345
2346+ console . log ( "Manually checking for compileCommands file changes." ) ;
23252347 const compileCommandsFile : string | undefined = this . resolvePath ( compileCommands ) ;
2326- fs . stat ( compileCommandsFile , ( err , stats ) => {
2327- if ( err ) {
2328- if ( err . code === "ENOENT" && this . compileCommandsFile ) {
2329- this . onCompileCommandsChanged ( compileCommandsFile , "periodic checker - file deleted" ) ;
2330- this . compileCommandsFile = null ; // File deleted
2331- }
2332- } else if ( stats . mtime > this . compileCommandsFileWatcherFallbackTime ) {
2348+ try {
2349+ const stats = fs . statSync ( compileCommandsFile ) ;
2350+ if ( this . compileCommandsFile === undefined || stats . mtime > this . compileCommandsFileWatcherFallbackTime ) {
23332351 this . compileCommandsFileWatcherFallbackTime = new Date ( ) ;
2334- this . onCompileCommandsChanged ( compileCommandsFile , "periodic checker - file created" ) ;
2335- this . compileCommandsFile = vscode . Uri . file ( compileCommandsFile ) ; // File created.
2352+ console . log ( "checkCompileCommands(): compileCommands file changed" ) ;
2353+ this . onCompileCommandsChanged ( compileCommandsFile ) ;
2354+ this . compileCommandsFile = compileCommandsFile ; // File created.
23362355 }
2337- } ) ;
2356+ }
2357+ catch ( err : any ) {
2358+ if ( err . code === "ENOENT" && this . compileCommandsFile ) {
2359+ console . log ( "checkCompileCommands(): compileCommands file deleted" ) ;
2360+ this . onCompileCommandsChanged ( compileCommandsFile ) ;
2361+ this . compileCommandsFile = undefined ; // File deleted
2362+ }
2363+ }
2364+ console . log ( "checkCompileCommands(): done" ) ;
23382365
2339- // if the compileCommands file is set (and not using a configuration provider), try to watch it
2340- if ( this . compileCommandsFile ) {
2366+ const providerInsufficient : boolean = ! this . CurrentConfiguration ?. configurationProvider || this . configurationProviderFailedToProvide ;
2367+ if ( this . compileCommandsFile !== undefined && providerInsufficient ) {
2368+ console . log ( "try to watch compileCommands file" ) ;
23412369 this . updateCompileCommandsFileWatcher ( ) ;
23422370 }
2371+ else {
2372+ // debug which condition was not met
2373+ if ( this . compileCommandsFile === undefined ) {
2374+ console . log ( "compileCommandsFile is undefined" ) ;
2375+ }
2376+ if ( this . CurrentConfiguration ?. configurationProvider ) {
2377+ console . log ( "configurationProvider is set" ) ;
2378+ }
2379+ }
23432380 }
23442381
23452382 dispose ( ) : void {
0 commit comments