Skip to content

Commit

Permalink
chore: Separate Client into own class (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
phated authored Aug 31, 2023
1 parent 607a3d4 commit 1e5f9ef
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 145 deletions.
94 changes: 94 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { workspace, WorkspaceFolder, Uri, window, OutputChannel } from "vscode";

import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
TextDocumentFilter,
} from "vscode-languageclient/node";

import { extensionName, languageId } from "./constants";
import findNargo from "./find-nargo";

function globFromUri(uri: Uri, glob: string) {
// globs always need to use `/`
return `${uri.fsPath}${glob}`.replaceAll("\\", "/");
}

function getLspCommand(uri: Uri) {
let config = workspace.getConfiguration("noir", uri);

let lspEnabled = config.get<boolean>("enableLSP");

if (!lspEnabled) {
return;
}

let command = config.get<string | undefined>("nargoPath") || findNargo();

let flags = config.get<string | undefined>("nargoFlags") || "";

// Remove empty strings from the flags list
let args = ["lsp", ...flags.split(" ")].filter((arg) => arg !== "");

return [command, args] as const;
}

export default class Client extends LanguageClient {
#uri: Uri;
#command: string;
#args: string[];
#output: OutputChannel;

constructor(uri: Uri, workspaceFolder?: WorkspaceFolder) {
let outputChannel = window.createOutputChannel(extensionName, languageId);

let [command, args] = getLspCommand(uri);

let documentSelector: TextDocumentFilter[] = [];
if (workspaceFolder) {
documentSelector.push({
scheme: "file",
language: languageId,
// Glob starts with `/` because it just appends both segments
pattern: `${globFromUri(uri, "/**/*")}`,
});
} else {
documentSelector.push({
scheme: uri.scheme,
language: languageId,
pattern: `${globFromUri(uri, "")}`,
});
}

let clientOptions: LanguageClientOptions = {
documentSelector,
workspaceFolder,
outputChannel,
};

let serverOptions: ServerOptions = {
command,
args,
};

super(languageId, extensionName, serverOptions, clientOptions);

this.#uri = uri;
this.#command = command;
this.#args = args;
this.#output = outputChannel;
}

async start(): Promise<void> {
let command = this.#command;
let args = this.#args.join(" ");
this.info(`Starting LSP client using command: ${command} ${args}`);

await super.start();
}

async dispose(timeout?: number): Promise<void> {
await super.dispose(timeout);
}
}
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export let extensionName = "Noir Language Server";

export let languageId = "noir";
163 changes: 18 additions & 145 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
WorkspaceFoldersChangeEvent,
ConfigurationChangeEvent,
Uri,
window,
tasks,
Task,
TaskScope,
Expand All @@ -36,23 +35,12 @@ import {
ProcessExecution,
} from "vscode";

import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
} from "vscode-languageclient/node";

import which from "which";

let extensionName = "Noir Language Server";

let languageId = "noir";

let outputChannel = window.createOutputChannel(extensionName, languageId);
import { languageId } from "./constants";
import Client from "./client";
import findNargo from "./find-nargo";

let activeCommands: Map<string, Disposable> = new Map();
let fileClients: Map<string, LanguageClient> = new Map();
let workspaceClients: Map<string, LanguageClient> = new Map();
let clients: Map<string, Client> = new Map();

let activeMutex: Set<string> = new Set();

Expand All @@ -75,21 +63,6 @@ function mutex(key: string, fn: (...args: unknown[]) => Promise<void>) {
};
}

const nargoBinaries = ["nargo"];

function findNargo() {
for (const bin of nargoBinaries) {
try {
const nargo = which.sync(bin);
// If it didn't throw, we found a nargo binary
return nargo;
} catch (err) {
// Not found
}
}
throw new Error("Unable to locate any nargo binary. Did you install it?");
}

