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 all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- 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
- Add experimental signature help for function calls. https://github.com/rescript-lang/rescript-vscode/pull/547

#### :bug: Bug Fix

Expand Down
11 changes: 7 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,11 @@ 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` |
| Signature Help (experimental) | This tells the editor to show signature help when you're writing function calls. Enable using `rescript.settings.signatureHelp.enable: 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 Down Expand Up @@ -103,6 +107,10 @@ let main () =
(match supportsMarkdownLinks with
| "true" -> true
| _ -> 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 @@ -79,6 +79,15 @@ let hover ~path ~pos ~currentFile ~debug ~supportsMarkdownLinks =
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 @@ -335,6 +344,13 @@ let test ~path =
hover ~supportsMarkdownLinks:true ~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
43 changes: 1 addition & 42 deletions analysis/src/CompletionFrontEnd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,6 @@ let extractJsxProps ~(compName : Longident.t Location.loc) ~args =
in
args |> processProps ~acc:[]

type labelled = {
name: string;
opt: bool;
posStart: int * int;
posEnd: int * int;
}

type label = labelled option
type arg = {label: label; exp: Parsetree.expression}

let findNamedArgCompletable ~(args : arg list) ~endPos ~posBeforeCursor
~(contextPath : Completable.contextPath) ~posAfterFunExpr =
let allNames =
Expand Down Expand Up @@ -145,37 +135,6 @@ let findNamedArgCompletable ~(args : arg list) ~endPos ~posBeforeCursor
in
loop args

let extractExpApplyArgs ~args =
let rec processArgs ~acc args =
match args with
| (((Asttypes.Labelled s | Optional s) as label), (e : Parsetree.expression))
:: rest -> (
let namedArgLoc =
e.pexp_attributes
|> List.find_opt (fun ({Asttypes.txt}, _) -> txt = "ns.namedArgLoc")
in
match namedArgLoc with
| Some ({loc}, _) ->
let labelled =
{
name = s;
opt =
(match label with
| Optional _ -> true
| _ -> false);
posStart = Loc.start loc;
posEnd = Loc.end_ loc;
}
in
processArgs ~acc:({label = Some labelled; exp = e} :: acc) rest
| None -> processArgs ~acc rest)
| (Asttypes.Nolabel, (e : Parsetree.expression)) :: rest ->
if e.pexp_loc.loc_ghost then processArgs ~acc rest
else processArgs ~acc:({label = None; exp = e} :: acc) rest
| [] -> List.rev acc
in
args |> processArgs ~acc:[]

let rec exprToContextPath (e : Parsetree.expression) =
match e.pexp_desc with
| Pexp_constant (Pconst_string _) -> Some Completable.CPString
Expand Down Expand Up @@ -204,7 +163,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
let line, col = posCursor in
(line, max 0 col - offset + offsetNoWhite)
in
let posBeforeCursor = (fst posCursor, max 0 (snd posCursor - 1)) in
let posBeforeCursor = Pos.posBeforeCursor posCursor in
let charBeforeCursor, blankAfterCursor =
match Pos.positionToOffset text posCursor with
| Some offset when offset > 0 -> (
Expand Down
2 changes: 2 additions & 0 deletions analysis/src/Pos.ml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ let positionToOffset text (line, character) =
| Some bol ->
if bol + character <= String.length text then Some (bol + character)
else None

let posBeforeCursor pos = (fst pos, max 0 (snd pos - 1))
56 changes: 56 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; documentation: markupContent}

(* 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,45 @@ let stringifyCodeLens (codeLens : codeLens) =
| None -> ""
| Some command -> stringifyCommand command)

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

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
Loading