diff --git a/Configuration.md b/Configuration.md index 50dfd6a4..f830407c 100644 --- a/Configuration.md +++ b/Configuration.md @@ -102,6 +102,11 @@ In case you want to manually edit the configuration, below are the explanation a } ``` +- `envFile` - Absolute path to a file containing environment variable definitions. + ```json + "envFile": "${workspaceFolder}/.env" + ``` + ### Don't step into the specified classes or methods - `stepFilters` - Skip the specified classes or methods you don't want to step into. Class names should be fully qualified. Wildcard is supported. diff --git a/README.md b/README.md index 9ac581ba..73eef2b3 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Please also check the documentation of [Language Support for Java by Red Hat](ht - `projectName` - The preferred project in which the debugger searches for classes. There could be duplicated class names in different projects. This setting also works when the debugger looks for the specified main class when launching a program. It is required when the workspace has multiple java projects, otherwise the expression evaluation and conditional breakpoint may not work. - `cwd` - The working directory of the program. Defaults to `${workspaceFolder}`. - `env` - The extra environment variables for the program. +- `envFile` - Absolute path to a file containing environment variable definitions. - `stopOnEntry` - Automatically pause the program after launching. - `console` - The specified console to launch the program. If not specified, use the console specified by the `java.debug.settings.console` user setting. - `internalConsole` - VS Code debug console (input stream not supported). diff --git a/TestPlan.md b/TestPlan.md index f2ca1b56..4070f2eb 100644 --- a/TestPlan.md +++ b/TestPlan.md @@ -441,6 +441,7 @@ Exception in thread "main" java.lang.IllegalStateException ``` CustomEnv: This env is for test plan. SystemPath: + FileEnv: Successfully loaded an env from a file. ``` ## Runtime classpath entry diff --git a/package.json b/package.json index 37cb58af..fbfa386e 100644 --- a/package.json +++ b/package.json @@ -294,6 +294,11 @@ "description": "%java.debugger.launch.env.description%", "default": {} }, + "envFile": { + "type": "string", + "description": "%java.debugger.launch.envFile.description%", + "default": "${workspaceFolder}/.env" + }, "stopOnEntry": { "type": "boolean", "description": "%java.debugger.launch.stopOnEntry.description%", diff --git a/package.nls.json b/package.nls.json index ad012756..e95ede68 100644 --- a/package.nls.json +++ b/package.nls.json @@ -9,6 +9,7 @@ "java.debugger.launch.encoding.description": "The file.encoding setting for the JVM. If not specified, 'UTF-8' will be used. Possible values can be found in https://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html.", "java.debugger.launch.cwd.description": "The working directory of the program. Defaults to the current workspace root.", "java.debugger.launch.env.description": "The extra environment variables for the program.", + "java.debugger.launch.envFile.description": "Absolute path to a file containing environment variable definitions.", "java.debugger.launch.stopOnEntry.description": "Automatically pause the program after launching.", "java.debugger.launch.internalConsole.description": "VS Code debug console (input stream not supported).", "java.debugger.launch.integratedTerminal.description": "VS Code integrated terminal.", diff --git a/package.nls.zh.json b/package.nls.zh.json index 9e951069..f388e4ef 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -9,6 +9,7 @@ "java.debugger.launch.encoding.description": "JVM的file.encoding设置。如果未指定,将使用“UTF-8”。可以在http://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html中找到可能的值。", "java.debugger.launch.cwd.description": "应用程序的工作目录。默认为当前工作空间根目录。", "java.debugger.launch.env.description": "启动应用程序时自定义的环境变量。", + "java.debugger.launch.envFile.description": "环境变量文件绝对路径。", "java.debugger.launch.stopOnEntry.description": "启动后自动暂停应用程序。", "java.debugger.launch.internalConsole.description": "VS Code调试控制台(不支持输入流)。", "java.debugger.launch.integratedTerminal.description": "VS Code集成终端。", diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index d26a9013..91882259 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -20,7 +20,7 @@ import { IProgressReporter } from "./progressAPI"; import { progressProvider } from "./progressImpl"; import * as utility from "./utility"; -const platformNameMappings: {[key: string]: string} = { +const platformNameMappings: { [key: string]: string } = { win32: "windows", linux: "linux", darwin: "osx", @@ -103,7 +103,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration const isOnStandardMode = await utility.waitForStandardMode(progressReporter); if (!isOnStandardMode) { resolve([defaultLaunchConfig]); - return ; + return; } if (progressReporter.isCancelled()) { @@ -154,7 +154,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } } - private constructLaunchConfigName(mainClass: string, cache: {[key: string]: any}) { + private constructLaunchConfigName(mainClass: string, cache: { [key: string]: any }) { const name = `Launch ${mainClass.substr(mainClass.lastIndexOf(".") + 1)}`; if (cache[name] === undefined) { cache[name] = 0; @@ -165,6 +165,25 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } } + private mergeEnvFile(config: vscode.DebugConfiguration) { + const baseEnv = config.env || {}; + let result = baseEnv; + if (config.envFile) { + try { + result = { + ...baseEnv, + ...readEnvFile(config.envFile), + }; + } catch (e) { + throw new utility.UserError({ + message: "Cannot load environment file.", + type: Type.USAGEERROR, + }); + } + } + config.env = result; + } + private async resolveAndValidateDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, token?: vscode.CancellationToken) { let progressReporter = progressProvider.getProgressReporter(config.__progressId); @@ -198,6 +217,8 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } if (config.request === "launch") { + this.mergeEnvFile(config); + // If the user doesn't specify 'vmArgs' in launch.json, use the global setting to get the default vmArgs. if (config.vmArgs === undefined) { const debugSettings: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("java.debug.settings"); @@ -383,7 +404,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration private async resolveAndValidateMainClass(folder: vscode.Uri | undefined, config: vscode.DebugConfiguration, progressReporter: IProgressReporter): Promise { if (!config.mainClass || this.isFile(config.mainClass)) { - const currentFile = config.mainClass || _.get(vscode.window.activeTextEditor, "document.uri.fsPath"); + const currentFile = config.mainClass || _.get(vscode.window.activeTextEditor, "document.uri.fsPath"); if (currentFile) { const mainEntries = await lsPlugin.resolveMainMethod(vscode.Uri.file(currentFile)); if (progressReporter.isCancelled()) { @@ -427,7 +448,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration private async fixMainClass(folder: vscode.Uri | undefined, config: vscode.DebugConfiguration, validationResponse: lsPlugin.ILaunchValidationResponse, progressReporter: IProgressReporter): - Promise { + Promise { const errors: string[] = []; if (!validationResponse.mainClass.isValid) { errors.push(String(validationResponse.mainClass.message)); @@ -576,3 +597,35 @@ function convertLogLevel(commonLogLevel: string) { return "FINE"; } } + +// from vscode-js-debug https://github.com/microsoft/vscode-js-debug/blob/master/src/targets/node/nodeLauncherBase.ts +function readEnvFile(file: string): { [key: string]: string } { + if (!fs.existsSync(file)) { + return {}; + } + + const buffer = stripBOM(fs.readFileSync(file, "utf8")); + const env: { [key: string]: string } = {}; + for (const line of buffer.split("\n")) { + const r = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/); + if (!r) { + continue; + } + + let value = r[2] || ""; + // .env variables never overwrite existing variables (see #21169) + if (value.length > 0 && value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') { + value = value.replace(/\\n/gm, "\n"); + } + env[r[1]] = value.replace(/(^['"]|['"]$)/g, ""); + } + + return env; +} + +function stripBOM(s: string): string { + if (s && s[0] === "\uFEFF") { + s = s.substr(1); + } + return s; +} diff --git a/testprojects/26.environmentVariables/.env b/testprojects/26.environmentVariables/.env new file mode 100644 index 00000000..89095410 --- /dev/null +++ b/testprojects/26.environmentVariables/.env @@ -0,0 +1 @@ +FILE_ENV_FOR_TEST_PLAN=Successfully loaded an env from a file. \ No newline at end of file diff --git a/testprojects/26.environmentVariables/.vscode/launch.json b/testprojects/26.environmentVariables/.vscode/launch.json index 8bd81fb1..95dfde8f 100644 --- a/testprojects/26.environmentVariables/.vscode/launch.json +++ b/testprojects/26.environmentVariables/.vscode/launch.json @@ -17,7 +17,8 @@ "projectName": "26.environmentVariables", "env": { "CUSTOM_ENV_FOR_TEST_PLAN": "This env is for test plan." - } + }, + "envFile": "${workspaceFolder}/.env" } ] } \ No newline at end of file diff --git a/testprojects/26.environmentVariables/src/main/java/EnvrionmentVariable.java b/testprojects/26.environmentVariables/src/main/java/EnvrionmentVariable.java index 2efe8edd..0e4f4325 100644 --- a/testprojects/26.environmentVariables/src/main/java/EnvrionmentVariable.java +++ b/testprojects/26.environmentVariables/src/main/java/EnvrionmentVariable.java @@ -2,7 +2,9 @@ public class EnvrionmentVariable { public static void main(String[] args) { String customEnv = System.getenv("CUSTOM_ENV_FOR_TEST_PLAN"); String systemPath = System.getenv("PATH"); + String envFromFile = System.getenv("FILE_ENV_FOR_TEST_PLAN"); System.out.println(String.format("CustomEnv: %s", customEnv)); System.out.println(String.format("SystemPath: %s", systemPath)); + System.out.println(String.format("FileEnv: %s", envFromFile)); } } \ No newline at end of file