diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e8c7b44f..f4dbd9603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 27b4ae002..7df6d8a99 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -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"; [ diff --git a/analysis/src/Packages.ml b/analysis/src/Packages.ml index c0ef38b03..8f3a559fa 100644 --- a/analysis/src/Packages.ml +++ b/analysis/src/Packages.ml @@ -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 diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index cc25c5031..92d17d273 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -492,6 +492,7 @@ type builtInCompletionModules = { listModulePath: string list; resultModulePath: string list; exnModulePath: string list; + regexpModulePath: string list; } type package = { diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 534ec7391..8964fe1da 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -449,6 +449,7 @@ type builtinType = | Result | Lazy | Char + | RegExp type pipeCompletionType = | Builtin of builtinType * Types.type_expr @@ -456,16 +457,21 @@ type pipeCompletionType = 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 diff --git a/analysis/tests/src/CompletionExpressions.res b/analysis/tests/src/CompletionExpressions.res index ccb1746f6..3572caede 100644 --- a/analysis/tests/src/CompletionExpressions.res +++ b/analysis/tests/src/CompletionExpressions.res @@ -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 diff --git a/analysis/tests/src/CompletionPipeChain.res b/analysis/tests/src/CompletionPipeChain.res index 82276baaf..2a16c21cf 100644 --- a/analysis/tests/src/CompletionPipeChain.res +++ b/analysis/tests/src/CompletionPipeChain.res @@ -98,3 +98,8 @@ let renderer = CompletionSupport2.makeRenderer( // Console.log(int->t) // ^com + +let r = %re("/t/g") + +// r->la +// ^com diff --git a/analysis/tests/src/expected/CompletionExpressions.res.txt b/analysis/tests/src/expected/CompletionExpressions.res.txt index 0b5a19564..d84e5bfe5 100644 --- a/analysis/tests/src/expected/CompletionExpressions.res.txt +++ b/analysis/tests/src/expected/CompletionExpressions.res.txt @@ -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 + }] + diff --git a/analysis/tests/src/expected/CompletionPipeChain.res.txt b/analysis/tests/src/expected/CompletionPipeChain.res.txt index dd107c59f..52e23054c 100644 --- a/analysis/tests/src/expected/CompletionPipeChain.res.txt +++ b/analysis/tests/src/expected/CompletionPipeChain.res.txt @@ -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"} + }] +