Skip to content
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

Enhance regexp completion #903

Merged
merged 2 commits into from
Jan 29, 2024
Merged
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -12,6 +12,11 @@
## master

#### :nail_care: Polish

- Prefer Core's `RegExp` when Core is open and completing for regexp functions. https://github.com/rescript-lang/rescript-vscode/pull/903
- 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

## 1.36.0

#### :bug: Bug Fix
30 changes: 23 additions & 7 deletions analysis/src/CompletionBackEnd.ml
Original file line number Diff line number Diff line change
@@ -941,6 +941,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
promiseModulePath;
listModulePath;
resultModulePath;
regexpModulePath;
} =
package.builtInCompletionModules
in
@@ -954,6 +955,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
| Promise -> promiseModulePath
| List -> listModulePath
| Result -> resultModulePath
| RegExp -> regexpModulePath
| Lazy -> ["Lazy"]
| Char -> ["Char"])
| TypExpr t -> (
@@ -1291,13 +1293,27 @@ let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens
Hashtbl.add functionsReturningTypeT
((base |> String.concat ".") ^ "." ^ name)
item));
Hashtbl.fold
(fun fnName typeExpr all ->
createWithSnippet
~name:(Printf.sprintf "%s()" fnName)
~insertText:(fnName ^ "($0)") ~kind:(Value typeExpr) ~env ()
:: all)
functionsReturningTypeT []

