From 8f6733c48584a621a5d3e57bed75dcb807fb151b Mon Sep 17 00:00:00 2001 From: Markus Moltke Date: Wed, 15 Nov 2023 13:58:41 +0100 Subject: [PATCH 1/3] fix: EPERM error on windows installation --- editors/vscode/src/main.ts | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/editors/vscode/src/main.ts b/editors/vscode/src/main.ts index 6c2489d3ce51..02a76929edc2 100644 --- a/editors/vscode/src/main.ts +++ b/editors/vscode/src/main.ts @@ -26,6 +26,9 @@ import { setContextValue } from "./utils"; import resolveImpl = require("resolve/async"); import { createRequire } from "module"; +import * as os from "os"; +import * as path from "path"; +import { copyFile } from "fs/promises"; import type * as resolve from "resolve"; const resolveAsync = promisify( @@ -74,12 +77,22 @@ export async function activate(context: ExtensionContext) { outputChannel.appendLine(`Using Biome from ${command}`); + const tmpDestination = path.resolve(os.tmpdir(), "./biome/biome.exe"); + + const copyFileToTmpFolder = () => { + outputChannel.appendLine(`Copying file to tmp folder: ${tmpDestination}`); + + copyFile(command, tmpDestination); + }; + + copyFileToTmpFolder(); + const statusBar = new StatusBar(); const serverOptions: ServerOptions = createMessageTransports.bind( undefined, outputChannel, - command, + tmpDestination, ); const documentSelector: DocumentFilter[] = [ @@ -134,6 +147,21 @@ export async function activate(context: ExtensionContext) { }), ); + workspace.onDidChangeWorkspaceFolders((event) => { + for (const folder of [...event.removed, ...event.added]) { + // Check that the path includes node_modules/@biomejs. No reason otherwise. + if (folder.uri.path.includes("node_modules/@biomejs")) { + if (client.isRunning()) { + client.stop(); + } + // Copy the new version to the tmpdir. + copyFileToTmpFolder(); + client.start(); + break; + } + } + }); + const handleActiveTextEditorChanged = (textEditor?: TextEditor) => { if (!textEditor) { statusBar.setActive(false); From 37519629f9641abb36a8e68c5f952e74666a9ece Mon Sep 17 00:00:00 2001 From: Markus Moltke Date: Wed, 15 Nov 2023 16:52:43 +0100 Subject: [PATCH 2/3] fix: onDidChangeWorkspaceFolders did not work for reloading --- editors/vscode/src/main.ts | 121 ++++++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/editors/vscode/src/main.ts b/editors/vscode/src/main.ts index 02a76929edc2..31e4c0d47452 100644 --- a/editors/vscode/src/main.ts +++ b/editors/vscode/src/main.ts @@ -6,10 +6,11 @@ import { ExtensionContext, OutputChannel, TextEditor, - Uri, languages, window, workspace, + RelativePattern, + Uri, } from "vscode"; import { DocumentFilter, @@ -28,8 +29,9 @@ import resolveImpl = require("resolve/async"); import { createRequire } from "module"; import * as os from "os"; import * as path from "path"; -import { copyFile } from "fs/promises"; +import { copyFileSync } from "fs"; import type * as resolve from "resolve"; +import { readFile } from "fs/promises"; const resolveAsync = promisify( resolveImpl, @@ -73,27 +75,9 @@ export async function activate(context: ExtensionContext) { "yourself and use it with this extension with the biome.lspBin setting", ); return; - } - - outputChannel.appendLine(`Using Biome from ${command}`); - - const tmpDestination = path.resolve(os.tmpdir(), "./biome/biome.exe"); - - const copyFileToTmpFolder = () => { - outputChannel.appendLine(`Copying file to tmp folder: ${tmpDestination}`); - - copyFile(command, tmpDestination); - }; - - copyFileToTmpFolder(); - - const statusBar = new StatusBar(); - - const serverOptions: ServerOptions = createMessageTransports.bind( - undefined, - outputChannel, - tmpDestination, - ); + } + + const statusBar = new StatusBar(); const documentSelector: DocumentFilter[] = [ { language: "javascript", scheme: "file" }, @@ -110,12 +94,70 @@ export async function activate(context: ExtensionContext) { traceOutputChannel, }; - client = new LanguageClient( - "biome_lsp", - "Biome", - serverOptions, - clientOptions, - ); + + const reloadClient = async () => { + outputChannel.appendLine(`Using Biome from ${command}`); + + let tmpDestination: string + + try { + tmpDestination = path.resolve(os.tmpdir(), "./biome.exe"); + outputChannel.appendLine(`Copying file to tmp folder: ${tmpDestination}`); + copyFileSync(command, tmpDestination); + } catch (error) { + if (error.code === 'EBUSY') { + // The file is busy, but attempt to use it anyways. + tmpDestination = path.resolve(os.tmpdir(), "./biome.exe"); + } else { + outputChannel.appendLine("Error copying file: " + error) + } + } + + const serverOptions: ServerOptions = createMessageTransports.bind( + undefined, + outputChannel, + tmpDestination ?? command, + ); + + client = new LanguageClient( + "biome_lsp", + "Biome", + serverOptions, + clientOptions, + ); + + context.subscriptions.push( + client.onDidChangeState((evt) => { + outputChannel.appendLine(evt.newState + ' ' + evt.oldState) + statusBar.setServerState(client, evt.newState); + }), + ); + } + + reloadClient() + + const watcher = workspace.createFileSystemWatcher("**/yarn.lock"); + context.subscriptions.push( + watcher.onDidChange(async() => { + try { + // When the lockfile changes, reload the biome executable. + outputChannel.appendLine('Reloading biome executable..'); + if (client.isRunning()) { + await client.stop(); + } + await reloadClient(); + + if (client.isRunning()) { + await client.restart(); + } else { + await client.start(); + } + } catch (error) { + outputChannel.appendLine('Reloading client failed: ' + error) + client.error("Reloading client failed", error, "force"); + } + }) + ) const session = new Session(context, client); @@ -141,27 +183,6 @@ export async function activate(context: ExtensionContext) { } }); - context.subscriptions.push( - client.onDidChangeState((evt) => { - statusBar.setServerState(client, evt.newState); - }), - ); - - workspace.onDidChangeWorkspaceFolders((event) => { - for (const folder of [...event.removed, ...event.added]) { - // Check that the path includes node_modules/@biomejs. No reason otherwise. - if (folder.uri.path.includes("node_modules/@biomejs")) { - if (client.isRunning()) { - client.stop(); - } - // Copy the new version to the tmpdir. - copyFileToTmpFolder(); - client.start(); - break; - } - } - }); - const handleActiveTextEditorChanged = (textEditor?: TextEditor) => { if (!textEditor) { statusBar.setActive(false); From af1591e0a2f1e136a45fcd7bd5d6246b6644cd8d Mon Sep 17 00:00:00 2001 From: Markus Moltke Date: Wed, 15 Nov 2023 23:52:27 +0100 Subject: [PATCH 3/3] fix: made cross platform & support multiple package managers --- editors/vscode/src/main.ts | 144 +++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 71 deletions(-) diff --git a/editors/vscode/src/main.ts b/editors/vscode/src/main.ts index 31e4c0d47452..02a1f0ef4534 100644 --- a/editors/vscode/src/main.ts +++ b/editors/vscode/src/main.ts @@ -1,16 +1,15 @@ import { type ChildProcess, spawn } from "child_process"; import { type Socket, connect } from "net"; import { dirname, isAbsolute } from "path"; -import { TextDecoder, promisify } from "util"; +import { promisify } from "util"; import { ExtensionContext, OutputChannel, TextEditor, + Uri, languages, window, workspace, - RelativePattern, - Uri, } from "vscode"; import { DocumentFilter, @@ -26,12 +25,11 @@ import { StatusBar } from "./statusBar"; import { setContextValue } from "./utils"; import resolveImpl = require("resolve/async"); +import { copyFileSync } from "fs"; import { createRequire } from "module"; import * as os from "os"; import * as path from "path"; -import { copyFileSync } from "fs"; import type * as resolve from "resolve"; -import { readFile } from "fs/promises"; const resolveAsync = promisify( resolveImpl, @@ -75,9 +73,9 @@ export async function activate(context: ExtensionContext) { "yourself and use it with this extension with the biome.lspBin setting", ); return; - } - - const statusBar = new StatusBar(); + } + + const statusBar = new StatusBar(); const documentSelector: DocumentFilter[] = [ { language: "javascript", scheme: "file" }, @@ -94,70 +92,74 @@ export async function activate(context: ExtensionContext) { traceOutputChannel, }; + const reloadClient = async () => { + outputChannel.appendLine(`Using Biome from ${command}`); + + let tmpDestination: string; + + try { + tmpDestination = path.resolve( + os.tmpdir(), + `./biome${process.platform === "win32" ? ".exe" : ""}`, + ); + outputChannel.appendLine(`Copying file to tmp folder: ${tmpDestination}`); + copyFileSync(command, tmpDestination); + } catch (error) { + if (error.code === "EBUSY") { + // The file is busy, but attempt to use it anyways. + tmpDestination = path.resolve( + os.tmpdir(), + `./biome${process.platform === "win32" ? ".exe" : ""}`, + ); + } else { + outputChannel.appendLine(`Error copying file: ${error}`); + } + } - const reloadClient = async () => { - outputChannel.appendLine(`Using Biome from ${command}`); - - let tmpDestination: string - - try { - tmpDestination = path.resolve(os.tmpdir(), "./biome.exe"); - outputChannel.appendLine(`Copying file to tmp folder: ${tmpDestination}`); - copyFileSync(command, tmpDestination); - } catch (error) { - if (error.code === 'EBUSY') { - // The file is busy, but attempt to use it anyways. - tmpDestination = path.resolve(os.tmpdir(), "./biome.exe"); - } else { - outputChannel.appendLine("Error copying file: " + error) - } - } - - const serverOptions: ServerOptions = createMessageTransports.bind( - undefined, - outputChannel, - tmpDestination ?? command, - ); - - client = new LanguageClient( - "biome_lsp", - "Biome", - serverOptions, - clientOptions, - ); - - context.subscriptions.push( - client.onDidChangeState((evt) => { - outputChannel.appendLine(evt.newState + ' ' + evt.oldState) - statusBar.setServerState(client, evt.newState); - }), - ); - } - - reloadClient() - - const watcher = workspace.createFileSystemWatcher("**/yarn.lock"); - context.subscriptions.push( - watcher.onDidChange(async() => { - try { - // When the lockfile changes, reload the biome executable. - outputChannel.appendLine('Reloading biome executable..'); - if (client.isRunning()) { - await client.stop(); - } - await reloadClient(); - - if (client.isRunning()) { - await client.restart(); - } else { - await client.start(); - } - } catch (error) { - outputChannel.appendLine('Reloading client failed: ' + error) - client.error("Reloading client failed", error, "force"); - } - }) - ) + const serverOptions: ServerOptions = createMessageTransports.bind( + undefined, + outputChannel, + tmpDestination ?? command, + ); + + client = new LanguageClient( + "biome_lsp", + "Biome", + serverOptions, + clientOptions, + ); + + context.subscriptions.push( + client.onDidChangeState((evt) => { + statusBar.setServerState(client, evt.newState); + }), + ); + }; + + reloadClient(); + + // Best way to determine package updates. Will work for npm, yarn, pnpm and bun. (Might work for more files also). + // It is not possible to listen node_modules, because it is usually gitignored. + const watcher = workspace.createFileSystemWatcher("**/*lock*"); + context.subscriptions.push( + watcher.onDidChange(async () => { + try { + // When the lockfile changes, reload the biome executable. + outputChannel.appendLine("Reloading biome executable.."); + if (client.isRunning()) { + await client.stop(); + } + await reloadClient(); + if (client.isRunning()) { + await client.restart(); + } else { + await client.start(); + } + } catch (error) { + outputChannel.appendLine(`Reloading client failed: ${error}`); + } + }), + ); const session = new Session(context, client);