diff --git a/CHANGELOG.md b/CHANGELOG.md index 50e01c9a9b..8397d5732d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ #### :nail_care: Polish - Suggest awaiting promise before using it when types mismatch. https://github.com/rescript-lang/rescript/pull/7498 +- Complete from `RegExp` stdlib module for regexes. https://github.com/rescript-lang/rescript/pull/7425 # 12.0.0-alpha.13 diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index c9b1acd011..4c89a2c117 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -1265,4 +1265,9 @@ let completionPathFromMaybeBuiltin path = | Some ("result", _) -> Some ["Stdlib"; "Result"] | Some ("dict", _) -> Some ["Stdlib"; "Dict"] | Some ("char", _) -> Some ["Stdlib"; "Char"] - | _ -> None + | _ -> ( + match path |> Utils.expandPath |> List.rev with + | [mainModule; "t"] when String.starts_with ~prefix:"Stdlib_" mainModule -> + (* Route Stdlib_X to Stdlib.X for proper completions without the Stdlib_ prefix *) + Some (String.split_on_char '_' mainModule) + | _ -> None) diff --git a/compiler/frontend/ast_comb.ml b/compiler/frontend/ast_comb.ml index 3b1740145e..0561d9a4ab 100644 --- a/compiler/frontend/ast_comb.ml +++ b/compiler/frontend/ast_comb.ml @@ -57,9 +57,9 @@ let tuple_type_pair ?loc kind arity = result ) | [] -> assert false -let re_id = Ast_literal.Lid.js_re_id +let regexp_id = Ast_literal.Lid.regexp_id -let to_js_re_type loc = Typ.constr ~loc {txt = re_id; loc} [] +let to_regexp_type loc = Typ.constr ~loc {txt = regexp_id; loc} [] let to_undefined_type loc x = Typ.constr ~loc {txt = Ast_literal.Lid.js_undefined; loc} [x] diff --git a/compiler/frontend/ast_comb.mli b/compiler/frontend/ast_comb.mli index 21dbfa989d..9addbbe395 100644 --- a/compiler/frontend/ast_comb.mli +++ b/compiler/frontend/ast_comb.mli @@ -39,7 +39,7 @@ val tuple_type_pair : val to_undefined_type : Location.t -> Parsetree.core_type -> Parsetree.core_type -val to_js_re_type : Location.t -> Parsetree.core_type +val to_regexp_type : Location.t -> Parsetree.core_type val single_non_rec_value : ?attrs:Parsetree.attributes -> diff --git a/compiler/frontend/ast_exp_extension.ml b/compiler/frontend/ast_exp_extension.ml index 0ef83dcfa1..58391e36cb 100644 --- a/compiler/frontend/ast_exp_extension.ml +++ b/compiler/frontend/ast_exp_extension.ml @@ -60,7 +60,7 @@ let handle_extension e (self : Bs_ast_mapper.mapper) | "re" -> Exp.constraint_ ~loc (Ast_exp_handle_external.handle_raw ~kind:Raw_re loc payload) - (Ast_comb.to_js_re_type loc) + (Ast_comb.to_regexp_type loc) | "external" -> ( Location.deprecated loc "%external is deprecated, use %raw or regular FFI syntax instead."; diff --git a/compiler/frontend/ast_literal.ml b/compiler/frontend/ast_literal.ml index 367f7f44ec..50ea292d53 100644 --- a/compiler/frontend/ast_literal.ml +++ b/compiler/frontend/ast_literal.ml @@ -70,8 +70,7 @@ module Lid = struct (* FIXME: Use primitive module *) let js_null_undefined : t = Ldot (Lident "Js", "null_undefined") - (* FIXME: Use primitive module *) - let js_re_id : t = Ldot (Ldot (Lident "Js", "Re"), "t") + let regexp_id : t = Ldot (Lident "Stdlib_RegExp", "t") end module No_loc = struct diff --git a/compiler/frontend/ast_literal.mli b/compiler/frontend/ast_literal.mli index 0b34d29d80..82d5726171 100644 --- a/compiler/frontend/ast_literal.mli +++ b/compiler/frontend/ast_literal.mli @@ -57,7 +57,7 @@ module Lid : sig val js_null_undefined : t - val js_re_id : t + val regexp_id : t end type expression_lit = Parsetree.expression lit diff --git a/tests/analysis_tests/tests/src/CompletionRegexp.res b/tests/analysis_tests/tests/src/CompletionRegexp.res new file mode 100644 index 0000000000..ebb124363b --- /dev/null +++ b/tests/analysis_tests/tests/src/CompletionRegexp.res @@ -0,0 +1,3 @@ +let emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ +// emailPattern-> +// ^com diff --git a/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt b/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt index 30c0ce9668..dcf268e218 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt @@ -582,15 +582,14 @@ Resolved opens 1 Stdlib ContextPath Value[r]->la ContextPath Value[r] Path r -CPPipe pathFromEnv:Js.Re found:false -Path Js.Re.la +Path Stdlib.RegExp.la Path la [{ - "label": "Js.Re.lastIndex", + "label": "RegExp.lastIndex", "kind": 12, "tags": [], "detail": "t => int", - "documentation": {"kind": "markdown", "value": "\nReturns the index where the next match will start its search. This property\nwill be modified when the RegExp object is used, if the global (\"g\") flag is\nset.\n\n## Examples\n\n```rescript\nlet re = /ab*TODO/g\nlet str = \"abbcdefabh\"\n\nlet break = ref(false)\nwhile !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\nSee\n[`RegExp: lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex)\non MDN.\n"} + "documentation": {"kind": "markdown", "value": "\n`lastIndex(regexp)` returns the index the next match will start from.\n\nSee [`RegExp.lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex) on MDN.\n\n## Examples\n```rescript\n// Match the first word in a sentence\nlet regexp = RegExp.fromString(\"\\\\w+\")\nlet someStr = \"Many words here.\"\n\nConsole.log(regexp->RegExp.lastIndex) // Logs `0` to the console\n\nregexp->RegExp.exec(someStr)->ignore\n\nConsole.log(regexp->RegExp.lastIndex) // Logs `4` to the console\n```\n"} }] Complete src/CompletionPipeChain.res 112:7 diff --git a/tests/analysis_tests/tests/src/expected/CompletionRegexp.res.txt b/tests/analysis_tests/tests/src/expected/CompletionRegexp.res.txt new file mode 100644 index 0000000000..ebb2bd0c8d --- /dev/null +++ b/tests/analysis_tests/tests/src/expected/CompletionRegexp.res.txt @@ -0,0 +1,78 @@ +Complete src/CompletionRegexp.res 1:17 +posCursor:[1:17] posNoWhite:[1:16] Found expr:[1:3->0:-1] +Completable: Cpath Value[emailPattern]-> +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[emailPattern]-> +ContextPath Value[emailPattern] +Path emailPattern +Path Stdlib.RegExp. +Path +[{ + "label": "RegExp.lastIndex", + "kind": 12, + "tags": [], + "detail": "t => int", + "documentation": {"kind": "markdown", "value": "\n`lastIndex(regexp)` returns the index the next match will start from.\n\nSee [`RegExp.lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex) on MDN.\n\n## Examples\n```rescript\n// Match the first word in a sentence\nlet regexp = RegExp.fromString(\"\\\\w+\")\nlet someStr = \"Many words here.\"\n\nConsole.log(regexp->RegExp.lastIndex) // Logs `0` to the console\n\nregexp->RegExp.exec(someStr)->ignore\n\nConsole.log(regexp->RegExp.lastIndex) // Logs `4` to the console\n```\n"} + }, { + "label": "RegExp.setLastIndex", + "kind": 12, + "tags": [], + "detail": "(t, int) => unit", + "documentation": {"kind": "markdown", "value": "\n`setLastIndex(regexp, index)` set the index the next match will start from.\n\nSee [`RegExp.lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex) on MDN.\n\n## Examples\n```rescript\n// Match the first word in a sentence\nlet regexp = RegExp.fromString(\"\\\\w+\")\nlet someStr = \"Many words here.\"\n\nregexp->RegExp.setLastIndex(4)\nregexp->RegExp.exec(someStr)->ignore\n\nConsole.log(regexp->RegExp.lastIndex) // Logs `10` to the console\n```\n"} + }, { + "label": "RegExp.sticky", + "kind": 12, + "tags": [], + "detail": "t => bool", + "documentation": {"kind": "markdown", "value": "\n`sticky(regexp)` returns whether the sticky (`y`) flag is set on this `RegExp`.\n\nSee [`RegExp.sticky`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky) on MDN.\n\n## Examples\n```rescript\nlet regexp1 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"g\")\nConsole.log(regexp1->RegExp.unicode) // Logs `false`, since `y` is not set\n\nlet regexp2 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"my\")\nConsole.log(regexp2->RegExp.unicode) // Logs `true`, since `y` is set\n```\n"} + }, { + "label": "RegExp.ignore", + "kind": 12, + "tags": [], + "detail": "t => unit", + "documentation": {"kind": "markdown", "value": "\n `ignore(regExp)` ignores the provided regExp and returns unit.\n\n This helper is useful when you want to discard a value (for example, the result of an operation with side effects)\n without having to store or process it further.\n"} + }, { + "label": "RegExp.exec", + "kind": 12, + "tags": [], + "detail": "(t, string) => option", + "documentation": {"kind": "markdown", "value": "\n`exec(regexp, string)` executes the provided regexp on the provided string, optionally returning a `RegExp.Result.t` if the regexp matches on the string.\n\nSee [`RegExp.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec) on MDN.\n\n## Examples\n```rescript\n// Match the first word in a sentence\nlet regexp = RegExp.fromString(\"\\\\w+\")\n\nswitch regexp->RegExp.exec(\"ReScript is pretty cool, right?\") {\n| None => Console.log(\"Nope, no match...\")\n| Some(result) => Console.log(result->RegExp.Result.fullMatch) // Prints \"ReScript\"\n}\n```\n"} + }, { + "label": "RegExp.ignoreCase", + "kind": 12, + "tags": [], + "detail": "t => bool", + "documentation": {"kind": "markdown", "value": "\n`ignoreCase(regexp)` returns whether the ignore case (`i`) flag is set on this `RegExp`.\n\nSee [`RegExp.ignoreCase`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase) on MDN.\n\n## Examples\n```rescript\nlet regexp1 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"g\")\nConsole.log(regexp1->RegExp.ignoreCase) // Logs `false`, since `i` is not set\n\nlet regexp2 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"i\")\nConsole.log(regexp2->RegExp.ignoreCase) // Logs `true`, since `i` is set\n```\n"} + }, { + "label": "RegExp.global", + "kind": 12, + "tags": [], + "detail": "t => bool", + "documentation": {"kind": "markdown", "value": "\n`global(regexp)` returns whether the global (`g`) flag is set on this `RegExp`.\n\nSee [`RegExp.global`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global) on MDN.\n\n## Examples\n```rescript\nlet regexp1 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"g\")\nConsole.log(regexp1->RegExp.global) // Logs `true`, since `g` is set\n\nlet regexp2 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"i\")\nConsole.log(regexp2->RegExp.global) // Logs `false`, since `g` is not set\n```\n"} + }, { + "label": "RegExp.multiline", + "kind": 12, + "tags": [], + "detail": "t => bool", + "documentation": {"kind": "markdown", "value": "\n`multiline(regexp)` returns whether the multiline (`m`) flag is set on this `RegExp`.\n\nSee [`RegExp.multiline`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline) on MDN.\n\n## Examples\n```rescript\nlet regexp1 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"g\")\nConsole.log(regexp1->RegExp.multiline) // Logs `false`, since `m` is not set\n\nlet regexp2 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"mi\")\nConsole.log(regexp2->RegExp.multiline) // Logs `true`, since `m` is set\n```\n"} + }, { + "label": "RegExp.test", + "kind": 12, + "tags": [], + "detail": "(t, string) => bool", + "documentation": {"kind": "markdown", "value": "\n`test(regexp, string)` tests whether the provided `regexp` matches on the provided string.\n\nSee [`RegExp.test`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test) on MDN.\n\n## Examples\n```rescript\n// Match the first word in a sentence\nlet regexp = RegExp.fromString(\"\\\\w+\")\n\nif regexp->RegExp.test(\"ReScript is cool!\") {\n Console.log(\"Yay, there's a word in there.\")\n}\n```\n"} + }, { + "label": "RegExp.unicode", + "kind": 12, + "tags": [], + "detail": "t => bool", + "documentation": {"kind": "markdown", "value": "\n`unicode(regexp)` returns whether the unicode (`y`) flag is set on this `RegExp`.\n\nSee [`RegExp.unicode`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode) on MDN.\n\n## Examples\n```rescript\nlet regexp1 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"g\")\nConsole.log(regexp1->RegExp.unicode) // Logs `false`, since `u` is not set\n\nlet regexp2 = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"mu\")\nConsole.log(regexp2->RegExp.unicode) // Logs `true`, since `u` is set\n```\n"} + }, { + "label": "RegExp.source", + "kind": 12, + "tags": [], + "detail": "t => string", + "documentation": {"kind": "markdown", "value": "\n`source(regexp)` returns the source text for this `RegExp`, without the two forward slashes (if present), and without any set flags.\n\nSee [`RegExp.source`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source) on MDN.\n\n## Examples\n```rescript\nlet regexp = RegExp.fromStringWithFlags(\"\\\\w+\", ~flags=\"g\")\nConsole.log(regexp->RegExp.source) // Logs `\\w+`, the source text of the `RegExp`\n```\n"} + }] +