From ac78bb2136b955aedb8f083d5b45074033ac094e Mon Sep 17 00:00:00 2001 From: Chase Colman Date: Sat, 26 Dec 2020 17:57:07 +0800 Subject: [PATCH 1/5] Implement envFile --- Configuration.md | 5 ++ TestPlan.md | 1 + package.json | 5 ++ package.nls.json | 1 + package.nls.zh.json | 1 + src/configurationProvider.ts | 71 ++++++++++++++++--- testprojects/26.environmentVariables/.env | 1 + .../.vscode/launch.json | 3 +- .../src/main/java/EnvrionmentVariable.java | 2 + 9 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 testprojects/26.environmentVariables/.env 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/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..df0705c6 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": "" + }, "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..f12d3e51 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..0a28d037 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", @@ -54,7 +54,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration // Try to add all missing attributes to the debug configuration being launched. public resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, - config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): + config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult { // If no debug configuration is provided, then generate one in memory. if (this.isEmptyConfig(config)) { @@ -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,8 +165,27 @@ 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) { + token?: vscode.CancellationToken) { let progressReporter = progressProvider.getProgressReporter(config.__progressId); if (!progressReporter && config.__progressId) { return undefined; @@ -196,6 +215,8 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration config.name = "Java Debug"; config.request = "launch"; } + + this.mergeEnvFile(config); if (config.request === "launch") { // If the user doesn't specify 'vmArgs' in launch.json, use the global setting to get the default vmArgs. @@ -381,9 +402,9 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } private async resolveAndValidateMainClass(folder: vscode.Uri | undefined, config: vscode.DebugConfiguration, - progressReporter: IProgressReporter): Promise { + 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()) { @@ -426,8 +447,8 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } private async fixMainClass(folder: vscode.Uri | undefined, config: vscode.DebugConfiguration, - validationResponse: lsPlugin.ILaunchValidationResponse, progressReporter: IProgressReporter): - Promise { + validationResponse: lsPlugin.ILaunchValidationResponse, progressReporter: IProgressReporter): + 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 +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; +} \ No newline at end of file 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 From 87c9738687fba3220822184886476eb69f150faa Mon Sep 17 00:00:00 2001 From: Chase Colman Date: Sat, 26 Dec 2020 18:30:21 +0800 Subject: [PATCH 2/5] Fix linting issues --- src/configurationProvider.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index 0a28d037..4c8c6b8d 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -54,7 +54,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration // Try to add all missing attributes to the debug configuration being launched. public resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, - config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): + config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult { // If no debug configuration is provided, then generate one in memory. if (this.isEmptyConfig(config)) { @@ -172,7 +172,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration try { result = { ...baseEnv, - ...readEnvFile(config.envFile) + ...readEnvFile(config.envFile), }; } catch (e) { throw new utility.UserError({ @@ -185,7 +185,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } private async resolveAndValidateDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, - token?: vscode.CancellationToken) { + token?: vscode.CancellationToken) { let progressReporter = progressProvider.getProgressReporter(config.__progressId); if (!progressReporter && config.__progressId) { return undefined; @@ -215,7 +215,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration config.name = "Java Debug"; config.request = "launch"; } - + this.mergeEnvFile(config); if (config.request === "launch") { @@ -402,7 +402,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } private async resolveAndValidateMainClass(folder: vscode.Uri | undefined, config: vscode.DebugConfiguration, - progressReporter: IProgressReporter): Promise { + progressReporter: IProgressReporter): Promise { if (!config.mainClass || this.isFile(config.mainClass)) { const currentFile = config.mainClass || _.get(vscode.window.activeTextEditor, "document.uri.fsPath"); if (currentFile) { @@ -447,7 +447,7 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration } private async fixMainClass(folder: vscode.Uri | undefined, config: vscode.DebugConfiguration, - validationResponse: lsPlugin.ILaunchValidationResponse, progressReporter: IProgressReporter): + validationResponse: lsPlugin.ILaunchValidationResponse, progressReporter: IProgressReporter): Promise { const errors: string[] = []; if (!validationResponse.mainClass.isValid) { @@ -604,28 +604,28 @@ function readEnvFile(file: string): { [key: string]: string } { return {}; } - const buffer = stripBOM(fs.readFileSync(file, 'utf8')); + const buffer = stripBOM(fs.readFileSync(file, "utf8")); const env: { [key: string]: string } = {}; - for (const line of buffer.split('\n')) { + for (const line of buffer.split("\n")) { const r = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/); if (!r) { continue; } - let value = r[2] || ''; + 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'); + value = value.replace(/\\n/gm, "\n"); } - env[r[1]] = value.replace(/(^['"]|['"]$)/g, ''); + env[r[1]] = value.replace(/(^['"]|['"]$)/g, ""); } return env; } function stripBOM(s: string): string { - if (s && s[0] === '\uFEFF') { + if (s && s[0] === "\uFEFF") { s = s.substr(1); } return s; -} \ No newline at end of file +} From d6b6ae324dfcd1a767947b833bb8456735e0481e Mon Sep 17 00:00:00 2001 From: Chase Colman Date: Sat, 26 Dec 2020 18:51:04 +0800 Subject: [PATCH 3/5] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) 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). From d3f3b6a068c0a47e21bddb8e3604b24f05166c6f Mon Sep 17 00:00:00 2001 From: Chase Colman Date: Sat, 26 Dec 2020 18:51:31 +0800 Subject: [PATCH 4/5] Only merge envFile on launch type --- src/configurationProvider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index 4c8c6b8d..771469f0 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -216,9 +216,9 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration config.request = "launch"; } - this.mergeEnvFile(config); - 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"); From f102e070c3325eafdc5a4695201be142c82ab05f Mon Sep 17 00:00:00 2001 From: Chase Colman Date: Wed, 13 Jan 2021 01:07:28 +0800 Subject: [PATCH 5/5] Changes as requested by @testforstephen --- package.json | 2 +- package.nls.zh.json | 2 +- src/configurationProvider.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index df0705c6..fbfa386e 100644 --- a/package.json +++ b/package.json @@ -297,7 +297,7 @@ "envFile": { "type": "string", "description": "%java.debugger.launch.envFile.description%", - "default": "" + "default": "${workspaceFolder}/.env" }, "stopOnEntry": { "type": "boolean", diff --git a/package.nls.zh.json b/package.nls.zh.json index f12d3e51..f388e4ef 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -9,7 +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.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 771469f0..91882259 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -598,7 +598,7 @@ function convertLogLevel(commonLogLevel: string) { } } -// from vscode-js-debug +// 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 {};