From 20bb352b0241bd0b626aaf3876edc9b25dd74489 Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Fri, 24 Mar 2023 18:08:48 -0300 Subject: [PATCH 1/7] make analysis lib --- .github/workflows/ci.yml | 1 - .gitignore | 4 +- .vscode/settings.json | 2 +- CONTRIBUTING.md | 8 +- .../rescript-vscode.opam => analysis.opam | 7 +- analysis/Makefile | 2 +- analysis/bin/dune | 6 + analysis/{src/Cli.ml => bin/main.ml} | 12 +- analysis/dune | 2 +- analysis/reanalyze/examples/deadcode/test.sh | 4 +- .../reanalyze/examples/termination/test.sh | 2 +- analysis/src/Commands.ml | 262 ++++-------------- analysis/src/Completions.ml | 8 + analysis/src/CreateInterface.ml | 4 +- analysis/src/Definition.ml | 28 ++ analysis/src/Diagnostics.ml | 19 +- analysis/src/DocumentSymbol.ml | 2 +- analysis/src/Formatter.ml | 21 ++ analysis/src/Hint.ml | 32 +-- analysis/src/Hover.ml | 47 +++- analysis/src/Protocol.ml | 17 +- analysis/src/References.ml | 20 ++ analysis/src/Rename.ml | 58 ++++ analysis/src/SemanticTokens.ml | 2 +- analysis/src/TypeDefinition.ml | 12 + analysis/src/dune | 8 +- analysis/tests/src/expected/Cross.res.txt | 41 +-- .../tests/src/expected/References.res.txt | 27 +- .../expected/ReferencesWithInterface.res.txt | 8 +- .../expected/ReferencesWithInterface.resi.txt | 8 +- analysis/tests/src/expected/Rename.res.txt | 12 +- .../src/expected/RenameWithInterface.res.txt | 12 +- .../src/expected/RenameWithInterface.resi.txt | 12 +- analysis/dune-project => dune-project | 8 +- 34 files changed, 340 insertions(+), 378 deletions(-) rename analysis/rescript-vscode.opam => analysis.opam (84%) create mode 100644 analysis/bin/dune rename analysis/{src/Cli.ml => bin/main.ml} (94%) create mode 100644 analysis/src/Definition.ml create mode 100644 analysis/src/Formatter.ml create mode 100644 analysis/src/Rename.ml create mode 100644 analysis/src/TypeDefinition.ml rename analysis/dune-project => dune-project (80%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e186337a0..4b15036e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,6 @@ jobs: - name: Build and test run: opam exec -- make test - working-directory: analysis # Also avoids artifacts upload permission loss: # https://github.com/actions/upload-artifact/tree/ee69f02b3dfdecd58bb31b4d133da38ba6fe3700#permission-loss diff --git a/.gitignore b/.gitignore index 8590b9e9a..fdb06b70b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ node_modules examples/*/lib analysis/tests/lib analysis/tests/.bsb.lock -analysis/_build analysis/tests/.merlin analysis/rescript-editor-analysis.exe -analysis/_opam +_build +_opam diff --git a/.vscode/settings.json b/.vscode/settings.json index 11909bf1d..c9a75752e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,6 @@ }, "ocaml.sandbox": { "kind": "opam", - "switch": "${workspaceFolder:rescript-vscode}/analysis" + "switch": "${workspaceFolder:rescript-vscode}" } } \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9bbc16c1e..8655cd09b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,8 @@ Thanks for your interest. Below is an informal spec of how the plugin's server c │ └── src │ └── extension.ts // Language Client entry point ├── analysis // Native binary powering hover, autocomplete, etc. -│ ├── src +│ ├── bin // Analysis binary +│ ├── src // Analysis Library │ └── rescript-editor-analysis.exe // Dev-time analysis binary ├── package.json // The extension manifest └── server // Language Server. Usable standalone @@ -32,10 +33,7 @@ This is needed for the `analysis` folder, which is native code. ```sh # If you haven't created the switch, do it. OPAM(https://opam.ocaml.org) -opam switch 4.14.0 # can also create local switch with opam switch create . 4.14.0 - -# Install dev dependencies from OPAM -opam install . --deps-only +opam switch create . --deps-only # For IDE support, install the OCaml language server opam install ocaml-lsp-server diff --git a/analysis/rescript-vscode.opam b/analysis.opam similarity index 84% rename from analysis/rescript-vscode.opam rename to analysis.opam index 1645dea53..24d54617c 100644 --- a/analysis/rescript-vscode.opam +++ b/analysis.opam @@ -1,6 +1,6 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" -synopsis: "ReScript vscode support" +synopsis: "ReScript Analysis" maintainer: ["Cristiano Calcagno"] authors: ["Cristiano Calcagno"] homepage: "https://github.com/rescript-lang/rescript-vscode" @@ -10,10 +10,11 @@ depends: [ "ocamlformat" {= "0.22.4"} "cppo" {= "1.6.9"} "reanalyze" {= "2.23.0"} - "dune" + "dune" {>= "3.6"} + "odoc" {with-doc} ] build: [ - ["dune" "subst"] {pinned} + ["dune" "subst"] {dev} [ "dune" "build" diff --git a/analysis/Makefile b/analysis/Makefile index 069dbbbe4..7dea0b02f 100644 --- a/analysis/Makefile +++ b/analysis/Makefile @@ -3,7 +3,7 @@ SHELL = /bin/bash build-analysis-binary: rm -f rescript-editor-analysis.exe dune build - cp _build/install/default/bin/rescript-editor-analysis rescript-editor-analysis.exe + cp ../_build/default/analysis/bin/main.exe rescript-editor-analysis.exe build-tests: make -C tests build diff --git a/analysis/bin/dune b/analysis/bin/dune new file mode 100644 index 000000000..2005419d5 --- /dev/null +++ b/analysis/bin/dune @@ -0,0 +1,6 @@ +(executable + (name main) + (public_name analysis) + (package analysis) + (modes byte exe) + (libraries analysis)) diff --git a/analysis/src/Cli.ml b/analysis/bin/main.ml similarity index 94% rename from analysis/src/Cli.ml rename to analysis/bin/main.ml index 407e9cde2..ca1595145 100644 --- a/analysis/src/Cli.ml +++ b/analysis/bin/main.ml @@ -1,3 +1,4 @@ +open Analysis let help = {| **Private CLI For rescript-vscode usage only** @@ -102,7 +103,7 @@ let main () = Commands.typeDefinition ~path ~pos:(int_of_string line, int_of_string col) ~debug:false - | [_; "documentSymbol"; path] -> DocumentSymbol.command ~path + | [_; "documentSymbol"; path] -> Commands.documentSymbol ~path | [_; "hover"; path; line; col; currentFile; supportsMarkdownLinks] -> Commands.hover ~path ~pos:(int_of_string line, int_of_string col) @@ -140,13 +141,10 @@ let main () = Commands.rename ~path ~pos:(int_of_string line, int_of_string col) ~newName ~debug:false - | [_; "semanticTokens"; currentFile] -> - SemanticTokens.semanticTokens ~currentFile + | [_; "semanticTokens"; currentFile] -> Commands.semanticTokens ~currentFile | [_; "createInterface"; path; cmiFile] -> - Printf.printf "\"%s\"" - (Json.escape (CreateInterface.command ~path ~cmiFile)) - | [_; "format"; path] -> - Printf.printf "\"%s\"" (Json.escape (Commands.format ~path)) + Commands.createInterface ~path ~cmiFile + | [_; "format"; path] -> Commands.format ~path | [_; "test"; path] -> Cfg.supportsSnippets := true; Commands.test ~path diff --git a/analysis/dune b/analysis/dune index 5ce956be5..67bb821b5 100644 --- a/analysis/dune +++ b/analysis/dune @@ -1,4 +1,4 @@ -(dirs src reanalyze vendor) +(dirs src bin reanalyze vendor) (env (dev diff --git a/analysis/reanalyze/examples/deadcode/test.sh b/analysis/reanalyze/examples/deadcode/test.sh index ea28ad778..358a7ffe6 100755 --- a/analysis/reanalyze/examples/deadcode/test.sh +++ b/analysis/reanalyze/examples/deadcode/test.sh @@ -6,7 +6,7 @@ else exclude_dirs="src/exception" suppress="src/ToSuppress.res" fi -dune exec rescript-editor-analysis -- reanalyze -config -debug -ci -exclude-paths $exclude_dirs -live-names globallyLive1 -live-names globallyLive2,globallyLive3 -suppress $suppress > $output +dune exec analysis -- reanalyze -config -debug -ci -exclude-paths $exclude_dirs -live-names globallyLive1 -live-names globallyLive2,globallyLive3 -suppress $suppress > $output # CI. We use LF, and the CI OCaml fork prints CRLF. Convert. if [ "$RUNNER_OS" == "Windows" ]; then perl -pi -e 's/\r\n/\n/g' -- $output @@ -18,7 +18,7 @@ if [ "$RUNNER_OS" == "Windows" ]; then else unsuppress_dirs="src/exception" fi -dune exec rescript-editor-analysis -- reanalyze -exception -ci -suppress src -unsuppress $unsuppress_dirs > $output +dune exec analysis -- reanalyze -exception -ci -suppress src -unsuppress $unsuppress_dirs > $output # CI. We use LF, and the CI OCaml fork prints CRLF. Convert. if [ "$RUNNER_OS" == "Windows" ]; then perl -pi -e 's/\r\n/\n/g' -- $output diff --git a/analysis/reanalyze/examples/termination/test.sh b/analysis/reanalyze/examples/termination/test.sh index 3897ae695..eeb05a3d6 100755 --- a/analysis/reanalyze/examples/termination/test.sh +++ b/analysis/reanalyze/examples/termination/test.sh @@ -1,5 +1,5 @@ output="expected/termination.txt" -dune exec rescript-editor-analysis -- reanalyze -config -ci -debug > $output +dune exec analysis -- reanalyze -config -ci -debug > $output # CI. We use LF, and the CI OCaml fork prints CRLF. Convert. if [ "$RUNNER_OS" == "Windows" ]; then perl -pi -e 's/\r\n/\n/g' -- $output diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 4341c4f82..7abaad5c1 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -1,21 +1,13 @@ let completion ~debug ~path ~pos ~currentFile = - let completions = - match - Completions.getCompletions ~debug ~path ~pos ~currentFile ~forHover:false - with - | None -> [] - | Some (completions, _, _) -> completions - in print_endline - (completions - |> List.map CompletionBackEnd.completionToItem + (Completions.complete ~debug ~path ~pos ~currentFile |> List.map Protocol.stringifyCompletionItem |> Protocol.array) let inlayhint ~path ~pos ~maxLength ~debug = let result = match Hint.inlay ~path ~pos ~maxLength ~debug with - | Some hints -> hints |> Protocol.array + | Some hints -> hints |> List.map Protocol.stringifyHint |> Protocol.array | None -> Protocol.null in print_endline result @@ -23,53 +15,16 @@ let inlayhint ~path ~pos ~maxLength ~debug = let codeLens ~path ~debug = let result = match Hint.codeLens ~path ~debug with - | Some lens -> lens |> Protocol.array + | Some lens -> lens |> List.map Protocol.stringifyCodeLens |> Protocol.array | None -> Protocol.null in print_endline result let hover ~path ~pos ~currentFile ~debug ~supportsMarkdownLinks = - let result = - match Cmt.loadFullCmtFromPath ~path with - | None -> Protocol.null - | Some full -> ( - match References.getLocItem ~full ~pos ~debug with - | None -> ( - if debug then - Printf.printf - "Nothing at that position. Now trying to use completion.\n"; - match - Hover.getHoverViaCompletions ~debug ~path ~pos ~currentFile - ~forHover:true ~supportsMarkdownLinks - with - | None -> Protocol.null - | Some hover -> hover) - | Some locItem -> ( - let isModule = - match locItem.locType with - | LModule _ | TopLevelModule _ -> true - | TypeDefinition _ | Typed _ | Constant _ -> false - in - let uriLocOpt = References.definitionForLocItem ~full locItem in - let skipZero = - match uriLocOpt with - | None -> false - | Some (_, loc) -> - let isInterface = full.file.uri |> Uri.isInterface in - let posIsZero {Lexing.pos_lnum; pos_bol; pos_cnum} = - (not isInterface) && pos_lnum = 1 && pos_cnum - pos_bol = 0 - in - (* Skip if range is all zero, unless it's a module *) - (not isModule) && posIsZero loc.loc_start && posIsZero loc.loc_end - in - if skipZero then Protocol.null - else - let hoverText = Hover.newHover ~supportsMarkdownLinks ~full locItem in - match hoverText with - | None -> Protocol.null - | Some s -> Protocol.stringifyHover s)) - in - print_endline result + (match Hover.hover ~path ~pos ~currentFile ~debug ~supportsMarkdownLinks with + | None -> Protocol.null + | Some content -> content |> Protocol.stringifyHover) + |> print_endline let signatureHelp ~path ~pos ~currentFile ~debug = let result = @@ -85,180 +40,64 @@ let codeAction ~path ~pos ~currentFile ~debug = |> CodeActions.stringifyCodeActions |> print_endline let definition ~path ~pos ~debug = - let locationOpt = - match Cmt.loadFullCmtFromPath ~path with - | None -> None - | Some full -> ( - match References.getLocItem ~full ~pos ~debug with - | None -> None - | Some locItem -> ( - match References.definitionForLocItem ~full locItem with - | None -> None - | Some (uri, loc) -> - let isInterface = full.file.uri |> Uri.isInterface in - let posIsZero {Lexing.pos_lnum; pos_bol; pos_cnum} = - (* range is zero *) - pos_lnum = 1 && pos_cnum - pos_bol = 0 - in - let isModule = - match locItem.locType with - | LModule _ | TopLevelModule _ -> true - | TypeDefinition _ | Typed _ | Constant _ -> false - in - let skipLoc = - (not isModule) && (not isInterface) && posIsZero loc.loc_start - && posIsZero loc.loc_end - in - if skipLoc then None - else - Some - {Protocol.uri = Uri.toString uri; range = Utils.cmtLocToRange loc} - )) - in - print_endline - (match locationOpt with - | None -> Protocol.null - | Some location -> location |> Protocol.stringifyLocation) + (match Definition.definition ~path ~pos ~debug with + | Some loc -> loc |> Protocol.stringifyLocation + | None -> Protocol.null) + |> print_endline let typeDefinition ~path ~pos ~debug = - let maybeLocation = - match Cmt.loadFullCmtFromPath ~path with - | None -> None - | Some full -> ( - match References.getLocItem ~full ~pos ~debug with - | None -> None - | Some locItem -> ( - match References.typeDefinitionForLocItem ~full locItem with - | None -> None - | Some (uri, loc) -> - Some - {Protocol.uri = Uri.toString uri; range = Utils.cmtLocToRange loc})) - in print_endline - (match maybeLocation with + (match TypeDefinition.typeDefinition ~path ~pos ~debug with | None -> Protocol.null | Some location -> location |> Protocol.stringifyLocation) let references ~path ~pos ~debug = - let allLocs = - match Cmt.loadFullCmtFromPath ~path with - | None -> [] - | Some full -> ( - match References.getLocItem ~full ~pos ~debug with - | None -> [] - | Some locItem -> - let allReferences = References.allReferencesForLocItem ~full locItem in - allReferences - |> List.fold_left - (fun acc {References.uri = uri2; locOpt} -> - let loc = - match locOpt with - | Some loc -> loc - | None -> Uri.toTopLevelLoc uri2 - in - Protocol.stringifyLocation - {uri = Uri.toString uri2; range = Utils.cmtLocToRange loc} - :: acc) - []) - in print_endline - (if allLocs = [] then Protocol.null - else "[\n" ^ (allLocs |> String.concat ",\n") ^ "\n]") + (match References.get ~path ~pos ~debug with + | [] -> Protocol.null + | locs -> locs |> List.map Protocol.stringifyLocation |> Protocol.array) let rename ~path ~pos ~newName ~debug = let result = - match Cmt.loadFullCmtFromPath ~path with + match Rename.rename ~path ~pos ~newName ~debug with | None -> Protocol.null - | Some full -> ( - match References.getLocItem ~full ~pos ~debug with - | None -> Protocol.null - | Some locItem -> - let allReferences = References.allReferencesForLocItem ~full locItem in - let referencesToToplevelModules = - allReferences - |> Utils.filterMap (fun {References.uri = uri2; locOpt} -> - if locOpt = None then Some uri2 else None) - in - let referencesToItems = - allReferences - |> Utils.filterMap (function - | {References.uri = uri2; locOpt = Some loc} -> Some (uri2, loc) - | {locOpt = None} -> None) - in - let fileRenames = - referencesToToplevelModules - |> List.map (fun uri -> - let path = Uri.toPath uri in - let dir = Filename.dirname path in - let newPath = - Filename.concat dir (newName ^ Filename.extension path) - in - let newUri = Uri.fromPath newPath in - Protocol. - { - oldUri = uri |> Uri.toString; - newUri = newUri |> Uri.toString; - }) - in - let textDocumentEdits = - let module StringMap = Misc.StringMap in - let textEditsByUri = - referencesToItems - |> List.map (fun (uri, loc) -> (Uri.toString uri, loc)) - |> List.fold_left - (fun acc (uri, loc) -> - let textEdit = - Protocol. - {range = Utils.cmtLocToRange loc; newText = newName} - in - match StringMap.find_opt uri acc with - | None -> StringMap.add uri [textEdit] acc - | Some prevEdits -> - StringMap.add uri (textEdit :: prevEdits) acc) - StringMap.empty - in - StringMap.fold - (fun uri edits acc -> - let textDocumentEdit = - Protocol.{textDocument = {uri; version = None}; edits} - in - textDocumentEdit :: acc) - textEditsByUri [] - in - let fileRenamesString = - fileRenames |> List.map Protocol.stringifyRenameFile - in - let textDocumentEditsString = - textDocumentEdits |> List.map Protocol.stringifyTextDocumentEdit - in - "[\n" - ^ (fileRenamesString @ textDocumentEditsString |> String.concat ",\n") - ^ "\n]") + | Some documentChanges -> + documentChanges + |> List.map (fun (changes : Protocol.documentChanges) -> + match changes with + | RenameFile renames -> + renames |> List.map Protocol.stringifyRenameFile + | TextDocumentEdit textDocumentEdits -> + textDocumentEdits |> List.map Protocol.stringifyTextDocumentEdit) + |> List.flatten |> Protocol.array in print_endline result let format ~path = - if Filename.check_suffix path ".res" then - let {Res_driver.parsetree = structure; comments; diagnostics} = - Res_driver.parsingEngine.parseImplementation ~forPrinter:true - ~filename:path - in - if List.length diagnostics > 0 then "" - else - Res_printer.printImplementation ~width:!Res_cli.ResClflags.width ~comments - structure - else if Filename.check_suffix path ".resi" then - let {Res_driver.parsetree = signature; comments; diagnostics} = - Res_driver.parsingEngine.parseInterface ~forPrinter:true ~filename:path - in - if List.length diagnostics > 0 then "" - else - Res_printer.printInterface ~width:!Res_cli.ResClflags.width ~comments - signature - else "" + let result = + match Formatter.format ~path with + | Some text -> text + | None -> "" + in + Printf.printf "\"%s\"" (Json.escape result) let diagnosticSyntax ~path = - print_endline (Diagnostics.document_syntax ~path |> Protocol.array) + Diagnostics.document_syntax ~path + |> List.map Protocol.stringifyDiagnostic + |> Protocol.array |> print_endline + +let semanticTokens ~currentFile = + Printf.printf "{\"data\":[%s]}" (SemanticTokens.semanticTokens ~currentFile) + +let createInterface ~path ~cmiFile = + (match CreateInterface.command ~path ~cmiFile with + | Some text -> text + | None -> "") + |> Json.escape |> Printf.printf "\"%s\"" + +let documentSymbol ~path = + DocumentSymbol.command ~path + |> Protocol.stringifyDocumentSymbolItems |> print_endline let test ~path = Uri.stripPath := true; @@ -322,7 +161,7 @@ let test ~path = DceCommand.command () | "doc" -> print_endline ("DocumentSymbol " ^ path); - DocumentSymbol.command ~path + documentSymbol ~path | "hig" -> print_endline ("Highlight " ^ path); SemanticTokens.command ~debug:true @@ -352,7 +191,10 @@ let test ~path = let dir = dirname path in dir ++ parent_dir_name ++ "lib" ++ "bs" ++ "src" ++ name in - Printf.printf "%s" (CreateInterface.command ~path ~cmiFile) + Printf.printf "%s" + (match CreateInterface.command ~path ~cmiFile with + | Some text -> text + | None -> "") | "ref" -> print_endline ("References " ^ path ^ " " ^ string_of_int line ^ ":" diff --git a/analysis/src/Completions.ml b/analysis/src/Completions.ml index 42176bb3b..23bcb176a 100644 --- a/analysis/src/Completions.ml +++ b/analysis/src/Completions.ml @@ -20,3 +20,11 @@ let getCompletions ~debug ~path ~pos ~currentFile ~forHover = ~forHover in Some (completables, full, scope))) + +let complete ~debug ~path ~pos ~currentFile = + let completions = + match getCompletions ~debug ~path ~pos ~currentFile ~forHover:false with + | None -> [] + | Some (completions, _, _) -> completions + in + completions |> List.map CompletionBackEnd.completionToItem diff --git a/analysis/src/CreateInterface.ml b/analysis/src/CreateInterface.ml index 25048d4e8..b49a69e13 100644 --- a/analysis/src/CreateInterface.ml +++ b/analysis/src/CreateInterface.ml @@ -411,5 +411,5 @@ let command ~path ~cmiFile = match Shared.tryReadCmi cmiFile with | Some cmi_info -> let extractor = SourceFileExtractor.create ~path in - printSignature ~extractor ~signature:cmi_info.cmi_sign - | None -> "" + Some (printSignature ~extractor ~signature:cmi_info.cmi_sign) + | None -> None diff --git a/analysis/src/Definition.ml b/analysis/src/Definition.ml new file mode 100644 index 000000000..32494fe63 --- /dev/null +++ b/analysis/src/Definition.ml @@ -0,0 +1,28 @@ +let definition ~path ~pos ~debug = + match Cmt.loadFullCmtFromPath ~path with + | None -> None + | Some full -> ( + match References.getLocItem ~full ~pos ~debug with + | None -> None + | Some locItem -> ( + match References.definitionForLocItem ~full locItem with + | None -> None + | Some (uri, loc) -> + let isInterface = full.file.uri |> Uri.isInterface in + let posIsZero {Lexing.pos_lnum; pos_bol; pos_cnum} = + (* range is zero *) + pos_lnum = 1 && pos_cnum - pos_bol = 0 + in + let isModule = + match locItem.locType with + | LModule _ | TopLevelModule _ -> true + | TypeDefinition _ | Typed _ | Constant _ -> false + in + let skipLoc = + (not isModule) && (not isInterface) && posIsZero loc.loc_start + && posIsZero loc.loc_end + in + if skipLoc then None + else + Some + {Protocol.uri = Uri.toString uri; range = Utils.cmtLocToRange loc})) diff --git a/analysis/src/Diagnostics.ml b/analysis/src/Diagnostics.ml index b4c073425..060335782 100644 --- a/analysis/src/Diagnostics.ml +++ b/analysis/src/Diagnostics.ml @@ -2,22 +2,13 @@ let document_syntax ~path = let get_diagnostics diagnostics = diagnostics |> List.map (fun diagnostic -> - let _, startline, startcol = - Location.get_pos_info (Res_diagnostics.getStartPos diagnostic) + let start = + Utils.cmtPosToPosition (Res_diagnostics.getStartPos diagnostic) in - let _, endline, endcol = - Location.get_pos_info (Res_diagnostics.getEndPos diagnostic) + let end_ = + Utils.cmtPosToPosition (Res_diagnostics.getEndPos diagnostic) in - Protocol.stringifyDiagnostic - { - range = - { - start = {line = startline - 1; character = startcol}; - end_ = {line = endline - 1; character = endcol}; - }; - message = Res_diagnostics.explain diagnostic; - severity = 1; - }) + Protocol.{range = {start; end_}; message = diagnostic; severity = 1}) in if FindFiles.isImplementation path then let parseImplementation = diff --git a/analysis/src/DocumentSymbol.ml b/analysis/src/DocumentSymbol.ml index 356541c5f..3ea3f8b21 100644 --- a/analysis/src/DocumentSymbol.ml +++ b/analysis/src/DocumentSymbol.ml @@ -197,4 +197,4 @@ let command ~path = in let sortedSymbols = !symbols |> List.sort compareSymbol in let symbolsWithChildren = [] |> addSortedSymbolsToChildren ~sortedSymbols in - print_endline (Protocol.stringifyDocumentSymbolItems symbolsWithChildren) + symbolsWithChildren diff --git a/analysis/src/Formatter.ml b/analysis/src/Formatter.ml new file mode 100644 index 000000000..7e732ba54 --- /dev/null +++ b/analysis/src/Formatter.ml @@ -0,0 +1,21 @@ +let format ~path = + if Filename.check_suffix path ".res" then + let {Res_driver.parsetree = structure; comments; diagnostics} = + Res_driver.parsingEngine.parseImplementation ~forPrinter:true + ~filename:path + in + if List.length diagnostics > 0 then None + else + Some + (Res_printer.printImplementation ~width:!Res_cli.ResClflags.width + ~comments structure) + else if Filename.check_suffix path ".resi" then + let {Res_driver.parsetree = signature; comments; diagnostics} = + Res_driver.parsingEngine.parseInterface ~forPrinter:true ~filename:path + in + if List.length diagnostics > 0 then None + else + Some + (Res_printer.printInterface ~width:!Res_cli.ResClflags.width ~comments + signature) + else None diff --git a/analysis/src/Hint.ml b/analysis/src/Hint.ml index 227d70f38..06f59a431 100644 --- a/analysis/src/Hint.ml +++ b/analysis/src/Hint.ml @@ -95,7 +95,7 @@ let inlay ~path ~pos ~maxLength ~debug = match locItemToTypeHint locItem ~full with | Some label -> ( let result = - Protocol.stringifyHint + Protocol. { kind = inlayKindToNumber hintKind; position; @@ -153,21 +153,21 @@ let codeLens ~path ~debug = with | Some {locType = Typed (_, typeExpr, _)} -> Some - (Protocol.stringifyCodeLens - { - range; - command = - Some - { - (* Code lenses can run commands. An empty command string means we just want the editor - to print the text, not link to running a command. *) - command = ""; - (* Print the type with a huge line width, because the code lens always prints on a - single line in the editor. *) - title = - typeExpr |> Shared.typeToString ~lineWidth:400; - }; - }) + Protocol. + { + range; + command = + Some + { + (* Code lenses can run commands. An empty command string means we just want the editor + to print the text, not link to running a command. *) + command = ""; + (* Print the type with a huge line width, because the code lens always prints on a + single line in the editor. *) + title = + typeExpr |> Shared.typeToString ~lineWidth:400; + }; + } | _ -> None) in Some result diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml index d0df179a9..beac31095 100644 --- a/analysis/src/Hover.ml +++ b/analysis/src/Hover.ml @@ -133,7 +133,7 @@ let getHoverViaCompletions ~debug ~path ~pos ~currentFile ~forHover (if typString = "" then [] else [Markdown.codeBlock typString]) @ docstring in - Some (Protocol.stringifyHover (String.concat "\n\n" parts)) + Some (String.concat "\n\n" parts) | {kind = Field _; env; docstring} :: _ -> ( let opens = CompletionBackEnd.getOpens ~debug ~rawOpens ~package ~env in match @@ -145,7 +145,7 @@ let getHoverViaCompletions ~debug ~path ~pos ~currentFile ~forHover hoverWithExpandedTypes ~file ~package ~supportsMarkdownLinks typ in let parts = typeString :: docstring in - Some (Protocol.stringifyHover (String.concat "\n\n" parts)) + Some (String.concat "\n\n" parts) | None -> None) | {env} :: _ -> ( let opens = CompletionBackEnd.getOpens ~debug ~rawOpens ~package ~env in @@ -157,7 +157,7 @@ let getHoverViaCompletions ~debug ~path ~pos ~currentFile ~forHover let typeString = hoverWithExpandedTypes ~file ~package ~supportsMarkdownLinks typ in - Some (Protocol.stringifyHover typeString) + Some typeString | None -> None) | _ -> None) @@ -250,3 +250,44 @@ let newHover ~full:{file; package} ~supportsMarkdownLinks locItem = typeString :: docstring) in Some (String.concat "\n\n" parts) + +let hover ~path ~pos ~currentFile ~debug ~supportsMarkdownLinks = + let value = + match Cmt.loadFullCmtFromPath ~path with + | None -> None + | Some full -> ( + match References.getLocItem ~full ~pos ~debug with + | None -> ( + if debug then + Printf.printf + "Nothing at that position. Now trying to use completion.\n"; + match + getHoverViaCompletions ~debug ~path ~pos ~currentFile ~forHover:true + ~supportsMarkdownLinks + with + | None -> None + | Some hover -> Some hover) + | Some locItem -> + let isModule = + match locItem.locType with + | LModule _ | TopLevelModule _ -> true + | TypeDefinition _ | Typed _ | Constant _ -> false + in + let uriLocOpt = References.definitionForLocItem ~full locItem in + let skipZero = + match uriLocOpt with + | None -> false + | Some (_, loc) -> + let isInterface = full.file.uri |> Uri.isInterface in + let posIsZero {Lexing.pos_lnum; pos_bol; pos_cnum} = + (not isInterface) && pos_lnum = 1 && pos_cnum - pos_bol = 0 + in + (* Skip if range is all zero, unless it's a module *) + (not isModule) && posIsZero loc.loc_start && posIsZero loc.loc_end + in + if skipZero then None else newHover ~supportsMarkdownLinks ~full locItem + ) + in + match value with + | Some value -> Some Protocol.{kind = "markdown"; value} + | None -> None diff --git a/analysis/src/Protocol.ml b/analysis/src/Protocol.ml index 1f23f522e..9b30dc5d8 100644 --- a/analysis/src/Protocol.ml +++ b/analysis/src/Protocol.ml @@ -62,7 +62,7 @@ type documentSymbolItem = { type renameFile = {oldUri: string; newUri: string} type textEdit = {range: range; newText: string} -type diagnostic = {range: range; message: string; severity: int} +type diagnostic = {range: range; message: Res_diagnostics.t; severity: int} type optionalVersionedTextDocumentIdentifier = { version: int option; @@ -74,6 +74,12 @@ type textDocumentEdit = { edits: textEdit list; } +type documentChanges = + | RenameFile of renameFile list + | TextDocumentEdit of textDocumentEdit list + +type workspaceEdit = {documentChanges: documentChanges} + type codeActionEdit = {documentChanges: textDocumentEdit list} type codeActionKind = RefactorRewrite @@ -138,9 +144,8 @@ let stringifyCompletionItem c = Some (Printf.sprintf "%i" (insertTextFormatToInt insertTextFormat)) ); ] -let stringifyHover value = - Printf.sprintf {|{"contents": %s}|} - (stringifyMarkupContent {kind = "markdown"; value}) +let stringifyHover {kind; value} = + Printf.sprintf {|{"contents": %s}|} (stringifyMarkupContent {kind; value}) let stringifyLocation (h : location) = Printf.sprintf {|{"uri": "%s", "range": %s}|} (Json.escape h.uri) @@ -309,4 +314,6 @@ let stringifyDiagnostic d = "severity": %d, "source": "ReScript" }|} - (stringifyRange d.range) (Json.escape d.message) d.severity + (stringifyRange d.range) + (Json.escape (Res_diagnostics.explain d.message)) + d.severity diff --git a/analysis/src/References.ml b/analysis/src/References.ml index e19439a20..324b267eb 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -571,3 +571,23 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = ^ " and stamp " ^ string_of_int stamp ^ " and tip " ^ Tip.toString tip); forLocalStamp ~full stamp tip))) + +let get ~path ~pos ~debug = + match Cmt.loadFullCmtFromPath ~path with + | None -> [] + | Some full -> ( + match getLocItem ~full ~pos ~debug with + | None -> [] + | Some locItem -> + let allReferences = allReferencesForLocItem ~full locItem in + allReferences + |> List.fold_left + (fun acc {uri = uri2; locOpt} -> + let loc = + match locOpt with + | Some loc -> loc + | None -> Uri.toTopLevelLoc uri2 + in + Protocol.{uri = Uri.toString uri2; range = Utils.cmtLocToRange loc} + :: acc) + []) diff --git a/analysis/src/Rename.ml b/analysis/src/Rename.ml new file mode 100644 index 000000000..27fa421e2 --- /dev/null +++ b/analysis/src/Rename.ml @@ -0,0 +1,58 @@ +let rename ~path ~pos ~newName ~debug = + match Cmt.loadFullCmtFromPath ~path with + | None -> None + | Some full -> ( + match References.getLocItem ~full ~pos ~debug with + | None -> None + | Some locItem -> + let allReferences = References.allReferencesForLocItem ~full locItem in + let referencesToToplevelModules = + allReferences + |> Utils.filterMap (fun {References.uri = uri2; locOpt} -> + if locOpt = None then Some uri2 else None) + in + let referencesToItems = + allReferences + |> Utils.filterMap (function + | {References.uri = uri2; locOpt = Some loc} -> Some (uri2, loc) + | {locOpt = None} -> None) + in + let fileRenames = + referencesToToplevelModules + |> List.map (fun uri -> + let path = Uri.toPath uri in + let dir = Filename.dirname path in + let newPath = + Filename.concat dir (newName ^ Filename.extension path) + in + let newUri = Uri.fromPath newPath in + Protocol. + {oldUri = uri |> Uri.toString; newUri = newUri |> Uri.toString}) + in + let textDocumentEdits = + let module StringMap = Misc.StringMap in + let textEditsByUri = + referencesToItems + |> List.map (fun (uri, loc) -> (Uri.toString uri, loc)) + |> List.fold_left + (fun acc (uri, loc) -> + let textEdit = + Protocol.{range = Utils.cmtLocToRange loc; newText = newName} + in + match StringMap.find_opt uri acc with + | None -> StringMap.add uri [textEdit] acc + | Some prevEdits -> + StringMap.add uri (textEdit :: prevEdits) acc) + StringMap.empty + in + StringMap.fold + (fun uri edits acc -> + let textDocumentEdit = + Protocol.{textDocument = {uri; version = None}; edits} + in + textDocumentEdit :: acc) + textEditsByUri [] + in + let renames = Protocol.RenameFile fileRenames in + let textDocumentEdits = Protocol.TextDocumentEdit textDocumentEdits in + Some ([renames] @ [textDocumentEdits])) diff --git a/analysis/src/SemanticTokens.ml b/analysis/src/SemanticTokens.ml index 1e7b54160..3c03e0795 100644 --- a/analysis/src/SemanticTokens.ml +++ b/analysis/src/SemanticTokens.ml @@ -457,4 +457,4 @@ let command ~debug ~emitter ~path = let semanticTokens ~currentFile = let emitter = Token.createEmitter () in command ~emitter ~debug:false ~path:currentFile; - Printf.printf "{\"data\":[%s]}" (Token.emit emitter) + Token.emit emitter diff --git a/analysis/src/TypeDefinition.ml b/analysis/src/TypeDefinition.ml new file mode 100644 index 000000000..858b5bfb5 --- /dev/null +++ b/analysis/src/TypeDefinition.ml @@ -0,0 +1,12 @@ +let typeDefinition ~path ~pos ~debug = + match Cmt.loadFullCmtFromPath ~path with + | None -> None + | Some full -> ( + match References.getLocItem ~full ~pos ~debug with + | None -> None + | Some locItem -> ( + match References.typeDefinitionForLocItem ~full locItem with + | None -> None + | Some (uri, loc) -> + Some {Protocol.uri = Uri.toString uri; range = Utils.cmtLocToRange loc}) + ) diff --git a/analysis/src/dune b/analysis/src/dune index bc16552c3..c1bd828c1 100644 --- a/analysis/src/dune +++ b/analysis/src/dune @@ -1,9 +1,5 @@ -(executable - (public_name rescript-editor-analysis) - (modes byte exe) - ; The main module that will become the binary. - (name Cli) +(library + (name analysis) (flags (-w "+6+26+27+32+33+39")) - ; Depends on: (libraries unix str ext ml jsonlib syntax reanalyze)) diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index aa2d598ff..99fcb2555 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -1,35 +1,19 @@ References src/Cross.res 0:17 -[ -{"uri": "Cross.res", "range": {"start": {"line": 0, "character": 15}, "end": {"line": 0, "character": 25}}}, -{"uri": "Cross.res", "range": {"start": {"line": 3, "character": 16}, "end": {"line": 3, "character": 26}}}, -{"uri": "Cross.res", "range": {"start": {"line": 5, "character": 13}, "end": {"line": 5, "character": 23}}}, -{"uri": "Cross.res", "range": {"start": {"line": 7, "character": 16}, "end": {"line": 7, "character": 26}}}, -{"uri": "References.res", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}} -] +[{"uri": "Cross.res", "range": {"start": {"line": 0, "character": 15}, "end": {"line": 0, "character": 25}}}, {"uri": "Cross.res", "range": {"start": {"line": 3, "character": 16}, "end": {"line": 3, "character": 26}}}, {"uri": "Cross.res", "range": {"start": {"line": 5, "character": 13}, "end": {"line": 5, "character": 23}}}, {"uri": "Cross.res", "range": {"start": {"line": 7, "character": 16}, "end": {"line": 7, "character": 26}}}, {"uri": "References.res", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}}] References src/Cross.res 9:31 -[ -{"uri": "Cross.res", "range": {"start": {"line": 9, "character": 28}, "end": {"line": 9, "character": 51}}}, -{"uri": "Cross.res", "range": {"start": {"line": 12, "character": 29}, "end": {"line": 12, "character": 52}}}, -{"uri": "Cross.res", "range": {"start": {"line": 14, "character": 26}, "end": {"line": 14, "character": 49}}}, -{"uri": "Cross.res", "range": {"start": {"line": 16, "character": 29}, "end": {"line": 16, "character": 52}}}, -{"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}}, -{"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}} -] +[{"uri": "Cross.res", "range": {"start": {"line": 9, "character": 28}, "end": {"line": 9, "character": 51}}}, {"uri": "Cross.res", "range": {"start": {"line": 12, "character": 29}, "end": {"line": 12, "character": 52}}}, {"uri": "Cross.res", "range": {"start": {"line": 14, "character": 26}, "end": {"line": 14, "character": 49}}}, {"uri": "Cross.res", "range": {"start": {"line": 16, "character": 29}, "end": {"line": 16, "character": 52}}}, {"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}}, {"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}}] Rename src/Cross.res 18:13 RenameWithInterfacePrime -[ -{ +[{ "kind": "rename", "oldUri": "RenameWithInterface.resi", "newUri": "RenameWithInterfacePrime.resi" -}, -{ +}, { "kind": "rename", "oldUri": "RenameWithInterface.res", "newUri": "RenameWithInterfacePrime.res" -}, -{ +}, { "textDocument": { "version": null, "uri": "Cross.res" @@ -41,12 +25,10 @@ Rename src/Cross.res 18:13 RenameWithInterfacePrime "range": {"start": {"line": 21, "character": 8}, "end": {"line": 21, "character": 27}}, "newText": "RenameWithInterfacePrime" }] - } -] + }] Rename src/Cross.res 21:28 xPrime -[ -{ +[{ "textDocument": { "version": null, "uri": "RenameWithInterface.resi" @@ -55,8 +37,7 @@ Rename src/Cross.res 21:28 xPrime "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, "newText": "xPrime" }] - }, -{ + }, { "textDocument": { "version": null, "uri": "RenameWithInterface.res" @@ -65,8 +46,7 @@ Rename src/Cross.res 21:28 xPrime "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, "newText": "xPrime" }] - }, -{ + }, { "textDocument": { "version": null, "uri": "Cross.res" @@ -78,8 +58,7 @@ Rename src/Cross.res 21:28 xPrime "range": {"start": {"line": 21, "character": 28}, "end": {"line": 21, "character": 29}}, "newText": "xPrime" }] - } -] + }] TypeDefinition src/Cross.res 24:5 {"uri": "TypeDefinition.res", "range": {"start": {"line": 2, "character": 0}, "end": {"line": 2, "character": 28}}} diff --git a/analysis/tests/src/expected/References.res.txt b/analysis/tests/src/expected/References.res.txt index ea3108dae..6467a9d3e 100644 --- a/analysis/tests/src/expected/References.res.txt +++ b/analysis/tests/src/expected/References.res.txt @@ -1,33 +1,14 @@ References src/References.res 0:4 -[ -{"uri": "Cross.res", "range": {"start": {"line": 0, "character": 26}, "end": {"line": 0, "character": 27}}}, -{"uri": "Cross.res", "range": {"start": {"line": 3, "character": 27}, "end": {"line": 3, "character": 28}}}, -{"uri": "Cross.res", "range": {"start": {"line": 7, "character": 27}, "end": {"line": 7, "character": 28}}}, -{"uri": "References.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}, -{"uri": "References.res", "range": {"start": {"line": 3, "character": 8}, "end": {"line": 3, "character": 9}}}, -{"uri": "References.res", "range": {"start": {"line": 7, "character": 8}, "end": {"line": 7, "character": 9}}} -] +[{"uri": "Cross.res", "range": {"start": {"line": 0, "character": 26}, "end": {"line": 0, "character": 27}}}, {"uri": "Cross.res", "range": {"start": {"line": 3, "character": 27}, "end": {"line": 3, "character": 28}}}, {"uri": "Cross.res", "range": {"start": {"line": 7, "character": 27}, "end": {"line": 7, "character": 28}}}, {"uri": "References.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}, {"uri": "References.res", "range": {"start": {"line": 3, "character": 8}, "end": {"line": 3, "character": 9}}}, {"uri": "References.res", "range": {"start": {"line": 7, "character": 8}, "end": {"line": 7, "character": 9}}}] References src/References.res 9:19 -[ -{"uri": "References.res", "range": {"start": {"line": 9, "character": 11}, "end": {"line": 9, "character": 14}}}, -{"uri": "References.res", "range": {"start": {"line": 9, "character": 19}, "end": {"line": 9, "character": 21}}} -] +[{"uri": "References.res", "range": {"start": {"line": 9, "character": 11}, "end": {"line": 9, "character": 14}}}, {"uri": "References.res", "range": {"start": {"line": 9, "character": 19}, "end": {"line": 9, "character": 21}}}] References src/References.res 20:12 -[ -{"uri": "References.res", "range": {"start": {"line": 13, "character": 2}, "end": {"line": 13, "character": 13}}}, -{"uri": "References.res", "range": {"start": {"line": 18, "character": 11}, "end": {"line": 18, "character": 13}}}, -{"uri": "References.res", "range": {"start": {"line": 20, "character": 11}, "end": {"line": 20, "character": 13}}} -] +[{"uri": "References.res", "range": {"start": {"line": 13, "character": 2}, "end": {"line": 13, "character": 13}}}, {"uri": "References.res", "range": {"start": {"line": 18, "character": 11}, "end": {"line": 18, "character": 13}}}, {"uri": "References.res", "range": {"start": {"line": 20, "character": 11}, "end": {"line": 20, "character": 13}}}] References src/References.res 23:15 getLocItem #4: heuristic for within fragments: take make as makeProps does not work the type is not great but jump to definition works -[ -{"uri": "ReferencesInner.res", "range": {"start": {"line": 1, "character": 28}, "end": {"line": 1, "character": 32}}}, -{"uri": "References.res", "range": {"start": {"line": 23, "character": 19}, "end": {"line": 23, "character": 23}}}, -{"uri": "ComponentInner.res", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}}, -{"uri": "ComponentInner.resi", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}} -] +[{"uri": "ReferencesInner.res", "range": {"start": {"line": 1, "character": 28}, "end": {"line": 1, "character": 32}}}, {"uri": "References.res", "range": {"start": {"line": 23, "character": 19}, "end": {"line": 23, "character": 23}}}, {"uri": "ComponentInner.res", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}}, {"uri": "ComponentInner.resi", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}}] diff --git a/analysis/tests/src/expected/ReferencesWithInterface.res.txt b/analysis/tests/src/expected/ReferencesWithInterface.res.txt index 33f2d105d..4b49191f7 100644 --- a/analysis/tests/src/expected/ReferencesWithInterface.res.txt +++ b/analysis/tests/src/expected/ReferencesWithInterface.res.txt @@ -1,9 +1,3 @@ References src/ReferencesWithInterface.res 0:4 -[ -{"uri": "Cross.res", "range": {"start": {"line": 9, "character": 52}, "end": {"line": 9, "character": 53}}}, -{"uri": "Cross.res", "range": {"start": {"line": 12, "character": 53}, "end": {"line": 12, "character": 54}}}, -{"uri": "Cross.res", "range": {"start": {"line": 16, "character": 53}, "end": {"line": 16, "character": 54}}}, -{"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}, -{"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}} -] +[{"uri": "Cross.res", "range": {"start": {"line": 9, "character": 52}, "end": {"line": 9, "character": 53}}}, {"uri": "Cross.res", "range": {"start": {"line": 12, "character": 53}, "end": {"line": 12, "character": 54}}}, {"uri": "Cross.res", "range": {"start": {"line": 16, "character": 53}, "end": {"line": 16, "character": 54}}}, {"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}, {"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}] diff --git a/analysis/tests/src/expected/ReferencesWithInterface.resi.txt b/analysis/tests/src/expected/ReferencesWithInterface.resi.txt index 3e96fbc75..4846393b6 100644 --- a/analysis/tests/src/expected/ReferencesWithInterface.resi.txt +++ b/analysis/tests/src/expected/ReferencesWithInterface.resi.txt @@ -1,9 +1,3 @@ References src/ReferencesWithInterface.resi 0:4 -[ -{"uri": "Cross.res", "range": {"start": {"line": 9, "character": 52}, "end": {"line": 9, "character": 53}}}, -{"uri": "Cross.res", "range": {"start": {"line": 12, "character": 53}, "end": {"line": 12, "character": 54}}}, -{"uri": "Cross.res", "range": {"start": {"line": 16, "character": 53}, "end": {"line": 16, "character": 54}}}, -{"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}, -{"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}} -] +[{"uri": "Cross.res", "range": {"start": {"line": 9, "character": 52}, "end": {"line": 9, "character": 53}}}, {"uri": "Cross.res", "range": {"start": {"line": 12, "character": 53}, "end": {"line": 12, "character": 54}}}, {"uri": "Cross.res", "range": {"start": {"line": 16, "character": 53}, "end": {"line": 16, "character": 54}}}, {"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}, {"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}] diff --git a/analysis/tests/src/expected/Rename.res.txt b/analysis/tests/src/expected/Rename.res.txt index 5cd2adfee..1685cd283 100644 --- a/analysis/tests/src/expected/Rename.res.txt +++ b/analysis/tests/src/expected/Rename.res.txt @@ -1,6 +1,5 @@ Rename src/Rename.res 0:4 y -[ -{ +[{ "textDocument": { "version": null, "uri": "Rename.res" @@ -15,12 +14,10 @@ Rename src/Rename.res 0:4 y "range": {"start": {"line": 7, "character": 8}, "end": {"line": 7, "character": 9}}, "newText": "y" }] - } -] + }] Rename src/Rename.res 9:19 yy -[ -{ +[{ "textDocument": { "version": null, "uri": "Rename.res" @@ -32,6 +29,5 @@ Rename src/Rename.res 9:19 yy "range": {"start": {"line": 9, "character": 19}, "end": {"line": 9, "character": 21}}, "newText": "yy" }] - } -] + }] diff --git a/analysis/tests/src/expected/RenameWithInterface.res.txt b/analysis/tests/src/expected/RenameWithInterface.res.txt index a13988fa9..aa95ad7dc 100644 --- a/analysis/tests/src/expected/RenameWithInterface.res.txt +++ b/analysis/tests/src/expected/RenameWithInterface.res.txt @@ -1,6 +1,5 @@ Rename src/RenameWithInterface.res 0:4 y -[ -{ +[{ "textDocument": { "version": null, "uri": "RenameWithInterface.resi" @@ -9,8 +8,7 @@ Rename src/RenameWithInterface.res 0:4 y "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, "newText": "y" }] - }, -{ + }, { "textDocument": { "version": null, "uri": "RenameWithInterface.res" @@ -19,8 +17,7 @@ Rename src/RenameWithInterface.res 0:4 y "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, "newText": "y" }] - }, -{ + }, { "textDocument": { "version": null, "uri": "Cross.res" @@ -32,6 +29,5 @@ Rename src/RenameWithInterface.res 0:4 y "range": {"start": {"line": 21, "character": 28}, "end": {"line": 21, "character": 29}}, "newText": "y" }] - } -] + }] diff --git a/analysis/tests/src/expected/RenameWithInterface.resi.txt b/analysis/tests/src/expected/RenameWithInterface.resi.txt index 2a1dabb44..0c2f6ae56 100644 --- a/analysis/tests/src/expected/RenameWithInterface.resi.txt +++ b/analysis/tests/src/expected/RenameWithInterface.resi.txt @@ -1,6 +1,5 @@ Rename src/RenameWithInterface.resi 0:4 y -[ -{ +[{ "textDocument": { "version": null, "uri": "RenameWithInterface.resi" @@ -9,8 +8,7 @@ Rename src/RenameWithInterface.resi 0:4 y "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, "newText": "y" }] - }, -{ + }, { "textDocument": { "version": null, "uri": "RenameWithInterface.res" @@ -19,8 +17,7 @@ Rename src/RenameWithInterface.resi 0:4 y "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, "newText": "y" }] - }, -{ + }, { "textDocument": { "version": null, "uri": "Cross.res" @@ -32,6 +29,5 @@ Rename src/RenameWithInterface.resi 0:4 y "range": {"start": {"line": 21, "character": 28}, "end": {"line": 21, "character": 29}}, "newText": "y" }] - } -] + }] diff --git a/analysis/dune-project b/dune-project similarity index 80% rename from analysis/dune-project rename to dune-project index 47a122217..43873235d 100644 --- a/analysis/dune-project +++ b/dune-project @@ -1,4 +1,4 @@ -(lang dune 2.3) +(lang dune 3.6) (generate_opam_files true) @@ -11,8 +11,8 @@ (bug_reports "https://github.com/rescript-lang/rescript-vscode/issues") (package - (name rescript-vscode) - (synopsis "ReScript vscode support") + (name analysis) + (synopsis "ReScript Analysis") (depends (ocaml (>= 4.10)) @@ -22,4 +22,4 @@ (= 1.6.9)) (reanalyze (= 2.23.0)) - dune)) + dune)) \ No newline at end of file From baa15292b29c67e679d840201b801cafa2d487a1 Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Fri, 24 Mar 2023 19:26:07 -0300 Subject: [PATCH 2/7] fix ci --- .github/workflows/ci.yml | 3 +++ analysis.opam | 2 +- dune-project | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b15036e9..2d8c491e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,9 @@ jobs: # - run: opam pin add rescript-editor-analysis.dev . --no-action # - run: opam install . --deps-only --with-doc --with-test + - name: Install opam deps + run: opam install . --deps-only + - name: Build and test run: opam exec -- make test diff --git a/analysis.opam b/analysis.opam index 24d54617c..b65beba1c 100644 --- a/analysis.opam +++ b/analysis.opam @@ -6,11 +6,11 @@ authors: ["Cristiano Calcagno"] homepage: "https://github.com/rescript-lang/rescript-vscode" bug-reports: "https://github.com/rescript-lang/rescript-vscode/issues" depends: [ + "dune" {>= "3.6"} "ocaml" {>= "4.10"} "ocamlformat" {= "0.22.4"} "cppo" {= "1.6.9"} "reanalyze" {= "2.23.0"} - "dune" {>= "3.6"} "odoc" {with-doc} ] build: [ diff --git a/dune-project b/dune-project index 43873235d..38387236c 100644 --- a/dune-project +++ b/dune-project @@ -21,5 +21,4 @@ (cppo (= 1.6.9)) (reanalyze - (= 2.23.0)) - dune)) \ No newline at end of file + (= 2.23.0)))) From d2045fc2faf7b724ba93c289d449b6f8e829912d Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Fri, 24 Mar 2023 19:31:44 -0300 Subject: [PATCH 3/7] fix ci --- .github/workflows/ci.yml | 1 - analysis.opam | 2 +- dune-project | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d8c491e7..c8ab15d50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,6 @@ jobs: node-version: 16 - run: npm ci - - run: opam install dune cppo - run: npm run compile # These 2 runs (or just the second?) are for when you have opam dependencies. We don't. diff --git a/analysis.opam b/analysis.opam index b65beba1c..ed9d431a1 100644 --- a/analysis.opam +++ b/analysis.opam @@ -6,7 +6,7 @@ authors: ["Cristiano Calcagno"] homepage: "https://github.com/rescript-lang/rescript-vscode" bug-reports: "https://github.com/rescript-lang/rescript-vscode/issues" depends: [ - "dune" {>= "3.6"} + "dune" {>= "3.0"} "ocaml" {>= "4.10"} "ocamlformat" {= "0.22.4"} "cppo" {= "1.6.9"} diff --git a/dune-project b/dune-project index 38387236c..27daa10e2 100644 --- a/dune-project +++ b/dune-project @@ -1,4 +1,4 @@ -(lang dune 3.6) +(lang dune 3.0) (generate_opam_files true) From 8fc67ce14240e273094765114192c4b287b56454 Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Fri, 24 Mar 2023 20:09:29 -0300 Subject: [PATCH 4/7] fix diagnostic message type --- analysis/src/Diagnostics.ml | 7 ++++++- analysis/src/Protocol.ml | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/analysis/src/Diagnostics.ml b/analysis/src/Diagnostics.ml index 060335782..ea28c2f08 100644 --- a/analysis/src/Diagnostics.ml +++ b/analysis/src/Diagnostics.ml @@ -8,7 +8,12 @@ let document_syntax ~path = let end_ = Utils.cmtPosToPosition (Res_diagnostics.getEndPos diagnostic) in - Protocol.{range = {start; end_}; message = diagnostic; severity = 1}) + Protocol. + { + range = {start; end_}; + message = Res_diagnostics.explain diagnostic; + severity = 1; + }) in if FindFiles.isImplementation path then let parseImplementation = diff --git a/analysis/src/Protocol.ml b/analysis/src/Protocol.ml index 9b30dc5d8..6572c36f7 100644 --- a/analysis/src/Protocol.ml +++ b/analysis/src/Protocol.ml @@ -62,7 +62,7 @@ type documentSymbolItem = { type renameFile = {oldUri: string; newUri: string} type textEdit = {range: range; newText: string} -type diagnostic = {range: range; message: Res_diagnostics.t; severity: int} +type diagnostic = {range: range; message: string; severity: int} type optionalVersionedTextDocumentIdentifier = { version: int option; @@ -315,5 +315,5 @@ let stringifyDiagnostic d = "source": "ReScript" }|} (stringifyRange d.range) - (Json.escape (Res_diagnostics.explain d.message)) + (Json.escape d.message) d.severity From 09cc5fa2c8f515193738adb3975edd9503c0974c Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Fri, 24 Mar 2023 20:20:00 -0300 Subject: [PATCH 5/7] chore: format --- analysis/src/Protocol.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/analysis/src/Protocol.ml b/analysis/src/Protocol.ml index 6572c36f7..381c1e2ee 100644 --- a/analysis/src/Protocol.ml +++ b/analysis/src/Protocol.ml @@ -314,6 +314,4 @@ let stringifyDiagnostic d = "severity": %d, "source": "ReScript" }|} - (stringifyRange d.range) - (Json.escape d.message) - d.severity + (stringifyRange d.range) (Json.escape d.message) d.severity From c04bcaa8526b73033c8d13ea5ff776ddc8af2658 Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Sun, 21 May 2023 15:50:51 -0300 Subject: [PATCH 6/7] update CONTRIBUTING.md --- CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8655cd09b..ffd3cee56 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,6 +35,9 @@ This is needed for the `analysis` folder, which is native code. # If you haven't created the switch, do it. OPAM(https://opam.ocaml.org) opam switch create . --deps-only +# Install dev dependencies from OPAM +opam install . --deps-only + # For IDE support, install the OCaml language server opam install ocaml-lsp-server ``` From a40b0f1010fcdd017231e51fd02099db24c93547 Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Mon, 22 May 2023 20:24:02 -0300 Subject: [PATCH 7/7] refactor --- analysis/src/Commands.ml | 23 +++++++++++------------ analysis/src/References.ml | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 7abaad5c1..4297d2f93 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -1,8 +1,7 @@ let completion ~debug ~path ~pos ~currentFile = - print_endline - (Completions.complete ~debug ~path ~pos ~currentFile - |> List.map Protocol.stringifyCompletionItem - |> Protocol.array) + Completions.complete ~debug ~path ~pos ~currentFile + |> List.map Protocol.stringifyCompletionItem + |> Protocol.array |> print_endline let inlayhint ~path ~pos ~maxLength ~debug = let result = @@ -46,16 +45,16 @@ let definition ~path ~pos ~debug = |> print_endline let typeDefinition ~path ~pos ~debug = - print_endline - (match TypeDefinition.typeDefinition ~path ~pos ~debug with - | None -> Protocol.null - | Some location -> location |> Protocol.stringifyLocation) + (match TypeDefinition.typeDefinition ~path ~pos ~debug with + | None -> Protocol.null + | Some location -> location |> Protocol.stringifyLocation) + |> print_endline let references ~path ~pos ~debug = - print_endline - (match References.get ~path ~pos ~debug with - | [] -> Protocol.null - | locs -> locs |> List.map Protocol.stringifyLocation |> Protocol.array) + (match References.references ~path ~pos ~debug with + | [] -> Protocol.null + | locs -> locs |> List.map Protocol.stringifyLocation |> Protocol.array) + |> print_endline let rename ~path ~pos ~newName ~debug = let result = diff --git a/analysis/src/References.ml b/analysis/src/References.ml index 324b267eb..28cbbd613 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -572,7 +572,7 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = ^ Tip.toString tip); forLocalStamp ~full stamp tip))) -let get ~path ~pos ~debug = +let references ~path ~pos ~debug = match Cmt.loadFullCmtFromPath ~path with | None -> [] | Some full -> (