Skip to content

Commit 7d9b33f

Browse files
authoredJan 29, 2024
Enhance regexp completion (#903)
* enhance regexp completion a bit * changelog
1 parent 91714d8 commit 7d9b33f

9 files changed

+172
-73
lines changed
 

‎CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
1313
## master
1414

15+
#### :nail_care: Polish
16+
17+
- Prefer Core's `RegExp` when Core is open and completing for regexp functions. https://github.com/rescript-lang/rescript-vscode/pull/903
18+
- Add `%re("")` to the completions list when completing in a position where a regexp value is expected. https://github.com/rescript-lang/rescript-vscode/pull/903
19+
1520
## 1.36.0
1621

1722
#### :bug: Bug Fix

‎analysis/src/CompletionBackEnd.ml

+23-7
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
941941
promiseModulePath;
942942
listModulePath;
943943
resultModulePath;
944+
regexpModulePath;
944945
} =
945946
package.builtInCompletionModules
946947
in
@@ -954,6 +955,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
954955
| Promise -> promiseModulePath
955956
| List -> listModulePath
956957
| Result -> resultModulePath
958+
| RegExp -> regexpModulePath
957959
| Lazy -> ["Lazy"]
958960
| Char -> ["Char"])
959961
| TypExpr t -> (
@@ -1291,13 +1293,27 @@ let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens
12911293
Hashtbl.add functionsReturningTypeT
12921294
((base |> String.concat ".") ^ "." ^ name)
12931295
item));
1294-
Hashtbl.fold
1295-
(fun fnName typeExpr all ->
1296-
createWithSnippet
1297-
~name:(Printf.sprintf "%s()" fnName)
1298-
~insertText:(fnName ^ "($0)") ~kind:(Value typeExpr) ~env ()
1299-
:: all)
1300-
functionsReturningTypeT []
1296+
1297+
let completionItems =
1298+
Hashtbl.fold
1299+
(fun fnName typeExpr all ->
1300+
createWithSnippet
1301+
~name:(Printf.sprintf "%s()" fnName)
1302+
~insertText:(fnName ^ "($0)") ~kind:(Value typeExpr) ~env ()
1303+
:: all)
1304+
functionsReturningTypeT []
1305+
in
1306+
(* Special casing for things where we want extra things in the completions *)
1307+
let completionItems =
1308+
match path with
1309+
| Pdot (Pdot (Pident m, "Re", _), "t", _) when Ident.name m = "Js" ->
1310+
(* regexps *)
1311+
createWithSnippet ~name:"%re()" ~insertText:"%re(\"/$0/g\")"
1312+
~kind:(Label "Regular expression") ~env ()
1313+
:: completionItems
1314+
| _ -> completionItems
1315+
in
1316+
completionItems
13011317
| Tbool env ->
13021318
if Debug.verbose () then print_endline "[complete_typed_value]--> Tbool";
13031319
[

‎analysis/src/Packages.ml

+60-56
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ let newBsPackage ~rootPath =
3232
let bsconfigJson = Filename.concat rootPath "bsconfig.json" in
3333

3434
let parseRaw raw =
35-
let libBs = match !Cfg.isDocGenFromCompiler with
36-
| true -> BuildSystem.getStdlib rootPath
37-
| false -> BuildSystem.getLibBs rootPath
35+
let libBs =
36+
match !Cfg.isDocGenFromCompiler with
37+
| true -> BuildSystem.getStdlib rootPath
38+
| false -> BuildSystem.getLibBs rootPath
3839
in
3940
match Json.parse raw with
4041
| Some config -> (
@@ -48,10 +49,10 @@ let newBsPackage ~rootPath =
4849
(let namespace = FindFiles.getNamespace config in
4950
let rescriptVersion = getReScriptVersion () in
5051
let suffix =
51-
match config |> Json.get "suffix" with
52-
| Some (String suffix) -> suffix
53-
| _ -> ".js"
54-
in
52+
match config |> Json.get "suffix" with
53+
| Some (String suffix) -> suffix
54+
| _ -> ".js"
55+
in
5556
let uncurried =
5657
let ns = config |> Json.get "uncurried" in
5758
match (rescriptVersion, ns) with
@@ -132,55 +133,58 @@ let newBsPackage ~rootPath =
132133
namespace;
133134
builtInCompletionModules =
134135
(if
135-
opens_from_bsc_flags
136-
|> List.find_opt (fun opn ->
137-
match opn with
138-
| ["RescriptCore"] -> true
139-
| _ -> false)
140-
|> Option.is_some
141-
then
142-
{
143-
arrayModulePath = ["Array"];
144-
optionModulePath = ["Option"];
145-
stringModulePath = ["String"];
146-
intModulePath = ["Int"];
147-
floatModulePath = ["Float"];
148-
promiseModulePath = ["Promise"];
149-
listModulePath = ["List"];
150-
resultModulePath = ["Result"];
151-
exnModulePath = ["Exn"];
152-
}
153-
else if
154-
opens_from_bsc_flags
155-
|> List.find_opt (fun opn ->
156-
match opn with
157-
| ["Belt"] -> true
158-
| _ -> false)
159-
|> Option.is_some
160-
then
161-
{
162-
arrayModulePath = ["Array"];
163-
optionModulePath = ["Option"];
164-
stringModulePath = ["Js"; "String2"];
165-
intModulePath = ["Int"];
166-
floatModulePath = ["Float"];
167-
promiseModulePath = ["Js"; "Promise"];
168-
listModulePath = ["List"];
169-
resultModulePath = ["Result"];
170-
exnModulePath = ["Js"; "Exn"];
171-
}
172-
else
173-
{
174-
arrayModulePath = ["Js"; "Array2"];
175-
optionModulePath = ["Belt"; "Option"];
176-
stringModulePath = ["Js"; "String2"];
177-
intModulePath = ["Belt"; "Int"];
178-
floatModulePath = ["Belt"; "Float"];
179-
promiseModulePath = ["Js"; "Promise"];
180-
listModulePath = ["Belt"; "List"];
181-
resultModulePath = ["Belt"; "Result"];
182-
exnModulePath = ["Js"; "Exn"];
183-
});
136+
opens_from_bsc_flags
137+
|> List.find_opt (fun opn ->
138+
match opn with
139+
| ["RescriptCore"] -> true
140+
| _ -> false)
141+
|> Option.is_some
142+
then
143+
{
144+
arrayModulePath = ["Array"];
145+
optionModulePath = ["Option"];
146+
stringModulePath = ["String"];
147+
intModulePath = ["Int"];
148+
floatModulePath = ["Float"];
149+
promiseModulePath = ["Promise"];
150+
listModulePath = ["List"];
151+
resultModulePath = ["Result"];
152+
exnModulePath = ["Exn"];
153+
regexpModulePath = ["RegExp"];
154+
}
155+
else if
156+
opens_from_bsc_flags
157+
|> List.find_opt (fun opn ->
158+
match opn with
159+
| ["Belt"] -> true
160+
| _ -> false)
161+
|> Option.is_some
162+
then
163+
{
164+
arrayModulePath = ["Array"];
165+
optionModulePath = ["Option"];
166+
stringModulePath = ["Js"; "String2"];
167+
intModulePath = ["Int"];
168+
floatModulePath = ["Float"];
169+
promiseModulePath = ["Js"; "Promise"];
170+
listModulePath = ["List"];
171+
resultModulePath = ["Result"];
172+
exnModulePath = ["Js"; "Exn"];
173+
regexpModulePath = ["Js"; "Re"];
174+
}
175+
else
176+
{
177+
arrayModulePath = ["Js"; "Array2"];
178+
optionModulePath = ["Belt"; "Option"];
179+
stringModulePath = ["Js"; "String2"];
180+
intModulePath = ["Belt"; "Int"];
181+
floatModulePath = ["Belt"; "Float"];
182+
promiseModulePath = ["Js"; "Promise"];
183+
listModulePath = ["Belt"; "List"];
184+
resultModulePath = ["Belt"; "Result"];
185+
exnModulePath = ["Js"; "Exn"];
186+
regexpModulePath = ["Js"; "Re"];
187+
});
184188
uncurried;
185189
})))
186190
| None -> None

