diff --git a/analysis/bin/main.ml b/analysis/bin/main.ml index 259b1a300..505ac6902 100644 --- a/analysis/bin/main.ml +++ b/analysis/bin/main.ml @@ -161,10 +161,14 @@ let main () = (match allowForConstructorPayloads with | "true" -> true | _ -> false) + | [_; "inlayHint"; path; line_start; line_end; maxLength; currentFile] -> + Commands.inlayhint ~path + ~pos:(int_of_string line_start, int_of_string line_end) + ~maxLength ~currentFile:(Some currentFile) ~debug | [_; "inlayHint"; path; line_start; line_end; maxLength] -> Commands.inlayhint ~path ~pos:(int_of_string line_start, int_of_string line_end) - ~maxLength ~debug + ~maxLength ~currentFile:None ~debug | [_; "codeLens"; path] -> Commands.codeLens ~path ~debug | [_; "codeAction"; path; startLine; startCol; endLine; endCol; currentFile] -> diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 32bfe08ff..943810954 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -41,9 +41,9 @@ let completionResolve ~path ~modulePath = in print_endline docstring -let inlayhint ~path ~pos ~maxLength ~debug = +let inlayhint ~path ~pos ~maxLength ~currentFile ~debug = let result = - match Hint.inlay ~path ~pos ~maxLength ~debug with + match Hint.inlay ~path ~pos ~maxLength ~currentFile ~debug with | Some hints -> hints |> Protocol.array | None -> Protocol.null in @@ -491,7 +491,7 @@ let test ~path = ("Inlay Hint " ^ path ^ " " ^ string_of_int line_start ^ ":" ^ string_of_int line_end); inlayhint ~path ~pos:(line_start, line_end) ~maxLength:"25" - ~debug:false + ~currentFile:None ~debug:false | "cle" -> print_endline ("Code Lens " ^ path); codeLens ~path ~debug:false diff --git a/analysis/src/Hint.ml b/analysis/src/Hint.ml index 9f553d063..3650ddd8c 100644 --- a/analysis/src/Hint.ml +++ b/analysis/src/Hint.ml @@ -31,7 +31,7 @@ let locItemToTypeHint ~full:{file; package} locItem = | `Field -> fromType t)) | _ -> None -let inlay ~path ~pos ~maxLength ~debug = +let inlay ~path ~pos ~maxLength ~currentFile ~debug = let maxlen = try Some (int_of_string maxLength) with Failure _ -> None in let hints = ref [] in let start_line, end_line = pos in @@ -70,11 +70,16 @@ let inlay ~path ~pos ~maxLength ~debug = Ast_iterator.default_iterator.value_binding iterator vb in let iterator = {Ast_iterator.default_iterator with value_binding} in + let sourceFile = + match currentFile with + | Some f -> f + | None -> path + in (if Files.classifySourceFile path = Res then let parser = Res_driver.parsing_engine.parse_implementation ~for_printer:false in - let {Res_driver.parsetree = structure} = parser ~filename:path in + let {Res_driver.parsetree = structure} = parser ~filename:sourceFile in iterator.structure iterator structure |> ignore); match Cmt.loadFullCmtFromPath ~path with | None -> None diff --git a/server/src/server.ts b/server/src/server.ts index f4829dd0e..d55321855 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -572,6 +572,7 @@ let closedFile = async (fileUri: utils.FileURI) => { } }; +let refreshTimeout: ReturnType | null = null; let updateOpenedFile = (fileUri: utils.FileURI, fileContent: string) => { getLogger().info( `Updating opened file ${fileUri}, incremental TC enabled: ${config.extensionConfiguration.incrementalTypechecking?.enable}`, @@ -581,12 +582,18 @@ let updateOpenedFile = (fileUri: utils.FileURI, fileContent: string) => { stupidFileContentCache.set(filePath, fileContent); if (config.extensionConfiguration.incrementalTypechecking?.enable) { ic.handleUpdateOpenedFile(filePath, fileContent, send, () => { - if (config.extensionConfiguration.codeLens) { - sendCodeLensRefresh(); - } - if (config.extensionConfiguration.inlayHints) { - sendInlayHintsRefresh(); + if (refreshTimeout != null) { + clearTimeout(refreshTimeout); } + refreshTimeout = setTimeout(() => { + refreshTimeout = null; + if (config.extensionConfiguration.codeLens) { + sendCodeLensRefresh(); + } + if (config.extensionConfiguration.inlayHints) { + sendInlayHintsRefresh(); + } + }, 200); }); } }; @@ -646,18 +653,37 @@ async function inlayHint(msg: p.RequestMessage) { const filePath = utils.uriToNormalizedPath( params.textDocument.uri as utils.FileURI, ); - - const response = await utils.runAnalysisCommand( + let args: Array = [ + "inlayHint", filePath, - [ - "inlayHint", - filePath, - params.range.start.line, - params.range.end.line, - config.extensionConfiguration.inlayHints?.maxLength, - ], - msg, - ); + params.range.start.line, + params.range.end.line, + config.extensionConfiguration.inlayHints?.maxLength, + ]; + let tmpname: string | null = null; + let projectRootPath = utils.findProjectRootOfFile(filePath); + let rescriptVersion = projectRootPath + ? projectsFiles.get(projectRootPath)?.rescriptVersion + : null; + let supportsCurrentFile = + rescriptVersion != null && + semver.valid(rescriptVersion) != null && + semver.gte(rescriptVersion, "12.0.0-alpha.5"); + // The currentFile argument (passing unsaved buffer content via a temp file) + // is only supported by the analysis binary that ships with the compiler + // starting from 12.0.0-alpha.5. Older versions don't recognize the extra + // argument and would fail to match the CLI pattern. + if (supportsCurrentFile) { + let code = getOpenedFileContent(params.textDocument.uri as utils.FileURI); + let extension = path.extname(params.textDocument.uri); + tmpname = utils.createFileInTempDir(extension); + fs.writeFileSync(tmpname, code, { encoding: "utf-8" }); + args.push(tmpname); + } + const response = await utils.runAnalysisCommand(filePath, args, msg); + if (tmpname != null) { + fs.unlink(tmpname, () => null); + } return response; }