Skip to content

Signature help #547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
779e12a
wire up server capability + command for signature help
zth Jul 25, 2022
6a51a0d
set up analysis bin command, and needed things from the LS protocol
zth Jul 25, 2022
b311369
set up empty signature help test
zth Jul 25, 2022
6f19fbb
another test to help distinguish
zth Jul 25, 2022
9c26acd
implement signature help for function applications
zth Aug 6, 2022
7a5769c
changelog + readme
zth Aug 6, 2022
037831f
Merge branch 'master' of github.com:rescript-lang/rescript-vscode int…
zth Aug 6, 2022
07e2807
leverage the parser to figure out the parameter offsets
zth Aug 7, 2022
d970290
include docs for signature if available
zth Aug 7, 2022
ac24e93
if a function has only one (unlabelled) argument, we can always highl…
zth Aug 7, 2022
f1a02ed
Merge branch 'master' into signature-help
zth Sep 18, 2022
03cd58c
add debug utilities
zth Sep 18, 2022
f5ab1eb
Merge branch 'debug-utils' into signature-help
zth Sep 18, 2022
2f7c06e
get rid of hasPosInclusive
zth Sep 19, 2022
fc6afe3
move extractExpApplyArgs to SharedTypes for real
zth Sep 19, 2022
8c237f2
shared function
zth Sep 19, 2022
d2190f8
add way to parse via raw source string in parser, and replace current…
zth Sep 19, 2022
4df6121
refactor logic some
zth Sep 19, 2022
7ebf877
Merge branch 'master' into signature-help
zth Sep 19, 2022
093ebef
refactor hover so type expansion can be shared, and integrate into si…
zth Sep 21, 2022
4fdf6ff
Merge branch 'master' into signature-help
zth Sep 22, 2022
f02ebef
gate new experimental signature help behind configuration option
zth Sep 22, 2022
c9fe034
comment
zth Sep 22, 2022
0368f1d
changelog
zth Sep 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ The only 2 themes we don't (and can't) support, due to their lack of coloring, a
- Find references.
- Rename.
- Inlay Hints.
- Signature help.
- Code lenses.
- Snippets to ease a few syntaxes:
- `external` features such as `@bs.module` and `@bs.val`
Expand All @@ -85,14 +86,15 @@ ext install chenglou92.rescript-vscode
The plugin activates on `.res` and `.resi` files. If you've already got Reason-Language-Server installed, it's possible that the latter took precedence over this one. Make sure you're using this plugin ("ReScript syntax") rather than Reason-Language-Server ("BuckleScript syntax").

### Pre-release channel

There is a pre-release channel available. It is intended for testing new and therefore possibly unstable features. You can activate it by clicking on the "Switch to Pre-Release Version" button on the `rescript-vscode` extension page in VSCode. From this point on, pre-release versions will always have an odd version minor (1.5.x, 1.7.x, 2.1.x, etc.) while stable releases have even version minor numbers (1.4.x, 1.6.x, 2.0.0, etc.).

Even if the pre-release channel seems too experimental to you, we still suggest you to give it a try and submit any issues that you run into. In the long run it will give us a better editor experience overall.

## 📦 Commands

| Command | Description |
|------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ReScript: Create an interface file for this implementation file | Creates an interface file (`.resi`) for the current `.res` file, automatically filling in all types and values in the current file. |
| ReScript: Open the compiled JS file for this implementation file | Opens the compiled JS file for the current ReScript file. |
| ReScript: Switch implementation/interface | Switches between the implementation and interface file. If you're in a `.res` file, the command will open the corresponding `.resi` file (if it exists), and if you're in a `.resi` file the command will open the corresponding `.res` file. This can also be triggered with the keybinding `Alt+O`. |
Expand All @@ -104,10 +106,10 @@ You'll find all ReScript specific settings under the scope `rescript.settings`.

| Setting | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Prompt to Start Build | If there's no ReScript build running already in the opened project, the extension will prompt you and ask if you want to start a build automatically. You can turn off this automatic prompt via the setting `rescript.settings.askToStartBuild`. |
| Prompt to Start Build | If there's no ReScript build running already in the opened project, the extension will prompt you and ask if you want to start a build automatically. You can turn off this automatic prompt via the setting `rescript.settings.askToStartBuild`. |
| ReScript Binary Path | The extension will look for the existence of a `/node_modules/.bin/rescript` file and use its directory as the `binaryPath`. If it does not find it at the project root (which is where the nearest `bsconfig.json` resides), it goes up folders in the filesystem recursively until it either finds it (often the case in monorepos) or hits the top level. To override this lookup process, the path can be configured explicitly using the setting `rescript.settings.binaryPath` |
| Inlay Hints (experimental) | This allows an editor to place annotations inline with text to display type hints. Enable using `rescript.settings.inlayHints.enable: true` |
| Code Lens (experimental) | This tells the editor to add code lenses to function definitions, showing its full type above the definition. Enable using `rescript.settings.codeLens: true` |
| Inlay Hints (experimental) | This allows an editor to place annotations inline with text to display type hints. Enable using `rescript.settings.inlayHints.enable: true` |
| Code Lens (experimental) | This tells the editor to add code lenses to function definitions, showing its full type above the definition. Enable using `rescript.settings.codeLens: true` |
| Autostarting the Code Analyzer | The Code Analyzer needs to be started manually by default. However, you can configure the extension to start the Code Analyzer automatically via the setting `rescript.settings.autoRunCodeAnalysis`. |

**Default settings:**
Expand Down
8 changes: 8 additions & 0 deletions analysis/src/Cli.ml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ Options:

./rescript-editor-analysis.exe codeLens src/MyFile.res

