in src/expand.ts [123:237]
async function expandStringHelper(tmpl: string, opts: ExpansionOptions) {
const envPreNormalize = opts.envOverride ? opts.envOverride : process.env;
const env = EnvironmentUtils.create(envPreNormalize);
const repls = opts.vars;
// We accumulate a list of substitutions that we need to make, preventing
// recursively expanding or looping forever on bad replacements
const subs = new Map<string, string>();
const var_re = /\$\{(\w+)\}/g;
let mat: RegExpMatchArray | null = null;
while ((mat = var_re.exec(tmpl))) {
const full = mat[0];
const key = mat[1];
if (key !== 'dollar') {
// Replace dollar sign at the very end of the expanding process
const repl = repls[key];
if (!repl) {
log.warning(localize('invalid.variable.reference', 'Invalid variable reference {0} in string: {1}', full, tmpl));
} else {
subs.set(full, repl);
}
}
}
// Regular expression for variable value (between the variable suffix and the next ending curly bracket):
// .+? matches any character (except line terminators) between one and unlimited times,
// as few times as possible, expanding as needed (lazy)
const varValueRegexp = ".+?";
const env_re = RegExp(`\\$\\{env:(${varValueRegexp})\\}`, "g");
while ((mat = env_re.exec(tmpl))) {
const full = mat[0];
const varname = mat[1];
const repl = fixPaths(env[varname]) || '';
subs.set(full, repl);
}
const env_re2 = RegExp(`\\$\\{env\\.(${varValueRegexp})\\}`, "g");
while ((mat = env_re2.exec(tmpl))) {
const full = mat[0];
const varname = mat[1];
const repl = fixPaths(env[varname]) || '';
subs.set(full, repl);
}
const env_re3 = RegExp(`\\$env\\{(${varValueRegexp})\\}`, "g");
while ((mat = env_re3.exec(tmpl))) {
const full = mat[0];
const varname = mat[1];
const repl = fixPaths(env[varname]) || '';
subs.set(full, repl);
}
const penv_re = RegExp(`\\$penv\\{(${varValueRegexp})\\}`, "g");
while ((mat = penv_re.exec(tmpl))) {
const full = mat[0];
const varname = mat[1];
const repl = fixPaths(process.env[varname]) || '';
subs.set(full, repl);
}
if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) {
const folder_re = RegExp(`\\$\\{workspaceFolder:(${varValueRegexp})\\}`, "g");
mat = folder_re.exec(tmpl);
while (mat) {
const full = mat[0];
const folderName = mat[1];
const f = vscode.workspace.workspaceFolders.find(folder => folder.name.toLocaleLowerCase() === folderName.toLocaleLowerCase());
if (f) {
subs.set(full, f.uri.fsPath);
}
mat = folder_re.exec(tmpl);
}
}
if (opts.variantVars) {
const variants = opts.variantVars;
const variant_regex = RegExp(`\\$\\{variant:(${varValueRegexp})\\}`, "g");
while ((mat = variant_regex.exec(tmpl))) {
const full = mat[0];
const varname = mat[1];
const repl = variants[varname] || '';
subs.set(full, repl);
}
}
const command_re = RegExp(`\\$\\{command:(${varValueRegexp})\\}`, "g");
while ((mat = command_re.exec(tmpl))) {
if (opts.doNotSupportCommands) {
log.warning(localize('command.not.supported', 'Commands are not supported for string: {0}', tmpl));
break;
}
const full = mat[0];
const command = mat[1];
if (subs.has(full)) {
continue; // Don't execute commands more than once per string
}
try {
const command_ret = await vscode.commands.executeCommand(command, opts.vars.workspaceFolder);
subs.set(full, `${command_ret}`);
} catch (e) {
log.warning(localize('exception.executing.command', 'Exception while executing command {0} for string: {1} {2}', command, tmpl, errorToString(e)));
}
}
let final_str = tmpl;
let didReplacement = false;
subs.forEach((value, key) => {
if (value !== key) {
final_str = replaceAll(final_str, key, value);
didReplacement = true;
}
});
return { result: final_str, didReplacement };
}