-
Notifications
You must be signed in to change notification settings - Fork 60
Feature: Support Inlay Hint #453
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
Changes from 34 commits
84fa4dd
19d6585
a4fae36
5e0648c
a75a2b8
b4172dc
3105227
c73fa08
49b79df
935a8df
8b41f21
f46998e
a2562c6
834af28
4c2bad3
a3457d9
f43ef11
f4e9f1d
0e38c52
29b77d1
fefee51
b995ac2
91ecfe8
516f701
5ee6f66
13be958
7bd68f2
02f080a
a4353ef
674e8be
9a30439
b8d1ece
bd4cfdd
bc5494c
6921165
13b804a
2d03db2
58d3d67
83a7d74
c185c0a
9b4bac9
fc3e3f9
99bd073
65fe0ec
21338b0
de032db
dfe05f1
4df48ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
open SharedTypes | ||
|
||
type inlayHintKind = Type | Parameter | ||
let inlayKindToNumber = function | ||
| Type -> 1 | ||
| Parameter -> 2 | ||
|
||
let locItemToTypeHint ~full:{file; package} locItem = | ||
match locItem.locType with | ||
| Constant t -> | ||
Some | ||
(match t with | ||
| Const_int _ -> "int" | ||
| Const_char _ -> "char" | ||
| Const_string _ -> "string" | ||
| Const_float _ -> "float" | ||
| Const_int32 _ -> "int32" | ||
| Const_int64 _ -> "int64" | ||
| Const_nativeint _ -> "int") | ||
| Typed (_, t, locKind) -> | ||
let fromType typ = | ||
typ |> Shared.typeToString | ||
|> Str.global_replace (Str.regexp "[\r\n\t]") "" | ||
zth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
in | ||
Some | ||
(match References.definedForLoc ~file ~package locKind with | ||
| None -> fromType t | ||
| Some (_, res) -> ( | ||
match res with | ||
| `Declared -> fromType t | ||
| `Constructor _ -> fromType t | ||
| `Field -> fromType t)) | ||
| _ -> None | ||
|
||
let inlay ~path ~maxLength ~debug = | ||
let maxlen = try Some(int_of_string maxLength) with Failure _ -> None in | ||
let hints = ref [] in | ||
let rec processFunction (exp : Parsetree.expression) = | ||
match exp.pexp_desc with | ||
| Pexp_fun (_, _, pat_exp, e) -> ( | ||
match pat_exp with | ||
| {ppat_desc = Ppat_var _} -> | ||
hints := (pat_exp.ppat_loc, Type) :: !hints; | ||
processFunction e | ||
| _ -> processFunction e) | ||
| _ -> () | ||
in | ||
let value_binding (iterator : Ast_iterator.iterator) | ||
(vb : Parsetree.value_binding) = | ||
(match vb with | ||
| { | ||
pvb_pat = {ppat_desc = Ppat_var _}; | ||
pvb_expr = | ||
{ | ||
pexp_desc = | ||
( Pexp_constant _ | Pexp_tuple _ | Pexp_record _ | Pexp_variant _ | ||
| Pexp_apply _ | Pexp_match _ | Pexp_construct _ | Pexp_ifthenelse _ | ||
| Pexp_array _ | Pexp_ident _ | Pexp_try _ | Pexp_lazy _ | ||
| Pexp_send _ | Pexp_field _ | Pexp_open _ ); | ||
}; | ||
} -> | ||
hints := (vb.pvb_pat.ppat_loc, Type) :: !hints | ||
| {pvb_pat = {ppat_desc = Ppat_tuple tuples}} -> | ||
List.iter | ||
(fun (tuple : Parsetree.pattern) -> | ||
hints := (tuple.ppat_loc, Type) :: !hints) | ||
tuples | ||
| { | ||
pvb_pat = {ppat_desc = Ppat_var _}; | ||
pvb_expr = {pexp_desc = Pexp_fun (_, _, pat, e)}; | ||
} -> | ||
(match pat with | ||
| {ppat_desc = Ppat_var _} -> hints := (pat.ppat_loc, Type) :: !hints | ||
| _ -> ()); | ||
processFunction e | ||
| _ -> ()); | ||
Ast_iterator.default_iterator.value_binding iterator vb | ||
in | ||
let iterator = {Ast_iterator.default_iterator with value_binding} in | ||
(if Filename.check_suffix path ".res" then | ||
let parser = | ||
Res_driver.parsingEngine.parseImplementation ~forPrinter:false | ||
in | ||
let {Res_driver.parsetree = structure} = parser ~filename:path in | ||
iterator.structure iterator structure |> ignore | ||
else | ||
let parser = Res_driver.parsingEngine.parseInterface ~forPrinter:false in | ||
let {Res_driver.parsetree = signature} = parser ~filename:path in | ||
iterator.signature iterator signature |> ignore); | ||
!hints | ||
|> List.filter_map (fun (locOfName, hintKind) -> | ||
let range = Utils.cmtLocToRange locOfName in | ||
match Cmt.fullFromPath ~path with | ||
| None -> None | ||
| Some full -> ( | ||
match | ||
References.getLocItem ~full | ||
~pos:(range.start.line, range.start.character + 1) | ||
~debug | ||
with | ||
| None -> None | ||
| Some locItem -> ( | ||
let position : Protocol.position = | ||
{line = range.start.line; character = range.end_.character} | ||
in | ||
match locItemToTypeHint locItem ~full with | ||
| Some label -> ( | ||
let result = | ||
Protocol.stringifyHint | ||
{ | ||
kind = inlayKindToNumber hintKind; | ||
position; | ||
paddingLeft = true; | ||
paddingRight = false; | ||
label = ": " ^ label; | ||
} | ||
in | ||
match maxlen with | ||
| Some value -> if (String.length label) > value then None else Some(result) | ||
| None -> Some(result)) | ||
| None -> None))) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
let string = "ReScript" | ||
let number = 1 | ||
let float = 1.1 | ||
let char = 'c' | ||
|
||
let add = (x, y) => x + y | ||
|
||
let my_sum = 3->add(1)->add(1)->add(1)->add(8) | ||
|
||
let withAs = (~xx as yyy) => yyy + 1 | ||
|
||
|
||
@react.component | ||
let make = (~name) => React.string(name) | ||
|
||
let tuple = ("ReScript", "lol") | ||
|
||
let (lang, _) = tuple | ||
|
||
//^hint |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
{ | ||
"position": {"line": 17, "character": 9}, | ||
"label": ": string", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 15, "character": 9}, | ||
"label": ": (string, string)", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 13, "character": 17}, | ||
"label": ": string", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 9, "character": 24}, | ||
"label": ": int", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 7, "character": 10}, | ||
"label": ": int", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 5, "character": 15}, | ||
"label": ": int", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 5, "character": 12}, | ||
"label": ": int", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 3, "character": 8}, | ||
"label": ": char", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 2, "character": 9}, | ||
"label": ": float", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 1, "character": 10}, | ||
"label": ": int", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}, { | ||
"position": {"line": 0, "character": 10}, | ||
"label": ": string", | ||
"kind": 1, | ||
"paddingLeft": true, | ||
"paddingRight": false | ||
}] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -235,6 +235,17 @@ export function activate(context: ExtensionContext) { | |
// Start the client. This will also launch the server | ||
client.start(); | ||
|
||
// Restart the language client automatically certain configuration changes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. check the text There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's my text, I'll fix it |
||
// These are typically settings that affect the capabilities of the language | ||
// client, and because of that requires a full restart. | ||
context.subscriptions.push( | ||
workspace.onDidChangeConfiguration(({ affectsConfiguration }) => { | ||
if (affectsConfiguration("rescript.settings.inlayHints")) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a special reason to restart only when this specific setting changes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's only restarted automatically when required. Some configs can be changed on the fly, but other configs require restarting the server because they affect how the lsp is instantiated. If we don't have this then the user would need to be promoted to restart the server manually to have their config take effect. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The question is why not the other changes too. Overall, why special case thins config, is the question. |
||
commands.executeCommand("rescript-vscode.restart_language_server"); | ||
} | ||
}) | ||
); | ||
|
||
// Autostart code analysis if wanted | ||
if ( | ||
workspace | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: why separate these 2 cases?