From 5e5af67a80983bd175bd4c95da6914eea39b43d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20L=C3=A9vesque?= Date: Wed, 10 Jan 2024 16:10:24 -0500 Subject: [PATCH 1/4] Add config to start Lexical in workspace sub-directory --- package.json | 5 +++++ src/configuration.ts | 18 ++++++++++++++++++ src/extension.ts | 17 +++++++++++++++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 07eb0ff..d34c7bb 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,11 @@ "scope": "window", "type": "string", "markdownDescription": "The path to the lexical executable. The path should point to a folder containing the `start_lexical.sh` or an executable file. Note that setting this to any value will disable automatic installation of Lexical." + }, + "lexical.server.projectDir": { + "scope": "window", + "type": "string", + "markdownDescription": "A subdirectory of the current workspace in which to start Lexical." } } }, diff --git a/src/configuration.ts b/src/configuration.ts index b43d94a..c7cae78 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -1,4 +1,6 @@ +import path = require("path"); import { workspace } from "vscode"; +import { URI } from "vscode-uri"; namespace Configuration { export function getReleasePathOverride(): string | undefined { @@ -6,6 +8,22 @@ namespace Configuration { .getConfiguration("lexical.server") .get("releasePathOverride"); } + + export function getProjectDirUri(): URI { + const projectDirConfig = workspace + .getConfiguration("lexical.server") + .get("projectDir"); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const workspacePath = workspace.workspaceFolders![0].uri.path; + + if (typeof projectDirConfig === "string") { + const fullDirectoryPath = path.join(workspacePath, projectDirConfig); + return URI.file(fullDirectoryPath); + } else { + return URI.file(workspacePath); + } + } } export default Configuration; diff --git a/src/extension.ts b/src/extension.ts index 8b26fcd..b3ae3a9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,14 +12,16 @@ import { join } from "path"; import * as fs from "fs"; import Commands from "./commands"; import restartServer from "./commands/restart-server"; +import { URI } from "vscode-uri"; // This method is called when your extension is activated // Your extension is activated the very first time the command is executed export async function activate(context: ExtensionContext): Promise { const startScriptOrReleaseFolderPath = await maybeAutoInstall(context); + const projectDir = Configuration.getProjectDirUri(); if (startScriptOrReleaseFolderPath !== undefined) { - const client = await start(startScriptOrReleaseFolderPath); + const client = await start(startScriptOrReleaseFolderPath, projectDir); const registerCommand = Commands.getRegisterFunction((id, handler) => { context.subscriptions.push(commands.registerCommand(id, handler)); @@ -69,9 +71,15 @@ function isExecutableFile(path: fs.PathLike): boolean { } async function start( - startScriptOrReleaseFolderPath: string + startScriptOrReleaseFolderPath: string, + workspaceUri: URI ): Promise { const outputChannel = window.createOutputChannel("Lexical"); + + outputChannel.appendLine( + `Starting Lexical in directory ${workspaceUri?.fsPath}` + ); + const startScriptPath = isExecutableFile(startScriptOrReleaseFolderPath) ? startScriptOrReleaseFolderPath : join(startScriptOrReleaseFolderPath, "start_lexical.sh"); @@ -94,6 +102,11 @@ async function start( { language: "phoenix-heex", scheme: "file" }, { language: "phoenix-heex", scheme: "untitled" }, ], + workspaceFolder: { + index: 0, + uri: workspaceUri, + name: workspaceUri.path, + }, }; const client = new LanguageClient( From 6b98b861a2b3eafaf3f2c329e59c4afb6cf7257d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20L=C3=A9vesque?= Date: Wed, 10 Jan 2024 16:11:35 -0500 Subject: [PATCH 2/4] extract config helper --- src/configuration.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index c7cae78..924e2fb 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -4,15 +4,11 @@ import { URI } from "vscode-uri"; namespace Configuration { export function getReleasePathOverride(): string | undefined { - return workspace - .getConfiguration("lexical.server") - .get("releasePathOverride"); + return getConfig("releasePathOverride") as string | undefined; } export function getProjectDirUri(): URI { - const projectDirConfig = workspace - .getConfiguration("lexical.server") - .get("projectDir"); + const projectDirConfig = getConfig("projectDir"); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const workspacePath = workspace.workspaceFolders![0].uri.path; @@ -24,6 +20,10 @@ namespace Configuration { return URI.file(workspacePath); } } + + function getConfig(section: string) { + return workspace.getConfiguration("lexical.server").get(section); + } } export default Configuration; From d2152292b0cf76a5843d6490369850f68071e825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20L=C3=A9vesque?= Date: Wed, 10 Jan 2024 16:43:56 -0500 Subject: [PATCH 3/4] add configuration tests --- src/configuration.ts | 17 +++++++++------ src/extension.ts | 10 ++++++--- src/test/configuration.test.ts | 30 ++++++++++++++++++++++++++ src/test/fixtures/workspace-fixture.ts | 13 +++++++++++ 4 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 src/test/configuration.test.ts create mode 100644 src/test/fixtures/workspace-fixture.ts diff --git a/src/configuration.ts b/src/configuration.ts index 924e2fb..024442d 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -1,13 +1,20 @@ import path = require("path"); -import { workspace } from "vscode"; +import type { workspace as vsWorkspace } from "vscode"; import { URI } from "vscode-uri"; namespace Configuration { - export function getReleasePathOverride(): string | undefined { + type GetConfig = (section: string) => unknown; + + export function getReleasePathOverride( + getConfig: GetConfig + ): string | undefined { return getConfig("releasePathOverride") as string | undefined; } - export function getProjectDirUri(): URI { + export function getProjectDirUri( + getConfig: GetConfig, + workspace: typeof vsWorkspace + ): URI { const projectDirConfig = getConfig("projectDir"); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -20,10 +27,6 @@ namespace Configuration { return URI.file(workspacePath); } } - - function getConfig(section: string) { - return workspace.getConfiguration("lexical.server").get(section); - } } export default Configuration; diff --git a/src/extension.ts b/src/extension.ts index b3ae3a9..8a27097 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,6 +1,6 @@ // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below -import { ExtensionContext, commands, window } from "vscode"; +import { ExtensionContext, commands, window, workspace } from "vscode"; import LanguageServer from "./language-server"; import Configuration from "./configuration"; import { @@ -18,7 +18,7 @@ import { URI } from "vscode-uri"; // Your extension is activated the very first time the command is executed export async function activate(context: ExtensionContext): Promise { const startScriptOrReleaseFolderPath = await maybeAutoInstall(context); - const projectDir = Configuration.getProjectDirUri(); + const projectDir = Configuration.getProjectDirUri(getConfig, workspace); if (startScriptOrReleaseFolderPath !== undefined) { const client = await start(startScriptOrReleaseFolderPath, projectDir); @@ -40,7 +40,7 @@ export function deactivate(): void {} async function maybeAutoInstall( context: ExtensionContext ): Promise { - const releasePathOverride = Configuration.getReleasePathOverride(); + const releasePathOverride = Configuration.getReleasePathOverride(getConfig); if (releasePathOverride !== undefined && releasePathOverride !== "") { console.log( @@ -128,3 +128,7 @@ async function start( return client; } + +function getConfig(section: string) { + return workspace.getConfiguration("lexical.server").get(section); +} diff --git a/src/test/configuration.test.ts b/src/test/configuration.test.ts new file mode 100644 index 0000000..d8cc752 --- /dev/null +++ b/src/test/configuration.test.ts @@ -0,0 +1,30 @@ +import { describe, test, jest, expect } from "@jest/globals"; +import Configuration from "../configuration"; +import { URI } from "vscode-uri"; +import WorkspaceFixture from "./fixtures/workspace-fixture"; + +describe("Configuration", () => { + test("getProjectDirUri returns the workspace URI when project dir is not configured", () => { + const getConfigMock = jest.fn().mockReturnValue(undefined); + const workspace = WorkspaceFixture.withUri(URI.file("/stub")); + const projectDirUri = Configuration.getProjectDirUri( + getConfigMock, + workspace + ); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + expect(projectDirUri).toEqual(workspace.workspaceFolders![0].uri); + }); + + test("getProjectDirUri returns the full directory URI when project dir is configured", () => { + const getConfigMock = jest.fn().mockReturnValue("subdirectory"); + const workspace = WorkspaceFixture.withUri(URI.file("/stub")); + const projectDirUri = Configuration.getProjectDirUri( + getConfigMock, + workspace + ); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + expect(projectDirUri).toEqual(URI.file("/stub/subdirectory")); + }); +}); diff --git a/src/test/fixtures/workspace-fixture.ts b/src/test/fixtures/workspace-fixture.ts new file mode 100644 index 0000000..056ac2d --- /dev/null +++ b/src/test/fixtures/workspace-fixture.ts @@ -0,0 +1,13 @@ +import { URI } from "vscode-uri"; + +import type { workspace as vsWorkspace } from "vscode"; + +namespace WorkspaceFixture { + export function withUri(uri: URI): typeof vsWorkspace { + return { + workspaceFolders: [{ uri }], + } as unknown as typeof vsWorkspace; + } +} + +export default WorkspaceFixture; From c1ef6a9ad1ac8aabf92d0f08141e75972cfa2870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20L=C3=A9vesque?= Date: Wed, 10 Jan 2024 16:47:08 -0500 Subject: [PATCH 4/4] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8ace6c..0f09942 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ if it isn't already running. Useful to quickly restart Lexical if initial start failed due to environment reasons, like an incompatible version of Erlang or Elixir. +- Added the `projectDir` configuration option which lets users specify a + subdirectory in which Lexical should be started instead of the workspace root. ### Documentation