regex-explain 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +275 -1
- package/dist/cjs/compiler.d.ts +34 -0
- package/dist/cjs/compiler.js +160 -0
- package/dist/cjs/explainer.d.ts +123 -0
- package/dist/cjs/explainer.js +628 -0
- package/dist/cjs/extlinks.d.ts +21 -0
- package/dist/cjs/extlinks.js +25 -0
- package/dist/cjs/index.d.ts +22 -0
- package/dist/cjs/index.js +22 -0
- package/dist/cjs/locator.d.ts +80 -0
- package/dist/cjs/locator.js +341 -0
- package/dist/cjs/matches.d.ts +133 -0
- package/dist/cjs/matches.js +238 -0
- package/dist/cjs/render.d.ts +120 -0
- package/dist/cjs/render.js +238 -0
- package/dist/cjs/strings.d.ts +39 -0
- package/dist/cjs/strings.js +85 -0
- package/dist/cjs/text.d.ts +49 -0
- package/dist/cjs/text.js +109 -0
- package/dist/esm/index.mjs +1207 -0
- package/package.json +62 -4
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeEscape = decodeEscape;
|
|
4
|
+
exports.splitDelimitedPattern = splitDelimitedPattern;
|
|
5
|
+
exports.translateFlags = translateFlags;
|
|
6
|
+
/** Single-character escapes that mean the same in every supported language that has them. */
|
|
7
|
+
const SIMPLE_ESCAPES = {
|
|
8
|
+
b: "\b",
|
|
9
|
+
f: "\f",
|
|
10
|
+
n: "\n",
|
|
11
|
+
r: "\r",
|
|
12
|
+
t: "\t",
|
|
13
|
+
v: "\v",
|
|
14
|
+
};
|
|
15
|
+
/** Closing delimiters of PCRE patterns that open with a bracket. */
|
|
16
|
+
const CLOSING_BRACKETS = {
|
|
17
|
+
"(": ")",
|
|
18
|
+
"[": "]",
|
|
19
|
+
"{": "}",
|
|
20
|
+
"<": ">",
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Decodes one escape sequence from a string literal.
|
|
24
|
+
*
|
|
25
|
+
* Unknown single-character escapes such as `\q` decode to the character, as in JavaScript and Ruby.
|
|
26
|
+
* Grammars for languages that keep the backslash, such as Python and PHP, do not mark those as
|
|
27
|
+
* escapes, so they never reach this function.
|
|
28
|
+
*
|
|
29
|
+
* @param escape The escape's source text, starting with a backslash.
|
|
30
|
+
*
|
|
31
|
+
* @returns The decoded text, or undefined for escapes that cannot be decoded without knowing the
|
|
32
|
+
* language, such as Python's `\N{name}`, or `\a` and `\e`, which are control characters in some
|
|
33
|
+
* languages and plain letters in JavaScript.
|
|
34
|
+
*/
|
|
35
|
+
function decodeEscape(escape) {
|
|
36
|
+
const body = escape.slice(1);
|
|
37
|
+
const hex = /^(?:x([\da-f]{1,2})|[xu]\{([\da-f]+)\}|u([\da-f]{4})|U([\da-f]{8}))$/i.exec(body);
|
|
38
|
+
if (hex) {
|
|
39
|
+
const codePoint = parseInt(hex.slice(1).find((group) => group !== undefined) ?? "", 16);
|
|
40
|
+
return codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : undefined;
|
|
41
|
+
}
|
|
42
|
+
if (/^[0-7]{1,3}$/.test(body)) {
|
|
43
|
+
return String.fromCodePoint(parseInt(body, 8));
|
|
44
|
+
}
|
|
45
|
+
if (/^\r?\n$/.test(body)) {
|
|
46
|
+
// A backslash before a line break continues the string on the next line.
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
if ([...body].length !== 1 || body === "a" || body === "e") {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return SIMPLE_ESCAPES[body] ?? body;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Splits a PCRE-style pattern such as PHP's `'/a+/i'` into its pattern and flags.
|
|
56
|
+
*
|
|
57
|
+
* @param value The string's value. Its first character is the delimiter; brackets close with their
|
|
58
|
+
* pair.
|
|
59
|
+
*
|
|
60
|
+
* @returns The parts, or undefined if the value is not delimited.
|
|
61
|
+
*/
|
|
62
|
+
function splitDelimitedPattern(value) {
|
|
63
|
+
const open = value.charAt(0);
|
|
64
|
+
if (open === "" || /[\p{L}\p{N}\s\\]/u.test(open)) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const close = value.lastIndexOf(CLOSING_BRACKETS[open] ?? open);
|
|
68
|
+
if (close < 1) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return { pattern: value.slice(1, close), flags: value.slice(close + 1) };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Rewrites flags into their JavaScript equivalents.
|
|
75
|
+
*
|
|
76
|
+
* @param flags The flags as written in the source language.
|
|
77
|
+
* @param map Replacement for each flag that differs; an empty string drops a flag that does not
|
|
78
|
+
* change what matches. Flags not in the map are kept, so the explainer reports unknown ones.
|
|
79
|
+
*
|
|
80
|
+
* @returns The JavaScript flags.
|
|
81
|
+
*/
|
|
82
|
+
function translateFlags(flags, map) {
|
|
83
|
+
return [...flags].map((flag) => map[flag] ?? flag).join("");
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=strings.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Small wording helpers shared by the explainer and renderers. */
|
|
2
|
+
/**
|
|
3
|
+
* Wraps text in a Markdown code span, choosing a fence that survives embedded backticks.
|
|
4
|
+
*
|
|
5
|
+
* @param text The text to wrap.
|
|
6
|
+
*
|
|
7
|
+
* @returns The code span.
|
|
8
|
+
*/
|
|
9
|
+
export declare function code(text: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Describes a single code point as a noun phrase.
|
|
12
|
+
*
|
|
13
|
+
* @param cp The code point.
|
|
14
|
+
*
|
|
15
|
+
* @returns A phrase such as "the character `a`", "a tab", or "the character U+200B".
|
|
16
|
+
*/
|
|
17
|
+
export declare function describeCodePoint(cp: number): string;
|
|
18
|
+
/**
|
|
19
|
+
* Lowercases the first character.
|
|
20
|
+
*
|
|
21
|
+
* @param text The text.
|
|
22
|
+
*
|
|
23
|
+
* @returns The text with its first character lowercased.
|
|
24
|
+
*/
|
|
25
|
+
export declare function lowerFirst(text: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Prefixes a noun with "a" or "an".
|
|
28
|
+
*
|
|
29
|
+
* @param noun The noun phrase.
|
|
30
|
+
*
|
|
31
|
+
* @returns The phrase with its article.
|
|
32
|
+
*/
|
|
33
|
+
export declare function withArticle(noun: string): string;
|
|
34
|
+
/** A run of description text, either plain or from a code span. */
|
|
35
|
+
export interface TextPart {
|
|
36
|
+
/** The text, with code span fences and padding removed. */
|
|
37
|
+
readonly text: string;
|
|
38
|
+
/** Whether the text came from a code span. */
|
|
39
|
+
readonly code: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Splits text into plain runs and the contents of its Markdown code spans, so that descriptions can
|
|
43
|
+
* be rendered without a Markdown parser. It reverses {@link code}.
|
|
44
|
+
*
|
|
45
|
+
* @param text Text that may contain code spans, such as an explanation's description.
|
|
46
|
+
*
|
|
47
|
+
* @returns The parts in order. Adjacent plain text is one part.
|
|
48
|
+
*/
|
|
49
|
+
export declare function splitCodeSpans(text: string): TextPart[];
|
package/dist/cjs/text.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Small wording helpers shared by the explainer and renderers. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.code = code;
|
|
5
|
+
exports.describeCodePoint = describeCodePoint;
|
|
6
|
+
exports.lowerFirst = lowerFirst;
|
|
7
|
+
exports.withArticle = withArticle;
|
|
8
|
+
exports.splitCodeSpans = splitCodeSpans;
|
|
9
|
+
/** Names for code points that have no visible glyph or are easy to misread. */
|
|
10
|
+
const NAMED_CODE_POINTS = {
|
|
11
|
+
0x00: "a null character",
|
|
12
|
+
0x08: "a backspace",
|
|
13
|
+
0x09: "a tab",
|
|
14
|
+
0x0a: "a line feed (newline)",
|
|
15
|
+
0x0b: "a vertical tab",
|
|
16
|
+
0x0c: "a form feed",
|
|
17
|
+
0x0d: "a carriage return",
|
|
18
|
+
0x20: "a space",
|
|
19
|
+
0xa0: "a non-breaking space",
|
|
20
|
+
0x2028: "a line separator",
|
|
21
|
+
0x2029: "a paragraph separator",
|
|
22
|
+
0xfeff: "a byte order mark",
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Wraps text in a Markdown code span, choosing a fence that survives embedded backticks.
|
|
26
|
+
*
|
|
27
|
+
* @param text The text to wrap.
|
|
28
|
+
*
|
|
29
|
+
* @returns The code span.
|
|
30
|
+
*/
|
|
31
|
+
function code(text) {
|
|
32
|
+
const longest = Math.max(0, ...(text.match(/`+/g) ?? []).map((m) => m.length));
|
|
33
|
+
const fence = "`".repeat(longest + 1);
|
|
34
|
+
// CommonMark strips one space from each side when both are present, so pad text that starts or
|
|
35
|
+
// ends with a backtick, or that is already padded.
|
|
36
|
+
const pad = text.startsWith("`") || text.endsWith("`") || /^ .* $/.test(text) ? " " : "";
|
|
37
|
+
return `${fence}${pad}${text}${pad}${fence}`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Describes a single code point as a noun phrase.
|
|
41
|
+
*
|
|
42
|
+
* @param cp The code point.
|
|
43
|
+
*
|
|
44
|
+
* @returns A phrase such as "the character `a`", "a tab", or "the character U+200B".
|
|
45
|
+
*/
|
|
46
|
+
function describeCodePoint(cp) {
|
|
47
|
+
const named = NAMED_CODE_POINTS[cp];
|
|
48
|
+
if (named) {
|
|
49
|
+
return named;
|
|
50
|
+
}
|
|
51
|
+
const ch = String.fromCodePoint(cp);
|
|
52
|
+
if (/^[\p{C}\p{Z}]$/u.test(ch)) {
|
|
53
|
+
return `the character U+${cp.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
54
|
+
}
|
|
55
|
+
return `the character ${code(ch)}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Lowercases the first character.
|
|
59
|
+
*
|
|
60
|
+
* @param text The text.
|
|
61
|
+
*
|
|
62
|
+
* @returns The text with its first character lowercased.
|
|
63
|
+
*/
|
|
64
|
+
function lowerFirst(text) {
|
|
65
|
+
return text.charAt(0).toLowerCase() + text.slice(1);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Prefixes a noun with "a" or "an".
|
|
69
|
+
*
|
|
70
|
+
* @param noun The noun phrase.
|
|
71
|
+
*
|
|
72
|
+
* @returns The phrase with its article.
|
|
73
|
+
*/
|
|
74
|
+
function withArticle(noun) {
|
|
75
|
+
return `${/^[aeiou]/i.test(noun) ? "an" : "a"} ${noun}`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Splits text into plain runs and the contents of its Markdown code spans, so that descriptions can
|
|
79
|
+
* be rendered without a Markdown parser. It reverses {@link code}.
|
|
80
|
+
*
|
|
81
|
+
* @param text Text that may contain code spans, such as an explanation's description.
|
|
82
|
+
*
|
|
83
|
+
* @returns The parts in order. Adjacent plain text is one part.
|
|
84
|
+
*/
|
|
85
|
+
function splitCodeSpans(text) {
|
|
86
|
+
const spans = [...text.matchAll(/(?<!`)(`+)(?!`)([\s\S]+?)(?<!`)\1(?!`)/g)];
|
|
87
|
+
// Where the plain text before each span starts, then where the text after the last span starts.
|
|
88
|
+
const plainStarts = [0, ...spans.map((span) => span.index + span[0].length)];
|
|
89
|
+
const parts = spans.flatMap((span, i) => {
|
|
90
|
+
const content = span[2];
|
|
91
|
+
const padded = content.length > 2 && content.startsWith(" ") && content.endsWith(" ");
|
|
92
|
+
return [
|
|
93
|
+
...plainPart(text.slice(plainStarts[i], span.index)),
|
|
94
|
+
{ text: padded ? content.slice(1, -1) : content, code: true },
|
|
95
|
+
];
|
|
96
|
+
});
|
|
97
|
+
return [...parts, ...plainPart(text.slice(plainStarts[spans.length]))];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Wraps plain text as a part, unless it is empty.
|
|
101
|
+
*
|
|
102
|
+
* @param text The text.
|
|
103
|
+
*
|
|
104
|
+
* @returns One plain part, or none for empty text.
|
|
105
|
+
*/
|
|
106
|
+
function plainPart(text) {
|
|
107
|
+
return text === "" ? [] : [{ text, code: false }];
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=text.js.map
|