@fuzdev/fuz_code 0.37.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 +25 -0
- package/README.md +185 -0
- package/dist/Code.svelte +146 -0
- package/dist/Code.svelte.d.ts +79 -0
- package/dist/Code.svelte.d.ts.map +1 -0
- package/dist/CodeHighlight.svelte +205 -0
- package/dist/CodeHighlight.svelte.d.ts +101 -0
- package/dist/CodeHighlight.svelte.d.ts.map +1 -0
- package/dist/code_sample.d.ts +8 -0
- package/dist/code_sample.d.ts.map +1 -0
- package/dist/code_sample.js +2 -0
- package/dist/grammar_clike.d.ts +12 -0
- package/dist/grammar_clike.d.ts.map +1 -0
- package/dist/grammar_clike.js +43 -0
- package/dist/grammar_css.d.ts +11 -0
- package/dist/grammar_css.d.ts.map +1 -0
- package/dist/grammar_css.js +70 -0
- package/dist/grammar_js.d.ts +11 -0
- package/dist/grammar_js.d.ts.map +1 -0
- package/dist/grammar_js.js +180 -0
- package/dist/grammar_json.d.ts +11 -0
- package/dist/grammar_json.d.ts.map +1 -0
- package/dist/grammar_json.js +35 -0
- package/dist/grammar_markdown.d.ts +8 -0
- package/dist/grammar_markdown.d.ts.map +1 -0
- package/dist/grammar_markdown.js +228 -0
- package/dist/grammar_markup.d.ts +31 -0
- package/dist/grammar_markup.d.ts.map +1 -0
- package/dist/grammar_markup.js +192 -0
- package/dist/grammar_svelte.d.ts +12 -0
- package/dist/grammar_svelte.d.ts.map +1 -0
- package/dist/grammar_svelte.js +150 -0
- package/dist/grammar_ts.d.ts +11 -0
- package/dist/grammar_ts.d.ts.map +1 -0
- package/dist/grammar_ts.js +95 -0
- package/dist/highlight_manager.d.ts +25 -0
- package/dist/highlight_manager.d.ts.map +1 -0
- package/dist/highlight_manager.js +139 -0
- package/dist/highlight_priorities.d.ts +3 -0
- package/dist/highlight_priorities.d.ts.map +1 -0
- package/dist/highlight_priorities.gen.d.ts +4 -0
- package/dist/highlight_priorities.gen.d.ts.map +1 -0
- package/dist/highlight_priorities.gen.js +58 -0
- package/dist/highlight_priorities.js +55 -0
- package/dist/syntax_styler.d.ts +277 -0
- package/dist/syntax_styler.d.ts.map +1 -0
- package/dist/syntax_styler.js +426 -0
- package/dist/syntax_styler_global.d.ts +3 -0
- package/dist/syntax_styler_global.d.ts.map +1 -0
- package/dist/syntax_styler_global.js +18 -0
- package/dist/syntax_token.d.ts +34 -0
- package/dist/syntax_token.d.ts.map +1 -0
- package/dist/syntax_token.js +27 -0
- package/dist/theme.css +98 -0
- package/dist/theme_highlight.css +160 -0
- package/dist/theme_variables.css +20 -0
- package/dist/tokenize_syntax.d.ts +28 -0
- package/dist/tokenize_syntax.d.ts.map +1 -0
- package/dist/tokenize_syntax.js +194 -0
- package/package.json +117 -0
- package/src/lib/code_sample.ts +10 -0
- package/src/lib/grammar_clike.ts +48 -0
- package/src/lib/grammar_css.ts +84 -0
- package/src/lib/grammar_js.ts +215 -0
- package/src/lib/grammar_json.ts +38 -0
- package/src/lib/grammar_markdown.ts +289 -0
- package/src/lib/grammar_markup.ts +225 -0
- package/src/lib/grammar_svelte.ts +165 -0
- package/src/lib/grammar_ts.ts +114 -0
- package/src/lib/highlight_manager.ts +182 -0
- package/src/lib/highlight_priorities.gen.ts +71 -0
- package/src/lib/highlight_priorities.ts +110 -0
- package/src/lib/syntax_styler.ts +583 -0
- package/src/lib/syntax_styler_global.ts +20 -0
- package/src/lib/syntax_token.ts +49 -0
- package/src/lib/tokenize_syntax.ts +270 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import type {AddSyntaxGrammar} from './syntax_styler.js';
|
|
2
|
+
import {grammar_markup_add_attribute, grammar_markup_add_inlined} from './grammar_markup.js';
|
|
3
|
+
import {class_keywords} from './grammar_clike.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Based on Prism (https://github.com/PrismJS/prism)
|
|
7
|
+
* by Lea Verou (https://lea.verou.me/)
|
|
8
|
+
*
|
|
9
|
+
* MIT license
|
|
10
|
+
*
|
|
11
|
+
* @see LICENSE
|
|
12
|
+
*/
|
|
13
|
+
export const add_grammar_js: AddSyntaxGrammar = (syntax_styler) => {
|
|
14
|
+
const grammar_clike = syntax_styler.get_lang('clike');
|
|
15
|
+
|
|
16
|
+
// Main JS keywords (from keyword pattern, excluding those with special lookaheads)
|
|
17
|
+
const main_keywords =
|
|
18
|
+
'class|const|debugger|delete|enum|extends|function|implements|in|instanceof|interface|let|new|null|of|package|private|protected|public|static|super|this|typeof|undefined|var|void|with';
|
|
19
|
+
|
|
20
|
+
// Keywords that are treated specially (inserted before 'function')
|
|
21
|
+
const special_keywords =
|
|
22
|
+
'as|await|break|case|catch|continue|default|do|else|export|finally|for|from|if|import|return|switch|throw|try|while|yield';
|
|
23
|
+
|
|
24
|
+
// All JS keywords (for negative lookahead in parameter pattern)
|
|
25
|
+
// Note: 'assert', 'async', 'get', 'set' have special lookahead requirements in the main keyword pattern
|
|
26
|
+
const all_js_keywords = `assert|async|${main_keywords}|get|set|${special_keywords}`;
|
|
27
|
+
|
|
28
|
+
const grammar_js = syntax_styler.add_extended_lang(
|
|
29
|
+
'clike',
|
|
30
|
+
'js',
|
|
31
|
+
{
|
|
32
|
+
class_name: [
|
|
33
|
+
...grammar_clike.class_name!,
|
|
34
|
+
{
|
|
35
|
+
pattern:
|
|
36
|
+
/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,
|
|
37
|
+
lookbehind: true,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
keyword: [
|
|
41
|
+
{
|
|
42
|
+
pattern: new RegExp(
|
|
43
|
+
`(^|[^.]|\\.\\.\\.\\s*)\\b(?:assert(?=\\s*\\{)|async(?=\\s*(?:function\\b|\\*|\\(|[$\\w\\xA0-\\uFFFF]|$))|${main_keywords}|(?:get|set)(?=\\s*(?:[#[$\\w\\xA0-\\uFFFF]|$)))\\b`,
|
|
44
|
+
),
|
|
45
|
+
lookbehind: true,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
// Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
|
|
49
|
+
function:
|
|
50
|
+
/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,
|
|
51
|
+
number: {
|
|
52
|
+
pattern: RegExp(
|
|
53
|
+
/(^|[^\w$])/.source +
|
|
54
|
+
'(?:' +
|
|
55
|
+
// constant
|
|
56
|
+
(/NaN|Infinity/.source +
|
|
57
|
+
'|' +
|
|
58
|
+
// binary integer
|
|
59
|
+
/0[bB][01]+(?:_[01]+)*n?/.source +
|
|
60
|
+
'|' +
|
|
61
|
+
// octal integer
|
|
62
|
+
/0[oO][0-7]+(?:_[0-7]+)*n?/.source +
|
|
63
|
+
'|' +
|
|
64
|
+
// hexadecimal integer
|
|
65
|
+
/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source +
|
|
66
|
+
'|' +
|
|
67
|
+
// decimal bigint
|
|
68
|
+
/\d+(?:_\d+)*n/.source +
|
|
69
|
+
'|' +
|
|
70
|
+
// decimal number (integer or float) but no bigint
|
|
71
|
+
/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/
|
|
72
|
+
.source) +
|
|
73
|
+
')' +
|
|
74
|
+
/(?![\w$])/.source,
|
|
75
|
+
),
|
|
76
|
+
lookbehind: true,
|
|
77
|
+
},
|
|
78
|
+
operator:
|
|
79
|
+
/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/,
|
|
80
|
+
},
|
|
81
|
+
['javascript'],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
(grammar_js as any).class_name[0].pattern = new RegExp(
|
|
85
|
+
`(\\b(?:${class_keywords})\\s+)[\\w.\\\\]+`,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
syntax_styler.grammar_insert_before('js', 'function', {
|
|
89
|
+
special_keyword: new RegExp(`\\b(?:${special_keywords})\\b`),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
syntax_styler.grammar_insert_before('js', 'keyword', {
|
|
93
|
+
regex: {
|
|
94
|
+
pattern: RegExp(
|
|
95
|
+
// lookbehind
|
|
96
|
+
/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source +
|
|
97
|
+
// Regex pattern:
|
|
98
|
+
// There are 2 regex patterns here. The RegExp set notation proposal added support for nested character
|
|
99
|
+
// classes if the `v` flag is present. Unfortunately, nested CCs are both context-free and incompatible
|
|
100
|
+
// with the only syntax, so we have to define 2 different regex patterns.
|
|
101
|
+
/\//.source +
|
|
102
|
+
'(?:' +
|
|
103
|
+
/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\[\r\n])+\/[dgimyus]{0,7}/.source +
|
|
104
|
+
'|' +
|
|
105
|
+
// `v` flag syntax. This supports 3 levels of nested character classes.
|
|
106
|
+
/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/
|
|
107
|
+
.source +
|
|
108
|
+
')' +
|
|
109
|
+
// lookahead
|
|
110
|
+
/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source,
|
|
111
|
+
),
|
|
112
|
+
lookbehind: true,
|
|
113
|
+
greedy: true,
|
|
114
|
+
inside: {
|
|
115
|
+
regex_source: {
|
|
116
|
+
pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/,
|
|
117
|
+
lookbehind: true,
|
|
118
|
+
alias: 'lang_regex',
|
|
119
|
+
inside: syntax_styler.langs.regex, // TODO use `get_lang` after adding `regex` support
|
|
120
|
+
},
|
|
121
|
+
regex_delimiter: /^\/|\/$/,
|
|
122
|
+
regex_flags: /^[a-z]+$/,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
// Arrow function and function expression variable names
|
|
126
|
+
// This must be declared before keyword because we use "function" inside the look-forward
|
|
127
|
+
function_variable: {
|
|
128
|
+
pattern:
|
|
129
|
+
/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,
|
|
130
|
+
alias: 'function',
|
|
131
|
+
},
|
|
132
|
+
parameter: [
|
|
133
|
+
{
|
|
134
|
+
pattern:
|
|
135
|
+
/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,
|
|
136
|
+
lookbehind: true,
|
|
137
|
+
inside: grammar_js,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
pattern:
|
|
141
|
+
/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,
|
|
142
|
+
lookbehind: true,
|
|
143
|
+
inside: grammar_js,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
pattern: /(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,
|
|
147
|
+
lookbehind: true,
|
|
148
|
+
inside: grammar_js,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
pattern: new RegExp(
|
|
152
|
+
`((?:\\b|\\s|^)(?!(?:${all_js_keywords})(?![$\\w\\xA0-\\uFFFF]))(?:(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*\\s*)\\(\\s*|\\]\\s*\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\)\\s*\\{)`,
|
|
153
|
+
),
|
|
154
|
+
lookbehind: true,
|
|
155
|
+
inside: grammar_js,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
constant: /\b[A-Z](?:[A-Z_]|\dx?)*\b/,
|
|
159
|
+
// Heuristic: treat capitalized identifiers as class names when not already matched
|
|
160
|
+
capitalized_identifier: {
|
|
161
|
+
pattern: /\b[A-Z][\w]*\b/,
|
|
162
|
+
alias: 'class_name',
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
syntax_styler.grammar_insert_before('js', 'string', {
|
|
167
|
+
hashbang: {
|
|
168
|
+
pattern: /^#!.*/,
|
|
169
|
+
greedy: true,
|
|
170
|
+
alias: 'comment',
|
|
171
|
+
},
|
|
172
|
+
template_string: {
|
|
173
|
+
pattern: /`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,
|
|
174
|
+
greedy: true,
|
|
175
|
+
inside: {
|
|
176
|
+
template_punctuation: {
|
|
177
|
+
pattern: /^`|`$/,
|
|
178
|
+
alias: 'string',
|
|
179
|
+
},
|
|
180
|
+
interpolation: {
|
|
181
|
+
pattern: /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,
|
|
182
|
+
lookbehind: true,
|
|
183
|
+
inside: {
|
|
184
|
+
interpolation_punctuation: {
|
|
185
|
+
pattern: /^\$\{|\}$/,
|
|
186
|
+
alias: 'punctuation',
|
|
187
|
+
},
|
|
188
|
+
rest: grammar_js as any, // TODO try to fix this type
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
string: /[\s\S]+/,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
string_property: {
|
|
195
|
+
pattern: /((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,
|
|
196
|
+
lookbehind: true,
|
|
197
|
+
greedy: true,
|
|
198
|
+
alias: 'property',
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
syntax_styler.grammar_insert_before('js', 'operator', {
|
|
203
|
+
literal_property: {
|
|
204
|
+
pattern: /((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,
|
|
205
|
+
lookbehind: true,
|
|
206
|
+
alias: 'property',
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
grammar_markup_add_inlined(syntax_styler, 'script', 'js');
|
|
211
|
+
|
|
212
|
+
// add attribute support for all DOM events (on* attributes)
|
|
213
|
+
// https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events
|
|
214
|
+
grammar_markup_add_attribute(syntax_styler, 'on\\w+', 'js');
|
|
215
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type {AddSyntaxGrammar, SyntaxGrammarRaw} from './syntax_styler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Based on Prism (https://github.com/PrismJS/prism)
|
|
5
|
+
* by Lea Verou (https://lea.verou.me/)
|
|
6
|
+
*
|
|
7
|
+
* MIT license
|
|
8
|
+
*
|
|
9
|
+
* @see LICENSE
|
|
10
|
+
*/
|
|
11
|
+
export const add_grammar_json: AddSyntaxGrammar = (syntax_styler) => {
|
|
12
|
+
const grammar_json = {
|
|
13
|
+
property: {
|
|
14
|
+
pattern: /(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,
|
|
15
|
+
lookbehind: true,
|
|
16
|
+
greedy: true,
|
|
17
|
+
},
|
|
18
|
+
string: {
|
|
19
|
+
pattern: /(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,
|
|
20
|
+
lookbehind: true,
|
|
21
|
+
greedy: true,
|
|
22
|
+
},
|
|
23
|
+
comment: {
|
|
24
|
+
pattern: /\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,
|
|
25
|
+
greedy: true,
|
|
26
|
+
},
|
|
27
|
+
number: /-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,
|
|
28
|
+
punctuation: /[{}[\],]/,
|
|
29
|
+
operator: /:/,
|
|
30
|
+
boolean: /\b(?:false|true)\b/,
|
|
31
|
+
null: {
|
|
32
|
+
pattern: /\bnull\b/,
|
|
33
|
+
alias: 'keyword',
|
|
34
|
+
},
|
|
35
|
+
} satisfies SyntaxGrammarRaw;
|
|
36
|
+
|
|
37
|
+
syntax_styler.add_lang('json', grammar_json);
|
|
38
|
+
};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AddSyntaxGrammar,
|
|
3
|
+
SyntaxGrammarTokenRaw,
|
|
4
|
+
SyntaxGrammarValueRaw,
|
|
5
|
+
SyntaxStyler,
|
|
6
|
+
} from './syntax_styler.js';
|
|
7
|
+
|
|
8
|
+
interface LangDefinition {
|
|
9
|
+
aliases: Array<string>;
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface FenceType {
|
|
14
|
+
backticks: string;
|
|
15
|
+
suffix: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Helper to create fenced code block pattern for a language
|
|
20
|
+
*/
|
|
21
|
+
const create_fence_pattern = (
|
|
22
|
+
backticks: string,
|
|
23
|
+
aliases: Array<string>,
|
|
24
|
+
lang_id: string,
|
|
25
|
+
syntax_styler: SyntaxStyler,
|
|
26
|
+
): SyntaxGrammarTokenRaw => {
|
|
27
|
+
const aliases_pattern = aliases.join('|');
|
|
28
|
+
const pattern = new RegExp(
|
|
29
|
+
`^${backticks}(?:${aliases_pattern})[^\\n\\r]*(?:\\r?\\n|\\r)[\\s\\S]*?^${backticks}$`,
|
|
30
|
+
'm',
|
|
31
|
+
);
|
|
32
|
+
const code_fence_pattern = new RegExp(`^${backticks}[^\\n\\r]*|^${backticks}$`, 'm');
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
pattern,
|
|
36
|
+
greedy: true,
|
|
37
|
+
inside: {
|
|
38
|
+
code_fence: {
|
|
39
|
+
pattern: code_fence_pattern,
|
|
40
|
+
alias: 'punctuation',
|
|
41
|
+
},
|
|
42
|
+
[`lang_${lang_id}`]: {
|
|
43
|
+
pattern: /[\s\S]+/,
|
|
44
|
+
inside: syntax_styler.get_lang(lang_id),
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Helper to create catch-all fence pattern (unknown languages)
|
|
52
|
+
*/
|
|
53
|
+
const create_catchall_fence = (backticks: string): SyntaxGrammarTokenRaw => {
|
|
54
|
+
const pattern = new RegExp(`^${backticks}[^\\n\\r]*(?:\\r?\\n|\\r)[\\s\\S]*?^${backticks}$`, 'm');
|
|
55
|
+
const code_fence_pattern = new RegExp(`^${backticks}[^\\n\\r]*|^${backticks}$`, 'm');
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
pattern,
|
|
59
|
+
greedy: true,
|
|
60
|
+
inside: {
|
|
61
|
+
code_fence: {
|
|
62
|
+
pattern: code_fence_pattern,
|
|
63
|
+
alias: 'punctuation',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Helper to create md self-reference placeholder pattern
|
|
71
|
+
*/
|
|
72
|
+
const create_md_placeholder = (backticks: string): SyntaxGrammarTokenRaw => {
|
|
73
|
+
const pattern = new RegExp(
|
|
74
|
+
`^${backticks}(?:md|markdown)[^\\n\\r]*(?:\\r?\\n|\\r)[\\s\\S]*?^${backticks}$`,
|
|
75
|
+
'm',
|
|
76
|
+
);
|
|
77
|
+
const code_fence_pattern = new RegExp(`^${backticks}[^\\n\\r]*|^${backticks}$`, 'm');
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
pattern,
|
|
81
|
+
greedy: true,
|
|
82
|
+
inside: {
|
|
83
|
+
code_fence: {
|
|
84
|
+
pattern: code_fence_pattern,
|
|
85
|
+
alias: 'punctuation',
|
|
86
|
+
},
|
|
87
|
+
// lang_md will be added after registration
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Markdown grammar extending markup.
|
|
94
|
+
* Supports: headings, fenced code blocks (3/4/5 backticks with nesting), lists, blockquotes,
|
|
95
|
+
* bold, italic, strikethrough, inline code, and links.
|
|
96
|
+
*/
|
|
97
|
+
export const add_grammar_markdown: AddSyntaxGrammar = (syntax_styler) => {
|
|
98
|
+
// Language definitions with aliases
|
|
99
|
+
const langs: Array<LangDefinition> = [
|
|
100
|
+
{aliases: ['ts', 'typescript'], id: 'ts'},
|
|
101
|
+
{aliases: ['js', 'javascript'], id: 'js'},
|
|
102
|
+
{aliases: ['css'], id: 'css'},
|
|
103
|
+
{aliases: ['html', 'markup'], id: 'markup'},
|
|
104
|
+
{aliases: ['json'], id: 'json'},
|
|
105
|
+
{aliases: ['svelte'], id: 'svelte'},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
// Fence types: higher counts first (for proper precedence in tokenization)
|
|
109
|
+
const fence_types: Array<FenceType> = [
|
|
110
|
+
{backticks: '`````', suffix: '5tick'},
|
|
111
|
+
{backticks: '````', suffix: '4tick'},
|
|
112
|
+
{backticks: '```', suffix: ''},
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
// Build grammar dynamically
|
|
116
|
+
const grammar: Record<string, SyntaxGrammarValueRaw> = {};
|
|
117
|
+
const md_self_refs: Array<string> = []; // Track md patterns for later self-reference
|
|
118
|
+
|
|
119
|
+
// Generate fence patterns for each type
|
|
120
|
+
for (const {backticks, suffix} of fence_types) {
|
|
121
|
+
const token_suffix = suffix ? `_${suffix}` : '';
|
|
122
|
+
|
|
123
|
+
// md pattern (self-reference added after registration)
|
|
124
|
+
const md_key = `fenced_code${token_suffix}_md`;
|
|
125
|
+
grammar[md_key] = create_md_placeholder(backticks);
|
|
126
|
+
md_self_refs.push(md_key);
|
|
127
|
+
|
|
128
|
+
// Other language patterns (use first alias as token name for backward compatibility)
|
|
129
|
+
for (const {aliases, id} of langs) {
|
|
130
|
+
const token_name = aliases[0]; // Use first alias for token name
|
|
131
|
+
grammar[`fenced_code${token_suffix}_${token_name}`] = create_fence_pattern(
|
|
132
|
+
backticks,
|
|
133
|
+
aliases,
|
|
134
|
+
id,
|
|
135
|
+
syntax_styler,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Catch-all fence
|
|
140
|
+
grammar[`fenced_code${token_suffix}`] = create_catchall_fence(backticks);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Register markdown grammar first, then add self-references for md fences
|
|
144
|
+
const grammar_md = syntax_styler.add_extended_lang(
|
|
145
|
+
'markup',
|
|
146
|
+
'md',
|
|
147
|
+
{
|
|
148
|
+
...grammar,
|
|
149
|
+
|
|
150
|
+
// Headings (# through ######)
|
|
151
|
+
heading: {
|
|
152
|
+
pattern: /^#{1,6}\s+.+$/m,
|
|
153
|
+
inside: {
|
|
154
|
+
punctuation: {
|
|
155
|
+
pattern: /^#{1,6}/,
|
|
156
|
+
alias: 'heading_punctuation',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
// Blockquotes (> at line start)
|
|
162
|
+
blockquote: {
|
|
163
|
+
pattern: /^>\s*.+$/m,
|
|
164
|
+
inside: {
|
|
165
|
+
punctuation: {
|
|
166
|
+
pattern: /^>/,
|
|
167
|
+
alias: 'blockquote_punctuation',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
// Lists (* or - with any preceding whitespace)
|
|
173
|
+
list: {
|
|
174
|
+
pattern: /^\s*[-*]\s+.+$/m,
|
|
175
|
+
inside: {
|
|
176
|
+
punctuation: /^\s*[-*]/,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
// Inline elements
|
|
181
|
+
|
|
182
|
+
// Links [text](url)
|
|
183
|
+
link: {
|
|
184
|
+
pattern: /\[[^[\]\n\r]+\]\([^)\n\r]+\)/,
|
|
185
|
+
inside: {
|
|
186
|
+
link_text_wrapper: {
|
|
187
|
+
pattern: /^\[[^\]]+\]/,
|
|
188
|
+
inside: {
|
|
189
|
+
punctuation: {
|
|
190
|
+
pattern: /^\[|\]$/,
|
|
191
|
+
alias: 'link_punctuation',
|
|
192
|
+
},
|
|
193
|
+
link_text: /[^[\]]+/,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
url_wrapper: {
|
|
197
|
+
pattern: /\([^)]+\)$/,
|
|
198
|
+
inside: {
|
|
199
|
+
punctuation: {
|
|
200
|
+
pattern: /^\(|\)$/,
|
|
201
|
+
alias: 'link_punctuation',
|
|
202
|
+
},
|
|
203
|
+
url: /[^()]+/,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// Inline code `text`
|
|
210
|
+
inline_code: {
|
|
211
|
+
pattern: /`[^`\n\r]+`/,
|
|
212
|
+
alias: 'code',
|
|
213
|
+
inside: {
|
|
214
|
+
punctuation: {
|
|
215
|
+
pattern: /^`|`$/,
|
|
216
|
+
alias: 'code_punctuation',
|
|
217
|
+
},
|
|
218
|
+
content: /[^`]+/,
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
// Bold **text** or __text__
|
|
223
|
+
bold: [
|
|
224
|
+
{
|
|
225
|
+
// **text** - no asterisks inside
|
|
226
|
+
pattern: /\*\*[^\s*][^*]*[^\s*]\*\*|\*\*[^\s*]\*\*/,
|
|
227
|
+
inside: {
|
|
228
|
+
punctuation: /^\*\*|\*\*$/,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
// __text__ - at word boundaries, no underscores inside
|
|
233
|
+
pattern: /\b__[^\s_][^_]*[^\s_]__\b|\b__[^\s_]__\b/,
|
|
234
|
+
inside: {
|
|
235
|
+
punctuation: /^__|__$/,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
|
|
240
|
+
// Italic *text* or _text_
|
|
241
|
+
italic: [
|
|
242
|
+
{
|
|
243
|
+
// *text* - no asterisks inside
|
|
244
|
+
pattern: /\*[^\s*][^*]*[^\s*]\*|\*[^\s*]\*/,
|
|
245
|
+
inside: {
|
|
246
|
+
punctuation: /^\*|\*$/,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
// _text_ - at word boundaries, no underscores inside
|
|
251
|
+
pattern: /\b_[^\s_][^_]*[^\s_]_\b|\b_[^\s_]_\b/,
|
|
252
|
+
inside: {
|
|
253
|
+
punctuation: /^_|_$/,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
|
|
258
|
+
// Strikethrough ~~text~~
|
|
259
|
+
strikethrough: {
|
|
260
|
+
pattern: /~~[^\s~][^~]*[^\s~]~~|~~[^\s~]~~/,
|
|
261
|
+
inside: {
|
|
262
|
+
punctuation: /^~~|~~$/,
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
['markdown'],
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Add self-reference for markdown-in-markdown (all fence types)
|
|
270
|
+
// This must be done after registration to avoid circular dependency
|
|
271
|
+
const lang_md_inside = {
|
|
272
|
+
pattern: /[\s\S]+/,
|
|
273
|
+
inside: syntax_styler.get_lang('md'),
|
|
274
|
+
};
|
|
275
|
+
for (const key of md_self_refs) {
|
|
276
|
+
// After normalization, grammar values are arrays of SyntaxGrammarToken
|
|
277
|
+
// We need to add lang_md as a normalized array
|
|
278
|
+
const patterns = grammar_md[key]!;
|
|
279
|
+
// Manually normalize the lang_md pattern we're adding
|
|
280
|
+
const lang_md_token = {
|
|
281
|
+
pattern: lang_md_inside.pattern,
|
|
282
|
+
lookbehind: false,
|
|
283
|
+
greedy: false,
|
|
284
|
+
alias: [],
|
|
285
|
+
inside: lang_md_inside.inside,
|
|
286
|
+
};
|
|
287
|
+
patterns[0]!.inside!.lang_md = [lang_md_token];
|
|
288
|
+
}
|
|
289
|
+
};
|