From 061807791a67396fbf358fde810faccea0780f08 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 13 Sep 2022 18:48:45 +0200 Subject: [PATCH 1/8] skeleton for extracting clickable links in hovers --- analysis/src/Hover.ml | 66 ++++++++++++++++--- analysis/src/Uri.mli | 1 + analysis/tests/src/expected/Auto.res.txt | 2 +- .../tests/src/expected/Definition.res.txt | 6 +- analysis/tests/src/expected/Div.res.txt | 2 +- .../tests/src/expected/DocComments.res.txt | 8 +-- analysis/tests/src/expected/Fragment.res.txt | 2 +- analysis/tests/src/expected/Hover.res.txt | 40 +++++------ analysis/tests/src/expected/Jsx2.resi.txt | 4 +- .../tests/src/expected/LongIdentTest.res.txt | 2 +- client/src/extension.ts | 24 +++++++ 11 files changed, 115 insertions(+), 42 deletions(-) diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml index a8e1cf619..a2389fc2f 100644 --- a/analysis/src/Hover.ml +++ b/analysis/src/Hover.ml @@ -2,6 +2,36 @@ open SharedTypes let codeBlock code = Printf.sprintf "```rescript\n%s\n```" code +let encodeURIComponent text = + let ln = String.length text in + let buf = Buffer.create ln in + let rec loop i = + if i < ln then ( + (match text.[i] with + | '"' -> Buffer.add_string buf "%22" + | ':' -> Buffer.add_string buf "%3A" + | '/' -> Buffer.add_string buf "%2F" + | '\\' -> Buffer.add_string buf "%5C" + | ',' -> Buffer.add_string buf "%2C" + | '&' -> Buffer.add_string buf "%26" + | '[' -> Buffer.add_string buf "%5B" + | ']' -> Buffer.add_string buf "%5D" + | c -> Buffer.add_char buf c); + loop (i + 1)) + in + loop 0; + Buffer.contents buf + +type link = {range: Protocol.range; file: string; label: string} + +let linkToCommandArgs link = + Printf.sprintf "[\"%s\",%i,%i,%i,%i]" link.file link.range.start.character + link.range.start.line link.range.end_.character link.range.end_.line + +let makeGotoCommand link = + Printf.sprintf "[%s](command:rescript-vscode.go_to_location?%s)" link.label + (encodeURIComponent (linkToCommandArgs link)) + let showModuleTopLevel ~docstring ~name (topLevel : Module.item list) = let contents = topLevel @@ -145,20 +175,37 @@ let newHover ~full:{file; package} locItem = constructors |> List.filter_map (fromConstructorPath ~env:envToSearch) in let typeString = typeString :: typeDefinitions |> String.concat "\n\n" in - (typeString, docstring) + let links = + "\n---\nGo to: " + ^ ([ + makeGotoCommand + { + label = "SomeModule"; + file = + "file:///Users/zth/git/rescript-vscode-official/analysis/examples/example-project/src/Json.res"; + range = + { + start = {character = 0; line = 34}; + end_ = {character = 0; line = 42}; + }; + }; + ] + |> String.concat " | ") + in + (typeString, docstring, links) in let parts = match References.definedForLoc ~file ~package locKind with | None -> - let typeString, docstring = t |> fromType ~docstring:[] in - typeString :: docstring + let typeString, docstring, links = t |> fromType ~docstring:[] in + List.concat [[typeString]; docstring; [links]] | Some (docstring, res) -> ( match res with | `Declared -> - let typeString, docstring = t |> fromType ~docstring in - typeString :: docstring + let typeString, docstring, links = t |> fromType ~docstring in + List.concat [[typeString]; docstring; [links]] | `Constructor {cname = {txt}; args} -> - let typeString, docstring = t |> fromType ~docstring in + let typeString, docstring, links = t |> fromType ~docstring in let argsString = match args with | [] -> "" @@ -167,9 +214,10 @@ let newHover ~full:{file; package} locItem = |> List.map (fun (t, _) -> Shared.typeToString t) |> String.concat ", " |> Printf.sprintf "(%s)" in - typeString :: codeBlock (txt ^ argsString) :: docstring + List.concat + [[typeString; codeBlock (txt ^ argsString)]; docstring; [links]] | `Field -> - let typeString, docstring = t |> fromType ~docstring in - typeString :: docstring) + let typeString, docstring, links = t |> fromType ~docstring in + List.concat [[typeString]; docstring; [links]]) in Some (String.concat "\n\n" parts) diff --git a/analysis/src/Uri.mli b/analysis/src/Uri.mli index 5e8013c06..847a8d2bf 100644 --- a/analysis/src/Uri.mli +++ b/analysis/src/Uri.mli @@ -6,3 +6,4 @@ val stripPath : bool ref val toPath : t -> string val toString : t -> string val toTopLevelLoc : t -> Location.t +val pathToUri : string -> string diff --git a/analysis/tests/src/expected/Auto.res.txt b/analysis/tests/src/expected/Auto.res.txt index b9ad6f2e9..a84166ca3 100644 --- a/analysis/tests/src/expected/Auto.res.txt +++ b/analysis/tests/src/expected/Auto.res.txt @@ -1,3 +1,3 @@ Hover src/Auto.res 2:13 -{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n```rescript\ntype t<'a> = list<'a>\n```\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n```rescript\ntype t<'a> = list<'a>\n```\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} diff --git a/analysis/tests/src/expected/Definition.res.txt b/analysis/tests/src/expected/Definition.res.txt index 9e2ec3299..fca5930f7 100644 --- a/analysis/tests/src/expected/Definition.res.txt +++ b/analysis/tests/src/expected/Definition.res.txt @@ -5,13 +5,13 @@ Definition src/Definition.res 10:23 {"uri": "Definition.res", "range": {"start": {"line": 6, "character": 7}, "end": {"line": 6, "character": 13}}} Hover src/Definition.res 14:14 -{"contents": "```rescript\n('a => 'b, list<'a>) => list<'b>\n```\n\n [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an],\n and builds the list [[f a1; ...; f an]]\n with the results returned by [f]. Not tail-recursive. "} +{"contents": "```rescript\n('a => 'b, list<'a>) => list<'b>\n```\n\n [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an],\n and builds the list [[f a1; ...; f an]]\n with the results returned by [f]. Not tail-recursive. \n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Definition.res 18:14 -{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n```rescript\ntype t<'a> = list<'a>\n```\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n```rescript\ntype t<'a> = list<'a>\n```\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Definition.res 23:3 -{"contents": "```rescript\n(. int, int) => int\n```"} +{"contents": "```rescript\n(. int, int) => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Definition src/Definition.res 26:3 {"uri": "Definition.res", "range": {"start": {"line": 21, "character": 4}, "end": {"line": 21, "character": 13}}} diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 8d5f1ef99..86a51f1db 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -1,6 +1,6 @@ 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```\n\n```rescript\ntype domProps = ReactDOM.Props.domProps\n```\n\n```rescript\ntype element\n```"} +{"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```\n\n```rescript\ntype domProps = ReactDOM.Props.domProps\n```\n\n```rescript\ntype element\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Complete src/Div.res 3:17 posCursor:[3:17] posNoWhite:[3:16] Found expr:[3:4->3:17] diff --git a/analysis/tests/src/expected/DocComments.res.txt b/analysis/tests/src/expected/DocComments.res.txt index 5cd5104b9..4d629f5e0 100644 --- a/analysis/tests/src/expected/DocComments.res.txt +++ b/analysis/tests/src/expected/DocComments.res.txt @@ -1,12 +1,12 @@ Hover src/DocComments.res 9:9 -{"contents": "```rescript\nint\n```\n\n Doc comment with a triple-backquote example\\n \\n ```res example\\n let a = 10\\n /*\\n * stuff\\n */\\n ```\\n"} +{"contents": "```rescript\nint\n```\n\n Doc comment with a triple-backquote example\\n \\n ```res example\\n let a = 10\\n /*\\n * stuff\\n */\\n ```\\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/DocComments.res 22:6 -{"contents": "```rescript\nint\n```\n\n\n Doc comment with a triple-backquote example\n \n ```res example\n let a = 10\n /*\n * stuff\n */\n ```\n"} +{"contents": "```rescript\nint\n```\n\n\n Doc comment with a triple-backquote example\n \n ```res example\n let a = 10\n /*\n * stuff\n */\n ```\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/DocComments.res 33:9 -{"contents": "```rescript\nint\n```\n\n Doc comment with a triple-backquote example\\n \\n ```res example\\n let a = 10\\n let b = 20\\n ```\\n"} +{"contents": "```rescript\nint\n```\n\n Doc comment with a triple-backquote example\\n \\n ```res example\\n let a = 10\\n let b = 20\\n ```\\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/DocComments.res 44:6 -{"contents": "```rescript\nint\n```\n\n\n Doc comment with a triple-backquote example\n \n ```res example\n let a = 10\n let b = 20\n ```\n"} +{"contents": "```rescript\nint\n```\n\n\n Doc comment with a triple-backquote example\n \n ```res example\n let a = 10\n let b = 20\n ```\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} diff --git a/analysis/tests/src/expected/Fragment.res.txt b/analysis/tests/src/expected/Fragment.res.txt index 10a05f5ab..3ee0b7246 100644 --- a/analysis/tests/src/expected/Fragment.res.txt +++ b/analysis/tests/src/expected/Fragment.res.txt @@ -1,7 +1,7 @@ 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```"} +{"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n```rescript\ntype component<'props> = componentLike<'props, element>\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Fragment.res 9:56 Nothing at that position. Now trying to use completion. diff --git a/analysis/tests/src/expected/Hover.res.txt b/analysis/tests/src/expected/Hover.res.txt index 1c52b769c..a1c09c679 100644 --- a/analysis/tests/src/expected/Hover.res.txt +++ b/analysis/tests/src/expected/Hover.res.txt @@ -1,5 +1,5 @@ Hover src/Hover.res 0:4 -{"contents": "```rescript\nint\n```"} +{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 3:5 {"contents": "```rescript\ntype t = (int, float)\n```"} @@ -11,20 +11,20 @@ Hover src/Hover.res 19:11 {"contents": "\nThis module is commented\n```rescript\nmodule Dep = {\n let customDouble: int => int\n}\n```"} Hover src/Hover.res 22:11 -{"contents": "```rescript\nint => int\n```\n\nSome doc comment"} +{"contents": "```rescript\nint => int\n```\n\nSome doc comment\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 26:6 -{"contents": "```rescript\nint\n```"} +{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 33:4 -{"contents": "```rescript\nunit => int\n```\n\nDoc comment for functionWithTypeAnnotation"} +{"contents": "```rescript\nunit => int\n```\n\nDoc comment for functionWithTypeAnnotation\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} 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```"} +{"contents": "```rescript\nstring\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 42:15 getLocItem #7: heuristic for JSX on type-annotated labeled (~arg:t): @@ -33,7 +33,7 @@ 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```"} +{"contents": "```rescript\nstring\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 46:10 {"contents": "```rescript\nint\n```"} @@ -69,16 +69,16 @@ JSX 95:8] > _children:95:8 null Hover src/Hover.res 103:25 -{"contents": "```rescript\nfloat\n```"} +{"contents": "```rescript\nfloat\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 106:21 -{"contents": "```rescript\nint\n```"} +{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 116:16 -{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n```rescript\ntype cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```"} +{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n```rescript\ntype cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 119:25 -{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n```rescript\ntype cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```"} +{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n```rescript\ntype cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 122:3 Nothing at that position. Now trying to use completion. @@ -87,28 +87,28 @@ Completable: Cdecorator(live) {"contents": "The `@live` decorator is for reanalyze, a static analysis tool for ReScript that can do dead code analysis.\n\n`@live` tells the dead code analysis that the value should be considered live, even though it might appear to be dead. This is typically used in case of FFI where there are indirect ways to access values. It can be added to everything that could otherwise be considered unused by the dead code analysis - values, functions, arguments, records, individual record fields, and so on.\n\n[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#live-decorator).\n\nHint: Did you know you can run an interactive code analysis in your project by running the command `> ReScript: Start Code Analyzer`? Try it!"} Hover src/Hover.res 125:4 -{"contents": "```rescript\n(. ()) => unit => int\n```"} +{"contents": "```rescript\n(. ()) => unit => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 131:4 -{"contents": "```rescript\n(. ()) => (. ()) => int\n```"} +{"contents": "```rescript\n(. ()) => (. ()) => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 134:4 -{"contents": "```rescript\n(. unit, unit) => int\n```"} +{"contents": "```rescript\n(. unit, unit) => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 137:5 -{"contents": "```rescript\n(. ()) => unit => int\n```"} +{"contents": "```rescript\n(. ()) => unit => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 144:9 -{"contents": "```rescript\nint\n```\n\ndoc comment 1"} +{"contents": "```rescript\nint\n```\n\ndoc comment 1\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 148:6 -{"contents": "```rescript\nint\n```\n\n doc comment 2 "} +{"contents": "```rescript\nint\n```\n\n doc comment 2 \n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 165:23 -{"contents": "```rescript\nfoo\n```\n\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\n\n```rescript\ntype bar = {age: int}\n```"} +{"contents": "```rescript\nfoo\n```\n\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\n\n```rescript\ntype bar = {age: int}\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 167:22 -{"contents": "```rescript\nfoobar\n```\n\n```rescript\ntype foobar = foo\n```"} +{"contents": "```rescript\nfoobar\n```\n\n```rescript\ntype foobar = foo\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Complete src/Hover.res 170:16 posCursor:[170:16] posNoWhite:[170:15] Found expr:[170:5->170:16] @@ -159,8 +159,8 @@ Completable: Cpath Value[y2].content."" }] Hover src/Hover.res 197:4 -{"contents": "```rescript\nCompV4.props => React.element\n```\n\n```rescript\ntype props<'n, 's> = {?n: 'n, s: 's}\n```\n\n```rescript\ntype element\n```"} +{"contents": "```rescript\nCompV4.props => React.element\n```\n\n```rescript\ntype props<'n, 's> = {?n: 'n, s: 's}\n```\n\n```rescript\ntype element\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Hover.res 202:16 -{"contents": "```rescript\nuseR\n```\n\n```rescript\ntype useR = {x: int, y: list>>}\n```\n\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```"} +{"contents": "```rescript\nuseR\n```\n\n```rescript\ntype useR = {x: int, y: list>>}\n```\n\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} diff --git a/analysis/tests/src/expected/Jsx2.resi.txt b/analysis/tests/src/expected/Jsx2.resi.txt index bff16aa8c..77e53cc1f 100644 --- a/analysis/tests/src/expected/Jsx2.resi.txt +++ b/analysis/tests/src/expected/Jsx2.resi.txt @@ -1,10 +1,10 @@ Hover src/Jsx2.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```"} +{"contents": "```rescript\n(~first: string, ~key: string=?, unit) => {\"first\": string}\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Hover src/Jsx2.resi 4:4 -{"contents": "```rescript\nint\n```"} +{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} Complete src/Jsx2.resi 7:19 posCursor:[7:19] posNoWhite:[7:18] Found type:[7:12->7:19] diff --git a/analysis/tests/src/expected/LongIdentTest.res.txt b/analysis/tests/src/expected/LongIdentTest.res.txt index fd8238152..c9f2c8cea 100644 --- a/analysis/tests/src/expected/LongIdentTest.res.txt +++ b/analysis/tests/src/expected/LongIdentTest.res.txt @@ -1,3 +1,3 @@ Hover src/LongIdentTest.res 2:13 -{"contents": "```rescript\nint\n```"} +{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} diff --git a/client/src/extension.ts b/client/src/extension.ts index f324e4b11..cfd396d0e 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -6,6 +6,9 @@ import { languages, window, StatusBarAlignment, + Uri, + Range, + Position, } from "vscode"; import { @@ -105,6 +108,9 @@ export function activate(context: ExtensionContext) { extensionConfiguration: workspace.getConfiguration("rescript.settings"), }, outputChannel, + markdown: { + isTrusted: true, + }, }; const client = new LanguageClient( @@ -189,6 +195,24 @@ export function activate(context: ExtensionContext) { customCommands.openCompiled(client); }); + commands.registerCommand( + "rescript-vscode.go_to_location", + async ( + fileUri: string, + startLine: number, + startCol: number, + endLine: number, + endCol: number + ) => { + await window.showTextDocument(Uri.parse(fileUri), { + selection: new Range( + new Position(startLine, startCol), + new Position(endLine, endCol) + ), + }); + } + ); + // Starts the code analysis mode. commands.registerCommand("rescript-vscode.start_code_analysis", () => { // Save the directory this first ran from, and re-use that when continuously From b11d5bd2b0fa2936cb32b6cb96c21ce4f53f22d1 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 13 Sep 2022 18:51:33 +0200 Subject: [PATCH 2/8] remove unused --- analysis/src/Uri.mli | 1 - 1 file changed, 1 deletion(-) diff --git a/analysis/src/Uri.mli b/analysis/src/Uri.mli index 847a8d2bf..5e8013c06 100644 --- a/analysis/src/Uri.mli +++ b/analysis/src/Uri.mli @@ -6,4 +6,3 @@ val stripPath : bool ref val toPath : t -> string val toString : t -> string val toTopLevelLoc : t -> Location.t -val pathToUri : string -> string From 5bc769718b3af22a6c5800f91df9618e4543c186 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 14 Sep 2022 18:12:05 +0200 Subject: [PATCH 3/8] print link to type definition inline next to expanded type --- analysis/src/Hover.ml | 71 ++++++++++--------- analysis/src/PrintType.ml | 4 +- analysis/src/Shared.ml | 6 +- analysis/src/SharedTypes.ml | 7 ++ analysis/tests/src/expected/Auto.res.txt | 2 +- .../tests/src/expected/Definition.res.txt | 6 +- analysis/tests/src/expected/Div.res.txt | 2 +- .../tests/src/expected/DocComments.res.txt | 8 +-- analysis/tests/src/expected/Fragment.res.txt | 2 +- analysis/tests/src/expected/Hover.res.txt | 40 +++++------ analysis/tests/src/expected/Jsx2.resi.txt | 4 +- .../tests/src/expected/LongIdentTest.res.txt | 2 +- .../res_outcome_printer.ml | 4 +- .../res_outcome_printer.mli | 2 +- client/src/extension.ts | 10 +-- 15 files changed, 89 insertions(+), 81 deletions(-) diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml index a2389fc2f..b93be917f 100644 --- a/analysis/src/Hover.ml +++ b/analysis/src/Hover.ml @@ -22,11 +22,11 @@ let encodeURIComponent text = loop 0; Buffer.contents buf -type link = {range: Protocol.range; file: string; label: string} +type link = {startPos: Protocol.position; file: string; label: string} let linkToCommandArgs link = - Printf.sprintf "[\"%s\",%i,%i,%i,%i]" link.file link.range.start.character - link.range.start.line link.range.end_.character link.range.end_.line + Printf.sprintf "[\"%s\",%i,%i]" link.file link.startPos.line + link.startPos.character let makeGotoCommand link = Printf.sprintf "[%s](command:rescript-vscode.go_to_location?%s)" link.label @@ -131,9 +131,15 @@ let newHover ~full:{file; package} locItem = let fromConstructorPath ~env path = match References.digConstructor ~env ~package path with | None -> None - | Some (_env, {name = {txt}; item = {decl}}) -> + | Some (env, {extentLoc; item = {decl}}) -> if Utils.isUncurriedInternal path then None - else Some (decl |> Shared.declToString txt |> codeBlock) + else + Some + ( decl + |> Shared.declToString ~printNameAsIs:true + (SharedTypes.pathIdentToString path), + extentLoc, + env ) in let fromType ~docstring typ = let typeString = codeBlock (typ |> Shared.typeToString) in @@ -172,40 +178,40 @@ let newHover ~full:{file; package} locItem = | None -> (env, [typ]) in let constructors = Shared.findTypeConstructors typesToSearch in - constructors |> List.filter_map (fromConstructorPath ~env:envToSearch) + constructors + |> List.filter_map (fun constructorPath -> + match + constructorPath |> fromConstructorPath ~env:envToSearch + with + | None -> None + | Some (typString, extentLoc, env) -> + let startLine, startCol = Pos.ofLexing extentLoc.loc_start in + Some + (Shared.markdownSpacing ^ codeBlock typString ^ "\n" + ^ "Go to: " + ^ makeGotoCommand + { + label = "Type definition"; + file = Uri.toString env.file.uri; + startPos = {line = startLine; character = startCol}; + } + ^ "\n\n---\n")) in let typeString = typeString :: typeDefinitions |> String.concat "\n\n" in - let links = - "\n---\nGo to: " - ^ ([ - makeGotoCommand - { - label = "SomeModule"; - file = - "file:///Users/zth/git/rescript-vscode-official/analysis/examples/example-project/src/Json.res"; - range = - { - start = {character = 0; line = 34}; - end_ = {character = 0; line = 42}; - }; - }; - ] - |> String.concat " | ") - in - (typeString, docstring, links) + (typeString, docstring) in let parts = match References.definedForLoc ~file ~package locKind with | None -> - let typeString, docstring, links = t |> fromType ~docstring:[] in - List.concat [[typeString]; docstring; [links]] + let typeString, docstring = t |> fromType ~docstring:[] in + typeString :: docstring | Some (docstring, res) -> ( match res with | `Declared -> - let typeString, docstring, links = t |> fromType ~docstring in - List.concat [[typeString]; docstring; [links]] + let typeString, docstring = t |> fromType ~docstring in + typeString :: docstring | `Constructor {cname = {txt}; args} -> - let typeString, docstring, links = t |> fromType ~docstring in + let typeString, docstring = t |> fromType ~docstring in let argsString = match args with | [] -> "" @@ -214,10 +220,9 @@ let newHover ~full:{file; package} locItem = |> List.map (fun (t, _) -> Shared.typeToString t) |> String.concat ", " |> Printf.sprintf "(%s)" in - List.concat - [[typeString; codeBlock (txt ^ argsString)]; docstring; [links]] + typeString :: codeBlock (txt ^ argsString) :: docstring | `Field -> - let typeString, docstring, links = t |> fromType ~docstring in - List.concat [[typeString]; docstring; [links]]) + let typeString, docstring = t |> fromType ~docstring in + typeString :: docstring) in Some (String.concat "\n\n" parts) diff --git a/analysis/src/PrintType.ml b/analysis/src/PrintType.ml index 3da8293c0..f06239f60 100644 --- a/analysis/src/PrintType.ml +++ b/analysis/src/PrintType.ml @@ -3,8 +3,8 @@ let printExpr ?(lineWidth = 60) typ = Res_doc.toString ~width:lineWidth (Res_outcome_printer.printOutTypeDoc (Printtyp.tree_of_typexp false typ)) -let printDecl ~recStatus name decl = +let printDecl ?printNameAsIs ~recStatus name decl = Printtyp.reset_names (); Res_doc.toString ~width:60 - (Res_outcome_printer.printOutSigItemDoc + (Res_outcome_printer.printOutSigItemDoc ?printNameAsIs (Printtyp.tree_of_type_declaration (Ident.create name) decl recStatus)) diff --git a/analysis/src/Shared.ml b/analysis/src/Shared.ml index 37280d50f..af5f3748e 100644 --- a/analysis/src/Shared.ml +++ b/analysis/src/Shared.ml @@ -63,8 +63,8 @@ let findTypeConstructors (tel : Types.type_expr list) = tel |> List.iter loop; !paths |> List.rev -let declToString ?(recStatus = Types.Trec_not) name t = - PrintType.printDecl ~recStatus name t +let declToString ?printNameAsIs ?(recStatus = Types.Trec_not) name t = + PrintType.printDecl ?printNameAsIs ~recStatus name t let cacheTypeToString = ref false let typeTbl = Hashtbl.create 1 @@ -78,3 +78,5 @@ let typeToString ?lineWidth (t : Types.type_expr) = Hashtbl.replace typeTbl (t.id, t) s; s | Some s -> s + +let markdownSpacing = "\n```\n \n```\n" diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 314f7670b..03ad6935e 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -315,6 +315,13 @@ type path = string list let pathToString (path : path) = path |> String.concat "." +let rec pathIdentToString (p : Path.t) = + match p with + | Pident {name} -> name + | Pdot (nextPath, id, _) -> + Printf.sprintf "%s.%s" (pathIdentToString nextPath) id + | Papply _ -> "" + type locKind = | LocalReference of int * Tip.t | GlobalReference of string * string list * Tip.t diff --git a/analysis/tests/src/expected/Auto.res.txt b/analysis/tests/src/expected/Auto.res.txt index a84166ca3..c26bf07ed 100644 --- a/analysis/tests/src/expected/Auto.res.txt +++ b/analysis/tests/src/expected/Auto.res.txt @@ -1,3 +1,3 @@ Hover src/Auto.res 2:13 -{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n```rescript\ntype t<'a> = list<'a>\n```\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n---\n\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} diff --git a/analysis/tests/src/expected/Definition.res.txt b/analysis/tests/src/expected/Definition.res.txt index fca5930f7..18dd479df 100644 --- a/analysis/tests/src/expected/Definition.res.txt +++ b/analysis/tests/src/expected/Definition.res.txt @@ -5,13 +5,13 @@ Definition src/Definition.res 10:23 {"uri": "Definition.res", "range": {"start": {"line": 6, "character": 7}, "end": {"line": 6, "character": 13}}} Hover src/Definition.res 14:14 -{"contents": "```rescript\n('a => 'b, list<'a>) => list<'b>\n```\n\n [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an],\n and builds the list [[f a1; ...; f an]]\n with the results returned by [f]. Not tail-recursive. \n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n('a => 'b, list<'a>) => list<'b>\n```\n\n [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an],\n and builds the list [[f a1; ...; f an]]\n with the results returned by [f]. Not tail-recursive. "} Hover src/Definition.res 18:14 -{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n```rescript\ntype t<'a> = list<'a>\n```\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n---\n\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} Hover src/Definition.res 23:3 -{"contents": "```rescript\n(. int, int) => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(. int, int) => int\n```"} Definition src/Definition.res 26:3 {"uri": "Definition.res", "range": {"start": {"line": 21, "character": 4}, "end": {"line": 21, "character": 13}}} diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 86a51f1db..b9b76fdbc 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -1,6 +1,6 @@ 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```\n\n```rescript\ntype domProps = ReactDOM.Props.domProps\n```\n\n```rescript\ntype element\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```\n\n\n```\n \n```\n```rescript\ntype ReactDOMRe.domProps = ReactDOM.Props.domProps\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22ReactDOMRe.res%22%2C57%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n---\n"} Complete src/Div.res 3:17 posCursor:[3:17] posNoWhite:[3:16] Found expr:[3:4->3:17] diff --git a/analysis/tests/src/expected/DocComments.res.txt b/analysis/tests/src/expected/DocComments.res.txt index 4d629f5e0..5cd5104b9 100644 --- a/analysis/tests/src/expected/DocComments.res.txt +++ b/analysis/tests/src/expected/DocComments.res.txt @@ -1,12 +1,12 @@ Hover src/DocComments.res 9:9 -{"contents": "```rescript\nint\n```\n\n Doc comment with a triple-backquote example\\n \\n ```res example\\n let a = 10\\n /*\\n * stuff\\n */\\n ```\\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```\n\n Doc comment with a triple-backquote example\\n \\n ```res example\\n let a = 10\\n /*\\n * stuff\\n */\\n ```\\n"} Hover src/DocComments.res 22:6 -{"contents": "```rescript\nint\n```\n\n\n Doc comment with a triple-backquote example\n \n ```res example\n let a = 10\n /*\n * stuff\n */\n ```\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```\n\n\n Doc comment with a triple-backquote example\n \n ```res example\n let a = 10\n /*\n * stuff\n */\n ```\n"} Hover src/DocComments.res 33:9 -{"contents": "```rescript\nint\n```\n\n Doc comment with a triple-backquote example\\n \\n ```res example\\n let a = 10\\n let b = 20\\n ```\\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```\n\n Doc comment with a triple-backquote example\\n \\n ```res example\\n let a = 10\\n let b = 20\\n ```\\n"} Hover src/DocComments.res 44:6 -{"contents": "```rescript\nint\n```\n\n\n Doc comment with a triple-backquote example\n \n ```res example\n let a = 10\n let b = 20\n ```\n\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```\n\n\n Doc comment with a triple-backquote example\n \n ```res example\n let a = 10\n let b = 20\n ```\n"} diff --git a/analysis/tests/src/expected/Fragment.res.txt b/analysis/tests/src/expected/Fragment.res.txt index 3ee0b7246..f61e4c82e 100644 --- a/analysis/tests/src/expected/Fragment.res.txt +++ b/analysis/tests/src/expected/Fragment.res.txt @@ -1,7 +1,7 @@ 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```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n\n```\n \n```\n```rescript\ntype React.component<'props> = componentLike<\n 'props,\n element,\n>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n\n---\n"} Hover src/Fragment.res 9:56 Nothing at that position. Now trying to use completion. diff --git a/analysis/tests/src/expected/Hover.res.txt b/analysis/tests/src/expected/Hover.res.txt index a1c09c679..0b932cbc5 100644 --- a/analysis/tests/src/expected/Hover.res.txt +++ b/analysis/tests/src/expected/Hover.res.txt @@ -1,5 +1,5 @@ Hover src/Hover.res 0:4 -{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```"} Hover src/Hover.res 3:5 {"contents": "```rescript\ntype t = (int, float)\n```"} @@ -11,20 +11,20 @@ Hover src/Hover.res 19:11 {"contents": "\nThis module is commented\n```rescript\nmodule Dep = {\n let customDouble: int => int\n}\n```"} Hover src/Hover.res 22:11 -{"contents": "```rescript\nint => int\n```\n\nSome doc comment\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint => int\n```\n\nSome doc comment"} Hover src/Hover.res 26:6 -{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```"} Hover src/Hover.res 33:4 -{"contents": "```rescript\nunit => int\n```\n\nDoc comment for functionWithTypeAnnotation\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"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```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nstring\n```"} Hover src/Hover.res 42:15 getLocItem #7: heuristic for JSX on type-annotated labeled (~arg:t): @@ -33,7 +33,7 @@ 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```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nstring\n```"} Hover src/Hover.res 46:10 {"contents": "```rescript\nint\n```"} @@ -69,16 +69,16 @@ JSX 95:8] > _children:95:8 null Hover src/Hover.res 103:25 -{"contents": "```rescript\nfloat\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nfloat\n```"} Hover src/Hover.res 106:21 -{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```"} Hover src/Hover.res 116:16 -{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n```rescript\ntype cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)\n\n---\n"} Hover src/Hover.res 119:25 -{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n```rescript\ntype cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)\n\n---\n"} Hover src/Hover.res 122:3 Nothing at that position. Now trying to use completion. @@ -87,28 +87,28 @@ Completable: Cdecorator(live) {"contents": "The `@live` decorator is for reanalyze, a static analysis tool for ReScript that can do dead code analysis.\n\n`@live` tells the dead code analysis that the value should be considered live, even though it might appear to be dead. This is typically used in case of FFI where there are indirect ways to access values. It can be added to everything that could otherwise be considered unused by the dead code analysis - values, functions, arguments, records, individual record fields, and so on.\n\n[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#live-decorator).\n\nHint: Did you know you can run an interactive code analysis in your project by running the command `> ReScript: Start Code Analyzer`? Try it!"} Hover src/Hover.res 125:4 -{"contents": "```rescript\n(. ()) => unit => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(. ()) => unit => int\n```"} Hover src/Hover.res 131:4 -{"contents": "```rescript\n(. ()) => (. ()) => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(. ()) => (. ()) => int\n```"} Hover src/Hover.res 134:4 -{"contents": "```rescript\n(. unit, unit) => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(. unit, unit) => int\n```"} Hover src/Hover.res 137:5 -{"contents": "```rescript\n(. ()) => unit => int\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(. ()) => unit => int\n```"} Hover src/Hover.res 144:9 -{"contents": "```rescript\nint\n```\n\ndoc comment 1\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```\n\ndoc comment 1"} Hover src/Hover.res 148:6 -{"contents": "```rescript\nint\n```\n\n doc comment 2 \n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```\n\n doc comment 2 "} Hover src/Hover.res 165:23 -{"contents": "```rescript\nfoo\n```\n\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\n\n```rescript\ntype bar = {age: int}\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nfoo\n```\n\n\n```\n \n```\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C161%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype bar = {age: int}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C162%2C2%5D)\n\n---\n"} Hover src/Hover.res 167:22 -{"contents": "```rescript\nfoobar\n```\n\n```rescript\ntype foobar = foo\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nfoobar\n```\n\n\n```\n \n```\n```rescript\ntype foobar = foo\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C163%2C2%5D)\n\n---\n"} Complete src/Hover.res 170:16 posCursor:[170:16] posNoWhite:[170:15] Found expr:[170:5->170:16] @@ -159,8 +159,8 @@ Completable: Cpath Value[y2].content."" }] Hover src/Hover.res 197:4 -{"contents": "```rescript\nCompV4.props => React.element\n```\n\n```rescript\ntype props<'n, 's> = {?n: 'n, s: 's}\n```\n\n```rescript\ntype element\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nCompV4.props => React.element\n```\n\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {?n: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n---\n"} Hover src/Hover.res 202:16 -{"contents": "```rescript\nuseR\n```\n\n```rescript\ntype useR = {x: int, y: list>>}\n```\n\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nuseR\n```\n\n\n```\n \n```\n```rescript\ntype useR = {x: int, y: list>>}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C200%2C0%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C101%2C0%5D)\n\n---\n"} diff --git a/analysis/tests/src/expected/Jsx2.resi.txt b/analysis/tests/src/expected/Jsx2.resi.txt index 77e53cc1f..bff16aa8c 100644 --- a/analysis/tests/src/expected/Jsx2.resi.txt +++ b/analysis/tests/src/expected/Jsx2.resi.txt @@ -1,10 +1,10 @@ Hover src/Jsx2.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```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\n(~first: string, ~key: string=?, unit) => {\"first\": string}\n```"} Hover src/Jsx2.resi 4:4 -{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```"} Complete src/Jsx2.resi 7:19 posCursor:[7:19] posNoWhite:[7:18] Found type:[7:12->7:19] diff --git a/analysis/tests/src/expected/LongIdentTest.res.txt b/analysis/tests/src/expected/LongIdentTest.res.txt index c9f2c8cea..fd8238152 100644 --- a/analysis/tests/src/expected/LongIdentTest.res.txt +++ b/analysis/tests/src/expected/LongIdentTest.res.txt @@ -1,3 +1,3 @@ Hover src/LongIdentTest.res 2:13 -{"contents": "```rescript\nint\n```\n\n[SomeModule](command:rescript-vscode.go_to_location?%5B%22file%3A%2F%2Fwhatever%2Ffolder%2FSomeModule.res%22%2C0%2C0%2C0%2C0%5D)"} +{"contents": "```rescript\nint\n```"} diff --git a/analysis/vendor/res_outcome_printer/res_outcome_printer.ml b/analysis/vendor/res_outcome_printer/res_outcome_printer.ml index 2c544cec2..ec69e9e02 100644 --- a/analysis/vendor/res_outcome_printer/res_outcome_printer.ml +++ b/analysis/vendor/res_outcome_printer/res_outcome_printer.ml @@ -563,7 +563,7 @@ let printTypeParameterDoc (typ, (co, cn)) = (if typ = "_" then Doc.text "_" else Doc.text ("'" ^ typ)); ] -let rec printOutSigItemDoc (outSigItem : Outcometree.out_sig_item) = +let rec printOutSigItemDoc ?(printNameAsIs=false) (outSigItem : Outcometree.out_sig_item) = match outSigItem with | Osig_class _ | Osig_class_type _ -> Doc.nil | Osig_ellipsis -> Doc.dotdotdot @@ -728,7 +728,7 @@ let rec printOutSigItemDoc (outSigItem : Outcometree.out_sig_item) = [ attrs; kw; - printIdentLike ~allowUident:false outTypeDecl.otype_name; + if printNameAsIs then Doc.text outTypeDecl.otype_name else printIdentLike ~allowUident:false outTypeDecl.otype_name; typeParams; kind; ]); diff --git a/analysis/vendor/res_outcome_printer/res_outcome_printer.mli b/analysis/vendor/res_outcome_printer/res_outcome_printer.mli index 674a5eeb1..1b498dfa2 100644 --- a/analysis/vendor/res_outcome_printer/res_outcome_printer.mli +++ b/analysis/vendor/res_outcome_printer/res_outcome_printer.mli @@ -13,4 +13,4 @@ val setup : unit lazy_t [@@live] (* Needed for e.g. the playground to print typedtree data *) val printOutTypeDoc : Outcometree.out_type -> Res_doc.t [@@live] -val printOutSigItemDoc : Outcometree.out_sig_item -> Res_doc.t [@@live] +val printOutSigItemDoc : ?printNameAsIs: bool -> Outcometree.out_sig_item -> Res_doc.t [@@live] diff --git a/client/src/extension.ts b/client/src/extension.ts index cfd396d0e..137ad7286 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -197,17 +197,11 @@ export function activate(context: ExtensionContext) { commands.registerCommand( "rescript-vscode.go_to_location", - async ( - fileUri: string, - startLine: number, - startCol: number, - endLine: number, - endCol: number - ) => { + async (fileUri: string, startLine: number, startCol: number) => { await window.showTextDocument(Uri.parse(fileUri), { selection: new Range( new Position(startLine, startCol), - new Position(endLine, endCol) + new Position(startLine, startCol) ), }); } From 205509f75c1ae868e9e807c16dfda8b8023a13f7 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 14 Sep 2022 18:26:46 +0200 Subject: [PATCH 4/8] gate link to definition behind flag, to gracefully degrade for clients that cannot handle markdown links --- analysis/src/Cli.ml | 12 ++++++---- analysis/src/Commands.ml | 7 +++--- analysis/src/Hover.ml | 24 +++++++++++-------- analysis/tests/src/expected/Auto.res.txt | 2 +- .../tests/src/expected/Definition.res.txt | 2 +- analysis/tests/src/expected/Div.res.txt | 2 +- analysis/tests/src/expected/Fragment.res.txt | 2 +- analysis/tests/src/expected/Hover.res.txt | 12 +++++----- client/src/extension.ts | 6 +++++ server/src/server.ts | 19 +++++++++++++++ 10 files changed, 61 insertions(+), 27 deletions(-) diff --git a/analysis/src/Cli.ml b/analysis/src/Cli.ml index 90b82b1c8..cf657cec4 100644 --- a/analysis/src/Cli.ml +++ b/analysis/src/Cli.ml @@ -7,7 +7,7 @@ API examples: ./rescript-editor-analysis.exe definition src/MyFile.res 9 3 ./rescript-editor-analysis.exe typeDefinition src/MyFile.res 9 3 ./rescript-editor-analysis.exe documentSymbol src/Foo.res - ./rescript-editor-analysis.exe hover src/MyFile.res 10 2 + ./rescript-editor-analysis.exe hover src/MyFile.res 10 2 true ./rescript-editor-analysis.exe references src/MyFile.res 10 2 ./rescript-editor-analysis.exe rename src/MyFile.res 10 2 foo ./rescript-editor-analysis.exe diagnosticSyntax src/MyFile.res @@ -39,9 +39,9 @@ Options: ./rescript-editor-analysis.exe documentSymbol src/MyFile.res - hover: get inferred type for MyFile.res at line 10 column 2: + hover: get inferred type for MyFile.res at line 10 column 2 (supporting markdown links): - ./rescript-editor-analysis.exe hover src/MyFile.res 10 2 + ./rescript-editor-analysis.exe hover src/MyFile.res 10 2 true references: get all references to item in MyFile.res at line 10 column 2: @@ -95,10 +95,14 @@ let main () = ~pos:(int_of_string line, int_of_string col) ~debug:false | [_; "documentSymbol"; path] -> DocumentSymbol.command ~path - | [_; "hover"; path; line; col; currentFile] -> + | [_; "hover"; path; line; col; currentFile; supportsMarkdownLinks] -> Commands.hover ~path ~pos:(int_of_string line, int_of_string col) ~currentFile ~debug:false + ~supportsMarkdownLinks: + (match supportsMarkdownLinks with + | "true" -> true + | _ -> false) | [_; "inlayHint"; path; line_start; line_end; maxLength] -> Commands.inlayhint ~path ~pos:(int_of_string line_start, int_of_string line_end) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 5454ab056..f0ad3efd2 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -36,7 +36,7 @@ let codeLens ~path ~debug = let result = Hint.codeLens ~path ~debug |> Protocol.array in print_endline result -let hover ~path ~pos ~currentFile ~debug = +let hover ~path ~pos ~currentFile ~debug ~supportsMarkdownLinks = let result = match Cmt.fullFromPath ~path with | None -> Protocol.null @@ -81,7 +81,7 @@ let hover ~path ~pos ~currentFile ~debug = in if skipZero then Protocol.null else - let hoverText = Hover.newHover ~full locItem in + let hoverText = Hover.newHover ~supportsMarkdownLinks ~full locItem in match hoverText with | None -> Protocol.null | Some s -> Protocol.stringifyHover s)) @@ -341,7 +341,8 @@ let test ~path = ("Hover " ^ path ^ " " ^ string_of_int line ^ ":" ^ string_of_int col); let currentFile = createCurrentFile () in - hover ~path ~pos:(line, col) ~currentFile ~debug:true; + hover ~supportsMarkdownLinks:true ~path ~pos:(line, col) + ~currentFile ~debug:true; Sys.remove currentFile | "int" -> print_endline ("Create Interface " ^ path); diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml index b93be917f..7d628bdcd 100644 --- a/analysis/src/Hover.ml +++ b/analysis/src/Hover.ml @@ -66,7 +66,7 @@ let rec showModule ~docstring ~(file : File.t) ~name | Some {item = Ident path} -> Some ("Unable to resolve module reference " ^ Path.name path) -let newHover ~full:{file; package} locItem = +let newHover ~full:{file; package} ~supportsMarkdownLinks locItem = match locItem.locType with | TypeDefinition (name, decl, _stamp) -> let typeDef = Shared.declToString name decl in @@ -186,16 +186,20 @@ let newHover ~full:{file; package} locItem = | None -> None | Some (typString, extentLoc, env) -> let startLine, startCol = Pos.ofLexing extentLoc.loc_start in + let linkToTypeDefinitionStr = + if supportsMarkdownLinks then + "\nGo to: " + ^ makeGotoCommand + { + label = "Type definition"; + file = Uri.toString env.file.uri; + startPos = {line = startLine; character = startCol}; + } + else "" + in Some - (Shared.markdownSpacing ^ codeBlock typString ^ "\n" - ^ "Go to: " - ^ makeGotoCommand - { - label = "Type definition"; - file = Uri.toString env.file.uri; - startPos = {line = startLine; character = startCol}; - } - ^ "\n\n---\n")) + (Shared.markdownSpacing ^ codeBlock typString + ^ linkToTypeDefinitionStr ^ "\n\n---\n")) in let typeString = typeString :: typeDefinitions |> String.concat "\n\n" in (typeString, docstring) diff --git a/analysis/tests/src/expected/Auto.res.txt b/analysis/tests/src/expected/Auto.res.txt index c26bf07ed..d624de90e 100644 --- a/analysis/tests/src/expected/Auto.res.txt +++ b/analysis/tests/src/expected/Auto.res.txt @@ -1,3 +1,3 @@ Hover src/Auto.res 2:13 -{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n---\n\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} diff --git a/analysis/tests/src/expected/Definition.res.txt b/analysis/tests/src/expected/Definition.res.txt index 18dd479df..0cca205ca 100644 --- a/analysis/tests/src/expected/Definition.res.txt +++ b/analysis/tests/src/expected/Definition.res.txt @@ -8,7 +8,7 @@ Hover src/Definition.res 14:14 {"contents": "```rescript\n('a => 'b, list<'a>) => list<'b>\n```\n\n [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an],\n and builds the list [[f a1; ...; f an]]\n with the results returned by [f]. Not tail-recursive. "} Hover src/Definition.res 18:14 -{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n---\n\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} Hover src/Definition.res 23:3 {"contents": "```rescript\n(. int, int) => int\n```"} diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index b9b76fdbc..747521b83 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -1,6 +1,6 @@ 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```\n\n\n```\n \n```\n```rescript\ntype ReactDOMRe.domProps = ReactDOM.Props.domProps\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22ReactDOMRe.res%22%2C57%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n---\n"} +{"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```\n\n\n```\n \n```\n```rescript\ntype ReactDOMRe.domProps = ReactDOM.Props.domProps\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22ReactDOMRe.res%22%2C57%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)"} Complete src/Div.res 3:17 posCursor:[3:17] posNoWhite:[3:16] Found expr:[3:4->3:17] diff --git a/analysis/tests/src/expected/Fragment.res.txt b/analysis/tests/src/expected/Fragment.res.txt index f61e4c82e..162621bbd 100644 --- a/analysis/tests/src/expected/Fragment.res.txt +++ b/analysis/tests/src/expected/Fragment.res.txt @@ -1,7 +1,7 @@ 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\n```\n \n```\n```rescript\ntype React.component<'props> = componentLike<\n 'props,\n element,\n>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n\n---\n"} +{"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n\n```\n \n```\n```rescript\ntype React.component<'props> = componentLike<\n 'props,\n element,\n>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)"} Hover src/Fragment.res 9:56 Nothing at that position. Now trying to use completion. diff --git a/analysis/tests/src/expected/Hover.res.txt b/analysis/tests/src/expected/Hover.res.txt index 0b932cbc5..0fc822cef 100644 --- a/analysis/tests/src/expected/Hover.res.txt +++ b/analysis/tests/src/expected/Hover.res.txt @@ -75,10 +75,10 @@ Hover src/Hover.res 106:21 {"contents": "```rescript\nint\n```"} Hover src/Hover.res 116:16 -{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)\n\n---\n"} +{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)"} Hover src/Hover.res 119:25 -{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)\n\n---\n"} +{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)"} Hover src/Hover.res 122:3 Nothing at that position. Now trying to use completion. @@ -105,10 +105,10 @@ Hover src/Hover.res 148:6 {"contents": "```rescript\nint\n```\n\n doc comment 2 "} Hover src/Hover.res 165:23 -{"contents": "```rescript\nfoo\n```\n\n\n```\n \n```\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C161%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype bar = {age: int}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C162%2C2%5D)\n\n---\n"} +{"contents": "```rescript\nfoo\n```\n\n\n```\n \n```\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C161%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype bar = {age: int}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C162%2C2%5D)"} Hover src/Hover.res 167:22 -{"contents": "```rescript\nfoobar\n```\n\n\n```\n \n```\n```rescript\ntype foobar = foo\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C163%2C2%5D)\n\n---\n"} +{"contents": "```rescript\nfoobar\n```\n\n\n```\n \n```\n```rescript\ntype foobar = foo\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C163%2C2%5D)"} Complete src/Hover.res 170:16 posCursor:[170:16] posNoWhite:[170:15] Found expr:[170:5->170:16] @@ -159,8 +159,8 @@ Completable: Cpath Value[y2].content."" }] Hover src/Hover.res 197:4 -{"contents": "```rescript\nCompV4.props => React.element\n```\n\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {?n: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n---\n"} +{"contents": "```rescript\nCompV4.props => React.element\n```\n\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {?n: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)"} Hover src/Hover.res 202:16 -{"contents": "```rescript\nuseR\n```\n\n\n```\n \n```\n```rescript\ntype useR = {x: int, y: list>>}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C200%2C0%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C101%2C0%5D)\n\n---\n"} +{"contents": "```rescript\nuseR\n```\n\n\n```\n \n```\n```rescript\ntype useR = {x: int, y: list>>}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C200%2C0%5D)\n\n\n```\n \n```\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C101%2C0%5D)"} diff --git a/client/src/extension.ts b/client/src/extension.ts index 137ad7286..88048f5af 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -106,6 +106,12 @@ export function activate(context: ExtensionContext) { // We'll leave it like this for now, but might be worth revisiting later on. initializationOptions: { extensionConfiguration: workspace.getConfiguration("rescript.settings"), + + // Keep this in sync with the `extensionClientCapabilities` type in the + // server. + extensionClientCapabilities: { + supportsMarkdownLinks: true, + }, }, outputChannel, markdown: { diff --git a/server/src/server.ts b/server/src/server.ts index 08cf31945..f384e41fa 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -35,6 +35,14 @@ interface extensionConfiguration { binaryPath: string | null; } +// This holds client capabilities specific to our extension, and not necessarily +// related to the LS protocol. It's for enabling/disabling features that might +// work in one client, like VSCode, but perhaps not in others, like vim. +export interface extensionClientCapabilities { + supportsMarkdownLinks?: boolean | null; +} +let extensionClientCapabilities: extensionClientCapabilities = {}; + // All values here are temporary, and will be overridden as the server is // initialized, and the current config is received from the client. let extensionConfiguration: extensionConfiguration = { @@ -436,6 +444,7 @@ function hover(msg: p.RequestMessage) { params.position.line, params.position.character, tmpname, + Boolean(extensionClientCapabilities.supportsMarkdownLinks), ], msg ); @@ -1070,6 +1079,16 @@ function onMessage(msg: p.Message) { extensionConfiguration = initialConfiguration; } + // These are static configuration options the client can set to enable certain + let extensionClientCapabilitiesFromClient = initParams + .initializationOptions?.extensionClientCapabilities as + | extensionClientCapabilities + | undefined; + + if (extensionClientCapabilitiesFromClient != null) { + extensionClientCapabilities = extensionClientCapabilitiesFromClient; + } + // send the list of features we support let result: p.InitializeResult = { // This tells the client: "hey, we support the following operations". From e547b15632b7d8c4d3a0e3f6c8cfc4083248908a Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 14 Sep 2022 18:34:39 +0200 Subject: [PATCH 5/8] update tests --- analysis/tests/src/expected/Auto.res.txt | 2 +- analysis/tests/src/expected/Definition.res.txt | 2 +- analysis/tests/src/expected/Div.res.txt | 2 +- analysis/tests/src/expected/Fragment.res.txt | 2 +- analysis/tests/src/expected/Hover.res.txt | 12 ++++++------ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/analysis/tests/src/expected/Auto.res.txt b/analysis/tests/src/expected/Auto.res.txt index d624de90e..c26bf07ed 100644 --- a/analysis/tests/src/expected/Auto.res.txt +++ b/analysis/tests/src/expected/Auto.res.txt @@ -1,3 +1,3 @@ Hover src/Auto.res 2:13 -{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n---\n\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} diff --git a/analysis/tests/src/expected/Definition.res.txt b/analysis/tests/src/expected/Definition.res.txt index 0cca205ca..18dd479df 100644 --- a/analysis/tests/src/expected/Definition.res.txt +++ b/analysis/tests/src/expected/Definition.res.txt @@ -8,7 +8,7 @@ Hover src/Definition.res 14:14 {"contents": "```rescript\n('a => 'b, list<'a>) => list<'b>\n```\n\n [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an],\n and builds the list [[f a1; ...; f an]]\n with the results returned by [f]. Not tail-recursive. "} Hover src/Definition.res 18:14 -{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n---\n\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"} Hover src/Definition.res 23:3 {"contents": "```rescript\n(. int, int) => int\n```"} diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 747521b83..b9b76fdbc 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -1,6 +1,6 @@ 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```\n\n\n```\n \n```\n```rescript\ntype ReactDOMRe.domProps = ReactDOM.Props.domProps\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22ReactDOMRe.res%22%2C57%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)"} +{"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```\n\n\n```\n \n```\n```rescript\ntype ReactDOMRe.domProps = ReactDOM.Props.domProps\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22ReactDOMRe.res%22%2C57%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n---\n"} Complete src/Div.res 3:17 posCursor:[3:17] posNoWhite:[3:16] Found expr:[3:4->3:17] diff --git a/analysis/tests/src/expected/Fragment.res.txt b/analysis/tests/src/expected/Fragment.res.txt index 162621bbd..f61e4c82e 100644 --- a/analysis/tests/src/expected/Fragment.res.txt +++ b/analysis/tests/src/expected/Fragment.res.txt @@ -1,7 +1,7 @@ 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\n```\n \n```\n```rescript\ntype React.component<'props> = componentLike<\n 'props,\n element,\n>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)"} +{"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n\n```\n \n```\n```rescript\ntype React.component<'props> = componentLike<\n 'props,\n element,\n>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n\n---\n"} Hover src/Fragment.res 9:56 Nothing at that position. Now trying to use completion. diff --git a/analysis/tests/src/expected/Hover.res.txt b/analysis/tests/src/expected/Hover.res.txt index 0fc822cef..0b932cbc5 100644 --- a/analysis/tests/src/expected/Hover.res.txt +++ b/analysis/tests/src/expected/Hover.res.txt @@ -75,10 +75,10 @@ Hover src/Hover.res 106:21 {"contents": "```rescript\nint\n```"} Hover src/Hover.res 116:16 -{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)"} +{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)\n\n---\n"} Hover src/Hover.res 119:25 -{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)"} +{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)\n\n---\n"} Hover src/Hover.res 122:3 Nothing at that position. Now trying to use completion. @@ -105,10 +105,10 @@ Hover src/Hover.res 148:6 {"contents": "```rescript\nint\n```\n\n doc comment 2 "} Hover src/Hover.res 165:23 -{"contents": "```rescript\nfoo\n```\n\n\n```\n \n```\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C161%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype bar = {age: int}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C162%2C2%5D)"} +{"contents": "```rescript\nfoo\n```\n\n\n```\n \n```\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C161%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype bar = {age: int}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C162%2C2%5D)\n\n---\n"} Hover src/Hover.res 167:22 -{"contents": "```rescript\nfoobar\n```\n\n\n```\n \n```\n```rescript\ntype foobar = foo\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C163%2C2%5D)"} +{"contents": "```rescript\nfoobar\n```\n\n\n```\n \n```\n```rescript\ntype foobar = foo\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C163%2C2%5D)\n\n---\n"} Complete src/Hover.res 170:16 posCursor:[170:16] posNoWhite:[170:15] Found expr:[170:5->170:16] @@ -159,8 +159,8 @@ Completable: Cpath Value[y2].content."" }] Hover src/Hover.res 197:4 -{"contents": "```rescript\nCompV4.props => React.element\n```\n\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {?n: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)"} +{"contents": "```rescript\nCompV4.props => React.element\n```\n\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {?n: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n---\n"} Hover src/Hover.res 202:16 -{"contents": "```rescript\nuseR\n```\n\n\n```\n \n```\n```rescript\ntype useR = {x: int, y: list>>}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C200%2C0%5D)\n\n\n```\n \n```\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C101%2C0%5D)"} +{"contents": "```rescript\nuseR\n```\n\n\n```\n \n```\n```rescript\ntype useR = {x: int, y: list>>}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C200%2C0%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C101%2C0%5D)\n\n---\n"} From e695516c5d91e24bcc16fd426c601c46fe54757e Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 14 Sep 2022 18:41:41 +0200 Subject: [PATCH 6/8] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6baeb4237..f968d0863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Add support for prop completion for JSX V4 https://github.com/rescript-lang/rescript-vscode/pull/579 - Add support for create interface file for JSX V4 https://github.com/rescript-lang/rescript-vscode/pull/580 - Expand one level of type definition on hover. Dig into record/variant body. https://github.com/rescript-lang/rescript-vscode/pull/584 +- Add clickable links to type definitions in hovers. https://github.com/rescript-lang/rescript-vscode/pull/585 #### :bug: Bug Fix From 3bdcacaea0e4e2850daf2aefb9e762cd0cc525c8 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 15 Sep 2022 08:16:51 +0200 Subject: [PATCH 7/8] add more chars to url encode --- analysis/src/Hover.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml index 7d628bdcd..8032df250 100644 --- a/analysis/src/Hover.ml +++ b/analysis/src/Hover.ml @@ -2,6 +2,11 @@ open SharedTypes let codeBlock code = Printf.sprintf "```rescript\n%s\n```" code +(* Light weight, hopefully-enough-for-the-purpose fn to encode URI components. + Built to handle the reserved characters listed in + https://en.wikipedia.org/wiki/Percent-encoding. Note that this function is not + general purpose, rather it's currently only for URL encoding the argument list + passed to command links in markdown. *) let encodeURIComponent text = let ln = String.length text in let buf = Buffer.create ln in @@ -9,13 +14,21 @@ let encodeURIComponent text = if i < ln then ( (match text.[i] with | '"' -> Buffer.add_string buf "%22" + | '\'' -> Buffer.add_string buf "%22" | ':' -> Buffer.add_string buf "%3A" + | ';' -> Buffer.add_string buf "%3B" | '/' -> Buffer.add_string buf "%2F" | '\\' -> Buffer.add_string buf "%5C" | ',' -> Buffer.add_string buf "%2C" | '&' -> Buffer.add_string buf "%26" | '[' -> Buffer.add_string buf "%5B" | ']' -> Buffer.add_string buf "%5D" + | '#' -> Buffer.add_string buf "%23" + | '$' -> Buffer.add_string buf "%24" + | '+' -> Buffer.add_string buf "%2B" + | '=' -> Buffer.add_string buf "%3D" + | '?' -> Buffer.add_string buf "%3F" + | '@' -> Buffer.add_string buf "%40" | c -> Buffer.add_char buf c); loop (i + 1)) in From 456059c774adf7c1086b1bf9b4ac20f4b8dc2a31 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 15 Sep 2022 08:19:15 +0200 Subject: [PATCH 8/8] add pct to encode --- analysis/src/Hover.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml index 8032df250..2d0271dec 100644 --- a/analysis/src/Hover.ml +++ b/analysis/src/Hover.ml @@ -29,6 +29,7 @@ let encodeURIComponent text = | '=' -> Buffer.add_string buf "%3D" | '?' -> Buffer.add_string buf "%3F" | '@' -> Buffer.add_string buf "%40" + | '%' -> Buffer.add_string buf "%25" | c -> Buffer.add_char buf c); loop (i + 1)) in