From d7d8bfb564193721fa26f104de2e233fda613b5b Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Tue, 4 Jun 2024 09:17:58 +0900 Subject: [PATCH] perf(language-server): memoize possibly heavy IO utils --- server/package-lock.json | 11 ++++++++ server/package.json | 1 + server/src/server.ts | 15 ++--------- server/src/utils.ts | 55 ++++++++++++++++++++-------------------- 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 92599ac8e..01735059d 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "chokidar": "^3.5.1", + "memize": "^2.1.0", "vscode-jsonrpc": "^8.0.1", "vscode-languageserver": "^8.0.1", "vscode-languageserver-protocol": "^3.17.1" @@ -145,6 +146,11 @@ "node": ">=0.12.0" } }, + "node_modules/memize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.0.tgz", + "integrity": "sha512-yywVJy8ctVlN5lNPxsep5urnZ6TTclwPEyigM9M3Bi8vseJBOfqNrGWN/r8NzuIt3PovM323W04blJfGQfQSVg==" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -306,6 +312,11 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "memize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.0.tgz", + "integrity": "sha512-yywVJy8ctVlN5lNPxsep5urnZ6TTclwPEyigM9M3Bi8vseJBOfqNrGWN/r8NzuIt3PovM323W04blJfGQfQSVg==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", diff --git a/server/package.json b/server/package.json index 98e0a9cef..6382709e6 100644 --- a/server/package.json +++ b/server/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "chokidar": "^3.5.1", + "memize": "^2.1.0", "vscode-jsonrpc": "^8.0.1", "vscode-languageserver": "^8.0.1", "vscode-languageserver-protocol": "^3.17.1" diff --git a/server/src/server.ts b/server/src/server.ts index cfef4f572..9824da49c 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -74,17 +74,6 @@ let codeActionsFromDiagnostics: codeActions.filesCodeActions = {}; // will be properly defined later depending on the mode (stdio/node-rpc) let send: (msg: p.Message) => void = (_) => {}; -let findRescriptBinary = (projectRootPath: p.DocumentUri | null) => - config.extensionConfiguration.binaryPath == null - ? lookup.findFilePathFromProjectRoot( - projectRootPath, - path.join(c.nodeModulesBinDir, c.rescriptBinName) - ) - : utils.findBinary( - config.extensionConfiguration.binaryPath, - c.rescriptBinName - ); - let createInterfaceRequest = new v.RequestType< p.TextDocumentIdentifier, p.TextDocumentIdentifier, @@ -271,7 +260,7 @@ let openedFile = (fileUri: string, fileContent: string) => { // TODO: sometime stale .bsb.lock dangling. bsb -w knows .bsb.lock is // stale. Use that logic // TODO: close watcher when lang-server shuts down - if (findRescriptBinary(projectRootPath) != null) { + if (utils.findRescriptBinary(projectRootPath) != null) { let payload: clientSentBuildAction = { title: c.startBuildAction, projectRootPath: projectRootPath, @@ -1295,7 +1284,7 @@ function onMessage(msg: p.Message) { // TODO: close watcher when lang-server shuts down. However, by Node's // default, these subprocesses are automatically killed when this // language-server process exits - let rescriptBinaryPath = findRescriptBinary(projectRootPath); + let rescriptBinaryPath = utils.findRescriptBinary(projectRootPath); if (rescriptBinaryPath != null) { let bsbProcess = utils.runBuildWatcherUsingValidBuildPath( rescriptBinaryPath, diff --git a/server/src/utils.ts b/server/src/utils.ts index 8f952ffff..55242ab6c 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -1,13 +1,14 @@ -import * as childProcess from "child_process"; +import * as path from "node:path"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as childProcess from "node:child_process"; import * as p from "vscode-languageserver-protocol"; -import * as path from "path"; import * as t from "vscode-languageserver-types"; import { RequestMessage, ResponseMessage, } from "vscode-languageserver-protocol"; -import fs from "fs"; -import * as os from "os"; +import memo from "memize"; import * as codeActions from "./codeActions"; import * as c from "./constants"; @@ -26,7 +27,7 @@ export let createFileInTempDir = (extension = "") => { // TODO: races here? // TODO: this doesn't handle file:/// scheme -export let findProjectRootOfFile = ( +export let findProjectRootOfFile = memo(( source: p.DocumentUri ): null | p.DocumentUri => { let dir = path.dirname(source); @@ -43,10 +44,10 @@ export let findProjectRootOfFile = ( return findProjectRootOfFile(dir); } } -}; +}); // Check if binaryName exists inside binaryDirPath and return the joined path. -export let findBinary = ( +export let findBinary = memo(( binaryDirPath: p.DocumentUri | null, binaryName: string ): p.DocumentUri | null => { @@ -59,7 +60,21 @@ export let findBinary = ( } else { return null; } -}; +}); + +export let findRescriptBinary = memo(( + projectRootPath: p.DocumentUri | null +) => + config.extensionConfiguration.binaryPath == null + ? lookup.findFilePathFromProjectRoot( + projectRootPath, + path.join(c.nodeModulesBinDir, c.rescriptBinName) + ) + : findBinary( + config.extensionConfiguration.binaryPath, + c.rescriptBinName + ) +); type execResult = | { @@ -138,28 +153,14 @@ export let formatCode = ( } }; -let findReScriptVersion = (filePath: p.DocumentUri): string | undefined => { - let projectRoot = findProjectRootOfFile(filePath); - if (projectRoot == null) { - return undefined; - } - - let rescriptBinary = lookup.findFilePathFromProjectRoot( - projectRoot, - path.join(c.nodeModulesBinDir, c.rescriptBinName) - ); - - if (rescriptBinary == null) { - return undefined; - } - +let findReScriptVersion = memo((): string | undefined => { try { - let version = childProcess.execSync(`${rescriptBinary} -v`); - return version.toString().trim(); + let { version } = require('rescript/package.json'); + return version; } catch (e) { return undefined; } -}; +}); export let runAnalysisAfterSanityCheck = ( filePath: p.DocumentUri, @@ -179,7 +180,7 @@ export let runAnalysisAfterSanityCheck = ( if (projectRootPath == null && projectRequired) { return null; } - let rescriptVersion = findReScriptVersion(filePath); + let rescriptVersion = findReScriptVersion(); let options: childProcess.ExecFileSyncOptions = { cwd: projectRootPath || undefined, maxBuffer: Infinity,