Skip to content

Commit

Permalink
implement addNodeAddonIncludePaths (#6731)
Browse files Browse the repository at this point in the history
* add addNodeAddonIncludePaths, fix for #4854
  • Loading branch information
bmacnaughton authored Jan 14, 2021
1 parent 2ad35c8 commit 41b7678
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 3 deletions.
8 changes: 7 additions & 1 deletion Extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,12 @@
"markdownDescription": "%c_cpp.configuration.vcpkg.enabled.markdownDescription%",
"scope": "resource"
},
"C_Cpp.addNodeAddonIncludePaths": {
"type": "boolean",
"default": false,
"markdownDescription": "%c_cpp.configuration.addNodeAddonIncludePaths.description%",
"scope": "application"
},
"C_Cpp.renameRequiresIdentifier": {
"type": "boolean",
"default": true,
Expand Down Expand Up @@ -2679,4 +2685,4 @@
"integrity": "CF1A01AA75275F76800F6BC1D289F2066DCEBCD983376D344ABF6B03FDB8FEA0"
}
]
}
}
1 change: 1 addition & 0 deletions Extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
"c_cpp.configuration.enhancedColorization.description": "If enabled, code is colorized based on IntelliSense. This setting only applies if intelliSenseEngine is set to \"Default\".",
"c_cpp.configuration.codeFolding.description": "If enabled, code folding ranges are provided by the language server.",
"c_cpp.configuration.vcpkg.enabled.markdownDescription": "Enable integration services for the [vcpkg dependency manager](https://aka.ms/vcpkg/).",
"c_cpp.configuration.addNodeAddonIncludePaths.description": "Add include paths from nan and node-addon-api when they're dependencies.",
"c_cpp.configuration.renameRequiresIdentifier.description": "If true, 'Rename Symbol' will require a valid C/C++ identifier.",
"c_cpp.configuration.debugger.useBacktickCommandSubstitution.description": "If true, debugger shell command substitution will use obsolete backtick (`).",
"c_cpp.contributes.views.cppReferencesView.title": "C/C++: Other references results",
Expand Down
5 changes: 5 additions & 0 deletions Extension/src/LanguageServer/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,11 @@ export class DefaultClient implements Client {
this.semanticTokensProvider = undefined;
}
}
// if addNodeAddonIncludePaths was turned on but no includes have been found yet then 1) presume that nan
// or node-addon-api was installed so prompt for reload.
if (changedSettings["addNodeAddonIncludePaths"] && settings.addNodeAddonIncludePaths && this.configuration.nodeAddonIncludesFound() === 0) {
util.promptForReloadWindowDueToSettingsChange();
}
}
this.configuration.onDidChangeSettings();
telemetry.logLanguageServerEvent("CppSettingsChange", changedSettings, undefined);
Expand Down
80 changes: 78 additions & 2 deletions Extension/src/LanguageServer/configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export class CppProperties {
private defaultWindowsSdkVersion: string | null = null;
private vcpkgIncludes: string[] = [];
private vcpkgPathReady: boolean = false;
private nodeAddonIncludes: string[] = [];
private defaultIntelliSenseMode?: string;
private defaultCustomConfigurationVariables?: { [key: string]: string };
private readonly configurationGlobPattern: string = "c_cpp_properties.json";
Expand All @@ -155,6 +156,7 @@ export class CppProperties {
this.configFolder = path.join(rootPath, ".vscode");
this.diagnosticCollection = vscode.languages.createDiagnosticCollection(rootPath);
this.buildVcpkgIncludePath();
this.readNodeAddonIncludeLocations(rootPath);
this.disposables.push(vscode.Disposable.from(this.configurationsChanged, this.selectionChanged, this.compileCommandsChanged));
}

Expand Down Expand Up @@ -317,6 +319,7 @@ export class CppProperties {
} else {
configuration.includePath = [defaultFolder];
}

// browse.path is not set by default anymore. When it is not set, the includePath will be used instead.
if (isUnset(settings.defaultDefines)) {
configuration.defines = (process.platform === 'win32') ? ["_DEBUG", "UNICODE", "_UNICODE"] : [];
Expand Down Expand Up @@ -386,6 +389,72 @@ export class CppProperties {
}
}

public nodeAddonIncludesFound(): number {
return this.nodeAddonIncludes.length;
}

private async readNodeAddonIncludeLocations(rootPath: string): Promise<void> {
let error: Error | undefined;
let pdjFound: boolean = false;
const package_json: any = await fs.promises.readFile(path.join(rootPath, "package.json"), "utf8")
.then(pdj => {pdjFound = true; return JSON.parse(pdj); })
.catch(e => (error = e));

if (!error) {
try {
const pathToNode: string = which.sync("node");
const nodeAddonMap: { [dependency: string]: string } = {
"nan": `"${pathToNode}" --no-warnings -e "require('nan')"`,
"node-addon-api": `"${pathToNode}" --no-warnings -p "require('node-addon-api').include"`
};

for (const dep in nodeAddonMap) {
if (dep in package_json.dependencies) {
const execCmd: string = nodeAddonMap[dep];
let stdout: string = await util.execChildProcess(execCmd, rootPath);
if (!stdout) {
continue;
}

// cleanup newlines
if (stdout[stdout.length - 1] === "\n") {
stdout = stdout.slice(0, -1);
}
// node-addon-api returns a quoted string, e.g., '"/home/user/dir/node_modules/node-addon-api"'.
if (stdout[0] === "\"" && stdout[stdout.length - 1] === "\"") {
stdout = stdout.slice(1, -1);
}

// at this time both node-addon-api and nan return their own directory so this test is not really
// needed. but it does future proof the code.
if (!await util.checkDirectoryExists(stdout)) {
// nan returns a path relative to rootPath causing the previous check to fail because this code
// is executing in vscode's working directory.
stdout = path.join(rootPath, stdout);
if (!await util.checkDirectoryExists(stdout)) {
error = new Error(`${dep} directory ${stdout} doesn't exist`);
stdout = '';
}
}
if (stdout) {
this.nodeAddonIncludes.push(stdout);
}
}
}
} catch (e) {
error = e;
}
}
if (error) {
if (pdjFound) {
// only log an error if package.json exists.
console.log('readNodeAddonIncludeLocations', error.message);
}
} else {
this.handleConfigurationChange();
}
}

private getConfigIndexForPlatform(config: any): number | undefined {
if (!this.configurationJson) {
return undefined;
Expand Down Expand Up @@ -627,6 +696,12 @@ export class CppProperties {
const configuration: Configuration = this.configurationJson.configurations[i];

configuration.includePath = this.updateConfigurationStringArray(configuration.includePath, settings.defaultIncludePath, env);
// in case includePath is reset below
const origIncludePath: string[] | undefined = configuration.includePath;
if (settings.addNodeAddonIncludePaths) {
const includePath: string[] = origIncludePath || [];
configuration.includePath = includePath.concat(this.nodeAddonIncludes.filter(i => includePath.indexOf(i) < 0));
}
configuration.defines = this.updateConfigurationStringArray(configuration.defines, settings.defaultDefines, env);
configuration.macFrameworkPath = this.updateConfigurationStringArray(configuration.macFrameworkPath, settings.defaultMacFrameworkPath, env);
configuration.windowsSdkVersion = this.updateConfigurationString(configuration.windowsSdkVersion, settings.defaultWindowsSdkVersion, env);
Expand Down Expand Up @@ -663,8 +738,9 @@ export class CppProperties {
if (!configuration.windowsSdkVersion && !!this.defaultWindowsSdkVersion) {
configuration.windowsSdkVersion = this.defaultWindowsSdkVersion;
}
if (!configuration.includePath && !!this.defaultIncludes) {
configuration.includePath = this.defaultIncludes;
if (!origIncludePath && !!this.defaultIncludes) {
const includePath: string[] = configuration.includePath || [];
configuration.includePath = includePath.concat(this.defaultIncludes);
}
if (!configuration.macFrameworkPath && !!this.defaultFrameworks) {
configuration.macFrameworkPath = this.defaultFrameworks;
Expand Down
1 change: 1 addition & 0 deletions Extension/src/LanguageServer/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export class CppSettings extends Settings {
public get preferredPathSeparator(): string | undefined { return super.Section.get<string>("preferredPathSeparator"); }
public get updateChannel(): string | undefined { return super.Section.get<string>("updateChannel"); }
public get vcpkgEnabled(): boolean | undefined { return super.Section.get<boolean>("vcpkg.enabled"); }
public get addNodeAddonIncludePaths(): boolean | undefined { return super.Section.get<boolean>("addNodeAddonIncludePaths"); }
public get renameRequiresIdentifier(): boolean | undefined { return super.Section.get<boolean>("renameRequiresIdentifier"); }
public get defaultIncludePath(): string[] | undefined { return super.Section.get<string[]>("default.includePath"); }
public get defaultDefines(): string[] | undefined { return super.Section.get<string[]>("default.defines"); }
Expand Down

0 comments on commit 41b7678

Please sign in to comment.