‎analysis/src/SharedTypes.ml

+1
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ type builtInCompletionModules = {
493493
listModulePath: string list;
494494
resultModulePath: string list;
495495
exnModulePath: string list;
496+
regexpModulePath: string list;
496497
}
497498

498499
type package = {

‎analysis/src/TypeUtils.ml

+16-10
Original file line numberDiff line numberDiff line change
@@ -449,23 +449,29 @@ type builtinType =
449449
| Result
450450
| Lazy
451451
| Char
452+
| RegExp
452453

453454
type pipeCompletionType =
454455
| Builtin of builtinType * Types.type_expr
455456
| TypExpr of Types.type_expr
456457

457458
let getBuiltinFromTypePath path =
458459
match path with
459-
| Path.Pident id when Ident.name id = "array" -> Some Array
460-
| Path.Pident id when Ident.name id = "option" -> Some Option
461-
| Path.Pident id when Ident.name id = "string" -> Some String
462-
| Path.Pident id when Ident.name id = "int" -> Some Int
463-
| Path.Pident id when Ident.name id = "float" -> Some Float
464-
| Path.Pident id when Ident.name id = "promise" -> Some Promise
465-
| Path.Pident id when Ident.name id = "list" -> Some List
466-
| Path.Pident id when Ident.name id = "result" -> Some Result
467-
| Path.Pident id when Ident.name id = "lazy_t" -> Some Lazy
468-
| Path.Pident id when Ident.name id = "char" -> Some Char
460+
| Path.Pident _ -> (
461+
match Path.name path with
462+
| "array" -> Some Array
463+
| "option" -> Some Option
464+
| "string" -> Some String
465+
| "int" -> Some Int
466+
| "float" -> Some Float
467+
| "promise" -> Some Promise
468+
| "list" -> Some List
469+
| "result" -> Some Result
470+
| "lazy_t" -> Some Lazy
471+
| "char" -> Some Char
472+
| _ -> None)
473+
| Pdot (Pdot (Pident m, "Re", _), "t", _) when Ident.name m = "Js" ->
474+
Some RegExp
469475
| Pdot (Pident id, "result", _)
470476
when Ident.name id = "Pervasives" || Ident.name id = "PervasivesU" ->
471477
Some Result

‎analysis/tests/src/CompletionExpressions.res

+8
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,11 @@ type withIntLocal = {superInt: SuperInt.t}
313313
open CompletionSupport
314314
// CompletionSupport.makeTestHidden()
315315
// ^com
316+
317+
let mkStuff = (r: Js.Re.t) => {
318+
ignore(r)
319+
"hello"
320+
}
321+
322+
// mkStuff()
323+
// ^com

‎analysis/tests/src/CompletionPipeChain.res

+5
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,8 @@ let renderer = CompletionSupport2.makeRenderer(
9898

9999
// Console.log(int->t)
100100
// ^com
101+
102+
let r = %re("/t/g")
103+
104+
// r->la
105+
// ^com

‎analysis/tests/src/expected/CompletionExpressions.res.txt

+36
Original file line numberDiff line numberDiff line change
@@ -1296,3 +1296,39 @@ Path CompletionSupport.makeTestHidden
12961296
"insertTextFormat": 2
12971297
}]
12981298

