Skip to content

Commit

Permalink
feat: Allow specifying a subdirectory in which to start Lexical (#66)
Browse files Browse the repository at this point in the history
* Add config to start Lexical in workspace sub-directory

* extract config helper

* add configuration tests

* update changelog
  • Loading branch information
Blond11516 authored Jan 16, 2024
1 parent 0b8d593 commit 5f135b1
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
},
Expand Down
31 changes: 26 additions & 5 deletions src/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
import { workspace } from "vscode";
import path = require("path");
import type { workspace as vsWorkspace } from "vscode";
import { URI } from "vscode-uri";

namespace Configuration {
export function getReleasePathOverride(): string | undefined {
return workspace
.getConfiguration("lexical.server")
.get("releasePathOverride");
type GetConfig = (section: string) => unknown;

export function getReleasePathOverride(
getConfig: GetConfig
): string | undefined {
return getConfig("releasePathOverride") as string | undefined;
}

export function getProjectDirUri(
getConfig: GetConfig,
workspace: typeof vsWorkspace
): URI {
const projectDirConfig = getConfig("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);
}
}
}

Expand Down
25 changes: 21 additions & 4 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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<void> {
const startScriptOrReleaseFolderPath = await maybeAutoInstall(context);
const projectDir = Configuration.getProjectDirUri(getConfig, workspace);

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));
Expand All @@ -38,7 +40,7 @@ export function deactivate(): void {}
async function maybeAutoInstall(
context: ExtensionContext
): Promise<string | undefined> {
const releasePathOverride = Configuration.getReleasePathOverride();
const releasePathOverride = Configuration.getReleasePathOverride(getConfig);

if (releasePathOverride !== undefined && releasePathOverride !== "") {
console.log(
Expand Down Expand Up @@ -69,9 +71,15 @@ function isExecutableFile(path: fs.PathLike): boolean {
}

async function start(
startScriptOrReleaseFolderPath: string
startScriptOrReleaseFolderPath: string,
workspaceUri: URI
): Promise<LanguageClient> {
const outputChannel = window.createOutputChannel("Lexical");

outputChannel.appendLine(
`Starting Lexical in directory ${workspaceUri?.fsPath}`
);

const startScriptPath = isExecutableFile(startScriptOrReleaseFolderPath)
? startScriptOrReleaseFolderPath
: join(startScriptOrReleaseFolderPath, "start_lexical.sh");
Expand All @@ -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(
Expand All @@ -115,3 +128,7 @@ async function start(

return client;
}

function getConfig(section: string) {
return workspace.getConfiguration("lexical.server").get(section);
}
30 changes: 30 additions & 0 deletions src/test/configuration.test.ts
Original file line number Diff line number Diff line change
@@ -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"));
});
});
13 changes: 13 additions & 0 deletions src/test/fixtures/workspace-fixture.ts
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 5f135b1

Please sign in to comment.