diff --git a/vscode-extension/src/util.ts b/vscode-extension/src/util.ts index aab45e262..754fbba28 100644 --- a/vscode-extension/src/util.ts +++ b/vscode-extension/src/util.ts @@ -310,17 +310,12 @@ export async function setupEnvironmentVariables( ) { const homedir = require("os").homedir(); // This is cross-platform: https://stackoverflow.com/a/9081436 const defaultEnvPath = path.join(homedir, ".env"); - - // TODO: If there are multiple workspace folders, use common lowest - // ancestor as workspacePath: https://github.com/lastmile-ai/aiconfig/issues/1299 - const workspacePath = vscode.workspace.workspaceFolders - ? vscode.workspace.workspaceFolders[0].uri.fsPath - : null; + const lowestCommonWorkspacePath = getLowestCommonAncestorAcrossWorkspaces(); const envPath = await vscode.window.showInputBox({ prompt: "Enter the path of your .env file", value: defaultEnvPath, - validateInput: (input) => validateEnvPath(input, workspacePath), + validateInput: (input) => validateEnvPath(input, lowestCommonWorkspacePath), }); if (!envPath) { @@ -403,15 +398,53 @@ export function validateNewConfigName(name: string, mode: "json" | "yaml") { return null; } +/** + * Some VS Code setups can have multiple workspaces, in which + * case we should take the lowest common ancestor path that is shared + * across all of them so that the same .env file can be used for multiple + * AIConfig files + * @returns lowestCommonAncestorPath (string | undefined) + * -> string of path to lowest common ancestor: empty means no shared path + * -> undefined if no workspaces are defined in VS Code session + */ +function getLowestCommonAncestorAcrossWorkspaces(): string | undefined { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (workspaceFolders === undefined || workspaceFolders.length === 0) { + return undefined; + } + + const workspacePaths = workspaceFolders.map((folder) => + path.normalize(folder.uri.fsPath) + ); + let lowestCommonAncestorPath: string; + const separator = path.sep; // Handles Windows and Linux + lowestCommonAncestorPath = workspacePaths.reduce( + (currLowestCommonAncestorPath, currPath) => { + const ancestorFolders = currLowestCommonAncestorPath.split(separator); + const currPathFolders = currPath.split(separator); + const commonPathFolders: Array = []; + for (var i = 0; i < ancestorFolders.length; i++) { + if (ancestorFolders[i] === currPathFolders[i]) { + commonPathFolders.push(ancestorFolders[i]); + } else { + break; + } + } + return commonPathFolders.join(separator); + } + ); + return lowestCommonAncestorPath; +} + function validateEnvPath( inputPath: string, - workspacePath: string | null + workspacePath: string | undefined ): string | null { if (!inputPath) { return "File path is required"; } else if (path.basename(inputPath) !== ".env") { return 'Filename must be ".env"'; - } else if (workspacePath !== null) { + } else if (workspacePath != null && workspacePath !== "") { // loadenv() from Python checks each folder from the file/program where // it's invoked for the presence of an `.env` file. Therefore, the `.env // file must be saved either at the top-level directory of the workspace