https://github.com/DriftProc created https://github.com/llvm/llvm-project/pull/183989
Add variable substitution support for some settings of the LLDB DAP VS Code extension, include `lldb-dap.executable-path`, `lldb-dap.arguments` and `lldb-dap.environment`. Partially solve #183008. >From d4851f5d1bf52fd8ceb7a42f551e2326f4301051 Mon Sep 17 00:00:00 2001 From: DriftProc <[email protected]> Date: Sun, 1 Mar 2026 16:40:23 +0800 Subject: [PATCH] [lldb-dap] Add variable substitution support for LLDB DAP VS Code extension Add variable substitution support for some settings of the LLDB DAP VS Code extension, include `lldb-dap.executable-path`, `lldb-dap.arguments` and `lldb-dap.environment`. Partially solve #183008. --- .../extension/src/debug-adapter-factory.ts | 11 +-- lldb/tools/lldb-dap/extension/src/utils.ts | 73 +++++++++++++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/lldb/tools/lldb-dap/extension/src/debug-adapter-factory.ts b/lldb/tools/lldb-dap/extension/src/debug-adapter-factory.ts index e415097d1b8be..dd93afeca03d2 100644 --- a/lldb/tools/lldb-dap/extension/src/debug-adapter-factory.ts +++ b/lldb/tools/lldb-dap/extension/src/debug-adapter-factory.ts @@ -6,7 +6,7 @@ import * as vscode from "vscode"; import { LogFilePathProvider, LogType } from "./logging"; import { ErrorWithNotification } from "./ui/error-with-notification"; import { ConfigureButton, OpenSettingsButton } from "./ui/show-error-message"; -import { expandUser } from "./utils"; +import { expandUser, substitute } from "./utils"; const exec = util.promisify(child_process.execFile); @@ -130,7 +130,7 @@ async function getDAPExecutable( // Check if the executable was provided in the extension's configuration. const config = vscode.workspace.getConfiguration("lldb-dap", workspaceFolder); - const configPath = expandUser(config.get<string>("executable-path") ?? ""); + const configPath = expandUser(await substitute(config.get<string>("executable-path") ?? "")); if (configPath && configPath.length !== 0) { if (!(await isExecutable(configPath))) { throw new ErrorWithNotification( @@ -187,9 +187,10 @@ async function getDAPArguments( return debugConfigArgs; } // Fall back on the workspace configuration. - return vscode.workspace + return await substitute( + vscode.workspace .getConfiguration("lldb-dap", workspaceFolder) - .get<string[]>("arguments", []); + .get<string[]>("arguments", [])); } /** @@ -234,7 +235,7 @@ async function getDAPEnvironment( const config = vscode.workspace.workspaceFile ? vscode.workspace.getConfiguration("lldb-dap") : vscode.workspace.getConfiguration("lldb-dap", workspaceFolder); - return config.get<{ [key: string]: string }>("environment") || {}; + return await substitute(config.get<{ [key: string]: string }>("environment") || {}); } /** diff --git a/lldb/tools/lldb-dap/extension/src/utils.ts b/lldb/tools/lldb-dap/extension/src/utils.ts index 310642175abae..d12ba96599997 100644 --- a/lldb/tools/lldb-dap/extension/src/utils.ts +++ b/lldb/tools/lldb-dap/extension/src/utils.ts @@ -1,5 +1,6 @@ import * as os from "os"; import * as path from "path"; +import * as vscode from "vscode"; /** * Expands the character `~` to the user's home directory @@ -39,3 +40,75 @@ export function expandUser(file_path: string): string { return file_path; } + +// Traverse a JSON value, replacing placeholders in all strings. +export async function substitute<T>(val: T): Promise<T> { + if (typeof val === "string") { + const replacementPattern = /\$\{(.*?)\}/g; + const replacementPromises: Promise<string|undefined>[] = []; + const matches = val.matchAll(replacementPattern); + for (const match of matches) { + // match[1] is the first captured group + replacementPromises.push(replacement(match[1])); + } + const replacements = await Promise.all(replacementPromises); + val = val.replace( + replacementPattern, + // If there's no replacement available, keep the placeholder. + match => replacements.shift() ?? match) as unknown as T; + } else if (Array.isArray(val)) { + val = await Promise.all(val.map(substitute)) as T; + } else if (typeof val === "object") { + // Substitute values but not keys, so we don't deal with collisions. + const result = {} as {[k: string]: any}; + for (const key in val) { + result[key] = await substitute(val[key]); + } + val = result as T; + } + return val; +} + +// Subset of substitution variables that are most likely to be useful. +// https://code.visualstudio.com/docs/editor/variables-reference +async function replacement(name: string): Promise<string|undefined> { + if (name === "userHome") { + return os.homedir(); + } + if (name === "workspaceRoot" || name === "workspaceFolder" || + name === "cwd") { + if (vscode.workspace.rootPath !== undefined) + return vscode.workspace.rootPath; + if (vscode.window.activeTextEditor !== undefined) + return path.dirname(vscode.window.activeTextEditor.document.uri.fsPath); + return process.cwd(); + } + if (name === "workspaceFolderBasename" && + vscode.workspace.rootPath !== undefined) { + return path.basename(vscode.workspace.rootPath); + } + const envPrefix = "env:"; + if (name.startsWith(envPrefix)) + return process.env[name.substr(envPrefix.length)] ?? ""; + const configPrefix = "config:"; + if (name.startsWith(configPrefix)) { + const config = vscode.workspace.getConfiguration().get( + name.substr(configPrefix.length)); + return (typeof config === "string") ? config : undefined; + } + const commandPrefix = "command:"; + if (name.startsWith(commandPrefix)) { + const commandId = name.substr(commandPrefix.length); + try { + return await vscode.commands.executeCommand(commandId); + } catch (error) { + console.warn(`LLDB DAP: Error resolving command '${commandId}':`, error); + vscode.window.showWarningMessage( + `LLDB DAP: Failed to resolve ${commandId}`); + + return undefined; + } + } + + return undefined; +} _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