let completionItems =
Hashtbl.fold
(fun fnName typeExpr all ->
createWithSnippet
~name:(Printf.sprintf "%s()" fnName)
~insertText:(fnName ^ "($0)") ~kind:(Value typeExpr) ~env ()
:: all)
functionsReturningTypeT []
in
(* Special casing for things where we want extra things in the completions *)
let completionItems =
match path with
| Pdot (Pdot (Pident m, "Re", _), "t", _) when Ident.name m = "Js" ->
(* regexps *)
createWithSnippet ~name:"%re()" ~insertText:"%re(\"/$0/g\")"
~kind:(Label "Regular expression") ~env ()
:: completionItems
| _ -> completionItems
in
completionItems
| Tbool env ->
if Debug.verbose () then print_endline "[complete_typed_value]--> Tbool";
[
116 changes: 60 additions & 56 deletions analysis/src/Packages.ml
Original file line number Diff line number Diff line change
@@ -32,9 +32,10 @@ let newBsPackage ~rootPath =
let bsconfigJson = Filename.concat rootPath "bsconfig.json" in

let parseRaw raw =
let libBs = match !Cfg.isDocGenFromCompiler with
| true -> BuildSystem.getStdlib rootPath
| false -> BuildSystem.getLibBs rootPath
let libBs =
match !Cfg.isDocGenFromCompiler with
| true -> BuildSystem.getStdlib rootPath
| false -> BuildSystem.getLibBs rootPath
in
match Json.parse raw with
| Some config -> (
@@ -48,10 +49,10 @@ let newBsPackage ~rootPath =
(let namespace = FindFiles.getNamespace config in
let rescriptVersion = getReScriptVersion () in
let suffix =
match config |> Json.get "suffix" with
| Some (String suffix) -> suffix
| _ -> ".js"
in
match config |> Json.get "suffix" with
| Some (String suffix) -> suffix
| _ -> ".js"
in
let uncurried =
let ns = config |> Json.get "uncurried" in
match (rescriptVersion, ns) with
@@ -132,55 +133,58 @@ let newBsPackage ~rootPath =
namespace;
builtInCompletionModules =
(if
opens_from_bsc_flags
|> List.find_opt (fun opn ->
match opn with
| ["RescriptCore"] -> true
| _ -> false)
|> Option.is_some
then
{
arrayModulePath = ["Array"];
optionModulePath = ["Option"];
stringModulePath = ["String"];
intModulePath = ["Int"];
floatModulePath = ["Float"];
promiseModulePath = ["Promise"];
listModulePath = ["List"];
resultModulePath = ["Result"];
exnModulePath = ["Exn"];
}
else if
opens_from_bsc_flags
|> List.find_opt (fun opn ->
match opn with
| ["Belt"] -> true
| _ -> false)
|> Option.is_some
then
{
arrayModulePath = ["Array"];
optionModulePath = ["Option"];
stringModulePath = ["Js"; "String2"];
intModulePath = ["Int"];
floatModulePath = ["Float"];
promiseModulePath = ["Js"; "Promise"];
listModulePath = ["List"];
resultModulePath = ["Result"];
exnModulePath = ["Js"; "Exn"];
}
else
{
arrayModulePath = ["Js"; "Array2"];
optionModulePath = ["Belt"; "Option"];
stringModulePath = ["Js"; "String2"];
intModulePath = ["Belt"; "Int"];
floatModulePath = ["Belt"; "Float"];
promiseModulePath = ["Js"; "Promise"];
listModulePath = ["Belt"; "List"];
resultModulePath = ["Belt"; "Result"];
exnModulePath = ["Js"; "Exn"];
});
opens_from_bsc_flags
|> List.find_opt (fun opn ->
match opn with
| ["RescriptCore"] -> true
| _ -> false)
|> Option.is_some
then
{
arrayModulePath = ["Array"];
optionModulePath = ["Option"];
stringModulePath = ["String"];
intModulePath = ["Int"];
floatModulePath = ["Float"];
promiseModulePath = ["Promise"];
listModulePath = ["List"];
resultModulePath = ["Result"];
exnModulePath = ["Exn"];
regexpModulePath = ["RegExp"];
}
else if
opens_from_bsc_flags
|> List.find_opt (fun opn ->
match opn with
| ["Belt"] -> true
| _ -> false)
|> Option.is_some
then
{
arrayModulePath = ["Array"];
optionModulePath = ["Option"];
stringModulePath = ["Js"; "String2"];
intModulePath = ["Int"];
floatModulePath = ["Float"];
promiseModulePath = ["Js"; "Promise"];
listModulePath = ["List"];
resultModulePath = ["Result"];
exnModulePath = ["Js"; "Exn"];
regexpModulePath = ["Js"; "Re"];
}
else
{
arrayModulePath = ["Js"; "Array2"];
optionModulePath = ["Belt"; "Option"];
stringModulePath = ["Js"; "String2"];
intModulePath = ["Belt"; "Int"];
floatModulePath = ["Belt"; "Float"];
promiseModulePath = ["Js"; "Promise"];
listModulePath = ["Belt"; "List"];
resultModulePath = ["Belt"; "Result"];
exnModulePath = ["Js"; "Exn"];
regexpModulePath = ["Js"; "Re"];
});
uncurried;
})))
| None -> None
1 change: 1 addition & 0 deletions analysis/src/SharedTypes.ml
Original file line number Diff line number Diff line change
@@ -492,6 +492,7 @@ type builtInCompletionModules = {
listModulePath: string list;
resultModulePath: string list;
exnModulePath: string list;
regexpModulePath: string list;
}