signatureHelp: get signature help if available for position at line 10 column 2 in src/MyFile.res

./rescript-editor-analysis.exe signatureHelp src/MyFile.res 10 2

test: run tests specified by special comments in file src/MyFile.res

./rescript-editor-analysis.exe test src/src/MyFile.res
Expand All @@ -99,6 +103,10 @@ let main () =
Commands.hover ~path
~pos:(int_of_string line, int_of_string col)
~currentFile ~debug:false
| [_; "signatureHelp"; path; line; col; currentFile] ->
Commands.signatureHelp ~path
~pos:(int_of_string line, int_of_string col)
~currentFile ~debug:false
| [_; "inlayHint"; path; line_start; line_end; maxLength] ->
Commands.inlayhint ~path
~pos:(int_of_string line_start, int_of_string line_end)
Expand Down
16 changes: 16 additions & 0 deletions analysis/src/Commands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ let hover ~path ~pos ~currentFile ~debug =
in
print_endline result

let signatureHelp ~path ~pos ~currentFile ~debug =
let result =
match SignatureHelp.signatureHelp ~path ~pos ~currentFile ~debug with
| None ->
{Protocol.signatures = []; activeSignature = None; activeParameter = None}
| Some res -> res
in
print_endline (Protocol.stringifySignatureHelp result)

let codeAction ~path ~pos ~currentFile ~debug =
Xform.extractCodeActions ~path ~pos ~currentFile ~debug
|> CodeActions.stringifyCodeActions |> print_endline
Expand Down Expand Up @@ -343,6 +352,13 @@ let test ~path =
let currentFile = createCurrentFile () in
hover ~path ~pos:(line, col) ~currentFile ~debug:true;
Sys.remove currentFile
| "she" ->
print_endline
("Signature help " ^ path ^ " " ^ string_of_int line ^ ":"
^ string_of_int col);
let currentFile = createCurrentFile () in
signatureHelp ~path ~pos:(line, col) ~currentFile ~debug:true;
Sys.remove currentFile
| "int" ->
print_endline ("Create Interface " ^ path);
let cmiFile =
Expand Down
6 changes: 6 additions & 0 deletions analysis/src/Files.ml
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,9 @@ let classifySourceFile path =
if Filename.check_suffix path ".res" && exists path then Res
else if Filename.check_suffix path ".resi" && exists path then Resi
else Other

let writeTempFile ~prefix ~suffix contents =
let tempFile, outChannel = Filename.open_temp_file prefix suffix in
Printf.fprintf outChannel "%s" contents;
close_out outChannel;
tempFile
2 changes: 2 additions & 0 deletions analysis/src/Loc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ let toString (loc : t) =
(if loc.loc_ghost then "__ghost__" else "") ^ (loc |> range |> Range.toString)

let hasPos ~pos loc = start loc <= pos && pos < end_ loc

let hasPosInclusive ~pos loc = start loc <= pos && pos <= end_ loc
55 changes: 55 additions & 0 deletions analysis/src/Protocol.ml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,23 @@ type inlayHint = {
paddingRight: bool;
}

(* https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#parameterInformation *)
type parameterInformation = {label: int * int}

(* https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#signatureInformation *)
type signatureInformation = {
label: string;
parameters: parameterInformation list;
documentation: markupContent option;
}

(* https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#signatureHelp *)
type signatureHelp = {
signatures: signatureInformation list;
activeSignature: int option;
activeParameter: int option;
}

type completionItem = {
label: string;
kind: int;
Expand Down Expand Up @@ -170,6 +187,44 @@ let stringifyCodeLens (codeLens : codeLens) =
| None -> ""
| Some command -> stringifyCommand command)

let stringifyParameterInformation (parameterInformation : parameterInformation)
=
Printf.sprintf {|{"label": %s}|}
(let line, chr = parameterInformation.label in
"[" ^ string_of_int line ^ ", " ^ string_of_int chr ^ "]")

let stringifySignatureInformation (signatureInformation : signatureInformation)
=
Printf.sprintf
{|{
"label": "%s",
"parameters": %s%s
}|}
(Json.escape signatureInformation.label)
(signatureInformation.parameters
|> List.map stringifyParameterInformation
|> array)
(match signatureInformation.documentation with
| None -> ""
| Some docs ->
Printf.sprintf ",\n \"documentation\": %s"
(stringifyMarkupContent docs))
Comment on lines +211 to +212
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Poor mans optional prop stringification.


let stringifySignatureHelp (signatureHelp : signatureHelp) =
Printf.sprintf
{|{
"signatures": %s,
"activeSignature": %s,
"activeParameter": %s
}|}
(signatureHelp.signatures |> List.map stringifySignatureInformation |> array)
(match signatureHelp.activeSignature with
| None -> null
| Some activeSignature -> activeSignature |> string_of_int)
(match signatureHelp.activeParameter with
| None -> null
| Some activeParameter -> activeParameter |> string_of_int)

(* https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnostic *)
let stringifyDiagnostic d =
Printf.sprintf
Expand Down
7 changes: 5 additions & 2 deletions analysis/src/SharedTypes.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
let str s = if s = "" then "\"\"" else s
let list l = "[" ^ (l |> List.map str |> String.concat ", ") ^ "]"

let ident i = i |> List.map str |> String.concat "."

type modulePath =
| File of Uri.t * string
| NotVisible
Expand Down Expand Up @@ -435,8 +440,6 @@ module Completable = struct
(** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for <M.Comp id1=... id2=... ... id *)

let toString =
let str s = if s = "" then "\"\"" else s in
let list l = "[" ^ (l |> List.map str |> String.concat ", ") ^ "]" in
let completionContextToString = function
| Value -> "Value"
| Type -> "Type"
Expand Down
Loading