Skip to content

Commit 7ba01f8

Browse files
committed
initial implementation
1 parent fcd0b0c commit 7ba01f8

File tree

2 files changed

+68
-45
lines changed

2 files changed

+68
-45
lines changed

Extension/src/LanguageServer/client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,6 +2039,9 @@ export class DefaultClient implements Client {
20392039
const provideConfigurationAsync: () => Thenable<SourceFileConfigurationItem[] | undefined> = async () => {
20402040
try {
20412041
if (!await provider.canProvideConfiguration(docUri, tokenSource.token)) {
2042+
const forceCheck: boolean = true;
2043+
this.configuration.checkCompileCommands(forceCheck);
2044+
console.log("provideCustomConfigurationAsync(): Provider cannot provide configuration for: ", docUri.path);
20422045
return [];
20432046
}
20442047
} catch (err) {

Extension/src/LanguageServer/configurations.ts

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)