type package = {
26 changes: 16 additions & 10 deletions analysis/src/TypeUtils.ml
Original file line number Diff line number Diff line change
@@ -449,23 +449,29 @@ type builtinType =
| Result
| Lazy
| Char
| RegExp

type pipeCompletionType =
| Builtin of builtinType * Types.type_expr
| TypExpr of Types.type_expr

let getBuiltinFromTypePath path =
match path with
| Path.Pident id when Ident.name id = "array" -> Some Array
| Path.Pident id when Ident.name id = "option" -> Some Option
| Path.Pident id when Ident.name id = "string" -> Some String
| Path.Pident id when Ident.name id = "int" -> Some Int
| Path.Pident id when Ident.name id = "float" -> Some Float
| Path.Pident id when Ident.name id = "promise" -> Some Promise
| Path.Pident id when Ident.name id = "list" -> Some List
| Path.Pident id when Ident.name id = "result" -> Some Result
| Path.Pident id when Ident.name id = "lazy_t" -> Some Lazy
| Path.Pident id when Ident.name id = "char" -> Some Char
| Path.Pident _ -> (
match Path.name path with
| "array" -> Some Array
| "option" -> Some Option
| "string" -> Some String
| "int" -> Some Int
| "float" -> Some Float
| "promise" -> Some Promise
| "list" -> Some List
| "result" -> Some Result
| "lazy_t" -> Some Lazy
| "char" -> Some Char
| _ -> None)
| Pdot (Pdot (Pident m, "Re", _), "t", _) when Ident.name m = "Js" ->
Some RegExp
| Pdot (Pident id, "result", _)
when Ident.name id = "Pervasives" || Ident.name id = "PervasivesU" ->
Some Result
8 changes: 8 additions & 0 deletions analysis/tests/src/CompletionExpressions.res
Original file line number Diff line number Diff line change
@@ -313,3 +313,11 @@ type withIntLocal = {superInt: SuperInt.t}
open CompletionSupport
// CompletionSupport.makeTestHidden()
// ^com

let mkStuff = (r: Js.Re.t) => {
ignore(r)
"hello"
}

// mkStuff()
// ^com
5 changes: 5 additions & 0 deletions analysis/tests/src/CompletionPipeChain.res
Original file line number Diff line number Diff line change
@@ -98,3 +98,8 @@ let renderer = CompletionSupport2.makeRenderer(

// Console.log(int->t)
// ^com

let r = %re("/t/g")

// r->la
// ^com
36 changes: 36 additions & 0 deletions analysis/tests/src/expected/CompletionExpressions.res.txt
Original file line number Diff line number Diff line change
@@ -1296,3 +1296,39 @@ Path CompletionSupport.makeTestHidden
"insertTextFormat": 2
}]

Complete src/CompletionExpressions.res 321:11
posCursor:[321:11] posNoWhite:[321:10] Found expr:[321:3->321:12]
Pexp_apply ...[321:3->321:10] (...[321:11->321:12])
Completable: Cexpression CArgument Value[mkStuff]($0)
Raw opens: 1 CompletionSupport.place holder
Package opens Pervasives.JsxModules.place holder
Resolved opens 2 pervasives CompletionSupport.res
ContextPath CArgument Value[mkStuff]($0)
ContextPath Value[mkStuff]
Path mkStuff
[{
"label": "%re()",
"kind": 4,
"tags": [],
"detail": "Regular expression",
"documentation": null,
"insertText": "%re(\"/$0/g\")",
"insertTextFormat": 2
}, {
"label": "Js.Re.fromStringWithFlags()",
"kind": 12,
"tags": [],
"detail": "(string, ~flags: string) => t",
"documentation": null,
"insertText": "Js.Re.fromStringWithFlags($0)",
"insertTextFormat": 2
}, {
"label": "Js.Re.fromString()",
"kind": 12,
"tags": [],
"detail": "string => t",
"documentation": null,
"insertText": "Js.Re.fromString($0)",
"insertTextFormat": 2
}]

18 changes: 18 additions & 0 deletions analysis/tests/src/expected/CompletionPipeChain.res.txt
Original file line number Diff line number Diff line change
@@ -553,3 +553,21 @@ Path Integer.t
"documentation": null
}]

Complete src/CompletionPipeChain.res 103:8
posCursor:[103:8] posNoWhite:[103:7] Found expr:[103:3->103:8]
Completable: Cpath Value[r]->la
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[r]->la
ContextPath Value[r]
Path r
CPPipe env:CompletionPipeChain
Path Js.Re.la
[{
"label": "Js.Re.lastIndex",
"kind": 12,
"tags": [],
"detail": "t => int",
"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"}
}]