function dirpathFromUri(uri: Uri): string {
let dirPath = uri.toString();
if (!dirPath.endsWith("/")) {
Expand All @@ -98,11 +71,6 @@ function dirpathFromUri(uri: Uri): string {
return dirPath;
}

function globFromUri(uri: Uri, glob: string) {
// globs always need to use `/`
return `${uri.fsPath}${glob}`.replaceAll("\\", "/");
}

let workspaceFolders: string[] = [];
function sortWorkspaceFolders(): string[] {
if (workspaceFolders.length === 0) {
Expand All @@ -128,25 +96,6 @@ function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder {
return folder;
}

function getLspCommand(uri: Uri) {
let config = workspace.getConfiguration("noir", uri);

let lspEnabled = config.get<boolean>("enableLSP");

if (!lspEnabled) {
return;
}

let command = config.get<string | undefined>("nargoPath") || findNargo();

let flags = config.get<string | undefined>("nargoFlags") || "";

// Remove empty strings from the flags list
let args = ["lsp", ...flags.split(" ")].filter((arg) => arg !== "");

return [command, args] as const;
}

let INTERNAL_COMMANDS = [
{ type: "nargo", command: "test", group: TaskGroup.Test },
{ type: "nargo", command: "compile", group: TaskGroup.Build },
Expand Down Expand Up @@ -218,119 +167,46 @@ function disposeWorkspaceCommands(workspaceFolder: WorkspaceFolder) {
disposeCommands(workspaceFolder.uri);
}

async function startFileClient(uri: Uri) {
let [command, args] = getLspCommand(uri);

let clientOptions = {
documentSelector: [
{
scheme: uri.scheme,
language: languageId,
pattern: `${globFromUri(uri, "")}`,
},
],
outputChannel,
};

let serverOptions: ServerOptions = {
command,
args,
};

let client = new LanguageClient(
languageId,
extensionName,
serverOptions,
clientOptions
);

client.info(
`Starting LSP client using command: ${command} ${args.join(" ")}`
);

await client.start();

return client;
}

async function addFileClient(uri: Uri) {
let file = uri.toString();
if (!fileClients.has(file)) {
if (!clients.has(file)) {
// Start the client. This will also launch the server
let client = await startFileClient(uri);
fileClients.set(file, client);
let client = new Client(uri);
clients.set(file, client);
await client.start();
}
}

async function removeFileClient(uri: Uri) {
let file = uri.toString();
let client = fileClients.get(file);
let client = clients.get(file);
if (client) {
await client.stop();
fileClients.delete(file);
clients.delete(file);
}
}

async function startWorkspaceClient(workspaceFolder: WorkspaceFolder) {
let [command, args] = getLspCommand(workspaceFolder.uri);

let clientOptions: LanguageClientOptions = {
documentSelector: [
{
scheme: "file",
language: languageId,
// Glob starts with `/` because it just appends both segments
pattern: `${globFromUri(workspaceFolder.uri, "/**/*")}`,
},
],
workspaceFolder,
outputChannel,
};

let serverOptions: ServerOptions = {
command,
args,
};

let client = new LanguageClient(
languageId,
extensionName,
serverOptions,
clientOptions
);

client.info(
`Starting LSP client using command: ${command} ${args.join(" ")}`
);

await client.start();

return client;
}

async function addWorkspaceClient(workspaceFolder: WorkspaceFolder) {
let workspacePath = workspaceFolder.uri.toString();
if (!workspaceClients.has(workspacePath)) {
if (!clients.has(workspacePath)) {
// Start the client. This will also launch the server
let client = await startWorkspaceClient(workspaceFolder);
workspaceClients.set(workspacePath, client);
let client = new Client(workspaceFolder.uri, workspaceFolder);
clients.set(workspacePath, client);
await client.start();
}
}

async function removeWorkspaceClient(workspaceFolder: WorkspaceFolder) {
let workspacePath = workspaceFolder.uri.toString();
let client = workspaceClients.get(workspacePath);
let client = clients.get(workspacePath);
if (client) {
await client.stop();
workspaceClients.delete(workspacePath);
clients.delete(workspacePath);
}
}

async function restartAllClients() {
for (let client of fileClients.values()) {
await client.restart();
}
for (let client of workspaceClients.values()) {
for (let client of clients.values()) {
await client.restart();
}
}
Expand Down Expand Up @@ -449,10 +325,7 @@ export async function activate(context: ExtensionContext): Promise<void> {
}

export async function deactivate(): Promise<void> {
for (let client of fileClients.values()) {
await client.stop();
}
for (let client of workspaceClients.values()) {
for (let client of clients.values()) {
await client.stop();
}
}
16 changes: 16 additions & 0 deletions src/find-nargo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import which from "which";

const nargoBinaries = ["nargo"];

export default function findNargo() {
for (const bin of nargoBinaries) {
try {
const nargo = which.sync(bin);
// If it didn't throw, we found a nargo binary
return nargo;
} catch (err) {
// Not found
}
}
throw new Error("Unable to locate any nargo binary. Did you install it?");
}

0 comments on commit 1e5f9ef

Please sign in to comment.