diff --git a/analysis/src/Cli.ml b/analysis/src/Cli.ml index 4728a2478..a49326d92 100644 --- a/analysis/src/Cli.ml +++ b/analysis/src/Cli.ml @@ -72,18 +72,22 @@ let main () = ~pos:(int_of_string line, int_of_string col) ~currentFile | [_; "definition"; path; line; col] -> - Commands.definition ~path ~line:(int_of_string line) - ~col:(int_of_string col) + Commands.definition ~path + ~pos:(int_of_string line, int_of_string col) + ~debug:false | [_; "typeDefinition"; path; line; col] -> - Commands.typeDefinition ~path ~line:(int_of_string line) - ~col:(int_of_string col) + Commands.typeDefinition ~path + ~pos:(int_of_string line, int_of_string col) + ~debug:false | [_; "documentSymbol"; path] -> DocumentSymbol.command ~path | [_; "hover"; path; line; col; currentFile] -> - Commands.hover ~path ~line:(int_of_string line) ~col:(int_of_string col) + Commands.hover ~path + ~pos:(int_of_string line, int_of_string col) ~currentFile ~debug:false | [_; "codeAction"; path; line; col; currentFile] -> - Commands.codeAction ~path ~line:(int_of_string line) - ~col:(int_of_string col) ~currentFile + Commands.codeAction ~path + ~pos:(int_of_string line, int_of_string col) + ~currentFile ~debug:false | _ :: "reanalyze" :: _ -> let len = Array.length Sys.argv in for i = 1 to len - 2 do @@ -92,11 +96,13 @@ let main () = Sys.argv.(len - 1) <- ""; Reanalyze.cli () | [_; "references"; path; line; col] -> - Commands.references ~path ~line:(int_of_string line) - ~col:(int_of_string col) + Commands.references ~path + ~pos:(int_of_string line, int_of_string col) + ~debug:false | [_; "rename"; path; line; col; newName] -> - Commands.rename ~path ~line:(int_of_string line) ~col:(int_of_string col) - ~newName + Commands.rename ~path + ~pos:(int_of_string line, int_of_string col) + ~newName ~debug:false | [_; "semanticTokens"; currentFile] -> SemanticTokens.semanticTokens ~currentFile | [_; "createInterface"; path; cmiFile] -> diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 5ea11a58f..d6fdcc56c 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -28,19 +28,18 @@ let completion ~debug ~path ~pos ~currentFile = |> List.map Protocol.stringifyCompletionItem |> Protocol.array) -let hover ~path ~line ~col ~currentFile ~debug = +let hover ~path ~pos ~currentFile ~debug = let result = match Cmt.fullFromPath ~path with | None -> Protocol.null | Some full -> ( - match References.getLocItem ~full ~line ~col with + match References.getLocItem ~full ~pos ~debug with | None -> ( if debug then Printf.printf "Nothing at that position. Now trying to use completion.\n"; let completions = - getCompletions ~debug ~path ~pos:(line, col) ~currentFile - ~forHover:true + getCompletions ~debug ~path ~pos ~currentFile ~forHover:true in match completions with | {kind = Label typString; docstring} :: _ -> @@ -81,16 +80,16 @@ let hover ~path ~line ~col ~currentFile ~debug = in print_endline result -let codeAction ~path ~line ~col ~currentFile = - Xform.extractCodeActions ~path ~pos:(line, col) ~currentFile +let codeAction ~path ~pos ~currentFile ~debug = + Xform.extractCodeActions ~path ~pos ~currentFile ~debug |> CodeActions.stringifyCodeActions |> print_endline -let definition ~path ~line ~col = +let definition ~path ~pos ~debug = let locationOpt = match Cmt.fullFromPath ~path with | None -> None | Some full -> ( - match References.getLocItem ~full ~line ~col with + match References.getLocItem ~full ~pos ~debug with | None -> None | Some locItem -> ( match References.definitionForLocItem ~full locItem with @@ -123,12 +122,12 @@ let definition ~path ~line ~col = | None -> Protocol.null | Some location -> location |> Protocol.stringifyLocation) -let typeDefinition ~path ~line ~col = +let typeDefinition ~path ~pos ~debug = let maybeLocation = match Cmt.fullFromPath ~path with | None -> None | Some full -> ( - match References.getLocItem ~full ~line ~col with + match References.getLocItem ~full ~pos ~debug with | None -> None | Some locItem -> ( match References.typeDefinitionForLocItem ~full locItem with @@ -143,12 +142,12 @@ let typeDefinition ~path ~line ~col = | None -> Protocol.null | Some location -> location |> Protocol.stringifyLocation) -let references ~path ~line ~col = +let references ~path ~pos ~debug = let allLocs = match Cmt.fullFromPath ~path with | None -> [] | Some full -> ( - match References.getLocItem ~full ~line ~col with + match References.getLocItem ~full ~pos ~debug with | None -> [] | Some locItem -> let allReferences = References.allReferencesForLocItem ~full locItem in @@ -169,12 +168,12 @@ let references ~path ~line ~col = (if allLocs = [] then Protocol.null else "[\n" ^ (allLocs |> String.concat ",\n") ^ "\n]") -let rename ~path ~line ~col ~newName = +let rename ~path ~pos ~newName ~debug = let result = match Cmt.fullFromPath ~path with | None -> Protocol.null | Some full -> ( - match References.getLocItem ~full ~line ~col with + match References.getLocItem ~full ~pos ~debug with | None -> Protocol.null | Some locItem -> let allReferences = References.allReferencesForLocItem ~full locItem in @@ -305,24 +304,24 @@ let test ~path = print_endline ("Definition " ^ path ^ " " ^ string_of_int line ^ ":" ^ string_of_int col); - definition ~path ~line ~col + definition ~path ~pos:(line, col) ~debug:true | "typ" -> print_endline ("TypeDefinition " ^ path ^ " " ^ string_of_int line ^ ":" ^ string_of_int col); - typeDefinition ~path ~line ~col + typeDefinition ~path ~pos:(line, col) ~debug:true | "hov" -> print_endline ("Hover " ^ path ^ " " ^ string_of_int line ^ ":" ^ string_of_int col); let currentFile = createCurrentFile () in - hover ~path ~line ~col ~currentFile ~debug:true; + hover ~path ~pos:(line, col) ~currentFile ~debug:true; Sys.remove currentFile | "ref" -> print_endline ("References " ^ path ^ " " ^ string_of_int line ^ ":" ^ string_of_int col); - references ~path ~line ~col + references ~path ~pos:(line, col) ~debug:true | "doc" -> print_endline ("DocumentSymbol " ^ path); DocumentSymbol.command ~path @@ -333,7 +332,7 @@ let test ~path = ("Rename " ^ path ^ " " ^ string_of_int line ^ ":" ^ string_of_int col ^ " " ^ newName) in - rename ~path ~line ~col ~newName + rename ~path ~pos:(line, col) ~newName ~debug:true | "com" -> print_endline ("Complete " ^ path ^ " " ^ string_of_int line ^ ":" @@ -362,6 +361,7 @@ let test ~path = ^ string_of_int col); let codeActions = Xform.extractCodeActions ~path ~pos:(line, col) ~currentFile:path + ~debug:true in codeActions |> List.iter (fun {Protocol.title; edit = {documentChanges}} -> diff --git a/analysis/src/References.ml b/analysis/src/References.ml index 33f29439e..5c4916dcf 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -1,6 +1,7 @@ open SharedTypes let debugReferences = ref true + let maybeLog m = if !debugReferences then Log.log ("[ref] " ^ m) let checkPos (line, char) @@ -16,43 +17,56 @@ let checkPos (line, char) let locItemsForPos ~extra pos = extra.locItems |> List.filter (fun {loc; locType = _} -> checkPos pos loc) -let lineColToCmtLoc ~line ~col = (line + 1, col) +let lineColToCmtLoc ~pos:(line, col) = (line + 1, col) -let getLocItem ~full ~line ~col = - let pos = lineColToCmtLoc ~line ~col in +let getLocItem ~full ~pos ~debug = + let log n msg = if debug then Printf.printf "getLocItem #%d: %s\n" n msg in + let pos = lineColToCmtLoc ~pos in let locItems = locItemsForPos ~extra:full.extra pos in if !Log.verbose then print_endline ("locItems:\n " ^ (locItems |> List.map locItemToString |> String.concat "\n ")); + let nameOf li = + match li.locType with Typed (n, _, _) -> n | _ -> "NotFound" + in match locItems with - | _ :: _ :: _ :: ({locType = Typed ("makeProps", _, _)} as li) :: _ + | li1 :: li2 :: li3 :: ({locType = Typed ("makeProps", _, _)} as li4) :: _ when full.file.uri |> Uri2.isInterface -> - (* heuristic for makeProps in interface files *) - Some li + log 1 "heuristic for makeProps in interface files"; + if debug then + Printf.printf "n1:%s n2:%s n3:%s\n" (nameOf li1) (nameOf li2) (nameOf li3); + Some li4 | [ {locType = Typed ("fragment", _, _)}; {locType = Typed ("createElement", _, _)}; ] -> - (* heuristic for within a fragment *) + log 2 "heuristic for within a fragment"; None | [ {locType = Constant _}; ({locType = Typed ("createDOMElementVariadic", _, _)} as li2); ] -> - (* heuristic for
*) + log 3 "heuristic for
"; Some li2 | {locType = Typed ("makeProps", _, _)} :: ({locType = Typed ("make", _, _)} as li2) :: _ -> - (* heuristic for within fragments: take make as makeProps does not work - the type is not greatl but jump to definition works *) + log 4 + "heuristic for within fragments: take make as makeProps does not \ + work\n\ + the type is not great but jump to definition works"; + Some li2 + | [ + ({locType = Typed (_, _, LocalReference _)} as li1); + ({locType = Typed (_, _, _)} as li2); + ] + when li1.loc = li2.loc -> + log 5 + "heuristic for JSX and compiler combined:\n\ + ~x becomes Props#x\n\ + heuristic for: [Props, x], give loc of `x`"; + if debug then Printf.printf "n1:%s n2:%s\n" (nameOf li1) (nameOf li2); Some li2 - | [({locType = Typed (_, _, LocalReference _)} as li1); li3] - when li1.loc = li3.loc -> - (* JSX and compiler combined: - ~x becomes Props#x - heuristic for: [Props, x], give loc of `x` *) - Some li3 | [ ({locType = Typed (_, _, LocalReference _)} as li1); ({locType = Typed (_, _, GlobalReference ("Js_OO", ["unsafe_downgrade"], _))} @@ -61,28 +75,39 @@ let getLocItem ~full ~line ~col = ] (* For older compiler 9.0 or earlier *) when li1.loc = li2.loc && li2.loc = li3.loc -> - (* JSX and compiler combined: - ~x becomes Js_OO.unsafe_downgrade(Props)#x - heuristic for: [Props, unsafe_downgrade, x], give loc of `x` *) + (* Not currently testable on 9.1.4 *) + log 6 + "heuristic for JSX and compiler combined:\n\ + ~x becomes Js_OO.unsafe_downgrade(Props)#x\n\ + heuristic for: [Props, unsafe_downgrade, x], give loc of `x`"; Some li3 | [ - {locType = Typed (_, _, LocalReference (_, Value))}; + ({locType = Typed (_, _, LocalReference (_, Value))} as li1); ({locType = Typed (_, _, Definition (_, Value))} as li2); ] -> - (* JSX on type-annotated labeled (~arg:t): - (~arg:t) becomes Props#arg - Props has the location range of arg:t - arg has the location range of arg - heuristic for: [Props, arg], give loc of `arg` *) + log 7 + "heuristic for JSX on type-annotated labeled (~arg:t):\n\ + (~arg:t) becomes Props#arg\n\ + Props has the location range of arg:t\n\ + arg has the location range of arg\n\ + heuristic for: [Props, arg], give loc of `arg`"; + if debug then Printf.printf "n1:%s n2:%s\n" (nameOf li1) (nameOf li2); Some li2 | [li1; li2; li3] when li1.loc = li2.loc && li2.loc = li3.loc -> - (* JSX with at most one child - heuristic for: [makeProps, make, createElement], give the loc of `make` *) + (* Not currently testable on 9.1.4 *) + log 8 + "heuristic for JSX with at most one child\n\ + heuristic for: [makeProps, make, createElement], give the loc of `make` "; Some li2 | [li1; li2; li3; li4] when li1.loc = li2.loc && li2.loc = li3.loc && li3.loc = li4.loc -> - (* JSX variadic, e.g. {x} {y} - heuristic for: [makeProps , React.null, make, createElementVariadic], give the loc of `make` *) + log 9 + "heuristic for JSX variadic, e.g. {x} {y} \n\ + heuristic for: [React.null, makeProps, make, createElementVariadic], \ + give the loc of `make`"; + if debug then + Printf.printf "n1:%s n2:%s n3:%s n4:%s\n" (nameOf li1) (nameOf li2) + (nameOf li3) (nameOf li4); Some li3 | {locType = Typed (_, {desc = Tconstr (path, _, _)}, _)} :: li :: _ when Utils.isUncurriedInternal path -> diff --git a/analysis/src/Xform.ml b/analysis/src/Xform.ml index b0fa003a2..41ddfa697 100644 --- a/analysis/src/Xform.ml +++ b/analysis/src/Xform.ml @@ -223,16 +223,14 @@ module AddTypeAnnotation = struct in {Ast_iterator.default_iterator with structure_item} - let xform ~path ~pos ~full ~structure ~codeActions = - let line, col = pos in - + let xform ~path ~pos ~full ~structure ~codeActions ~debug = let result = ref None in let iterator = mkIterator ~pos ~result in iterator.structure iterator structure; match !result with | None -> () | Some annotation -> ( - match References.getLocItem ~full ~line ~col with + match References.getLocItem ~full ~pos ~debug with | None -> () | Some locItem -> ( match locItem.locType with @@ -297,14 +295,14 @@ let parse ~filename = in (structure, printExpr, printStructureItem) -let extractCodeActions ~path ~pos ~currentFile = +let extractCodeActions ~path ~pos ~currentFile ~debug = match Cmt.fullFromPath ~path with | Some full when Filename.check_suffix currentFile ".res" -> let structure, printExpr, printStructureItem = parse ~filename:currentFile in let codeActions = ref [] in - AddTypeAnnotation.xform ~path ~pos ~full ~structure ~codeActions; + AddTypeAnnotation.xform ~path ~pos ~full ~structure ~codeActions ~debug; IfThenElse.xform ~pos ~codeActions ~printExpr ~path structure; AddBracesToFn.xform ~pos ~codeActions ~path ~printStructureItem structure; !codeActions diff --git a/analysis/tests/package-lock.json b/analysis/tests/package-lock.json index cdf7c13db..2cf5d2fe8 100644 --- a/analysis/tests/package-lock.json +++ b/analysis/tests/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "rescript": "9.1.2" + "rescript": "^9.1.4" }, "devDependencies": { "@rescript/react": "^0.10.3" @@ -18,9 +18,9 @@ "dev": true }, "node_modules/rescript": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-9.1.2.tgz", - "integrity": "sha512-4wHvTDv3nyYnAPJHcg1RGG8z7u3HDiBf6RN3P/dITDv859Qo35aKOzJWQtfBzbAs0EKNafLqei3TnUqiAv6BwQ==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/rescript/-/rescript-9.1.4.tgz", + "integrity": "sha512-aXANK4IqecJzdnDpJUsU6pxMViCR5ogAxzuqS0mOr8TloMnzAjJFu63fjD6LCkWrKAhlMkFFzQvVQYaAaVkFXw==", "hasInstallScript": true, "bin": { "bsc": "bsc", @@ -38,9 +38,9 @@ "dev": true }, "rescript": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-9.1.2.tgz", - "integrity": "sha512-4wHvTDv3nyYnAPJHcg1RGG8z7u3HDiBf6RN3P/dITDv859Qo35aKOzJWQtfBzbAs0EKNafLqei3TnUqiAv6BwQ==" + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/rescript/-/rescript-9.1.4.tgz", + "integrity": "sha512-aXANK4IqecJzdnDpJUsU6pxMViCR5ogAxzuqS0mOr8TloMnzAjJFu63fjD6LCkWrKAhlMkFFzQvVQYaAaVkFXw==" } } } diff --git a/analysis/tests/package.json b/analysis/tests/package.json index e95584d29..7fd8c902a 100644 --- a/analysis/tests/package.json +++ b/analysis/tests/package.json @@ -3,11 +3,11 @@ "build": "rescript", "clean": "rescript clean -with-deps" }, - "dependencies": { - "rescript": "9.1.2" - }, "private": true, "devDependencies": { "@rescript/react": "^0.10.3" + }, + "dependencies": { + "rescript": "^9.1.4" } } diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 6cf9a26d4..aeb25d150 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -1,4 +1,5 @@ Hover src/Div.res 0:10 +getLocItem #3: heuristic for
{"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```"} Complete src/Div.res 3:17 diff --git a/analysis/tests/src/expected/Fragment.res.txt b/analysis/tests/src/expected/Fragment.res.txt index 49995e110..cf71d7bfd 100644 --- a/analysis/tests/src/expected/Fragment.res.txt +++ b/analysis/tests/src/expected/Fragment.res.txt @@ -1,7 +1,10 @@ Hover src/Fragment.res 6:19 +getLocItem #4: heuristic for within fragments: take make as makeProps does not work +the type is not great but jump to definition works {"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n```rescript\ntype component<'props> = componentLike<'props, element>\n```"} Hover src/Fragment.res 9:56 +getLocItem #2: heuristic for within a fragment Nothing at that position. Now trying to use completion. posCursor:[9:56] posNoWhite:[9:55] Found expr:[9:10->9:67] Pexp_construct :::[9:13->9:67] [9:13->9:67] diff --git a/analysis/tests/src/expected/Hover.res.txt b/analysis/tests/src/expected/Hover.res.txt index 9d9c0616e..2753e966b 100644 --- a/analysis/tests/src/expected/Hover.res.txt +++ b/analysis/tests/src/expected/Hover.res.txt @@ -20,9 +20,19 @@ Hover src/Hover.res 33:4 {"contents": "```rescript\nunit => int\n```\n\nDoc comment for functionWithTypeAnnotation"} Hover src/Hover.res 37:13 +getLocItem #5: heuristic for JSX and compiler combined: +~x becomes Props#x +heuristic for: [Props, x], give loc of `x` +n1:Props n2:name {"contents": "```rescript\nstring\n```"} Hover src/Hover.res 41:13 +getLocItem #7: heuristic for JSX on type-annotated labeled (~arg:t): +(~arg:t) becomes Props#arg +Props has the location range of arg:t +arg has the location range of arg +heuristic for: [Props, arg], give loc of `arg` +n1:Props n2:name {"contents": "```rescript\nstring\n```"} Hover src/Hover.res 44:10 @@ -47,9 +57,15 @@ Hover src/Hover.res 75:7 {"contents": "```rescript\nmodule A = {\n let x: int\n}\n```"} Hover src/Hover.res 85:10 +getLocItem #9: heuristic for JSX variadic, e.g. {x} {y} +heuristic for: [React.null, makeProps, make, createElementVariadic], give the loc of `make` +n1:null n2:makeProps n3:make n4:createElementVariadic {"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n```rescript\ntype component<'props> = componentLike<'props, element>\n```"} Hover src/Hover.res 88:10 +getLocItem #9: heuristic for JSX variadic, e.g. {x} {y} +heuristic for: [React.null, makeProps, make, createElementVariadic], give the loc of `make` +n1:null n2:makeProps n3:make n4:createElementVariadic {"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n```rescript\ntype component<'props> = componentLike<'props, element>\n```"} Hover src/Hover.res 93:25 diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index c2d3c0a1a..7d0699185 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -1,4 +1,6 @@ Definition src/Jsx.res 5:9 +getLocItem #4: heuristic for within fragments: take make as makeProps does not work +the type is not great but jump to definition works {"uri": "Jsx.res", "range": {"start": {"line": 2, "character": 6}, "end": {"line": 2, "character": 10}}} Complete src/Jsx.res 8:15 @@ -196,6 +198,8 @@ Completable: Cjsx([M], k, [prop, k]) }] Definition src/Jsx.res 58:11 +getLocItem #4: heuristic for within fragments: take make as makeProps does not work +the type is not great but jump to definition works {"uri": "Component.res", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}} Complete src/Jsx.res 68:10 diff --git a/analysis/tests/src/expected/Jsx.resi.txt b/analysis/tests/src/expected/Jsx.resi.txt index b1a9a727d..b0b987154 100644 --- a/analysis/tests/src/expected/Jsx.resi.txt +++ b/analysis/tests/src/expected/Jsx.resi.txt @@ -1,4 +1,6 @@ Hover src/Jsx.resi 1:4 +getLocItem #1: heuristic for makeProps in interface files +n1:componentLike n2:unit n3:string {"contents": "```rescript\n(~first: string, ~key: string=?, unit) => {\"first\": string}\n```"} Hover src/Jsx.resi 4:4