From 04b940b7102e0568cd51ddd55ffd149ffbb3a7f5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 31 May 2024 22:47:22 +0200 Subject: [PATCH 01/10] cache project config on demand --- analysis/bin/main.ml | 5 + analysis/src/Cache.ml | 39 +++++ analysis/src/Cfg.ml | 8 + analysis/src/Packages.ml | 318 ++++++++++++++++++++------------------- 4 files changed, 212 insertions(+), 158 deletions(-) create mode 100644 analysis/src/Cache.ml diff --git a/analysis/bin/main.ml b/analysis/bin/main.ml index c8af54330..7df0d0c6c 100644 --- a/analysis/bin/main.ml +++ b/analysis/bin/main.ml @@ -110,6 +110,11 @@ let main () = path line col in match args with + | [_; "cache-project"; rootPath] -> ( + let uri = Uri.fromPath rootPath in + match Packages.getPackage ~uri with + | Some package -> Cache.cacheProject package + | None -> ()) | [_; "completion"; path; line; col; currentFile] -> printHeaderInfo path line col; Commands.completion ~debug ~path diff --git a/analysis/src/Cache.ml b/analysis/src/Cache.ml new file mode 100644 index 000000000..62dd8b921 --- /dev/null +++ b/analysis/src/Cache.ml @@ -0,0 +1,39 @@ +open SharedTypes + +type cached = { + projectFiles: FileSet.t; + dependenciesFiles: FileSet.t; + pathsForModule: (file, paths) Hashtbl.t; +} + +let writeCache filename (data : cached) = + let oc = open_out_bin filename in + Marshal.to_channel oc data []; + close_out oc + +let readCache filename = + if !Cfg.useProjectConfigCache && Sys.file_exists filename then + try + let ic = open_in_bin filename in + let data : cached = Marshal.from_channel ic in + close_in ic; + Some data + with _ -> None + else None + +let targetFileFromLibBs libBs = Filename.concat libBs ".project-files-cache" + +let cacheProject (package : package) = + let cached = + { + projectFiles = package.projectFiles; + dependenciesFiles = package.dependenciesFiles; + pathsForModule = package.pathsForModule; + } + in + match BuildSystem.getLibBs package.rootPath with + | None -> () + | Some libBs -> + let targetFile = targetFileFromLibBs libBs in + writeCache targetFile cached; + print_endline "OK" diff --git a/analysis/src/Cfg.ml b/analysis/src/Cfg.ml index cf06b28d2..4c19f6033 100644 --- a/analysis/src/Cfg.ml +++ b/analysis/src/Cfg.ml @@ -9,3 +9,11 @@ let inIncrementalTypecheckingMode = | "true" -> true | _ -> false with _ -> false) + +let useProjectConfigCache = + ref + (try + match Sys.getenv "RESCRIPT_PROJECT_CONFIG_CACHE" with + | "true" -> true + | _ -> false + with _ -> false) diff --git a/analysis/src/Packages.ml b/analysis/src/Packages.ml index c3fd7d691..cd2d3b528 100644 --- a/analysis/src/Packages.ml +++ b/analysis/src/Packages.ml @@ -44,165 +44,167 @@ let newBsPackage ~rootPath = in match Json.parse raw with | Some config -> ( - match FindFiles.findDependencyFiles rootPath config with - | None -> None - | Some (dependencyDirectories, dependenciesFilesAndPaths) -> ( - match libBs with + let namespace = FindFiles.getNamespace config in + let rescriptVersion = getReScriptVersion () in + let suffix = + match config |> Json.get "suffix" with + | Some (String suffix) -> suffix + | _ -> ".js" + in + let uncurried = + let ns = config |> Json.get "uncurried" in + match (rescriptVersion, ns) with + | (major, _), None when major >= 11 -> Some true + | _, ns -> Option.bind ns Json.bool + in + let genericJsxModule = + let jsxConfig = config |> Json.get "jsx" in + match jsxConfig with + | Some jsxConfig -> ( + match jsxConfig |> Json.get "module" with + | Some (String m) when String.lowercase_ascii m <> "react" -> Some m + | _ -> None) | None -> None - | Some libBs -> - Some - (let namespace = FindFiles.getNamespace config in - let rescriptVersion = getReScriptVersion () in - let suffix = - match config |> Json.get "suffix" with - | Some (String suffix) -> suffix - | _ -> ".js" - in - let uncurried = - let ns = config |> Json.get "uncurried" in - match (rescriptVersion, ns) with - | (major, _), None when major >= 11 -> Some true - | _, ns -> Option.bind ns Json.bool - in - let genericJsxModule = - let jsxConfig = config |> Json.get "jsx" in - match jsxConfig with - | Some jsxConfig -> ( - match jsxConfig |> Json.get "module" with - | Some (String m) when String.lowercase_ascii m <> "react" -> - Some m - | _ -> None) - | None -> None - in - let uncurried = uncurried = Some true in - let sourceDirectories = - FindFiles.getSourceDirectories ~includeDev:true ~baseDir:rootPath - config - in - let projectFilesAndPaths = - FindFiles.findProjectFiles - ~public:(FindFiles.getPublic config) - ~namespace ~path:rootPath ~sourceDirectories ~libBs - in - projectFilesAndPaths - |> List.iter (fun (_name, paths) -> Log.log (showPaths paths)); - let pathsForModule = - makePathsForModule ~projectFilesAndPaths - ~dependenciesFilesAndPaths - in - let opens_from_namespace = - match namespace with - | None -> [] - | Some namespace -> - let cmt = Filename.concat libBs namespace ^ ".cmt" in - Log.log - ("############ Namespaced as " ^ namespace ^ " at " ^ cmt); - Hashtbl.add pathsForModule namespace (Namespace {cmt}); - let path = [FindFiles.nameSpaceToName namespace] in - [path] - in - Log.log - ("Dependency dirs: " - ^ String.concat " " - (dependencyDirectories |> List.map Utils.dumpPath)); - let opens_from_bsc_flags = - let bind f x = Option.bind x f in - match Json.get "bsc-flags" config |> bind Json.array with - | Some l -> - List.fold_left - (fun opens item -> - match item |> Json.string with - | None -> opens - | Some s -> ( - let parts = String.split_on_char ' ' s in - match parts with - | "-open" :: name :: _ -> - let path = name |> String.split_on_char '.' in - path :: opens - | _ -> opens)) - [] l - | None -> [] - in - let opens = - [ - (if uncurried then "PervasivesU" else "Pervasives"); - "JsxModules"; - ] - :: opens_from_namespace - |> List.rev_append opens_from_bsc_flags - |> List.map (fun path -> path @ ["place holder"]) - in - Log.log - ("Opens from ReScript config file: " - ^ (opens |> List.map pathToString |> String.concat " ")); - { - genericJsxModule; - suffix; - rescriptVersion; - rootPath; - projectFiles = - projectFilesAndPaths |> List.map fst |> FileSet.of_list; - dependenciesFiles = - dependenciesFilesAndPaths |> List.map fst |> FileSet.of_list; - pathsForModule; - opens; - namespace; - builtInCompletionModules = - (if - opens_from_bsc_flags - |> List.find_opt (fun opn -> - match opn with - | ["RescriptCore"] -> true - | _ -> false) - |> Option.is_some - then - { - arrayModulePath = ["Array"]; - optionModulePath = ["Option"]; - stringModulePath = ["String"]; - intModulePath = ["Int"]; - floatModulePath = ["Float"]; - promiseModulePath = ["Promise"]; - listModulePath = ["List"]; - resultModulePath = ["Result"]; - exnModulePath = ["Exn"]; - regexpModulePath = ["RegExp"]; - } - else if - opens_from_bsc_flags - |> List.find_opt (fun opn -> - match opn with - | ["Belt"] -> true - | _ -> false) - |> Option.is_some - then - { - arrayModulePath = ["Array"]; - optionModulePath = ["Option"]; - stringModulePath = ["Js"; "String2"]; - intModulePath = ["Int"]; - floatModulePath = ["Float"]; - promiseModulePath = ["Js"; "Promise"]; - listModulePath = ["List"]; - resultModulePath = ["Result"]; - exnModulePath = ["Js"; "Exn"]; - regexpModulePath = ["Js"; "Re"]; - } - else - { - arrayModulePath = ["Js"; "Array2"]; - optionModulePath = ["Belt"; "Option"]; - stringModulePath = ["Js"; "String2"]; - intModulePath = ["Belt"; "Int"]; - floatModulePath = ["Belt"; "Float"]; - promiseModulePath = ["Js"; "Promise"]; - listModulePath = ["Belt"; "List"]; - resultModulePath = ["Belt"; "Result"]; - exnModulePath = ["Js"; "Exn"]; - regexpModulePath = ["Js"; "Re"]; - }); - uncurried; - }))) + in + let uncurried = uncurried = Some true in + match libBs with + | None -> None + | Some libBs -> + let cached = Cache.readCache (Cache.targetFileFromLibBs libBs) in + let projectFiles, dependenciesFiles, pathsForModule = + match cached with + | Some cached -> + ( cached.projectFiles, + cached.dependenciesFiles, + cached.pathsForModule ) + | None -> + let dependenciesFilesAndPaths = + match FindFiles.findDependencyFiles rootPath config with + | None -> [] + | Some (_dependencyDirectories, dependenciesFilesAndPaths) -> + dependenciesFilesAndPaths + in + let sourceDirectories = + FindFiles.getSourceDirectories ~includeDev:true ~baseDir:rootPath + config + in + let projectFilesAndPaths = + FindFiles.findProjectFiles + ~public:(FindFiles.getPublic config) + ~namespace ~path:rootPath ~sourceDirectories ~libBs + in + let pathsForModule = + makePathsForModule ~projectFilesAndPaths + ~dependenciesFilesAndPaths + in + let projectFiles = + projectFilesAndPaths |> List.map fst |> FileSet.of_list + in + let dependenciesFiles = + dependenciesFilesAndPaths |> List.map fst |> FileSet.of_list + in + (projectFiles, dependenciesFiles, pathsForModule) + in + Some + (let opens_from_namespace = + match namespace with + | None -> [] + | Some namespace -> + let cmt = Filename.concat libBs namespace ^ ".cmt" in + Hashtbl.add pathsForModule namespace (Namespace {cmt}); + let path = [FindFiles.nameSpaceToName namespace] in + [path] + in + let opens_from_bsc_flags = + let bind f x = Option.bind x f in + match Json.get "bsc-flags" config |> bind Json.array with + | Some l -> + List.fold_left + (fun opens item -> + match item |> Json.string with + | None -> opens + | Some s -> ( + let parts = String.split_on_char ' ' s in + match parts with + | "-open" :: name :: _ -> + let path = name |> String.split_on_char '.' in + path :: opens + | _ -> opens)) + [] l + | None -> [] + in + let opens = + [(if uncurried then "PervasivesU" else "Pervasives"); "JsxModules"] + :: opens_from_namespace + |> List.rev_append opens_from_bsc_flags + |> List.map (fun path -> path @ ["place holder"]) + in + { + genericJsxModule; + suffix; + rescriptVersion; + rootPath; + projectFiles; + dependenciesFiles; + pathsForModule; + opens; + namespace; + builtInCompletionModules = + (if + opens_from_bsc_flags + |> List.find_opt (fun opn -> + match opn with + | ["RescriptCore"] -> true + | _ -> false) + |> Option.is_some + then + { + arrayModulePath = ["Array"]; + optionModulePath = ["Option"]; + stringModulePath = ["String"]; + intModulePath = ["Int"]; + floatModulePath = ["Float"]; + promiseModulePath = ["Promise"]; + listModulePath = ["List"]; + resultModulePath = ["Result"]; + exnModulePath = ["Exn"]; + regexpModulePath = ["RegExp"]; + } + else if + opens_from_bsc_flags + |> List.find_opt (fun opn -> + match opn with + | ["Belt"] -> true + | _ -> false) + |> Option.is_some + then + { + arrayModulePath = ["Array"]; + optionModulePath = ["Option"]; + stringModulePath = ["Js"; "String2"]; + intModulePath = ["Int"]; + floatModulePath = ["Float"]; + promiseModulePath = ["Js"; "Promise"]; + listModulePath = ["List"]; + resultModulePath = ["Result"]; + exnModulePath = ["Js"; "Exn"]; + regexpModulePath = ["Js"; "Re"]; + } + else + { + arrayModulePath = ["Js"; "Array2"]; + optionModulePath = ["Belt"; "Option"]; + stringModulePath = ["Js"; "String2"]; + intModulePath = ["Belt"; "Int"]; + floatModulePath = ["Belt"; "Float"]; + promiseModulePath = ["Js"; "Promise"]; + listModulePath = ["Belt"; "List"]; + resultModulePath = ["Belt"; "Result"]; + exnModulePath = ["Js"; "Exn"]; + regexpModulePath = ["Js"; "Re"]; + }); + uncurried; + })) | None -> None in From 9f4cb47cda7cc7b170469353668b74f57393002e Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 1 Jun 2024 13:03:30 +0200 Subject: [PATCH 02/10] e2e for the new cache mode --- analysis/bin/main.ml | 14 +++++++++- analysis/src/Cache.ml | 6 +++-- analysis/src/Packages.ml | 2 +- client/src/extension.ts | 3 ++- package.json | 5 ++++ server/src/config.ts | 12 ++++++++- server/src/constants.ts | 1 + server/src/server.ts | 57 ++++++++++++++++++++++++++++++++++------ server/src/utils.ts | 4 +++ 9 files changed, 90 insertions(+), 14 deletions(-) diff --git a/analysis/bin/main.ml b/analysis/bin/main.ml index 7df0d0c6c..cdc8c0b29 100644 --- a/analysis/bin/main.ml +++ b/analysis/bin/main.ml @@ -111,10 +111,22 @@ let main () = in match args with | [_; "cache-project"; rootPath] -> ( + Cfg.useProjectConfigCache := false; let uri = Uri.fromPath rootPath in match Packages.getPackage ~uri with | Some package -> Cache.cacheProject package - | None -> ()) + | None -> print_endline "\"ERR\"") + | [_; "cache-delete"; rootPath] -> ( + Cfg.useProjectConfigCache := false; + let uri = Uri.fromPath rootPath in + match Packages.findRoot ~uri (Hashtbl.create 0) with + | Some (`Bs rootPath) -> ( + match BuildSystem.getLibBs rootPath with + | None -> print_endline "\"ERR\"" + | Some libBs -> + Cache.deleteCache (Cache.targetFileFromLibBs libBs); + print_endline "\"OK\"") + | _ -> print_endline "\"ERR: Did not find root \"") | [_; "completion"; path; line; col; currentFile] -> printHeaderInfo path line col; Commands.completion ~debug ~path diff --git a/analysis/src/Cache.ml b/analysis/src/Cache.ml index 62dd8b921..90463408e 100644 --- a/analysis/src/Cache.ml +++ b/analysis/src/Cache.ml @@ -21,6 +21,8 @@ let readCache filename = with _ -> None else None +let deleteCache filename = try Sys.remove filename with _ -> () + let targetFileFromLibBs libBs = Filename.concat libBs ".project-files-cache" let cacheProject (package : package) = @@ -32,8 +34,8 @@ let cacheProject (package : package) = } in match BuildSystem.getLibBs package.rootPath with - | None -> () + | None -> print_endline "\"ERR\"" | Some libBs -> let targetFile = targetFileFromLibBs libBs in writeCache targetFile cached; - print_endline "OK" + print_endline "\"OK\"" diff --git a/analysis/src/Packages.ml b/analysis/src/Packages.ml index cd2d3b528..59021ce9e 100644 --- a/analysis/src/Packages.ml +++ b/analysis/src/Packages.ml @@ -231,7 +231,7 @@ let findRoot ~uri packagesByRoot = let parent = Filename.dirname path in if parent = path then (* reached root *) None else loop parent in - loop (Filename.dirname path) + loop (if Sys.is_directory path then path else Filename.dirname path) let getPackage ~uri = let open SharedTypes in diff --git a/client/src/extension.ts b/client/src/extension.ts index 08bcfd1fa..350d88718 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -341,7 +341,8 @@ export function activate(context: ExtensionContext) { affectsConfiguration("rescript.settings.inlayHints") || affectsConfiguration("rescript.settings.codeLens") || affectsConfiguration("rescript.settings.signatureHelp") || - affectsConfiguration("rescript.settings.incrementalTypechecking") + affectsConfiguration("rescript.settings.incrementalTypechecking") || + affectsConfiguration("rescript.settings.cache") ) { commands.executeCommand("rescript-vscode.restart_language_server"); } else { diff --git a/package.json b/package.json index 69829b9cd..7bb695c2c 100644 --- a/package.json +++ b/package.json @@ -192,6 +192,11 @@ "default": false, "description": "(debug) Enable debug logging (ends up in the extension output)." }, + "rescript.settings.cache.projectConfig.enabled": { + "type": "boolean", + "default": false, + "description": "(beta/experimental) Enable project config caching. Can speed up latency dramatically." + }, "rescript.settings.binaryPath": { "type": [ "string", diff --git a/server/src/config.ts b/server/src/config.ts index 9949d6bde..567f05585 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -21,6 +21,11 @@ export interface extensionConfiguration { acrossFiles?: boolean; debugLogging?: boolean; }; + cache?: { + projectConfig?: { + enabled?: boolean; + }; + }; } // All values here are temporary, and will be overridden as the server is @@ -43,7 +48,12 @@ let config: { extensionConfiguration: extensionConfiguration } = { incrementalTypechecking: { enabled: false, acrossFiles: false, - debugLogging: true, + debugLogging: false, + }, + cache: { + projectConfig: { + enabled: false, + }, }, }, }; diff --git a/server/src/constants.ts b/server/src/constants.ts index fd6e29472..a08525523 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -44,6 +44,7 @@ export let bsconfigPartialPath = "bsconfig.json"; export let rescriptJsonPartialPath = "rescript.json"; export let compilerDirPartialPath = path.join("lib", "bs"); export let compilerLogPartialPath = path.join("lib", "bs", ".compiler.log"); +export let buildNinjaPartialPath = path.join("lib", "bs", "build.ninja"); export let resExt = ".res"; export let resiExt = ".resi"; export let cmiExt = ".cmi"; diff --git a/server/src/server.ts b/server/src/server.ts index cfef4f572..42e09f42c 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -205,20 +205,49 @@ let sendCompilationFinishedMessage = () => { send(notification); }; +let debug = false; + +let syncProjectConfigCache = (rootPath: string) => { + try { + if (debug) console.log("syncing project config cache for " + rootPath); + utils.runAnalysisAfterSanityCheck(rootPath, ["cache-project", rootPath]); + if (debug) console.log("OK - synced project config cache for " + rootPath); + } catch (e) { + if (debug) console.error(e); + } +}; + +let deleteProjectConfigCache = (rootPath: string) => { + try { + if (debug) console.log("deleting project config cache for " + rootPath); + utils.runAnalysisAfterSanityCheck(rootPath, ["cache-delete", rootPath]); + if (debug) console.log("OK - deleted project config cache for " + rootPath); + } catch (e) { + if (debug) console.error(e); + } +}; + let compilerLogsWatcher = chokidar .watch([], { awaitWriteFinish: { stabilityThreshold: 1, }, }) - .on("all", (_e, _changedPath) => { - sendUpdatedDiagnostics(); - sendCompilationFinishedMessage(); - if (config.extensionConfiguration.inlayHints?.enable === true) { - sendInlayHintsRefresh(); - } - if (config.extensionConfiguration.codeLens === true) { - sendCodeLensRefresh(); + .on("all", (_e, changedPath) => { + if (changedPath.includes("build.ninja")) { + let projectRoot = utils.findProjectRootOfFile(changedPath); + if (projectRoot != null) { + syncProjectConfigCache(projectRoot); + } + } else { + sendUpdatedDiagnostics(); + sendCompilationFinishedMessage(); + if (config.extensionConfiguration.inlayHints?.enable === true) { + sendInlayHintsRefresh(); + } + if (config.extensionConfiguration.codeLens === true) { + sendCodeLensRefresh(); + } } }); let stopWatchingCompilerLog = () => { @@ -257,6 +286,14 @@ let openedFile = (fileUri: string, fileContent: string) => { compilerLogsWatcher.add( path.join(projectRootPath, c.compilerLogPartialPath) ); + if ( + config.extensionConfiguration.cache?.projectConfig?.enabled === true + ) { + compilerLogsWatcher.add( + path.join(projectRootPath, c.buildNinjaPartialPath) + ); + syncProjectConfigCache(projectRootPath); + } } let root = projectsFiles.get(projectRootPath)!; root.openFiles.add(filePath); @@ -335,6 +372,10 @@ let closedFile = (fileUri: string) => { compilerLogsWatcher.unwatch( path.join(projectRootPath, c.compilerLogPartialPath) ); + compilerLogsWatcher.unwatch( + path.join(projectRootPath, c.buildNinjaPartialPath) + ); + deleteProjectConfigCache(projectRootPath); deleteProjectDiagnostics(projectRootPath); if (root.bsbWatcherByEditor !== null) { root.bsbWatcherByEditor.kill(); diff --git a/server/src/utils.ts b/server/src/utils.ts index 8f952ffff..0fbe42a9c 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -190,6 +190,10 @@ export let runAnalysisAfterSanityCheck = ( config.extensionConfiguration.incrementalTypechecking?.enabled === true ? "true" : undefined, + RESCRIPT_PROJECT_CONFIG_CACHE: + config.extensionConfiguration.cache?.projectConfig?.enabled === true + ? "true" + : undefined, }, }; let stdout = ""; From 850ab05a6950beec4100cc0d915c304d627bb1da Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 3 Jun 2024 14:31:22 +0200 Subject: [PATCH 03/10] only look up project files etc when needed --- analysis/src/Cache.ml | 6 +- analysis/src/Cmt.ml | 6 +- analysis/src/CompletionBackEnd.ml | 7 +- analysis/src/Packages.ml | 214 ++++++++++++++++-------------- analysis/src/ProcessCmt.ml | 2 +- analysis/src/References.ml | 16 ++- analysis/src/SharedTypes.ml | 14 +- analysis/src/TypeUtils.ml | 2 +- 8 files changed, 148 insertions(+), 119 deletions(-) diff --git a/analysis/src/Cache.ml b/analysis/src/Cache.ml index 90463408e..21947ea4c 100644 --- a/analysis/src/Cache.ml +++ b/analysis/src/Cache.ml @@ -28,9 +28,9 @@ let targetFileFromLibBs libBs = Filename.concat libBs ".project-files-cache" let cacheProject (package : package) = let cached = { - projectFiles = package.projectFiles; - dependenciesFiles = package.dependenciesFiles; - pathsForModule = package.pathsForModule; + projectFiles = Lazy.force package.projectFiles; + dependenciesFiles = Lazy.force package.dependenciesFiles; + pathsForModule = Lazy.force package.pathsForModule; } in match BuildSystem.getLibBs package.rootPath with diff --git a/analysis/src/Cmt.ml b/analysis/src/Cmt.ml index abc93e4ee..7245c7eee 100644 --- a/analysis/src/Cmt.ml +++ b/analysis/src/Cmt.ml @@ -33,7 +33,7 @@ let fullFromUri ~uri = if Debug.verbose () then Printf.printf "[cmt] Found incremental cmt\n"; Some cmtInfo | None -> ( - match Hashtbl.find_opt package.pathsForModule moduleName with + match Hashtbl.find_opt (Lazy.force package.pathsForModule) moduleName with | Some paths -> let cmt = getCmtPath ~uri paths in fullForCmt ~moduleName ~package ~uri cmt @@ -42,8 +42,8 @@ let fullFromUri ~uri = None)) let fullsFromModule ~package ~moduleName = - if Hashtbl.mem package.pathsForModule moduleName then - let paths = Hashtbl.find package.pathsForModule moduleName in + if Hashtbl.mem (Lazy.force package.pathsForModule) moduleName then + let paths = Hashtbl.find (Lazy.force package.pathsForModule) moduleName in let uris = getUris paths in uris |> List.filter_map (fun uri -> fullFromUri ~uri) else [] diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 5d7542e83..e1eee1a98 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1057,7 +1057,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact resultModulePath; regexpModulePath; } = - package.builtInCompletionModules + Lazy.force package.builtInCompletionModules in Some (match builtin with @@ -1316,7 +1316,7 @@ let getOpens ~debug ~rawOpens ~package ~env = ^ string_of_int (List.length rawOpens) ^ " " ^ String.concat " ... " (rawOpens |> List.map pathToString)); - let packageOpens = package.opens in + let packageOpens = Lazy.force package.opens in if debug && packageOpens <> [] then Printf.printf "%s\n" ("Package opens " @@ -1812,7 +1812,8 @@ let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens if Debug.verbose () then print_endline "[complete_typed_value]--> Texn"; [ create - (full.package.builtInCompletionModules.exnModulePath @ ["Error(error)"] + ((Lazy.force full.package.builtInCompletionModules).exnModulePath + @ ["Error(error)"] |> ident) ~kind:(Label "Catches errors from JavaScript errors.") ~docstring: diff --git a/analysis/src/Packages.ml b/analysis/src/Packages.ml index 59021ce9e..f517c22e0 100644 --- a/analysis/src/Packages.ml +++ b/analysis/src/Packages.ml @@ -74,70 +74,91 @@ let newBsPackage ~rootPath = let projectFiles, dependenciesFiles, pathsForModule = match cached with | Some cached -> - ( cached.projectFiles, - cached.dependenciesFiles, - cached.pathsForModule ) + ( Lazy.from_val cached.projectFiles, + Lazy.from_val cached.dependenciesFiles, + Lazy.from_val cached.pathsForModule ) | None -> let dependenciesFilesAndPaths = - match FindFiles.findDependencyFiles rootPath config with - | None -> [] - | Some (_dependencyDirectories, dependenciesFilesAndPaths) -> - dependenciesFilesAndPaths + Lazy.from_fun (fun () -> + match FindFiles.findDependencyFiles rootPath config with + | None -> [] + | Some (_dependencyDirectories, dependenciesFilesAndPaths) -> + dependenciesFilesAndPaths) in let sourceDirectories = - FindFiles.getSourceDirectories ~includeDev:true ~baseDir:rootPath - config + Lazy.from_fun (fun () -> + FindFiles.getSourceDirectories ~includeDev:true + ~baseDir:rootPath config) in let projectFilesAndPaths = - FindFiles.findProjectFiles - ~public:(FindFiles.getPublic config) - ~namespace ~path:rootPath ~sourceDirectories ~libBs + Lazy.from_fun (fun () -> + FindFiles.findProjectFiles + ~public:(FindFiles.getPublic config) + ~namespace ~path:rootPath + ~sourceDirectories:(Lazy.force sourceDirectories) + ~libBs) in let pathsForModule = - makePathsForModule ~projectFilesAndPaths - ~dependenciesFilesAndPaths + Lazy.from_fun (fun () -> + makePathsForModule + ~projectFilesAndPaths:(Lazy.force projectFilesAndPaths) + ~dependenciesFilesAndPaths: + (Lazy.force dependenciesFilesAndPaths)) in let projectFiles = - projectFilesAndPaths |> List.map fst |> FileSet.of_list + Lazy.from_fun (fun () -> + projectFilesAndPaths |> Lazy.force |> List.map fst + |> FileSet.of_list) in let dependenciesFiles = - dependenciesFilesAndPaths |> List.map fst |> FileSet.of_list + Lazy.from_fun (fun () -> + dependenciesFilesAndPaths |> Lazy.force |> List.map fst + |> FileSet.of_list) in (projectFiles, dependenciesFiles, pathsForModule) in Some (let opens_from_namespace = - match namespace with - | None -> [] - | Some namespace -> - let cmt = Filename.concat libBs namespace ^ ".cmt" in - Hashtbl.add pathsForModule namespace (Namespace {cmt}); - let path = [FindFiles.nameSpaceToName namespace] in - [path] + Lazy.from_fun (fun () -> + match namespace with + | None -> [] + | Some namespace -> + let cmt = Filename.concat libBs namespace ^ ".cmt" in + Hashtbl.add + (Lazy.force pathsForModule) + namespace + (Namespace {cmt}); + let path = [FindFiles.nameSpaceToName namespace] in + [path]) in let opens_from_bsc_flags = - let bind f x = Option.bind x f in - match Json.get "bsc-flags" config |> bind Json.array with - | Some l -> - List.fold_left - (fun opens item -> - match item |> Json.string with - | None -> opens - | Some s -> ( - let parts = String.split_on_char ' ' s in - match parts with - | "-open" :: name :: _ -> - let path = name |> String.split_on_char '.' in - path :: opens - | _ -> opens)) - [] l - | None -> [] + Lazy.from_fun (fun () -> + let bind f x = Option.bind x f in + match Json.get "bsc-flags" config |> bind Json.array with + | Some l -> + List.fold_left + (fun opens item -> + match item |> Json.string with + | None -> opens + | Some s -> ( + let parts = String.split_on_char ' ' s in + match parts with + | "-open" :: name :: _ -> + let path = name |> String.split_on_char '.' in + path :: opens + | _ -> opens)) + [] l + | None -> []) in let opens = - [(if uncurried then "PervasivesU" else "Pervasives"); "JsxModules"] - :: opens_from_namespace - |> List.rev_append opens_from_bsc_flags - |> List.map (fun path -> path @ ["place holder"]) + Lazy.from_fun (fun () -> + [ + (if uncurried then "PervasivesU" else "Pervasives"); + "JsxModules"; + ] + :: Lazy.force opens_from_namespace + |> List.rev_append (Lazy.force opens_from_bsc_flags) + |> List.map (fun path -> path @ ["place holder"])) in { genericJsxModule; @@ -150,59 +171,60 @@ let newBsPackage ~rootPath = opens; namespace; builtInCompletionModules = - (if - opens_from_bsc_flags - |> List.find_opt (fun opn -> - match opn with - | ["RescriptCore"] -> true - | _ -> false) - |> Option.is_some - then - { - arrayModulePath = ["Array"]; - optionModulePath = ["Option"]; - stringModulePath = ["String"]; - intModulePath = ["Int"]; - floatModulePath = ["Float"]; - promiseModulePath = ["Promise"]; - listModulePath = ["List"]; - resultModulePath = ["Result"]; - exnModulePath = ["Exn"]; - regexpModulePath = ["RegExp"]; - } - else if - opens_from_bsc_flags - |> List.find_opt (fun opn -> - match opn with - | ["Belt"] -> true - | _ -> false) - |> Option.is_some - then - { - arrayModulePath = ["Array"]; - optionModulePath = ["Option"]; - stringModulePath = ["Js"; "String2"]; - intModulePath = ["Int"]; - floatModulePath = ["Float"]; - promiseModulePath = ["Js"; "Promise"]; - listModulePath = ["List"]; - resultModulePath = ["Result"]; - exnModulePath = ["Js"; "Exn"]; - regexpModulePath = ["Js"; "Re"]; - } - else - { - arrayModulePath = ["Js"; "Array2"]; - optionModulePath = ["Belt"; "Option"]; - stringModulePath = ["Js"; "String2"]; - intModulePath = ["Belt"; "Int"]; - floatModulePath = ["Belt"; "Float"]; - promiseModulePath = ["Js"; "Promise"]; - listModulePath = ["Belt"; "List"]; - resultModulePath = ["Belt"; "Result"]; - exnModulePath = ["Js"; "Exn"]; - regexpModulePath = ["Js"; "Re"]; - }); + Lazy.from_fun (fun () -> + if + opens_from_bsc_flags |> Lazy.force + |> List.find_opt (fun opn -> + match opn with + | ["RescriptCore"] -> true + | _ -> false) + |> Option.is_some + then + { + arrayModulePath = ["Array"]; + optionModulePath = ["Option"]; + stringModulePath = ["String"]; + intModulePath = ["Int"]; + floatModulePath = ["Float"]; + promiseModulePath = ["Promise"]; + listModulePath = ["List"]; + resultModulePath = ["Result"]; + exnModulePath = ["Exn"]; + regexpModulePath = ["RegExp"]; + } + else if + opens_from_bsc_flags |> Lazy.force + |> List.find_opt (fun opn -> + match opn with + | ["Belt"] -> true + | _ -> false) + |> Option.is_some + then + { + arrayModulePath = ["Array"]; + optionModulePath = ["Option"]; + stringModulePath = ["Js"; "String2"]; + intModulePath = ["Int"]; + floatModulePath = ["Float"]; + promiseModulePath = ["Js"; "Promise"]; + listModulePath = ["List"]; + resultModulePath = ["Result"]; + exnModulePath = ["Js"; "Exn"]; + regexpModulePath = ["Js"; "Re"]; + } + else + { + arrayModulePath = ["Js"; "Array2"]; + optionModulePath = ["Belt"; "Option"]; + stringModulePath = ["Js"; "String2"]; + intModulePath = ["Belt"; "Int"]; + floatModulePath = ["Belt"; "Float"]; + promiseModulePath = ["Js"; "Promise"]; + listModulePath = ["Belt"; "List"]; + resultModulePath = ["Belt"; "Result"]; + exnModulePath = ["Js"; "Exn"]; + regexpModulePath = ["Js"; "Re"]; + }); uncurried; })) | None -> None diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index e0748a28e..37f1a3cd2 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -663,7 +663,7 @@ let fileForCmt ~moduleName ~cmt ~uri = Some file) let fileForModule moduleName ~package = - match Hashtbl.find_opt package.pathsForModule moduleName with + match Hashtbl.find_opt (Lazy.force package.pathsForModule) moduleName with | Some paths -> let uri = getUri paths in let cmt = getCmtPath ~uri paths in diff --git a/analysis/src/References.ml b/analysis/src/References.ml index e19439a20..052ce1171 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -66,7 +66,7 @@ let getLocItem ~full ~pos ~debug = | [ ({locType = Typed (_, _, LocalReference _)} as li1); ({locType = Typed (_, _, GlobalReference ("Js_OO", ["unsafe_downgrade"], _))} - as li2); + as li2); li3; ] (* For older compiler 9.0 or earlier *) @@ -211,7 +211,9 @@ let definedForLoc ~file ~package locKind = (** Find alternative declaration: from res in case of interface, or from resi in case of implementation *) let alternateDeclared ~(file : File.t) ~package (declared : _ Declared.t) tip = - match Hashtbl.find_opt package.pathsForModule file.moduleName with + match + Hashtbl.find_opt (Lazy.force package.pathsForModule) file.moduleName + with | None -> None | Some paths -> ( match paths with @@ -362,7 +364,7 @@ let definitionForLocItem ~full:{file; package} locItem = None | TopLevelModule name -> ( maybeLog ("Toplevel " ^ name); - match Hashtbl.find_opt package.pathsForModule name with + match Hashtbl.find_opt (Lazy.force package.pathsForModule) name with | None -> None | Some paths -> let uri = getUri paths in @@ -486,7 +488,8 @@ let forLocalStamp ~full:{file; extra; package} stamp (tip : Tip.t) = maybeLog ("Now checking path " ^ pathToString path); let thisModuleName = file.moduleName in let externals = - package.projectFiles |> FileSet.elements + Lazy.force package.projectFiles + |> FileSet.elements |> List.filter (fun name -> name <> file.moduleName) |> List.map (fun moduleName -> Cmt.fullsFromModule ~package ~moduleName @@ -521,7 +524,8 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = match locItem.locType with | TopLevelModule moduleName -> let otherModulesReferences = - package.projectFiles |> FileSet.elements + Lazy.force package.projectFiles + |> FileSet.elements |> Utils.filterMap (fun name -> match ProcessCmt.fileForModule ~package name with | None -> None @@ -539,7 +543,7 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = |> List.flatten in let targetModuleReferences = - match Hashtbl.find_opt package.pathsForModule moduleName with + match Hashtbl.find_opt (Lazy.force package.pathsForModule) moduleName with | None -> [] | Some paths -> let moduleSrcToRef src = {uri = Uri.fromPath src; locOpt = None} in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index bdc5ff09a..03e41a3f6 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -500,18 +500,20 @@ type package = { genericJsxModule: string option; suffix: string; rootPath: filePath; - projectFiles: FileSet.t; - dependenciesFiles: FileSet.t; - pathsForModule: (file, paths) Hashtbl.t; + projectFiles: FileSet.t lazy_t; + dependenciesFiles: FileSet.t lazy_t; + pathsForModule: (file, paths) Hashtbl.t lazy_t; namespace: string option; - builtInCompletionModules: builtInCompletionModules; - opens: path list; + builtInCompletionModules: builtInCompletionModules lazy_t; + opens: path list lazy_t; uncurried: bool; rescriptVersion: int * int; } let allFilesInPackage package = - FileSet.union package.projectFiles package.dependenciesFiles + FileSet.union + (Lazy.force package.projectFiles) + (Lazy.force package.dependenciesFiles) type full = {extra: extra; file: File.t; package: package} diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 6701b57a7..386ba2e83 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -1114,7 +1114,7 @@ let removeOpensFromCompletionPath ~rawOpens ~package completionPath = in let completionPathMinusOpens = completionPath |> Utils.flattenAnyNamespaceInPath - |> removeRawOpens package.opens + |> removeRawOpens (Lazy.force package.opens) |> removeRawOpens rawOpens in completionPathMinusOpens From ea2c9c108b5a8c729ff3b27815fb49c16817df27 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 3 Jun 2024 14:33:45 +0200 Subject: [PATCH 04/10] comment --- analysis/src/Cache.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/analysis/src/Cache.ml b/analysis/src/Cache.ml index 21947ea4c..62c645453 100644 --- a/analysis/src/Cache.ml +++ b/analysis/src/Cache.ml @@ -25,6 +25,7 @@ let deleteCache filename = try Sys.remove filename with _ -> () let targetFileFromLibBs libBs = Filename.concat libBs ".project-files-cache" +(* TODO: Fix the potential infinite loop here *) let cacheProject (package : package) = let cached = { From cdb85f77549f64efc2589517afd00e5ac831b3bd Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 4 Jun 2024 09:03:51 +0200 Subject: [PATCH 05/10] Revert "only look up project files etc when needed" This reverts commit bc71f767b5fda7a1bf7880f70115ef7aedfeb8b9. --- analysis/src/Cache.ml | 6 +- analysis/src/Cmt.ml | 6 +- analysis/src/CompletionBackEnd.ml | 7 +- analysis/src/Packages.ml | 214 ++++++++++++++---------------- analysis/src/ProcessCmt.ml | 2 +- analysis/src/References.ml | 16 +-- analysis/src/SharedTypes.ml | 14 +- analysis/src/TypeUtils.ml | 2 +- 8 files changed, 119 insertions(+), 148 deletions(-) diff --git a/analysis/src/Cache.ml b/analysis/src/Cache.ml index 62c645453..d840ff511 100644 --- a/analysis/src/Cache.ml +++ b/analysis/src/Cache.ml @@ -29,9 +29,9 @@ let targetFileFromLibBs libBs = Filename.concat libBs ".project-files-cache" let cacheProject (package : package) = let cached = { - projectFiles = Lazy.force package.projectFiles; - dependenciesFiles = Lazy.force package.dependenciesFiles; - pathsForModule = Lazy.force package.pathsForModule; + projectFiles = package.projectFiles; + dependenciesFiles = package.dependenciesFiles; + pathsForModule = package.pathsForModule; } in match BuildSystem.getLibBs package.rootPath with diff --git a/analysis/src/Cmt.ml b/analysis/src/Cmt.ml index 7245c7eee..abc93e4ee 100644 --- a/analysis/src/Cmt.ml +++ b/analysis/src/Cmt.ml @@ -33,7 +33,7 @@ let fullFromUri ~uri = if Debug.verbose () then Printf.printf "[cmt] Found incremental cmt\n"; Some cmtInfo | None -> ( - match Hashtbl.find_opt (Lazy.force package.pathsForModule) moduleName with + match Hashtbl.find_opt package.pathsForModule moduleName with | Some paths -> let cmt = getCmtPath ~uri paths in fullForCmt ~moduleName ~package ~uri cmt @@ -42,8 +42,8 @@ let fullFromUri ~uri = None)) let fullsFromModule ~package ~moduleName = - if Hashtbl.mem (Lazy.force package.pathsForModule) moduleName then - let paths = Hashtbl.find (Lazy.force package.pathsForModule) moduleName in + if Hashtbl.mem package.pathsForModule moduleName then + let paths = Hashtbl.find package.pathsForModule moduleName in let uris = getUris paths in uris |> List.filter_map (fun uri -> fullFromUri ~uri) else [] diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index e1eee1a98..5d7542e83 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1057,7 +1057,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact resultModulePath; regexpModulePath; } = - Lazy.force package.builtInCompletionModules + package.builtInCompletionModules in Some (match builtin with @@ -1316,7 +1316,7 @@ let getOpens ~debug ~rawOpens ~package ~env = ^ string_of_int (List.length rawOpens) ^ " " ^ String.concat " ... " (rawOpens |> List.map pathToString)); - let packageOpens = Lazy.force package.opens in + let packageOpens = package.opens in if debug && packageOpens <> [] then Printf.printf "%s\n" ("Package opens " @@ -1812,8 +1812,7 @@ let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens if Debug.verbose () then print_endline "[complete_typed_value]--> Texn"; [ create - ((Lazy.force full.package.builtInCompletionModules).exnModulePath - @ ["Error(error)"] + (full.package.builtInCompletionModules.exnModulePath @ ["Error(error)"] |> ident) ~kind:(Label "Catches errors from JavaScript errors.") ~docstring: diff --git a/analysis/src/Packages.ml b/analysis/src/Packages.ml index f517c22e0..59021ce9e 100644 --- a/analysis/src/Packages.ml +++ b/analysis/src/Packages.ml @@ -74,91 +74,70 @@ let newBsPackage ~rootPath = let projectFiles, dependenciesFiles, pathsForModule = match cached with | Some cached -> - ( Lazy.from_val cached.projectFiles, - Lazy.from_val cached.dependenciesFiles, - Lazy.from_val cached.pathsForModule ) + ( cached.projectFiles, + cached.dependenciesFiles, + cached.pathsForModule ) | None -> let dependenciesFilesAndPaths = - Lazy.from_fun (fun () -> - match FindFiles.findDependencyFiles rootPath config with - | None -> [] - | Some (_dependencyDirectories, dependenciesFilesAndPaths) -> - dependenciesFilesAndPaths) + match FindFiles.findDependencyFiles rootPath config with + | None -> [] + | Some (_dependencyDirectories, dependenciesFilesAndPaths) -> + dependenciesFilesAndPaths in let sourceDirectories = - Lazy.from_fun (fun () -> - FindFiles.getSourceDirectories ~includeDev:true - ~baseDir:rootPath config) + FindFiles.getSourceDirectories ~includeDev:true ~baseDir:rootPath + config in let projectFilesAndPaths = - Lazy.from_fun (fun () -> - FindFiles.findProjectFiles - ~public:(FindFiles.getPublic config) - ~namespace ~path:rootPath - ~sourceDirectories:(Lazy.force sourceDirectories) - ~libBs) + FindFiles.findProjectFiles + ~public:(FindFiles.getPublic config) + ~namespace ~path:rootPath ~sourceDirectories ~libBs in let pathsForModule = - Lazy.from_fun (fun () -> - makePathsForModule - ~projectFilesAndPaths:(Lazy.force projectFilesAndPaths) - ~dependenciesFilesAndPaths: - (Lazy.force dependenciesFilesAndPaths)) + makePathsForModule ~projectFilesAndPaths + ~dependenciesFilesAndPaths in let projectFiles = - Lazy.from_fun (fun () -> - projectFilesAndPaths |> Lazy.force |> List.map fst - |> FileSet.of_list) + projectFilesAndPaths |> List.map fst |> FileSet.of_list in let dependenciesFiles = - Lazy.from_fun (fun () -> - dependenciesFilesAndPaths |> Lazy.force |> List.map fst - |> FileSet.of_list) + dependenciesFilesAndPaths |> List.map fst |> FileSet.of_list in (projectFiles, dependenciesFiles, pathsForModule) in Some (let opens_from_namespace = - Lazy.from_fun (fun () -> - match namespace with - | None -> [] - | Some namespace -> - let cmt = Filename.concat libBs namespace ^ ".cmt" in - Hashtbl.add - (Lazy.force pathsForModule) - namespace - (Namespace {cmt}); - let path = [FindFiles.nameSpaceToName namespace] in - [path]) + match namespace with + | None -> [] + | Some namespace -> + let cmt = Filename.concat libBs namespace ^ ".cmt" in + Hashtbl.add pathsForModule namespace (Namespace {cmt}); + let path = [FindFiles.nameSpaceToName namespace] in + [path] in let opens_from_bsc_flags = - Lazy.from_fun (fun () -> - let bind f x = Option.bind x f in - match Json.get "bsc-flags" config |> bind Json.array with - | Some l -> - List.fold_left - (fun opens item -> - match item |> Json.string with - | None -> opens - | Some s -> ( - let parts = String.split_on_char ' ' s in - match parts with - | "-open" :: name :: _ -> - let path = name |> String.split_on_char '.' in - path :: opens - | _ -> opens)) - [] l - | None -> []) + let bind f x = Option.bind x f in + match Json.get "bsc-flags" config |> bind Json.array with + | Some l -> + List.fold_left + (fun opens item -> + match item |> Json.string with + | None -> opens + | Some s -> ( + let parts = String.split_on_char ' ' s in + match parts with + | "-open" :: name :: _ -> + let path = name |> String.split_on_char '.' in + path :: opens + | _ -> opens)) + [] l + | None -> [] in let opens = - Lazy.from_fun (fun () -> - [ - (if uncurried then "PervasivesU" else "Pervasives"); - "JsxModules"; - ] - :: Lazy.force opens_from_namespace - |> List.rev_append (Lazy.force opens_from_bsc_flags) - |> List.map (fun path -> path @ ["place holder"])) + [(if uncurried then "PervasivesU" else "Pervasives"); "JsxModules"] + :: opens_from_namespace + |> List.rev_append opens_from_bsc_flags + |> List.map (fun path -> path @ ["place holder"]) in { genericJsxModule; @@ -171,60 +150,59 @@ let newBsPackage ~rootPath = opens; namespace; builtInCompletionModules = - Lazy.from_fun (fun () -> - if - opens_from_bsc_flags |> Lazy.force - |> List.find_opt (fun opn -> - match opn with - | ["RescriptCore"] -> true - | _ -> false) - |> Option.is_some - then - { - arrayModulePath = ["Array"]; - optionModulePath = ["Option"]; - stringModulePath = ["String"]; - intModulePath = ["Int"]; - floatModulePath = ["Float"]; - promiseModulePath = ["Promise"]; - listModulePath = ["List"]; - resultModulePath = ["Result"]; - exnModulePath = ["Exn"]; - regexpModulePath = ["RegExp"]; - } - else if - opens_from_bsc_flags |> Lazy.force - |> List.find_opt (fun opn -> - match opn with - | ["Belt"] -> true - | _ -> false) - |> Option.is_some - then - { - arrayModulePath = ["Array"]; - optionModulePath = ["Option"]; - stringModulePath = ["Js"; "String2"]; - intModulePath = ["Int"]; - floatModulePath = ["Float"]; - promiseModulePath = ["Js"; "Promise"]; - listModulePath = ["List"]; - resultModulePath = ["Result"]; - exnModulePath = ["Js"; "Exn"]; - regexpModulePath = ["Js"; "Re"]; - } - else - { - arrayModulePath = ["Js"; "Array2"]; - optionModulePath = ["Belt"; "Option"]; - stringModulePath = ["Js"; "String2"]; - intModulePath = ["Belt"; "Int"]; - floatModulePath = ["Belt"; "Float"]; - promiseModulePath = ["Js"; "Promise"]; - listModulePath = ["Belt"; "List"]; - resultModulePath = ["Belt"; "Result"]; - exnModulePath = ["Js"; "Exn"]; - regexpModulePath = ["Js"; "Re"]; - }); + (if + opens_from_bsc_flags + |> List.find_opt (fun opn -> + match opn with + | ["RescriptCore"] -> true + | _ -> false) + |> Option.is_some + then + { + arrayModulePath = ["Array"]; + optionModulePath = ["Option"]; + stringModulePath = ["String"]; + intModulePath = ["Int"]; + floatModulePath = ["Float"]; + promiseModulePath = ["Promise"]; + listModulePath = ["List"]; + resultModulePath = ["Result"]; + exnModulePath = ["Exn"]; + regexpModulePath = ["RegExp"]; + } + else if + opens_from_bsc_flags + |> List.find_opt (fun opn -> + match opn with + | ["Belt"] -> true + | _ -> false) + |> Option.is_some + then + { + arrayModulePath = ["Array"]; + optionModulePath = ["Option"]; + stringModulePath = ["Js"; "String2"]; + intModulePath = ["Int"]; + floatModulePath = ["Float"]; + promiseModulePath = ["Js"; "Promise"]; + listModulePath = ["List"]; + resultModulePath = ["Result"]; + exnModulePath = ["Js"; "Exn"]; + regexpModulePath = ["Js"; "Re"]; + } + else + { + arrayModulePath = ["Js"; "Array2"]; + optionModulePath = ["Belt"; "Option"]; + stringModulePath = ["Js"; "String2"]; + intModulePath = ["Belt"; "Int"]; + floatModulePath = ["Belt"; "Float"]; + promiseModulePath = ["Js"; "Promise"]; + listModulePath = ["Belt"; "List"]; + resultModulePath = ["Belt"; "Result"]; + exnModulePath = ["Js"; "Exn"]; + regexpModulePath = ["Js"; "Re"]; + }); uncurried; })) | None -> None diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index 37f1a3cd2..e0748a28e 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -663,7 +663,7 @@ let fileForCmt ~moduleName ~cmt ~uri = Some file) let fileForModule moduleName ~package = - match Hashtbl.find_opt (Lazy.force package.pathsForModule) moduleName with + match Hashtbl.find_opt package.pathsForModule moduleName with | Some paths -> let uri = getUri paths in let cmt = getCmtPath ~uri paths in diff --git a/analysis/src/References.ml b/analysis/src/References.ml index 052ce1171..e19439a20 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -66,7 +66,7 @@ let getLocItem ~full ~pos ~debug = | [ ({locType = Typed (_, _, LocalReference _)} as li1); ({locType = Typed (_, _, GlobalReference ("Js_OO", ["unsafe_downgrade"], _))} - as li2); + as li2); li3; ] (* For older compiler 9.0 or earlier *) @@ -211,9 +211,7 @@ let definedForLoc ~file ~package locKind = (** Find alternative declaration: from res in case of interface, or from resi in case of implementation *) let alternateDeclared ~(file : File.t) ~package (declared : _ Declared.t) tip = - match - Hashtbl.find_opt (Lazy.force package.pathsForModule) file.moduleName - with + match Hashtbl.find_opt package.pathsForModule file.moduleName with | None -> None | Some paths -> ( match paths with @@ -364,7 +362,7 @@ let definitionForLocItem ~full:{file; package} locItem = None | TopLevelModule name -> ( maybeLog ("Toplevel " ^ name); - match Hashtbl.find_opt (Lazy.force package.pathsForModule) name with + match Hashtbl.find_opt package.pathsForModule name with | None -> None | Some paths -> let uri = getUri paths in @@ -488,8 +486,7 @@ let forLocalStamp ~full:{file; extra; package} stamp (tip : Tip.t) = maybeLog ("Now checking path " ^ pathToString path); let thisModuleName = file.moduleName in let externals = - Lazy.force package.projectFiles - |> FileSet.elements + package.projectFiles |> FileSet.elements |> List.filter (fun name -> name <> file.moduleName) |> List.map (fun moduleName -> Cmt.fullsFromModule ~package ~moduleName @@ -524,8 +521,7 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = match locItem.locType with | TopLevelModule moduleName -> let otherModulesReferences = - Lazy.force package.projectFiles - |> FileSet.elements + package.projectFiles |> FileSet.elements |> Utils.filterMap (fun name -> match ProcessCmt.fileForModule ~package name with | None -> None @@ -543,7 +539,7 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = |> List.flatten in let targetModuleReferences = - match Hashtbl.find_opt (Lazy.force package.pathsForModule) moduleName with + match Hashtbl.find_opt package.pathsForModule moduleName with | None -> [] | Some paths -> let moduleSrcToRef src = {uri = Uri.fromPath src; locOpt = None} in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 03e41a3f6..bdc5ff09a 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -500,20 +500,18 @@ type package = { genericJsxModule: string option; suffix: string; rootPath: filePath; - projectFiles: FileSet.t lazy_t; - dependenciesFiles: FileSet.t lazy_t; - pathsForModule: (file, paths) Hashtbl.t lazy_t; + projectFiles: FileSet.t; + dependenciesFiles: FileSet.t; + pathsForModule: (file, paths) Hashtbl.t; namespace: string option; - builtInCompletionModules: builtInCompletionModules lazy_t; - opens: path list lazy_t; + builtInCompletionModules: builtInCompletionModules; + opens: path list; uncurried: bool; rescriptVersion: int * int; } let allFilesInPackage package = - FileSet.union - (Lazy.force package.projectFiles) - (Lazy.force package.dependenciesFiles) + FileSet.union package.projectFiles package.dependenciesFiles type full = {extra: extra; file: File.t; package: package} diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 386ba2e83..6701b57a7 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -1114,7 +1114,7 @@ let removeOpensFromCompletionPath ~rawOpens ~package completionPath = in let completionPathMinusOpens = completionPath |> Utils.flattenAnyNamespaceInPath - |> removeRawOpens (Lazy.force package.opens) + |> removeRawOpens package.opens |> removeRawOpens rawOpens in completionPathMinusOpens From 33b47fcfaf6ce4239810d79e865e4d7c2a791297 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 4 Jun 2024 09:04:30 +0200 Subject: [PATCH 06/10] remove now irrelevant comment --- analysis/src/Cache.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/analysis/src/Cache.ml b/analysis/src/Cache.ml index d840ff511..90463408e 100644 --- a/analysis/src/Cache.ml +++ b/analysis/src/Cache.ml @@ -25,7 +25,6 @@ let deleteCache filename = try Sys.remove filename with _ -> () let targetFileFromLibBs libBs = Filename.concat libBs ".project-files-cache" -(* TODO: Fix the potential infinite loop here *) let cacheProject (package : package) = let cached = { From 856a5daf404fff08fc050395e22a3318c2252d87 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 4 Jun 2024 09:05:13 +0200 Subject: [PATCH 07/10] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0374167fb..c34b1898a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ ## 1.52.0 +#### :rocket: New Feature + +- Experimental support for caching the project config to reduce latency. https://github.com/rescript-lang/rescript-vscode/pull/1000 + #### :bug: Bug Fix - Fix highlighting of other languages being affected by rescript-vscode. https://github.com/rescript-lang/rescript-vscode/pull/973 From dac8ae6cd9b090820028df3475fd112bb20c0edd Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 4 Jun 2024 09:06:39 +0200 Subject: [PATCH 08/10] conditionally --- server/src/server.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index 42e09f42c..2203ce851 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -235,9 +235,13 @@ let compilerLogsWatcher = chokidar }) .on("all", (_e, changedPath) => { if (changedPath.includes("build.ninja")) { - let projectRoot = utils.findProjectRootOfFile(changedPath); - if (projectRoot != null) { - syncProjectConfigCache(projectRoot); + if ( + config.extensionConfiguration.cache?.projectConfig?.enabled === true + ) { + let projectRoot = utils.findProjectRootOfFile(changedPath); + if (projectRoot != null) { + syncProjectConfigCache(projectRoot); + } } } else { sendUpdatedDiagnostics(); From 1561e59a8908ce37e1eefa032c70cfd8bd5f3937 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 4 Jun 2024 14:13:11 +0200 Subject: [PATCH 09/10] rename --- analysis/bin/main.ml | 4 ++-- analysis/src/Cache.ml | 2 +- analysis/src/Cfg.ml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/analysis/bin/main.ml b/analysis/bin/main.ml index cdc8c0b29..259b1a300 100644 --- a/analysis/bin/main.ml +++ b/analysis/bin/main.ml @@ -111,13 +111,13 @@ let main () = in match args with | [_; "cache-project"; rootPath] -> ( - Cfg.useProjectConfigCache := false; + Cfg.readProjectConfigCache := false; let uri = Uri.fromPath rootPath in match Packages.getPackage ~uri with | Some package -> Cache.cacheProject package | None -> print_endline "\"ERR\"") | [_; "cache-delete"; rootPath] -> ( - Cfg.useProjectConfigCache := false; + Cfg.readProjectConfigCache := false; let uri = Uri.fromPath rootPath in match Packages.findRoot ~uri (Hashtbl.create 0) with | Some (`Bs rootPath) -> ( diff --git a/analysis/src/Cache.ml b/analysis/src/Cache.ml index 90463408e..5e7b3203f 100644 --- a/analysis/src/Cache.ml +++ b/analysis/src/Cache.ml @@ -12,7 +12,7 @@ let writeCache filename (data : cached) = close_out oc let readCache filename = - if !Cfg.useProjectConfigCache && Sys.file_exists filename then + if !Cfg.readProjectConfigCache && Sys.file_exists filename then try let ic = open_in_bin filename in let data : cached = Marshal.from_channel ic in diff --git a/analysis/src/Cfg.ml b/analysis/src/Cfg.ml index 4c19f6033..bd4166d5a 100644 --- a/analysis/src/Cfg.ml +++ b/analysis/src/Cfg.ml @@ -10,7 +10,7 @@ let inIncrementalTypecheckingMode = | _ -> false with _ -> false) -let useProjectConfigCache = +let readProjectConfigCache = ref (try match Sys.getenv "RESCRIPT_PROJECT_CONFIG_CACHE" with From ab59b54736a7480d79db1cc7fe7b9664eeb57516 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 4 Jun 2024 14:16:22 +0200 Subject: [PATCH 10/10] replace instead of add --- analysis/src/Packages.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analysis/src/Packages.ml b/analysis/src/Packages.ml index 59021ce9e..caeeda424 100644 --- a/analysis/src/Packages.ml +++ b/analysis/src/Packages.ml @@ -111,7 +111,7 @@ let newBsPackage ~rootPath = | None -> [] | Some namespace -> let cmt = Filename.concat libBs namespace ^ ".cmt" in - Hashtbl.add pathsForModule namespace (Namespace {cmt}); + Hashtbl.replace pathsForModule namespace (Namespace {cmt}); let path = [FindFiles.nameSpaceToName namespace] in [path] in