Skip to content

Commit 1201a56

Browse files
authored
Merge pull request #6775 from microsoft/insiders
Merge to main: implement addNodeAddonIncludePaths (#6731)
2 parents 1145a49 + 3c32946 commit 1201a56

File tree

5 files changed

+92
-3
lines changed

5 files changed

+92
-3
lines changed

Extension/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,12 @@
11301130
"markdownDescription": "%c_cpp.configuration.vcpkg.enabled.markdownDescription%",
11311131
"scope": "resource"
11321132
},
1133+
"C_Cpp.addNodeAddonIncludePaths": {
1134+
"type": "boolean",
1135+
"default": false,
1136+
"markdownDescription": "%c_cpp.configuration.addNodeAddonIncludePaths.description%",
1137+
"scope": "application"
1138+
},
11331139
"C_Cpp.renameRequiresIdentifier": {
11341140
"type": "boolean",
11351141
"default": true,
@@ -2738,4 +2744,4 @@
27382744
"integrity": "8299A112D1260C2CEA53AC74D18FA73DE8533C058AAAB254571B503FBAC37297"
27392745
}
27402746
]
2741-
}
2747+
}

Extension/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@
160160
"c_cpp.configuration.enhancedColorization.description": "If enabled, code is colorized based on IntelliSense. This setting only applies if intelliSenseEngine is set to \"Default\".",
161161
"c_cpp.configuration.codeFolding.description": "If enabled, code folding ranges are provided by the language server.",
162162
"c_cpp.configuration.vcpkg.enabled.markdownDescription": "Enable integration services for the [vcpkg dependency manager](https://aka.ms/vcpkg/).",
163+
"c_cpp.configuration.addNodeAddonIncludePaths.description": "Add include paths from nan and node-addon-api when they're dependencies.",
163164
"c_cpp.configuration.renameRequiresIdentifier.description": "If true, 'Rename Symbol' will require a valid C/C++ identifier.",
164165
"c_cpp.configuration.debugger.useBacktickCommandSubstitution.description": "If true, debugger shell command substitution will use obsolete backtick (`).",
165166
"c_cpp.contributes.views.cppReferencesView.title": "C/C++: Other references results",

Extension/src/LanguageServer/client.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,6 +1362,11 @@ export class DefaultClient implements Client {
13621362
this.semanticTokensProvider = undefined;
13631363
}
13641364
}
1365+
// if addNodeAddonIncludePaths was turned on but no includes have been found yet then 1) presume that nan
1366+
// or node-addon-api was installed so prompt for reload.
1367+
if (changedSettings["addNodeAddonIncludePaths"] && settings.addNodeAddonIncludePaths && this.configuration.nodeAddonIncludesFound() === 0) {
1368+
util.promptForReloadWindowDueToSettingsChange();
1369+
}
13651370
}
13661371
this.configuration.onDidChangeSettings();
13671372
telemetry.logLanguageServerEvent("CppSettingsChange", changedSettings, undefined);

