Skip to content

Commit f035fbf

Browse files
zthcristianoc
authored andcommitted
handle pipe chains as assigned value
1 parent 29ab231 commit f035fbf

File tree

3 files changed

+94
-17
lines changed

3 files changed

+94
-17
lines changed

analysis/src/CompletionFrontEnd.ml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor
136136
})
137137
| _ -> loop args
138138

139-
(* TODO: Mimic pipe chain detection here? *)
140139
let rec exprToContextPath (e : Parsetree.expression) =
141140
match e.pexp_desc with
142141
| Pexp_constant (Pconst_string _) -> Some Completable.CPString
@@ -149,6 +148,7 @@ let rec exprToContextPath (e : Parsetree.expression) =
149148
(match exprs with
150149
| [] -> None
151150
| exp :: _ -> exprToContextPath exp))
151+
| Pexp_ident {txt = Lident "|."} -> None
152152
| Pexp_ident {txt} -> Some (CPId (Utils.flattenLongIdent txt, Value))
153153
| Pexp_field (e1, {txt = Lident name}) -> (
154154
match exprToContextPath e1 with
@@ -172,15 +172,15 @@ let rec exprToContextPath (e : Parsetree.expression) =
172172
else None
173173
| _ -> None
174174

175-
let completePipeChain ~(lhs : Parsetree.expression) =
175+
let completePipeChain (exp : Parsetree.expression) =
176176
(* Complete the end of pipe chains by reconstructing the pipe chain as a single pipe,
177177
so it can be completed.
178178
Example:
179179
someArray->Js.Array2.filter(v => v > 10)->Js.Array2.map(v => v + 2)->
180180
will complete as:
181181
Js.Array2.map(someArray->Js.Array2.filter(v => v > 10), v => v + 2)->
182182
*)
183-
match lhs.pexp_desc with
183+
match exp.pexp_desc with
184184
(* When the left side of the pipe we're completing is a function application.
185185
Example: someArray->Js.Array2.map(v => v + 2)-> *)
186186
| Pexp_apply
@@ -351,7 +351,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
351351
| _ -> ()
352352
in
353353
let scopeValueBinding (vb : Parsetree.value_binding) =
354-
let contextPath = exprToContextPath vb.pvb_expr in
354+
let contextPath =
355+
(* Pipe chains get special treatment here, because when assigning values
356+
we want the return of the entire pipe chain as a function call, rather
357+
than as a pipe completion call. *)
358+
match completePipeChain vb.pvb_expr with
359+
| Some (ctxPath, _) -> Some ctxPath
360+
| None -> exprToContextPath vb.pvb_expr
361+
in
355362
scopePattern ?contextPath vb.pvb_pat
356363
in
357364
let scopeTypeKind (tk : Parsetree.type_kind) =
@@ -690,7 +697,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
690697
(Loc.toString expr.pexp_loc)
691698
in
692699
let setPipeResult ~(lhs : Parsetree.expression) ~id =
693-
match completePipeChain ~lhs with
700+
match completePipeChain lhs with
694701
| None -> (
695702
match exprToContextPath lhs with
696703
| Some pipe ->

analysis/tests/src/CompletionInferValues.res

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ module Div = {
4646
// let _ = <Div onMouseEnter={event => { event->pr }} />
4747
// ^com
4848

49-
// Pipe are broken, need to fix
50-
// let _ = <div onMouseEnter={event => { let btn = event->JsxEvent.Mouse.button; btn-> }} />
51-
// ^com
49+
// let _ = <div onMouseEnter={event => { let btn = event->JsxEvent.Mouse.button; btn->t }} />
50+
// ^com
51+
52+
// let _ = <div onMouseEnter={event => { let btn = event->JsxEvent.Mouse.button->Belt.Int.toString; btn->spl }} />
53+
// ^com
54+
55+
// let _ = <div onMouseEnter={event => { let btn = event->JsxEvent.Mouse.button->Belt.Int.toString->Js.String2.split("/"); btn->ma }} />
56+
// ^com

analysis/tests/src/expected/CompletionInferValues.res.txt

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,78 @@ Completable: Cpath Value[event]->pr <<jsx>>
127127
"documentation": null
128128
}]
129129

130-
Complete src/CompletionInferValues.res 49:86
131-
posCursor:[49:86] posNoWhite:[49:85] Found expr:[49:12->49:92]
132-
JSX <div:[49:12->49:15] onMouseEnter[49:16->49:28]=...[49:36->49:88]> _children:49:90
133-
posCursor:[49:86] posNoWhite:[49:85] Found expr:[49:36->49:88]
134-
posCursor:[49:86] posNoWhite:[49:85] Found expr:[49:41->49:86]
135-
posCursor:[49:86] posNoWhite:[49:85] Found expr:[49:81->0:-1]
136-
posCursor:[49:86] posNoWhite:[49:85] Found expr:[49:81->0:-1]
137-
Completable: Cpath Value[btn]-> <<jsx>>
138-
[]
130+
Complete src/CompletionInferValues.res 48:87
131+
posCursor:[48:87] posNoWhite:[48:86] Found expr:[48:12->48:93]
132+
JSX <div:[48:12->48:15] onMouseEnter[48:16->48:28]=...[48:36->48:89]> _children:48:91
133+
posCursor:[48:87] posNoWhite:[48:86] Found expr:[48:36->48:89]
134+
posCursor:[48:87] posNoWhite:[48:86] Found expr:[48:41->48:87]
135+
posCursor:[48:87] posNoWhite:[48:86] Found expr:[48:81->48:87]
136+
Completable: Cpath Value[btn]->t <<jsx>>
137+
[{
138+
"label": "Belt.Int.toString",
139+
"kind": 12,
140+
"tags": [],
141+
"detail": "int => string",
142+
"documentation": {"kind": "markdown", "value": "\n Converts a given `int` to a `string`. Uses the JavaScript `String` constructor under the hood.\n\n ```res example\n Js.log(Belt.Int.toString(1) === \"1\") /* true */\n ```\n"}
143+
}, {
144+
"label": "Belt.Int.toFloat",
145+
"kind": 12,
146+
"tags": [],
147+
"detail": "int => float",
148+
"documentation": {"kind": "markdown", "value": "\n Converts a given `int` to a `float`.\n\n ```res example\n Js.log(Belt.Int.toFloat(1) === 1.0) /* true */\n ```\n"}
149+
}]
150+
151+
Complete src/CompletionInferValues.res 51:108
152+
posCursor:[51:108] posNoWhite:[51:107] Found expr:[51:12->51:114]
153+
JSX <div:[51:12->51:15] onMouseEnter[51:16->51:28]=...[51:36->51:110]> _children:51:112
154+
posCursor:[51:108] posNoWhite:[51:107] Found expr:[51:36->51:110]
155+
posCursor:[51:108] posNoWhite:[51:107] Found expr:[51:41->51:108]
156+
posCursor:[51:108] posNoWhite:[51:107] Found expr:[51:100->51:108]
157+
Completable: Cpath Value[btn]->spl <<jsx>>
158+
[{
159+
"label": "Js.String2.splitAtMost",
160+
"kind": 12,
161+
"tags": [],
162+
"detail": "(t, t, ~limit: int) => array<t>",
163+
"documentation": {"kind": "markdown", "value": "\n `splitAtMost delimiter ~limit: n str` splits the given `str` at every occurrence of `delimiter` and returns an array of the first `n` resulting substrings. If `n` is negative or greater than the number of substrings, the array will contain all the substrings.\n\n```\nsplitAtMost \"ant/bee/cat/dog/elk\" \"/\" ~limit: 3 = [|\"ant\"; \"bee\"; \"cat\"|];;\nsplitAtMost \"ant/bee/cat/dog/elk\" \"/\" ~limit: 0 = [| |];;\nsplitAtMost \"ant/bee/cat/dog/elk\" \"/\" ~limit: 9 = [|\"ant\"; \"bee\"; \"cat\"; \"dog\"; \"elk\"|];;\n```\n"}
164+
}, {
165+
"label": "Js.String2.splitByRe",
166+
"kind": 12,
167+
"tags": [],
168+
"detail": "(t, Js_re.t) => array<option<t>>",
169+
"documentation": {"kind": "markdown", "value": "\n`splitByRe(str, regex)` splits the given `str` at every occurrence of `regex`\nand returns an array of the resulting substrings.\n\nSee [`String.split`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split)\non MDN.\n\n```res example\nJs.String2.splitByRe(\"art; bed , cog ;dad\", %re(\"/\\s*[,;]\\s*/\")) == [\n Some(\"art\"),\n Some(\"bed\"),\n Some(\"cog\"),\n Some(\"dad\"),\n ]\n```\n"}
170+
}, {
171+
"label": "Js.String2.split",
172+
"kind": 12,
173+
"tags": [],
174+
"detail": "(t, t) => array<t>",
175+
"documentation": {"kind": "markdown", "value": "\n`split(str, delimiter)` splits the given `str` at every occurrence of\n`delimiter` and returns an array of the resulting substrings.\n\nSee [`String.split`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split)\non MDN.\n\n```res example\nJs.String2.split(\"2018-01-02\", \"-\") == [\"2018\", \"01\", \"02\"]\nJs.String2.split(\"a,b,,c\", \",\") == [\"a\", \"b\", \"\", \"c\"]\nJs.String2.split(\"good::bad as great::awful\", \"::\") == [\"good\", \"bad as great\", \"awful\"]\nJs.String2.split(\"has-no-delimiter\", \";\") == [\"has-no-delimiter\"]\n```\n"}
176+
}, {
177+
"label": "Js.String2.splitByReAtMost",
178+
"kind": 12,
179+
"tags": [],
180+
"detail": "(t, Js_re.t, ~limit: int) => array<option<t>>",
181+
"documentation": {"kind": "markdown", "value": "\n`splitByReAtMost(str, regex, ~limit:n)` splits the given `str` at every\noccurrence of `regex` and returns an array of the first `n` resulting\nsubstrings. If `n` is negative or greater than the number of substrings, the\narray will contain all the substrings.\n\nSee [`String.split`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split)\non MDN.\n\n```res example\nJs.String2.splitByReAtMost(\"one: two: three: four\", %re(\"/\\s*:\\s*/\"), ~limit=3) == [\n Some(\"one\"),\n Some(\"two\"),\n Some(\"three\"),\n ]\n\nJs.String2.splitByReAtMost(\"one: two: three: four\", %re(\"/\\s*:\\s*/\"), ~limit=0) == []\n\nJs.String2.splitByReAtMost(\"one: two: three: four\", %re(\"/\\s*:\\s*/\"), ~limit=8) == [\n Some(\"one\"),\n Some(\"two\"),\n Some(\"three\"),\n Some(\"four\"),\n ]\n```\n"}
182+
}]
183+
184+
Complete src/CompletionInferValues.res 54:130
185+
posCursor:[54:130] posNoWhite:[54:129] Found expr:[54:12->54:136]
186+
JSX <div:[54:12->54:15] onMouseEnter[54:16->54:28]=...[54:36->54:132]> _children:54:134
187+
posCursor:[54:130] posNoWhite:[54:129] Found expr:[54:36->54:132]
188+
posCursor:[54:130] posNoWhite:[54:129] Found expr:[54:41->54:130]
189+
posCursor:[54:130] posNoWhite:[54:129] Found expr:[54:123->54:130]
190+
Completable: Cpath Value[btn]->ma <<jsx>>
191+
[{
192+
"label": "Js.Array2.mapi",
193+
"kind": 12,
194+
"tags": [],
195+
"detail": "(t<'a>, ('a, int) => 'b) => t<'b>",
196+
"documentation": {"kind": "markdown", "value": "\nApplies the function (the second argument) to each item in the array, returning\na new array. The function acceps two arguments: an item from the array and its\nindex number. The result array does not have to have elements of the same type\nas the input array. See\n[`Array.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)\non MDN.\n\n```res example\n// multiply each item in array by its position\nlet product = (item, index) => item * index\nJs.Array2.mapi([10, 11, 12], product) == [0, 11, 24]\n```\n"}
197+
}, {
198+
"label": "Js.Array2.map",
199+
"kind": 12,
200+
"tags": [],
201+
"detail": "(t<'a>, 'a => 'b) => t<'b>",
202+
"documentation": {"kind": "markdown", "value": "\nApplies the function (the second argument) to each item in the array, returning\na new array. The result array does not have to have elements of the same type\nas the input array. See\n[`Array.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)\non MDN.\n\n```res example\nJs.Array2.map([12, 4, 8], x => x * x) == [144, 16, 64]\nJs.Array2.map([\"animal\", \"vegetable\", \"mineral\"], Js.String.length) == [6, 9, 7]\n```\n"}
203+
}]
139204

0 commit comments

Comments
 (0)