1299+
Complete src/CompletionExpressions.res 321:11
1300+
posCursor:[321:11] posNoWhite:[321:10] Found expr:[321:3->321:12]
1301+
Pexp_apply ...[321:3->321:10] (...[321:11->321:12])
1302+
Completable: Cexpression CArgument Value[mkStuff]($0)
1303+
Raw opens: 1 CompletionSupport.place holder
1304+
Package opens Pervasives.JsxModules.place holder
1305+
Resolved opens 2 pervasives CompletionSupport.res
1306+
ContextPath CArgument Value[mkStuff]($0)
1307+
ContextPath Value[mkStuff]
1308+
Path mkStuff
1309+
[{
1310+
"label": "%re()",
1311+
"kind": 4,
1312+
"tags": [],
1313+
"detail": "Regular expression",
1314+
"documentation": null,
1315+
"insertText": "%re(\"/$0/g\")",
1316+
"insertTextFormat": 2
1317+
}, {
1318+
"label": "Js.Re.fromStringWithFlags()",
1319+
"kind": 12,
1320+
"tags": [],
1321+
"detail": "(string, ~flags: string) => t",
1322+
"documentation": null,
1323+
"insertText": "Js.Re.fromStringWithFlags($0)",
1324+
"insertTextFormat": 2
1325+
}, {
1326+
"label": "Js.Re.fromString()",
1327+
"kind": 12,
1328+
"tags": [],
1329+
"detail": "string => t",
1330+
"documentation": null,
1331+
"insertText": "Js.Re.fromString($0)",
1332+
"insertTextFormat": 2
1333+
}]
1334+

‎analysis/tests/src/expected/CompletionPipeChain.res.txt

+18
Original file line numberDiff line numberDiff line change
@@ -553,3 +553,21 @@ Path Integer.t
553553
"documentation": null
554554
}]
555555

556+
Complete src/CompletionPipeChain.res 103:8
557+
posCursor:[103:8] posNoWhite:[103:7] Found expr:[103:3->103:8]
558+
Completable: Cpath Value[r]->la
559+
Package opens Pervasives.JsxModules.place holder
560+
Resolved opens 1 pervasives
561+
ContextPath Value[r]->la
562+
ContextPath Value[r]
563+
Path r
564+
CPPipe env:CompletionPipeChain
565+
Path Js.Re.la
566+
[{
567+
"label": "Js.Re.lastIndex",
568+
"kind": 12,
569+
"tags": [],
570+
"detail": "t => int",
571+
"documentation": {"kind": "markdown", "value": "\n Returns the index where the next match will start its search. This property\n will be modified when the RegExp object is used, if the global (\"g\") flag is\n set.\n\n ```res example\n let re = %re(\"/ab*/g\")\n let str = \"abbcdefabh\"\n\n let break = ref(false)\n while !break.contents {\n switch Js.Re.exec_(re, str) {\n | Some(result) => Js.Nullable.iter(Js.Re.captures(result)[0], (. match_) => {\n let next = Belt.Int.toString(Js.Re.lastIndex(re))\n Js.log(\"Found \" ++ (match_ ++ (\". Next match starts at \" ++ next)))\n })\n | None => break := true\n }\n }\n ```\n\n See\n [`RegExp: lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex)\n on MDN.\n"}
572+
}]
573+

0 commit comments

Comments
 (0)