diff --git a/package.json b/package.json index a5051ff9..5974a8a3 100644 --- a/package.json +++ b/package.json @@ -144,6 +144,12 @@ "default": "", "markdownDescription": "Pass additional arguments to the language server." }, + "haskell.serverEnvironment": { + "scope": "resource", + "type": "object", + "default": {}, + "markdownDescription": "Define environment variables for the language server." + }, "haskell.updateBehavior": { "scope": "machine", "type": "string", diff --git a/src/extension.ts b/src/extension.ts index 0d4bff9f..73bf44c7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -25,6 +25,11 @@ import { DocsBrowser } from './docsBrowser'; import { downloadHaskellLanguageServer } from './hlsBinaries'; import { directoryExists, executableExists, ExtensionLogger, resolvePathPlaceHolders } from './utils'; +// Used for environment variables later on +interface IEnvVars { + [key: string]: string; +} + // The current map of documents & folders to language servers. // It may be null to indicate that we are in the process of launching a server, // in which case don't try to launch another one for that uri @@ -212,8 +217,10 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold logger.info(`Activating the language server in the parent dir of the file: ${uri.fsPath}`); } + const serverEnvironment: IEnvVars = workspace.getConfiguration('haskell', uri).serverEnvironment; const exeOptions: ExecutableOptions = { cwd: folder ? undefined : path.dirname(uri.fsPath), + env: Object.assign(process.env, serverEnvironment), }; // We don't want empty strings in our args @@ -231,6 +238,12 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold if (exeOptions.cwd) { logger.info(`server cwd: ${exeOptions.cwd}`); } + if (serverEnvironment) { + logger.info('server environment variables:'); + Object.entries(serverEnvironment).forEach(([key, val]: [string, string | undefined]) => { + logger.info(` ${key}=${val}`); + }); + } const pat = folder ? `${folder.uri.fsPath}/**/*` : '**/*'; logger.info(`document selector patten: ${pat}`); diff --git a/test/suite/extension.test.ts b/test/suite/extension.test.ts index ac2e118c..08a6aacc 100644 --- a/test/suite/extension.test.ts +++ b/test/suite/extension.test.ts @@ -65,6 +65,7 @@ suite('Extension Test Suite', () => { await getHaskellConfig().update('logFile', 'hls.log'); await getHaskellConfig().update('trace.server', 'messages'); await getHaskellConfig().update('releasesDownloadStoragePath', path.normalize(getWorkspaceFile('bin').fsPath)); + await getHaskellConfig().update('serverEnvironment', { XDG_CACHE_HOME: path.normalize(getWorkspaceFile('cache-test').fsPath) }); const contents = new TextEncoder().encode('main = putStrLn "hi vscode tests"'); await vscode.workspace.fs.writeFile(getWorkspaceFile('Main.hs'), contents); }); @@ -99,6 +100,15 @@ suite('Extension Test Suite', () => { assert.ok(await withTimeout(30, existsWorkspaceFile('hls.log')), 'Server log not created in 30 seconds'); }); + test('Server should inherit environment variables defined in the settings', async () => { + await vscode.workspace.openTextDocument(getWorkspaceFile('Main.hs')); + assert.ok( + // Folder will have already been created by this point, so it will not trigger watcher in existsWorkspaceFile() + vscode.workspace.getWorkspaceFolder(getWorkspaceFile('cache-test')), + 'Server did not inherit XDG_CACHE_DIR from environment variables set in the settings' + ); + }); + suiteTeardown(async () => { disposables.forEach((d) => d.dispose()); await vscode.commands.executeCommand(CommandNames.StopServerCommandName);