diff --git a/CHANGELOG.md b/CHANGELOG.md index 58f8dbdb4..5825dd583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changes to Calva. ## [Unreleased] +- Fix: [Monorepo setup working on v2.0.333, broken on v2.0.334](https://github.com/BetterThanTomorrow/calva/issues/2088) +- Fix: [Calva v2.0.333 has startup and repl issues on Windows](https://github.com/BetterThanTomorrow/calva/issues/2087) + ## [2.0.334] - 2023-02-22 - Rollback of 2.0.333, first part of: [Calva v2.0.333 is not working for me in VSCode Insiders on Windows 11](https://github.com/BetterThanTomorrow/calva/issues/2087) diff --git a/src/lsp/api.ts b/src/lsp/api.ts index 66fbb780d..6f9a4fc97 100644 --- a/src/lsp/api.ts +++ b/src/lsp/api.ts @@ -13,6 +13,15 @@ export const clientIsAlive = (client: defs.LspClient) => { return [defs.LspStatus.Starting, defs.LspStatus.Running].includes(client.status); }; +/** + * Checks if a given path is the root of the filesystem in a platform agnostic manner (this should + * work identically on POSIX and Windows) + */ +const isRoot = (dir: string) => { + const normalized = path.normalize(dir); + return path.dirname(normalized) === normalized; +}; + /** * Find the closest, active clojure-lsp client to a given URI. This works by traversing up the path component of the * provided URI until a client at the same level is found. This works because clients in the LspClientStore use their @@ -23,13 +32,13 @@ export const clientIsAlive = (client: defs.LspClient) => { * must be explicitly referenced. */ export const getActiveClientForUri = (clients: defs.LspClientStore, uri: vscode.Uri) => { - let current = uri.path; - while (current !== '/') { + let current = uri.fsPath; + while (!isRoot(current)) { const client = clients.get(current); if (client && clientIsAlive(client)) { return client; } - current = path.resolve(current, '..'); + current = path.join(current, '..'); } const fallback_client = clients.get(FALLBACK_CLIENT_ID); if (fallback_client && clientIsAlive(fallback_client)) { diff --git a/src/lsp/client/client.ts b/src/lsp/client/client.ts index 74fbe6e8f..0f916694d 100644 --- a/src/lsp/client/client.ts +++ b/src/lsp/client/client.ts @@ -223,7 +223,7 @@ export const createClient = (params: CreateClientParams): defs.LspClient => { return { id: params.id, - path: params.uri.path, + uri: params.uri, client, get status() { return utils.lspClientStateToStatus(client.state); diff --git a/src/lsp/commands/vscode-commands.ts b/src/lsp/commands/vscode-commands.ts index c069d616a..33aef0813 100644 --- a/src/lsp/commands/vscode-commands.ts +++ b/src/lsp/commands/vscode-commands.ts @@ -11,7 +11,7 @@ export const filterOutRootsWithClients = ( clients: defs.LspClientStore ) => { return uris.filter((root) => { - const client = clients.get(root.uri.path); + const client = clients.get(root.uri.fsPath); return !client || !api.clientIsAlive(client); }); }; @@ -27,7 +27,7 @@ type StartHandler = (uri: vscode.Uri) => Promise; */ const startHandler = async (clients: defs.LspClientStore, handler: StartHandler, uri?: string) => { if (uri) { - await handler(vscode.Uri.parse(uri)); + await handler(vscode.Uri.file(uri)); return; } @@ -64,9 +64,9 @@ const pickClient = async (clients: defs.LspClientStore) => { if (clients.size === 1) { return Array.from(clients.keys())[0]; } - const choices = Array.from(clients.keys()).map((uri) => { + const choices = Array.from(clients.keys()).map((id) => { return { - label: uri, + label: id, }; }); const selected_client = await vscode.window.showQuickPick(choices, { title: 'clojure-lsp' }); @@ -103,10 +103,11 @@ const restartHandler = async ( return; } + const client = clients.get(id); await stopClient(clients, id); clients.delete(id); - await startHandler(vscode.Uri.parse(id)); + await startHandler(client.uri); }; async function showServerInfo(clients: defs.LspClientStore, id: string) { @@ -180,7 +181,7 @@ const manageHandler = async ( } return { - label: `${icon} ${project_utils.getPathRelativeToWorkspace(vscode.Uri.parse(client.path))}`, + label: `${icon} ${project_utils.getPathRelativeToWorkspace(client.uri)}`, detail: api.isFallbackClient(client) ? 'Fallback client for serving workspaces or files that do not belong to a project' : undefined, @@ -300,7 +301,7 @@ const manageHandler = async ( switch (action.value) { case '::start': { - void start(vscode.Uri.parse(choice.value)); + void start(vscode.Uri.file(choice.value)); return; } case '::stop': { @@ -308,10 +309,7 @@ const manageHandler = async ( return; } case '::restart': { - await stopClient(clients, choice.value); - clients.delete(choice.value); - - void start(vscode.Uri.parse(choice.value)); + void restartHandler(clients, start, choice.value); return; } case '::info': { diff --git a/src/lsp/definitions.ts b/src/lsp/definitions.ts index 8cf6a5de1..6fab1ffd5 100644 --- a/src/lsp/definitions.ts +++ b/src/lsp/definitions.ts @@ -1,5 +1,6 @@ // This file contains type definitions related to the language server protocol import * as vscode_lsp from 'vscode-languageclient/node'; +import * as vscode from 'vscode'; export interface Position { line: number; @@ -46,7 +47,7 @@ export enum LspStatus { export type LspClient = { id: string; - path: string; + uri: vscode.Uri; client: vscode_lsp.LanguageClient; status: LspStatus; }; diff --git a/src/lsp/provider.ts b/src/lsp/provider.ts index 32b1f0660..9d24f4036 100644 --- a/src/lsp/provider.ts +++ b/src/lsp/provider.ts @@ -101,7 +101,7 @@ export const createClientProvider = (params: CreateClientProviderParams) => { }; let lsp_server_path: Promise | undefined = undefined; - const provisionClient = async (uri: vscode.Uri, id = uri.path) => { + const provisionClient = async (uri: vscode.Uri, id = uri.fsPath) => { if (lsp_server_path === undefined) { lsp_server_path = lsp_client.ensureLSPServer(params.context).catch((err) => { console.error('Failed to download lsp server', err); @@ -150,12 +150,20 @@ export const createClientProvider = (params: CreateClientProviderParams) => { }); }; + /** + * Provision a fallback lsp client in an OS temp directory to service any clojure files not part of any opened + * valid clojure projects. + * + * This logic has been disabled for now as it does not function correctly on Windows. This can be re-enabled + * once support for windows has been added. + */ const provisionFallbackClient = async () => { + return; const dir = path.join(os.tmpdir(), 'calva-clojure-lsp'); await fs.mkdir(dir, { recursive: true, }); - return provisionClient(vscode.Uri.parse(dir), api.FALLBACK_CLIENT_ID); + return provisionClient(vscode.Uri.file(dir), api.FALLBACK_CLIENT_ID); }; const provisionClientForOpenedDocument = async (document: vscode.TextDocument) => { @@ -181,10 +189,16 @@ export const createClientProvider = (params: CreateClientProviderParams) => { const provisionClientInFirstWorkspaceRoot = async () => { const folder = vscode.workspace.workspaceFolders[0]; - if (folder && (await project_utils.isValidClojureProject(folder.uri))) { - return provisionClient(folder.uri); + if (!folder) { + return; } - return provisionFallbackClient(); + return provisionClient(folder.uri); + + // TODO: Rather provision fallback client if not a valid clojure project: + // if (folder && (await project_utils.isValidClojureProject(folder.uri))) { + // return provisionClient(folder.uri); + // } + // return provisionFallbackClient(); }; return { diff --git a/src/project-root.ts b/src/project-root.ts index 19b0da91a..fd95edf02 100644 --- a/src/project-root.ts +++ b/src/project-root.ts @@ -69,9 +69,9 @@ export async function findProjectRootsWithReasons(params?: FindRootParams) { } const candidateUris = await vscode.workspace.findFiles(projectFilesGlob, excludeDirsGlob, 10000); const projectFilePaths = candidateUris.map((uri) => { - let dir = vscode.Uri.parse(path.dirname(uri.path)); + let dir = vscode.Uri.file(path.dirname(uri.path)); if (lspDirectories.find((file) => uri.path.endsWith(file))) { - dir = vscode.Uri.parse(path.join(uri.path, '../..')); + dir = vscode.Uri.file(path.join(uri.path, '../..')); } return { uri: dir,