@wdprlib/render 2.0.0 → 3.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/dist/index.cjs +2332 -2032
- package/dist/index.d.cts +15 -13
- package/dist/index.d.ts +15 -13
- package/dist/index.js +2336 -2036
- package/package.json +5 -3
- package/src/context/attributes.ts +14 -0
- package/src/context/bibliography.ts +109 -0
- package/src/context/counters.ts +51 -0
- package/src/context/image-urls.ts +31 -0
- package/src/context/index.ts +285 -0
- package/src/context/output.ts +17 -0
- package/src/context/page-urls.ts +81 -0
- package/src/context/style-slots.ts +29 -0
- package/src/context/urls.ts +2 -0
- package/src/elements/bibliography/block.ts +27 -0
- package/src/elements/bibliography/cite.ts +23 -0
- package/src/elements/bibliography/ids.ts +9 -0
- package/src/elements/bibliography/index.ts +9 -0
- package/src/elements/clear-float.ts +27 -0
- package/src/elements/code/contents.ts +18 -0
- package/src/elements/code/index.ts +29 -0
- package/src/elements/collapsible/index.ts +31 -0
- package/src/elements/collapsible/labels.ts +35 -0
- package/src/elements/collapsible/link.ts +11 -0
- package/src/elements/collapsible/sections.ts +39 -0
- package/src/elements/color.ts +32 -0
- package/src/elements/container/attributes.ts +28 -0
- package/src/elements/container/header.ts +27 -0
- package/src/elements/container/index.ts +35 -0
- package/src/elements/container/string-container.ts +40 -0
- package/src/elements/container/string-types.ts +63 -0
- package/src/elements/container/wrappers.ts +32 -0
- package/src/elements/date/format.ts +20 -0
- package/src/elements/date/index.ts +34 -0
- package/src/elements/date/output.ts +6 -0
- package/src/elements/embed/iframe.ts +8 -0
- package/src/elements/embed/index.ts +28 -0
- package/src/elements/embed/providers.ts +43 -0
- package/src/elements/embed/validation.ts +15 -0
- package/src/elements/embed-block/allowlist.ts +60 -0
- package/src/elements/embed-block/boolean-attributes.ts +38 -0
- package/src/elements/embed-block/iframe.ts +33 -0
- package/src/elements/embed-block/index.ts +31 -0
- package/src/elements/embed-block/sanitize-config.ts +22 -0
- package/src/elements/embed-block/sanitize.ts +44 -0
- package/src/elements/expr/branch.ts +29 -0
- package/src/elements/expr/index.ts +63 -0
- package/src/elements/expr/result.ts +19 -0
- package/src/elements/footnote/body.ts +11 -0
- package/src/elements/footnote/index.ts +35 -0
- package/src/elements/footnote/ref.ts +16 -0
- package/src/elements/html/attributes.ts +24 -0
- package/src/elements/html/index.ts +39 -0
- package/src/elements/html/url.ts +19 -0
- package/src/elements/iframe/attributes.ts +28 -0
- package/src/elements/iframe/index.ts +22 -0
- package/src/elements/iftags/condition.ts +42 -0
- package/src/elements/iftags/index.ts +39 -0
- package/src/elements/iftags/style-slot.ts +23 -0
- package/src/elements/iftags/tokens.ts +36 -0
- package/src/elements/image/alignment.ts +44 -0
- package/src/elements/image/attributes.ts +10 -0
- package/src/elements/image/img-attributes.ts +26 -0
- package/src/elements/image/index.ts +36 -0
- package/src/elements/image/link-href.ts +24 -0
- package/src/elements/image/link.ts +13 -0
- package/src/elements/image/source.ts +16 -0
- package/src/elements/include/index.ts +35 -0
- package/src/elements/include/missing.ts +15 -0
- package/src/elements/index.ts +35 -0
- package/src/elements/line-break.ts +22 -0
- package/src/elements/link/anchor-name.ts +6 -0
- package/src/elements/link/anchor.ts +27 -0
- package/src/elements/link/attributes.ts +47 -0
- package/src/elements/link/index.ts +26 -0
- package/src/elements/link/label.ts +23 -0
- package/src/elements/link/target.ts +20 -0
- package/src/elements/list/attributes.ts +19 -0
- package/src/elements/list/definition-list.ts +16 -0
- package/src/elements/list/index.ts +48 -0
- package/src/elements/list/item-rendering.ts +38 -0
- package/src/elements/list/items.ts +61 -0
- package/src/elements/list/no-marker.ts +53 -0
- package/src/elements/list/paragraphs.ts +34 -0
- package/src/elements/list/trim.ts +38 -0
- package/src/elements/math/block.ts +29 -0
- package/src/elements/math/equation-ref.ts +12 -0
- package/src/elements/math/index.ts +14 -0
- package/src/elements/math/inline.ts +19 -0
- package/src/elements/math/latex.ts +27 -0
- package/src/elements/math/source.ts +18 -0
- package/src/elements/module/backlinks.ts +29 -0
- package/src/elements/module/categories.ts +27 -0
- package/src/elements/module/empty-container.ts +10 -0
- package/src/elements/module/index.ts +65 -0
- package/src/elements/module/join-markup.ts +10 -0
- package/src/elements/module/join.ts +28 -0
- package/src/elements/module/listpages.ts +27 -0
- package/src/elements/module/listusers.ts +27 -0
- package/src/elements/module/page-tree.ts +27 -0
- package/src/elements/module/rate-markup.ts +10 -0
- package/src/elements/module/rate.ts +35 -0
- package/src/elements/module/unknown.ts +11 -0
- package/src/elements/tab-view/ids.ts +16 -0
- package/src/elements/tab-view/index.ts +31 -0
- package/src/elements/tab-view/navigation.ts +15 -0
- package/src/elements/tab-view/panels.ts +16 -0
- package/src/elements/table/attributes.ts +23 -0
- package/src/elements/table/cell-attributes.ts +62 -0
- package/src/elements/table/cell.ts +13 -0
- package/src/elements/table/index.ts +27 -0
- package/src/elements/text/email.ts +20 -0
- package/src/elements/text/index.ts +11 -0
- package/src/elements/text/plain.ts +11 -0
- package/src/elements/text/raw.ts +20 -0
- package/src/elements/toc/body.ts +12 -0
- package/src/elements/toc/entries.ts +34 -0
- package/src/elements/toc/frame.ts +27 -0
- package/src/elements/toc/index.ts +17 -0
- package/src/elements/toc/link.ts +26 -0
- package/src/elements/user/index.ts +40 -0
- package/src/elements/user/markup.ts +34 -0
- package/src/elements/user/resolve.ts +6 -0
- package/src/escape/attribute-allowlists.ts +101 -0
- package/src/escape/attributes.ts +62 -0
- package/src/escape/css-color-functions.ts +18 -0
- package/src/escape/css-colors.ts +183 -0
- package/src/escape/css-danger.ts +22 -0
- package/src/escape/css-normalize.ts +54 -0
- package/src/escape/css-style.ts +78 -0
- package/src/escape/css-urls.ts +76 -0
- package/src/escape/css.ts +4 -0
- package/src/escape/email.ts +22 -0
- package/src/escape/html.ts +68 -0
- package/src/escape/index.ts +15 -0
- package/src/escape/url.ts +18 -0
- package/src/hash.ts +62 -0
- package/src/index.ts +26 -0
- package/src/libs/highlighter/engine/end-pattern.ts +26 -0
- package/src/libs/highlighter/engine/html.ts +19 -0
- package/src/libs/highlighter/engine/index.ts +3 -0
- package/src/libs/highlighter/engine/keywords.ts +22 -0
- package/src/libs/highlighter/engine/parts.ts +36 -0
- package/src/libs/highlighter/engine/preprocess.ts +10 -0
- package/src/libs/highlighter/engine/render.ts +31 -0
- package/src/libs/highlighter/engine/token.ts +7 -0
- package/src/libs/highlighter/engine/tokenizer.ts +266 -0
- package/src/libs/highlighter/engine/utils.ts +38 -0
- package/src/libs/highlighter/index.ts +70 -0
- package/src/libs/highlighter/languages/cpp.ts +345 -0
- package/src/libs/highlighter/languages/css.ts +104 -0
- package/src/libs/highlighter/languages/diff.ts +154 -0
- package/src/libs/highlighter/languages/dtd.ts +99 -0
- package/src/libs/highlighter/languages/html.ts +59 -0
- package/src/libs/highlighter/languages/java.ts +251 -0
- package/src/libs/highlighter/languages/javascript.ts +213 -0
- package/src/libs/highlighter/languages/php.ts +433 -0
- package/src/libs/highlighter/languages/python.ts +308 -0
- package/src/libs/highlighter/languages/ruby.ts +360 -0
- package/src/libs/highlighter/languages/sql.ts +125 -0
- package/src/libs/highlighter/languages/xml.ts +68 -0
- package/src/libs/highlighter/types.ts +44 -0
- package/src/render/collected-styles.ts +22 -0
- package/src/render/dispatch.ts +181 -0
- package/src/render/index.ts +28 -0
- package/src/render/primitives.ts +17 -0
- package/src/render/style-tag.ts +6 -0
- package/src/render/style.ts +15 -0
- package/src/types.ts +144 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LanguageDefinition } from "../types";
|
|
2
|
+
|
|
3
|
+
export function resolveKeywordClass(
|
|
4
|
+
def: LanguageDefinition,
|
|
5
|
+
state: number,
|
|
6
|
+
patternIndex: number,
|
|
7
|
+
matchStr: string,
|
|
8
|
+
fallback: string,
|
|
9
|
+
): string {
|
|
10
|
+
let kwDef = def.keywords[state]?.[patternIndex];
|
|
11
|
+
if (!kwDef || kwDef === -1 || typeof kwDef !== "object" || Object.keys(kwDef).length === 0) {
|
|
12
|
+
kwDef = def.keywords[-1]?.[patternIndex];
|
|
13
|
+
}
|
|
14
|
+
if (kwDef && kwDef !== -1 && typeof kwDef === "object") {
|
|
15
|
+
for (const [group, re] of Object.entries(kwDef)) {
|
|
16
|
+
if ((re as RegExp).test(matchStr)) {
|
|
17
|
+
return def.kwmap[group] ?? fallback;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { HighlightToken } from "./token";
|
|
2
|
+
|
|
3
|
+
export function buildPartTokens(
|
|
4
|
+
str: string,
|
|
5
|
+
match: RegExpExecArray,
|
|
6
|
+
partDef: Record<number, string>,
|
|
7
|
+
captureIndex: number,
|
|
8
|
+
count: number,
|
|
9
|
+
groupStart: number,
|
|
10
|
+
matchStr: string,
|
|
11
|
+
inner: string,
|
|
12
|
+
): HighlightToken[] {
|
|
13
|
+
const parts: HighlightToken[] = [];
|
|
14
|
+
let partpos = groupStart;
|
|
15
|
+
for (let j = 1; j <= count; j++) {
|
|
16
|
+
const subIdx = j + captureIndex;
|
|
17
|
+
if (subIdx >= match.length || match[subIdx] == null || match[subIdx] === "") continue;
|
|
18
|
+
const subStr = match[subIdx]!;
|
|
19
|
+
const subStart = str.indexOf(subStr, partpos);
|
|
20
|
+
if (subStart < 0) continue;
|
|
21
|
+
if (partDef[j]) {
|
|
22
|
+
if (subStart > partpos) {
|
|
23
|
+
parts.unshift({ class: inner, content: str.substring(partpos, subStart) });
|
|
24
|
+
}
|
|
25
|
+
parts.unshift({ class: partDef[j]!, content: subStr });
|
|
26
|
+
}
|
|
27
|
+
partpos = subStart + subStr.length;
|
|
28
|
+
}
|
|
29
|
+
if (partpos < groupStart + matchStr.length) {
|
|
30
|
+
parts.unshift({
|
|
31
|
+
class: inner,
|
|
32
|
+
content: str.substring(partpos, groupStart + matchStr.length),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return parts;
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preprocess source code the same way Text_Highlighter's HTML renderer does.
|
|
3
|
+
*/
|
|
4
|
+
export function preprocessHighlightInput(input: string): string {
|
|
5
|
+
return input
|
|
6
|
+
.replace(/\r\n/g, "\n")
|
|
7
|
+
.replace(/^$/gm, " ")
|
|
8
|
+
.replace(/\t/g, " ")
|
|
9
|
+
.replace(/\s+$/, "");
|
|
10
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { escapeHighlightHtml } from "./html";
|
|
2
|
+
import type { HighlightToken } from "./token";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Render an array of tokens to HTML with `hl-*` class spans.
|
|
6
|
+
*/
|
|
7
|
+
export function renderTokens(tokens: HighlightToken[]): string {
|
|
8
|
+
if (tokens.length === 0) return "";
|
|
9
|
+
|
|
10
|
+
let html = "";
|
|
11
|
+
let lastClass = "";
|
|
12
|
+
|
|
13
|
+
for (const token of tokens) {
|
|
14
|
+
if (token.content.length === 0) continue;
|
|
15
|
+
const escaped = escapeHighlightHtml(token.content);
|
|
16
|
+
if (token.class !== lastClass) {
|
|
17
|
+
if (lastClass) {
|
|
18
|
+
html += "</span>";
|
|
19
|
+
}
|
|
20
|
+
html += `<span class="hl-${token.class}">`;
|
|
21
|
+
lastClass = token.class;
|
|
22
|
+
}
|
|
23
|
+
html += escaped;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (lastClass) {
|
|
27
|
+
html += "</span>";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return `<div class="hl-main"><pre>${html}</pre></div>`;
|
|
31
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type { LanguageDefinition } from "../types";
|
|
2
|
+
import { buildEndPattern } from "./end-pattern";
|
|
3
|
+
import { resolveKeywordClass } from "./keywords";
|
|
4
|
+
import { buildPartTokens } from "./parts";
|
|
5
|
+
import { preprocessHighlightInput } from "./preprocess";
|
|
6
|
+
import type { HighlightToken } from "./token";
|
|
7
|
+
import { findGroupPosition } from "./utils";
|
|
8
|
+
|
|
9
|
+
interface HighlighterState {
|
|
10
|
+
state: number;
|
|
11
|
+
lastdelim: string;
|
|
12
|
+
lastinner: string;
|
|
13
|
+
endpattern: RegExp | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Tokenize source code using a language definition's state machine.
|
|
18
|
+
*/
|
|
19
|
+
export function tokenize(def: LanguageDefinition, input: string): HighlightToken[] {
|
|
20
|
+
const str = preprocessHighlightInput(input);
|
|
21
|
+
const len = str.length;
|
|
22
|
+
if (len === 0) return [];
|
|
23
|
+
|
|
24
|
+
let state = -1;
|
|
25
|
+
let pos = 0;
|
|
26
|
+
let lastinner = def.defClass;
|
|
27
|
+
let lastdelim = def.defClass;
|
|
28
|
+
let endpattern: RegExp | null = null;
|
|
29
|
+
const stateStack: HighlighterState[] = [];
|
|
30
|
+
const tokenStack: HighlightToken[] = [];
|
|
31
|
+
const result: HighlightToken[] = [];
|
|
32
|
+
|
|
33
|
+
function getToken(): HighlightToken | null {
|
|
34
|
+
if (tokenStack.length > 0) {
|
|
35
|
+
return tokenStack.pop()!;
|
|
36
|
+
}
|
|
37
|
+
if (pos >= len) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const endStateMatch = findEndStateMatch(str, pos, state, endpattern);
|
|
42
|
+
const token = matchStateToken({
|
|
43
|
+
def,
|
|
44
|
+
str,
|
|
45
|
+
pos,
|
|
46
|
+
state,
|
|
47
|
+
lastinner,
|
|
48
|
+
lastdelim,
|
|
49
|
+
endpattern,
|
|
50
|
+
stateStack,
|
|
51
|
+
tokenStack,
|
|
52
|
+
endStateMatch,
|
|
53
|
+
setPosition: (nextPos) => {
|
|
54
|
+
pos = nextPos;
|
|
55
|
+
},
|
|
56
|
+
setState: (next) => {
|
|
57
|
+
state = next.state;
|
|
58
|
+
lastinner = next.lastinner;
|
|
59
|
+
lastdelim = next.lastdelim;
|
|
60
|
+
endpattern = next.endpattern;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
if (token) {
|
|
64
|
+
return token;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (endStateMatch.endpos > -1) {
|
|
68
|
+
tokenStack.push({ class: lastdelim, content: endStateMatch.endmatch });
|
|
69
|
+
if (endStateMatch.endpos > pos) {
|
|
70
|
+
tokenStack.push({ class: lastinner, content: str.substring(pos, endStateMatch.endpos) });
|
|
71
|
+
}
|
|
72
|
+
const prev = stateStack.pop()!;
|
|
73
|
+
state = prev.state;
|
|
74
|
+
lastdelim = prev.lastdelim;
|
|
75
|
+
lastinner = prev.lastinner;
|
|
76
|
+
endpattern = prev.endpattern;
|
|
77
|
+
pos = endStateMatch.endpos + endStateMatch.endmatch.length;
|
|
78
|
+
if (tokenStack.length > 0) {
|
|
79
|
+
return tokenStack.pop()!;
|
|
80
|
+
}
|
|
81
|
+
return getToken();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const p = pos;
|
|
85
|
+
pos = len;
|
|
86
|
+
return { class: lastinner, content: str.substring(p) };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let token: HighlightToken | null;
|
|
90
|
+
while ((token = getToken()) !== null) {
|
|
91
|
+
result.push(token);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface EndStateMatch {
|
|
98
|
+
endpos: number;
|
|
99
|
+
endmatch: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function findEndStateMatch(
|
|
103
|
+
str: string,
|
|
104
|
+
pos: number,
|
|
105
|
+
state: number,
|
|
106
|
+
endpattern: RegExp | null,
|
|
107
|
+
): EndStateMatch {
|
|
108
|
+
if (state === -1 || !endpattern) {
|
|
109
|
+
return { endpos: -1, endmatch: "" };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
endpattern.lastIndex = pos;
|
|
113
|
+
const match = endpattern.exec(str);
|
|
114
|
+
return match ? { endpos: match.index, endmatch: match[0] } : { endpos: -1, endmatch: "" };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface MatchStateTokenArgs {
|
|
118
|
+
def: LanguageDefinition;
|
|
119
|
+
str: string;
|
|
120
|
+
pos: number;
|
|
121
|
+
state: number;
|
|
122
|
+
lastinner: string;
|
|
123
|
+
lastdelim: string;
|
|
124
|
+
endpattern: RegExp | null;
|
|
125
|
+
stateStack: HighlighterState[];
|
|
126
|
+
tokenStack: HighlightToken[];
|
|
127
|
+
endStateMatch: EndStateMatch;
|
|
128
|
+
setPosition(pos: number): void;
|
|
129
|
+
setState(state: HighlighterState): void;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function matchStateToken(args: MatchStateTokenArgs): HighlightToken | null {
|
|
133
|
+
const reg = args.def.regs[args.state];
|
|
134
|
+
if (!reg) return null;
|
|
135
|
+
|
|
136
|
+
reg.lastIndex = args.pos;
|
|
137
|
+
const match = reg.exec(args.str);
|
|
138
|
+
if (!match) return null;
|
|
139
|
+
|
|
140
|
+
const countsArr = args.def.counts[args.state]!;
|
|
141
|
+
let captureIndex = 1;
|
|
142
|
+
|
|
143
|
+
for (let patternIndex = 0; patternIndex < countsArr.length; patternIndex++) {
|
|
144
|
+
const count = countsArr[patternIndex]!;
|
|
145
|
+
if (captureIndex >= match.length) break;
|
|
146
|
+
|
|
147
|
+
if (
|
|
148
|
+
match[captureIndex] != null &&
|
|
149
|
+
(args.endStateMatch.endpos === -1 || match.index < args.endStateMatch.endpos)
|
|
150
|
+
) {
|
|
151
|
+
return emitMatchedPattern(args, match, patternIndex, captureIndex, count);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
captureIndex += count + 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function emitMatchedPattern(
|
|
161
|
+
args: MatchStateTokenArgs,
|
|
162
|
+
match: RegExpExecArray,
|
|
163
|
+
patternIndex: number,
|
|
164
|
+
captureIndex: number,
|
|
165
|
+
count: number,
|
|
166
|
+
): HighlightToken {
|
|
167
|
+
const statesArr = args.def.states[args.state]!;
|
|
168
|
+
const delimArr = args.def.delim[args.state]!;
|
|
169
|
+
const matchStart = match.index;
|
|
170
|
+
const matchStr = match[captureIndex]!;
|
|
171
|
+
const groupStart = findGroupPosition(args.str, match, captureIndex, matchStart);
|
|
172
|
+
|
|
173
|
+
if (statesArr[patternIndex] !== -1) {
|
|
174
|
+
args.tokenStack.push({ class: delimArr[patternIndex]!, content: matchStr });
|
|
175
|
+
} else {
|
|
176
|
+
pushNonTransitionTokens(args, match, patternIndex, captureIndex, count, groupStart, matchStr);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (groupStart > args.pos) {
|
|
180
|
+
args.tokenStack.push({
|
|
181
|
+
class: args.lastinner,
|
|
182
|
+
content: args.str.substring(args.pos, groupStart),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
args.setPosition(groupStart + matchStr.length);
|
|
187
|
+
|
|
188
|
+
if (statesArr[patternIndex] !== -1) {
|
|
189
|
+
enterState(args, match, patternIndex, captureIndex, count);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return args.tokenStack.pop()!;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function pushNonTransitionTokens(
|
|
196
|
+
args: MatchStateTokenArgs,
|
|
197
|
+
match: RegExpExecArray,
|
|
198
|
+
patternIndex: number,
|
|
199
|
+
captureIndex: number,
|
|
200
|
+
count: number,
|
|
201
|
+
groupStart: number,
|
|
202
|
+
matchStr: string,
|
|
203
|
+
): void {
|
|
204
|
+
let inner = args.def.inner[args.state]![patternIndex]!;
|
|
205
|
+
const partDef = args.def.parts[args.state]?.[patternIndex];
|
|
206
|
+
if (partDef) {
|
|
207
|
+
pushPartTokens(args, match, partDef, captureIndex, count, groupStart, matchStr, inner);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
inner = resolveKeywordClass(args.def, args.state, patternIndex, matchStr, inner);
|
|
212
|
+
args.tokenStack.push({ class: inner, content: matchStr });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function pushPartTokens(
|
|
216
|
+
args: MatchStateTokenArgs,
|
|
217
|
+
match: RegExpExecArray,
|
|
218
|
+
partDef: Record<number, string>,
|
|
219
|
+
captureIndex: number,
|
|
220
|
+
count: number,
|
|
221
|
+
groupStart: number,
|
|
222
|
+
matchStr: string,
|
|
223
|
+
inner: string,
|
|
224
|
+
): void {
|
|
225
|
+
const parts: HighlightToken[] = [];
|
|
226
|
+
parts.push(
|
|
227
|
+
...buildPartTokens(args.str, match, partDef, captureIndex, count, groupStart, matchStr, inner),
|
|
228
|
+
);
|
|
229
|
+
args.tokenStack.push(...parts);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function enterState(
|
|
233
|
+
args: MatchStateTokenArgs,
|
|
234
|
+
match: RegExpExecArray,
|
|
235
|
+
patternIndex: number,
|
|
236
|
+
captureIndex: number,
|
|
237
|
+
count: number,
|
|
238
|
+
): void {
|
|
239
|
+
const statesArr = args.def.states[args.state]!;
|
|
240
|
+
const delimArr = args.def.delim[args.state]!;
|
|
241
|
+
const innerArr = args.def.inner[args.state]!;
|
|
242
|
+
args.stateStack.push({
|
|
243
|
+
state: args.state,
|
|
244
|
+
lastdelim: args.lastdelim,
|
|
245
|
+
lastinner: args.lastinner,
|
|
246
|
+
endpattern: args.endpattern,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const prevState = args.state;
|
|
250
|
+
const nextState = statesArr[patternIndex]!;
|
|
251
|
+
const endRe = args.def.end[nextState];
|
|
252
|
+
args.setState({
|
|
253
|
+
state: nextState,
|
|
254
|
+
lastinner: innerArr[patternIndex]!,
|
|
255
|
+
lastdelim: delimArr[patternIndex]!,
|
|
256
|
+
endpattern: buildEndPattern(
|
|
257
|
+
args.def,
|
|
258
|
+
prevState,
|
|
259
|
+
patternIndex,
|
|
260
|
+
count,
|
|
261
|
+
captureIndex,
|
|
262
|
+
match,
|
|
263
|
+
endRe ?? undefined,
|
|
264
|
+
),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the actual position of capture group `n` within the source string.
|
|
3
|
+
*/
|
|
4
|
+
export function findGroupPosition(
|
|
5
|
+
str: string,
|
|
6
|
+
match: RegExpExecArray,
|
|
7
|
+
groupIndex: number,
|
|
8
|
+
matchStart: number,
|
|
9
|
+
): number {
|
|
10
|
+
const groupStr = match[groupIndex]!;
|
|
11
|
+
const idx = str.indexOf(groupStr, matchStart);
|
|
12
|
+
return idx >= 0 ? idx : matchStart;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Escape regex special characters in a string for safe use in `new RegExp()`.
|
|
17
|
+
*/
|
|
18
|
+
export function escapeRegex(str: string): string {
|
|
19
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Swap bracket characters to their matching counterparts.
|
|
24
|
+
*/
|
|
25
|
+
export function matchingBrackets(str: string): string {
|
|
26
|
+
return str.replace(/[()<>[\]{}]/g, (char) => MATCHING_BRACKETS[char] ?? char);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const MATCHING_BRACKETS: Record<string, string> = {
|
|
30
|
+
"(": ")",
|
|
31
|
+
")": "(",
|
|
32
|
+
"<": ">",
|
|
33
|
+
">": "<",
|
|
34
|
+
"[": "]",
|
|
35
|
+
"]": "[",
|
|
36
|
+
"{": "}",
|
|
37
|
+
"}": "{",
|
|
38
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* TypeScript port of PEAR Text_Highlighter 0.5.1 for server-side
|
|
4
|
+
* syntax highlighting of `[[code]]` blocks.
|
|
5
|
+
*
|
|
6
|
+
* Supports the same languages as Wikidot's original PHP implementation:
|
|
7
|
+
* CSS, C++, Diff, DTD, HTML, Java, JavaScript, PHP, Python, Ruby, SQL,
|
|
8
|
+
* and XML. Perl is excluded because its definition relies on PCRE-only
|
|
9
|
+
* regex features.
|
|
10
|
+
*
|
|
11
|
+
* Original: http://pear.php.net/package/Text_Highlighter
|
|
12
|
+
* Author: Andrey Demenev <demenev@on-line.jar.ru>
|
|
13
|
+
* Copyright: 2004 Andrey Demenev
|
|
14
|
+
* License: PHP License 3.0 (http://www.php.net/license/3_0.txt)
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { LanguageDefinition } from "./types";
|
|
20
|
+
import { tokenize, renderTokens } from "./engine";
|
|
21
|
+
import { cssLang } from "./languages/css";
|
|
22
|
+
import { cppLang } from "./languages/cpp";
|
|
23
|
+
import { diffLang } from "./languages/diff";
|
|
24
|
+
import { dtdLang } from "./languages/dtd";
|
|
25
|
+
import { htmlLang } from "./languages/html";
|
|
26
|
+
import { javaLang } from "./languages/java";
|
|
27
|
+
import { javascriptLang } from "./languages/javascript";
|
|
28
|
+
import { phpLang } from "./languages/php";
|
|
29
|
+
import { pythonLang } from "./languages/python";
|
|
30
|
+
import { rubyLang } from "./languages/ruby";
|
|
31
|
+
import { sqlLang } from "./languages/sql";
|
|
32
|
+
import { xmlLang } from "./languages/xml";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Registry of supported language definitions, keyed by lowercase name.
|
|
36
|
+
*
|
|
37
|
+
* `xhtml` is aliased to `html` (they share the same definition).
|
|
38
|
+
*/
|
|
39
|
+
const LANGUAGES: Record<string, LanguageDefinition> = {
|
|
40
|
+
css: cssLang,
|
|
41
|
+
cpp: cppLang,
|
|
42
|
+
diff: diffLang,
|
|
43
|
+
dtd: dtdLang,
|
|
44
|
+
html: htmlLang,
|
|
45
|
+
java: javaLang,
|
|
46
|
+
javascript: javascriptLang,
|
|
47
|
+
// perl: excluded (PCRE-only features)
|
|
48
|
+
php: phpLang,
|
|
49
|
+
python: pythonLang,
|
|
50
|
+
ruby: rubyLang,
|
|
51
|
+
sql: sqlLang,
|
|
52
|
+
xml: xmlLang,
|
|
53
|
+
xhtml: htmlLang,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Highlight source code using the Text_Highlighter-compatible engine.
|
|
58
|
+
*
|
|
59
|
+
* @param code - Raw source code to highlight.
|
|
60
|
+
* @param language - Programming language name (case-insensitive).
|
|
61
|
+
* @returns HTML string with `hl-*` class spans wrapped in
|
|
62
|
+
* `<div class="hl-main"><pre>...</pre></div>`, or `null` if the
|
|
63
|
+
* language is not supported.
|
|
64
|
+
*/
|
|
65
|
+
export function highlight(code: string, language: string): string | null {
|
|
66
|
+
const def = LANGUAGES[language.toLowerCase()];
|
|
67
|
+
if (!def) return null;
|
|
68
|
+
const tokens = tokenize(def, code);
|
|
69
|
+
return renderTokens(tokens);
|
|
70
|
+
}
|