From 21f2294231c4c06059f156f7837cd719784783b1 Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Mon, 8 Jun 2026 22:58:07 -0300 Subject: [PATCH 1/2] Refactor analysis CLI helpers to use source input Move document symbols and interface generation behind CLI helpers that operate on source content instead of reading files internally. Thread parsed max inlay hint length as an option, pass preloaded CMT data into code actions. Update the DocTemplate expected output for the new CMT lookup message emitted during non-compiled code action tests. --- CHANGELOG.md | 2 +- analysis/bin/main.ml | 9 ++--- analysis/src/cli.ml | 34 +++++++++++++++---- analysis/src/create_interface.ml | 15 +++----- analysis/src/document_symbol.ml | 20 +++++------ analysis/src/hint.ml | 3 +- analysis/src/xform.ml | 4 +-- .../expected/DocTemplate.resi.txt | 7 ++++ 8 files changed, 56 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c749bb74ae5..720da66ed67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,7 +61,7 @@ - Remove dead and unreachable compiler error and warning variants; add fixtures for the ones found to be reachable. https://github.com/rescript-lang/rescript/pull/8459 - Convert OCaml codebase to snake case format. https://github.com/rescript-lang/rescript/pull/8456 - Analysis refactor: remove global state `Shared_types.state`. https://github.com/rescript-lang/rescript/pull/8465 - +- Refactor analysis CLI helpers to use source input. https://github.com/rescript-lang/rescript/pull/8466 # 13.0.0-alpha.4 diff --git a/analysis/bin/main.ml b/analysis/bin/main.ml index 481cec377c4..ef40fdcb9aa 100644 --- a/analysis/bin/main.ml +++ b/analysis/bin/main.ml @@ -148,7 +148,7 @@ let main () = Cli.type_definition ~state ~path ~pos:(int_of_string line, int_of_string col) ~debug - | [_; "documentSymbol"; path] -> Document_symbol.command ~path + | [_; "documentSymbol"; path] -> Cli.document_symbol ~path | [_; "hover"; path; line; col; current_file; supports_markdown_links] -> Cli.hover ~state ~path ~pos:(int_of_string line, int_of_string col) @@ -174,6 +174,9 @@ let main () = | "true" -> true | _ -> false) | [_; "inlayHint"; path; line_start; line_end; max_length] -> + let max_length = + try Some (int_of_string max_length) with Failure _ -> None + in Cli.inlayhint ~state ~path ~pos:(int_of_string line_start, int_of_string line_end) ~max_length ~debug @@ -214,9 +217,7 @@ let main () = | [_; "semanticTokens"; current_file] -> Cli.semantic_tokens ~path:current_file | [_; "createInterface"; path; cmi_file] -> - `String (Create_interface.command ~state ~path ~cmi_file) - |> Yojson.Safe.pretty_to_string ~std:true - |> print_endline + Cli.create_interface ~path ~cmi_file | [_; "format"; path] -> Cli.format ~path | [_; "test"; path] -> Cli.test ~state ~path | [_; "cmt"; rescript_json; cmt_path] -> diff --git a/analysis/src/cli.ml b/analysis/src/cli.ml index 7622bf5c9ec..a30e36530a7 100644 --- a/analysis/src/cli.ml +++ b/analysis/src/cli.ml @@ -78,7 +78,9 @@ let code_action ~state ~path ~start_pos ~end_pos ~current_file ~debug = | None -> print_null () | Some source -> Xform.extract_code_actions ~state ~path ~start_pos ~end_pos ~source - ~kind_file ~debug + ~kind_file + ~full:(Cmt.load_full_cmt_from_path ~state ~path) + ~debug |> List.map (fun c -> Lsp.Types.CodeAction.yojson_of_t c) |> print_list @@ -161,6 +163,25 @@ let semantic_tokens ~path = let tokens = Semantic_tokens.semantic_tokens ~source ~kind_file in Lsp.Types.SemanticTokens.yojson_of_t tokens |> print_string +let document_symbol ~path = + match Files.read_file path with + | None -> print_null () + | Some source -> + let kind_file = Files.classify_source_file path in + let symbols = Document_symbol.get_symbols ~source ~kind_file in + print_list (symbols |> List.map Lsp.Types.DocumentSymbol.yojson_of_t) + +let create_interface ~path ~cmi_file = + let result = + match Files.read_file path with + | None -> "" + | Some source -> ( + match Create_interface.command ~source ~cmi_file with + | Ok content -> content + | Error _ -> "") + in + Printf.printf "%s" result + let test ~state ~path = Uri.strip_path := true; match Files.read_file path with @@ -247,7 +268,7 @@ let test ~state ~path = Dce_command.command () | "doc" -> print_endline ("DocumentSymbol " ^ path); - Document_symbol.command ~path + document_symbol ~path | "hig" -> print_endline ("Highlight " ^ path); let source = Files.read_file path |> Option.get in @@ -281,7 +302,7 @@ let test ~state ~path = let dir = dirname path in dir ++ parent_dir_name ++ "lib" ++ "bs" ++ "src" ++ name in - Printf.printf "%s" (Create_interface.command ~state ~path ~cmi_file) + create_interface ~path ~cmi_file | "ref" -> print_endline ("References " ^ path ^ " " ^ string_of_int line ^ ":" @@ -323,10 +344,11 @@ let test ~state ~path = let source = Files.read_file current_file |> Option.value ~default:"" in + let full = Cmt.load_full_cmt_from_path ~state ~path in let kind_file = Files.classify_source_file current_file in let code_actions = Xform.extract_code_actions ~state ~path ~start_pos ~end_pos - ~source ~kind_file ~debug:true + ~source ~kind_file ~full ~debug:true in Sys.remove current_file; code_actions @@ -404,8 +426,8 @@ let test ~state ~path = print_endline ("Inlay Hint " ^ path ^ " " ^ string_of_int line_start ^ ":" ^ string_of_int line_end); - inlayhint ~state ~path ~pos:(line_start, line_end) ~max_length:"25" - ~debug:false + inlayhint ~state ~path ~pos:(line_start, line_end) + ~max_length:(Some 25) ~debug:false | "cle" -> print_endline ("Code Lens " ^ path); code_lens ~state ~path ~debug:false diff --git a/analysis/src/create_interface.ml b/analysis/src/create_interface.ml index 9805a65e3a5..506b52cc778 100644 --- a/analysis/src/create_interface.ml +++ b/analysis/src/create_interface.ml @@ -1,9 +1,4 @@ module Source_file_extractor = struct - let create ~path = - match Files.read_file path with - | None -> [||] - | Some text -> text |> String.split_on_char '\n' |> Array.of_list - let extract lines ~pos_start ~pos_end = let line_start, col_start = pos_start in let line_end, col_end = pos_end in @@ -319,11 +314,9 @@ let print_signature ~extractor ~signature = process_signature ~indent:"" signature; Buffer.contents buf -let command ~state ~path ~cmi_file = +let command ~source ~cmi_file = match Shared.try_read_cmi cmi_file with | Some cmi_info -> - (* For reading the config *) - ignore (Cmt.load_full_cmt_from_path ~state ~path); - let extractor = Source_file_extractor.create ~path in - print_signature ~extractor ~signature:cmi_info.cmi_sign - | None -> "" + let extractor = source |> String.split_on_char '\n' |> Array.of_list in + Ok (print_signature ~extractor ~signature:cmi_info.cmi_sign) + | None -> Error ("Failed to read cmi file " ^ cmi_file) diff --git a/analysis/src/document_symbol.ml b/analysis/src/document_symbol.ml index 49ae7b32d73..5fec0db5d78 100644 --- a/analysis/src/document_symbol.ml +++ b/analysis/src/document_symbol.ml @@ -1,6 +1,6 @@ (* https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol *) -let command ~path = +let get_symbols ~source ~kind_file = let symbols = ref [] in let add_symbol name loc kind = if @@ -115,17 +115,18 @@ let command ~path = } in - (if Filename.check_suffix path ".res" then + (if kind_file = Files.Res then let parser = - Res_driver.parsing_engine.parse_implementation ~for_printer:false + Res_driver.parsing_engine.parse_implementation_from_source + ~for_printer:false in - let {Res_driver.parsetree = structure} = parser ~filename:path in + let {Res_driver.parsetree = structure} = parser ~source in iterator.structure iterator structure |> ignore else let parser = - Res_driver.parsing_engine.parse_interface ~for_printer:false + Res_driver.parsing_engine.parse_interface_from_source ~for_printer:false in - let {Res_driver.parsetree = signature} = parser ~filename:path in + let {Res_driver.parsetree = signature} = parser ~source in iterator.signature iterator signature |> ignore); let is_inside ({ @@ -182,9 +183,4 @@ let command ~path = |> add_sorted_symbols_to_children ~sorted_symbols:rest in let sorted_symbols = !symbols |> List.sort compare_symbol in - let symbols_with_children = - [] |> add_sorted_symbols_to_children ~sorted_symbols - in - `List (symbols_with_children |> List.map Lsp.Types.DocumentSymbol.yojson_of_t) - |> Yojson.Safe.pretty_to_string ~std:true - |> print_endline + [] |> add_sorted_symbols_to_children ~sorted_symbols diff --git a/analysis/src/hint.ml b/analysis/src/hint.ml index df5cc3625ed..7206a6beb8f 100644 --- a/analysis/src/hint.ml +++ b/analysis/src/hint.ml @@ -32,7 +32,6 @@ let loc_item_to_type_hint ~state ~full:{file; package} loc_item = | _ -> None let inlay ~source ~kind_file ~pos ~max_length ~full ~state ~debug = - let maxlen = try Some (int_of_string max_length) with Failure _ -> None in let hints = ref [] in let start_line, end_line = pos in let push loc kind = @@ -103,7 +102,7 @@ let inlay ~source ~kind_file ~pos ~max_length ~full ~state ~debug = Lsp.Types.InlayHint.create ~position ~kind ~paddingLeft:true ~paddingRight:false ~label:(`String label) () in - match maxlen with + match max_length with | Some value -> if String.length label > value then None else Some result | None -> Some result) diff --git a/analysis/src/xform.ml b/analysis/src/xform.ml index 3f3320928d2..ac12e160357 100644 --- a/analysis/src/xform.ml +++ b/analysis/src/xform.ml @@ -915,7 +915,7 @@ let parse_interface ~source = (structure, print_signature_item) let extract_code_actions ~state ~path ~start_pos ~end_pos ~source ~kind_file - ~debug = + ~full ~debug = let pos = start_pos in let code_actions = ref [] in match kind_file with @@ -934,7 +934,7 @@ let extract_code_actions ~state ~path ~start_pos ~end_pos ~source ~kind_file (* This Code Action needs type info *) let () = - match Cmt.load_full_cmt_from_path ~state ~path with + match full with | Some full -> Add_type_annotation.xform ~path ~pos ~full ~structure ~code_actions ~debug; diff --git a/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.resi.txt b/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.resi.txt index 3937e7937d5..02ee2e56884 100644 --- a/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.resi.txt +++ b/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.resi.txt @@ -1,4 +1,5 @@ Xform not_compiled/DocTemplate.resi 3:3 +can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -16,6 +17,7 @@ type rec t = A | B and e = C Xform not_compiled/DocTemplate.resi 6:15 +can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -31,6 +33,7 @@ newText: @unboxed type name = Name(string) Xform not_compiled/DocTemplate.resi 8:4 +can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -46,6 +49,7 @@ newText: let a: int Xform not_compiled/DocTemplate.resi 10:4 +can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -61,6 +65,7 @@ newText: let inc: int => int Xform not_compiled/DocTemplate.resi 12:7 +can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -80,6 +85,7 @@ module T: { } Xform not_compiled/DocTemplate.resi 14:6 +can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -95,6 +101,7 @@ newText: let b: int Xform not_compiled/DocTemplate.resi 18:2 +can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi From 6d265680eb0b57bc83e5a4c18e6b01fccf1046dd Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Mon, 8 Jun 2026 23:23:23 -0300 Subject: [PATCH 2/2] add condition to print `can't find module XX` --- analysis/src/cmt.ml | 3 ++- .../tests/not_compiled/expected/DocTemplate.res.txt | 7 ------- .../tests/not_compiled/expected/DocTemplate.resi.txt | 7 ------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/analysis/src/cmt.ml b/analysis/src/cmt.ml index 6462b682086..a2c5e92eed9 100644 --- a/analysis/src/cmt.ml +++ b/analysis/src/cmt.ml @@ -50,7 +50,8 @@ let full_from_uri ~state ~uri = let cmt = get_cmt_path ~uri paths in full_for_cmt ~module_name ~package ~uri cmt | None -> - prerr_endline ("can't find module " ^ module_name); + if Debug.verbose () then + prerr_endline ("can't find module " ^ module_name); None)) let full_from_module ~package ~module_name = diff --git a/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.res.txt b/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.res.txt index 2e222e81d67..5921931b5e1 100644 --- a/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.res.txt +++ b/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.res.txt @@ -1,5 +1,4 @@ Xform not_compiled/DocTemplate.res 3:3 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.res @@ -17,7 +16,6 @@ type rec t = A | B and e = C Xform not_compiled/DocTemplate.res 6:15 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.res @@ -33,7 +31,6 @@ newText: @unboxed type name = Name(string) Xform not_compiled/DocTemplate.res 8:4 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.res @@ -49,7 +46,6 @@ newText: let a = 1 Xform not_compiled/DocTemplate.res 10:4 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.res @@ -65,7 +61,6 @@ newText: let inc = x => x + 1 Xform not_compiled/DocTemplate.res 12:7 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.res @@ -109,7 +104,6 @@ newText: Xform not_compiled/DocTemplate.res 14:6 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.res @@ -149,7 +143,6 @@ newText: Xform not_compiled/DocTemplate.res 18:2 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.res diff --git a/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.resi.txt b/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.resi.txt index 02ee2e56884..3937e7937d5 100644 --- a/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.resi.txt +++ b/tests/analysis_tests/tests/not_compiled/expected/DocTemplate.resi.txt @@ -1,5 +1,4 @@ Xform not_compiled/DocTemplate.resi 3:3 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -17,7 +16,6 @@ type rec t = A | B and e = C Xform not_compiled/DocTemplate.resi 6:15 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -33,7 +31,6 @@ newText: @unboxed type name = Name(string) Xform not_compiled/DocTemplate.resi 8:4 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -49,7 +46,6 @@ newText: let a: int Xform not_compiled/DocTemplate.resi 10:4 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -65,7 +61,6 @@ newText: let inc: int => int Xform not_compiled/DocTemplate.resi 12:7 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -85,7 +80,6 @@ module T: { } Xform not_compiled/DocTemplate.resi 14:6 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi @@ -101,7 +95,6 @@ newText: let b: int Xform not_compiled/DocTemplate.resi 18:2 -can't find module DocTemplate Hit: Add Documentation template TextDocumentEdit: DocTemplate.resi