Extension/src/LanguageServer/configurations.ts

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export class CppProperties {
133133
private defaultWindowsSdkVersion: string | null = null;
134134
private vcpkgIncludes: string[] = [];
135135
private vcpkgPathReady: boolean = false;
136+
private nodeAddonIncludes: string[] = [];
136137
private defaultIntelliSenseMode?: string;
137138
private defaultCustomConfigurationVariables?: { [key: string]: string };
138139
private readonly configurationGlobPattern: string = "c_cpp_properties.json";
@@ -158,6 +159,7 @@ export class CppProperties {
158159
this.configFolder = path.join(rootPath, ".vscode");
159160
this.diagnosticCollection = vscode.languages.createDiagnosticCollection(rootPath);
160161
this.buildVcpkgIncludePath();
162+
this.readNodeAddonIncludeLocations(rootPath);
161163
this.disposables.push(vscode.Disposable.from(this.configurationsChanged, this.selectionChanged, this.compileCommandsChanged));
162164
}
163165

@@ -320,6 +322,7 @@ export class CppProperties {
320322
} else {
321323
configuration.includePath = [defaultFolder];
322324
}
325+
323326
// browse.path is not set by default anymore. When it is not set, the includePath will be used instead.
324327
if (isUnset(settings.defaultDefines)) {
325328
configuration.defines = (process.platform === 'win32') ? ["_DEBUG", "UNICODE", "_UNICODE"] : [];
@@ -389,6 +392,72 @@ export class CppProperties {
389392
}
390393
}
391394

395+
public nodeAddonIncludesFound(): number {
396+
return this.nodeAddonIncludes.length;
397+
}
398+
399+
private async readNodeAddonIncludeLocations(rootPath: string): Promise<void> {
400+
let error: Error | undefined;
401+
let pdjFound: boolean = false;
402+
const package_json: any = await fs.promises.readFile(path.join(rootPath, "package.json"), "utf8")
403+
.then(pdj => {pdjFound = true; return JSON.parse(pdj); })
404+
.catch(e => (error = e));
405+
406+
if (!error) {
407+
try {
408+
const pathToNode: string = which.sync("node");
409+
const nodeAddonMap: { [dependency: string]: string } = {
410+
"nan": `"${pathToNode}" --no-warnings -e "require('nan')"`,
411+
"node-addon-api": `"${pathToNode}" --no-warnings -p "require('node-addon-api').include"`
412+
};
413+
414+
for (const dep in nodeAddonMap) {
415+
if (dep in package_json.dependencies) {
416+
const execCmd: string = nodeAddonMap[dep];
417+
let stdout: string = await util.execChildProcess(execCmd, rootPath);
418+
if (!stdout) {
419+
continue;
420+
}
421+
422+
// cleanup newlines
423+
if (stdout[stdout.length - 1] === "\n") {
424+
stdout = stdout.slice(0, -1);
425+
}
426+
// node-addon-api returns a quoted string, e.g., '"/home/user/dir/node_modules/node-addon-api"'.
427+
if (stdout[0] === "\"" && stdout[stdout.length - 1] === "\"") {
428+
stdout = stdout.slice(1, -1);
429+
}
430+
431+
// at this time both node-addon-api and nan return their own directory so this test is not really
432+
// needed. but it does future proof the code.
433+
if (!await util.checkDirectoryExists(stdout)) {
434+
// nan returns a path relative to rootPath causing the previous check to fail because this code
435+
// is executing in vscode's working directory.
436+
stdout = path.join(rootPath, stdout);
437+
if (!await util.checkDirectoryExists(stdout)) {
438+
error = new Error(`${dep} directory ${stdout} doesn't exist`);
439+
stdout = '';
440+
}
441+
}
442+
if (stdout) {
443+
this.nodeAddonIncludes.push(stdout);
444+
}
445+
}
446+
}
447+
} catch (e) {
448+
error = e;
449+
}
450+
}
451+
if (error) {
452+
if (pdjFound) {
453+
// only log an error if package.json exists.
454+
console.log('readNodeAddonIncludeLocations', error.message);
455+
}
456+
} else {
457+
this.handleConfigurationChange();
458+
}
459+
}
460+
392461
private getConfigIndexForPlatform(config: any): number | undefined {
393462
if (!this.configurationJson) {
394463
return undefined;
@@ -627,6 +696,12 @@ export class CppProperties {
627696
const configuration: Configuration = this.configurationJson.configurations[i];
628697

629698
configuration.includePath = this.updateConfigurationStringArray(configuration.includePath, settings.defaultIncludePath, env);
699+
// in case includePath is reset below
700+
const origIncludePath: string[] | undefined = configuration.includePath;
701+
if (settings.addNodeAddonIncludePaths) {
702+
const includePath: string[] = origIncludePath || [];
703+
configuration.includePath = includePath.concat(this.nodeAddonIncludes.filter(i => includePath.indexOf(i) < 0));
704+
}
630705
configuration.defines = this.updateConfigurationStringArray(configuration.defines, settings.defaultDefines, env);
631706
configuration.macFrameworkPath = this.updateConfigurationStringArray(configuration.macFrameworkPath, settings.defaultMacFrameworkPath, env);
632707
configuration.windowsSdkVersion = this.updateConfigurationString(configuration.windowsSdkVersion, settings.defaultWindowsSdkVersion, env);
@@ -663,8 +738,9 @@ export class CppProperties {
663738
if (!configuration.windowsSdkVersion && !!this.defaultWindowsSdkVersion) {
664739
configuration.windowsSdkVersion = this.defaultWindowsSdkVersion;
665740
}
666-
if (!configuration.includePath && !!this.defaultIncludes) {
667-
configuration.includePath = this.defaultIncludes;
741+
if (!origIncludePath && !!this.defaultIncludes) {
742+
const includePath: string[] = configuration.includePath || [];
743+
configuration.includePath = includePath.concat(this.defaultIncludes);
668744
}
669745
if (!configuration.macFrameworkPath && !!this.defaultFrameworks) {
670746
configuration.macFrameworkPath = this.defaultFrameworks;

Extension/src/LanguageServer/settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ export class CppSettings extends Settings {
141141
public get preferredPathSeparator(): string | undefined { return super.Section.get<string>("preferredPathSeparator"); }
142142
public get updateChannel(): string | undefined { return super.Section.get<string>("updateChannel"); }
143143
public get vcpkgEnabled(): boolean | undefined { return super.Section.get<boolean>("vcpkg.enabled"); }
144+
public get addNodeAddonIncludePaths(): boolean | undefined { return super.Section.get<boolean>("addNodeAddonIncludePaths"); }
144145
public get renameRequiresIdentifier(): boolean | undefined { return super.Section.get<boolean>("renameRequiresIdentifier"); }
145146
public get defaultIncludePath(): string[] | undefined { return super.Section.get<string[]>("default.includePath"); }
146147
public get defaultDefines(): string[] | undefined { return super.Section.get<string[]>("default.defines"); }

0 commit comments

Comments
 (0)