utopia-project 0.37.6 → 0.38.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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/context/documentation-guidelines.md +12 -7
  4. data/lib/utopia/project/import_map.rb +1 -0
  5. data/lib/utopia/project/version.rb +1 -1
  6. data/pages/_page.xnode +8 -13
  7. data/public/_components/@socketry/syntax/Syntax/CodeElement.js +293 -0
  8. data/public/_components/@socketry/syntax/Syntax/Errors.js +52 -0
  9. data/public/_components/@socketry/syntax/Syntax/Language/apache.js +49 -0
  10. data/public/_components/@socketry/syntax/Syntax/Language/applescript.js +157 -0
  11. data/public/_components/@socketry/syntax/Syntax/Language/assembly.js +42 -0
  12. data/public/_components/@socketry/syntax/Syntax/Language/bash-script.js +108 -0
  13. data/public/_components/@socketry/syntax/Syntax/Language/bash.js +32 -0
  14. data/public/_components/@socketry/syntax/Syntax/Language/basic.js +232 -0
  15. data/public/_components/@socketry/syntax/Syntax/Language/c++.js +1 -0
  16. data/public/_components/@socketry/syntax/Syntax/Language/c.js +1 -0
  17. data/public/_components/@socketry/syntax/Syntax/Language/clang.js +201 -0
  18. data/public/_components/@socketry/syntax/Syntax/Language/cpp.js +1 -0
  19. data/public/_components/@socketry/syntax/Syntax/Language/csharp.js +166 -0
  20. data/public/_components/@socketry/syntax/Syntax/Language/css.js +244 -0
  21. data/public/_components/@socketry/syntax/Syntax/Language/diff.js +24 -0
  22. data/public/_components/@socketry/syntax/Syntax/Language/go.js +135 -0
  23. data/public/_components/@socketry/syntax/Syntax/Language/haskell.js +110 -0
  24. data/public/_components/@socketry/syntax/Syntax/Language/html.js +69 -0
  25. data/public/_components/@socketry/syntax/Syntax/Language/io.js +68 -0
  26. data/public/_components/@socketry/syntax/Syntax/Language/java.js +134 -0
  27. data/public/_components/@socketry/syntax/Syntax/Language/javascript.js +89 -0
  28. data/public/_components/@socketry/syntax/Syntax/Language/json.js +36 -0
  29. data/public/_components/@socketry/syntax/Syntax/Language/lisp.js +38 -0
  30. data/public/_components/@socketry/syntax/Syntax/Language/lua.js +87 -0
  31. data/public/_components/@socketry/syntax/Syntax/Language/markdown.js +112 -0
  32. data/public/_components/@socketry/syntax/Syntax/Language/nginx.js +37 -0
  33. data/public/_components/@socketry/syntax/Syntax/Language/objective-c.js +1 -0
  34. data/public/_components/@socketry/syntax/Syntax/Language/ocaml.js +225 -0
  35. data/public/_components/@socketry/syntax/Syntax/Language/pascal.js +166 -0
  36. data/public/_components/@socketry/syntax/Syntax/Language/patch.js +2 -0
  37. data/public/_components/@socketry/syntax/Syntax/Language/perl5.js +317 -0
  38. data/public/_components/@socketry/syntax/Syntax/Language/php-script.js +112 -0
  39. data/public/_components/@socketry/syntax/Syntax/Language/php.js +18 -0
  40. data/public/_components/@socketry/syntax/Syntax/Language/plain.js +20 -0
  41. data/public/_components/@socketry/syntax/Syntax/Language/protobuf.js +77 -0
  42. data/public/_components/@socketry/syntax/Syntax/Language/python.js +208 -0
  43. data/public/_components/@socketry/syntax/Syntax/Language/ruby.js +124 -0
  44. data/public/_components/@socketry/syntax/Syntax/Language/scala.js +81 -0
  45. data/public/_components/@socketry/syntax/Syntax/Language/smalltalk.js +30 -0
  46. data/public/_components/@socketry/syntax/Syntax/Language/sql.js +865 -0
  47. data/public/_components/@socketry/syntax/Syntax/Language/super-collider.js +70 -0
  48. data/public/_components/@socketry/syntax/Syntax/Language/swift.js +176 -0
  49. data/public/_components/@socketry/syntax/Syntax/Language/xml.js +76 -0
  50. data/public/_components/@socketry/syntax/Syntax/Language/xrb.js +33 -0
  51. data/public/_components/@socketry/syntax/Syntax/Language/yaml.js +29 -0
  52. data/public/_components/@socketry/syntax/Syntax/Language.js +276 -0
  53. data/public/_components/@socketry/syntax/Syntax/Loader.js +78 -0
  54. data/public/_components/@socketry/syntax/Syntax/Match.js +546 -0
  55. data/public/_components/@socketry/syntax/Syntax/Rule.js +306 -0
  56. data/public/_components/@socketry/syntax/Syntax.js +356 -0
  57. data/public/_components/@socketry/syntax/license.md +21 -0
  58. data/public/_components/@socketry/syntax/package.json +43 -0
  59. data/public/_components/@socketry/syntax/readme.md +162 -0
  60. data/public/_components/@socketry/syntax/themes/base/apache.css +1 -0
  61. data/public/_components/@socketry/syntax/themes/base/applescript.css +1 -0
  62. data/public/_components/@socketry/syntax/themes/base/assembly.css +1 -0
  63. data/public/_components/@socketry/syntax/themes/base/bash.css +1 -0
  64. data/public/_components/@socketry/syntax/themes/base/basic.css +1 -0
  65. data/public/_components/@socketry/syntax/themes/base/c.css +1 -0
  66. data/public/_components/@socketry/syntax/themes/base/clang.css +0 -0
  67. data/public/_components/@socketry/syntax/themes/base/csharp.css +1 -0
  68. data/public/_components/@socketry/syntax/themes/base/css.css +22 -0
  69. data/public/_components/@socketry/syntax/themes/base/diff.css +48 -0
  70. data/public/_components/@socketry/syntax/themes/base/go.css +1 -0
  71. data/public/_components/@socketry/syntax/themes/base/haskell.css +1 -0
  72. data/public/_components/@socketry/syntax/themes/base/html.css +1 -0
  73. data/public/_components/@socketry/syntax/themes/base/io.css +1 -0
  74. data/public/_components/@socketry/syntax/themes/base/java.css +1 -0
  75. data/public/_components/@socketry/syntax/themes/base/javascript.css +1 -0
  76. data/public/_components/@socketry/syntax/themes/base/json.css +41 -0
  77. data/public/_components/@socketry/syntax/themes/base/lisp.css +1 -0
  78. data/public/_components/@socketry/syntax/themes/base/lua.css +1 -0
  79. data/public/_components/@socketry/syntax/themes/base/markdown.css +16 -0
  80. data/public/_components/@socketry/syntax/themes/base/nginx.css +1 -0
  81. data/public/_components/@socketry/syntax/themes/base/ocaml.css +1 -0
  82. data/public/_components/@socketry/syntax/themes/base/pascal.css +1 -0
  83. data/public/_components/@socketry/syntax/themes/base/perl5.css +1 -0
  84. data/public/_components/@socketry/syntax/themes/base/php-script.css +1 -0
  85. data/public/_components/@socketry/syntax/themes/base/php.css +1 -0
  86. data/public/_components/@socketry/syntax/themes/base/plain.css +1 -0
  87. data/public/_components/@socketry/syntax/themes/base/protobuf.css +1 -0
  88. data/public/_components/@socketry/syntax/themes/base/python.css +1 -0
  89. data/public/_components/@socketry/syntax/themes/base/ruby.css +23 -0
  90. data/public/_components/@socketry/syntax/themes/base/scala.css +3 -0
  91. data/public/_components/@socketry/syntax/themes/base/smalltalk.css +1 -0
  92. data/public/_components/@socketry/syntax/themes/base/sql.css +1 -0
  93. data/public/_components/@socketry/syntax/themes/base/super-collider.css +33 -0
  94. data/public/_components/@socketry/syntax/themes/base/swift.css +1 -0
  95. data/public/_components/@socketry/syntax/themes/base/syntax.css +63 -0
  96. data/public/_components/@socketry/syntax/themes/base/xml.css +1 -0
  97. data/public/_components/@socketry/syntax/themes/base/xrb.css +29 -0
  98. data/public/_components/@socketry/syntax/themes/base/yaml.css +1 -0
  99. data/public/_components/@socketry/syntax/themes/theming.md +233 -0
  100. data/public/_static/sidebar.js +55 -22
  101. data/public/_static/site.css +0 -4
  102. data.tar.gz.sig +0 -0
  103. metadata +94 -1
  104. metadata.gz.sig +0 -0
@@ -0,0 +1,70 @@
1
+ import {Language} from '../Language.js';
2
+ import {Rule} from '../Rule.js';
3
+
4
+ const language = new Language('super-collider');
5
+
6
+ // Keywords
7
+ language.push(['const', 'arg', 'classvar', 'var'], {type: 'keyword'});
8
+
9
+ // Operators
10
+ language.push(
11
+ ['`', '+', '@', ':', '*', '/', '-', '&', '|', '~', '!', '%', '<', '=', '>'],
12
+ {
13
+ type: 'operator'
14
+ }
15
+ );
16
+
17
+ // Constants/values
18
+ language.push(
19
+ [
20
+ 'thisFunctionDef',
21
+ 'thisFunction',
22
+ 'thisMethod',
23
+ 'thisProcess',
24
+ 'thisThread',
25
+ 'this',
26
+ 'super',
27
+ 'true',
28
+ 'false',
29
+ 'nil',
30
+ 'inf'
31
+ ],
32
+ {type: 'constant'}
33
+ );
34
+
35
+ // CamelCase class/type names
36
+ language.push(Rule.camelCaseType);
37
+
38
+ // Single Characters like $a or $\n
39
+ language.push({pattern: /\$(\\)?.\b/, type: 'constant'});
40
+
41
+ // Symbols: \symbol and 'quotedSymbol'
42
+ language.push({pattern: /\\[a-z_][a-z0-9_]*/i, type: 'symbol'});
43
+ language.push({pattern: /'[^']+'/, type: 'symbol'});
44
+
45
+ // Comments and links
46
+ language.push(Rule.cStyleComment);
47
+ language.push(Rule.cppStyleComment);
48
+ language.push(Rule.webLink);
49
+
50
+ // Strings: SuperCollider uses double quotes for Strings
51
+ // Single quotes denote Symbols and are handled above.
52
+ language.push(Rule.doubleQuotedString);
53
+ language.push(Rule.stringEscape);
54
+
55
+ // Numbers
56
+ language.push(Rule.decimalNumber);
57
+ language.push(Rule.hexNumber);
58
+
59
+ // Method calls .method and c-style function pattern (identifier()
60
+ language.push({
61
+ pattern: /(?:\.)([a-z_][a-z0-9_]*)/i,
62
+ matches: Rule.extractMatches({type: 'function'})
63
+ });
64
+ language.push(Rule.cStyleFunction);
65
+
66
+ export default function register(syntax) {
67
+ syntax.register('super-collider', language);
68
+ // Alias: sc
69
+ syntax.alias('super-collider', ['sc']);
70
+ }
@@ -0,0 +1,176 @@
1
+ import {Language} from '../Language.js';
2
+ import {Rule} from '../Rule.js';
3
+
4
+ const language = new Language('swift');
5
+
6
+ const keywords = [
7
+ 'associatedtype',
8
+ 'class',
9
+ 'deinit',
10
+ 'enum',
11
+ 'extension',
12
+ 'fileprivate',
13
+ 'func',
14
+ 'import',
15
+ 'init',
16
+ 'inout',
17
+ 'internal',
18
+ 'let',
19
+ 'operator',
20
+ 'private',
21
+ 'protocol',
22
+ 'static',
23
+ 'struct',
24
+ 'subscript',
25
+ 'typealias',
26
+ 'var',
27
+ 'break',
28
+ 'case',
29
+ 'continue',
30
+ 'default',
31
+ 'defer',
32
+ 'do',
33
+ 'else',
34
+ 'fallthrough',
35
+ 'for',
36
+ 'guard',
37
+ 'if',
38
+ 'in',
39
+ 'repeat',
40
+ 'return',
41
+ 'switch',
42
+ 'where',
43
+ 'while',
44
+ 'as',
45
+ 'catch',
46
+ 'is',
47
+ 'rethrows',
48
+ 'throw',
49
+ 'throws',
50
+ 'try',
51
+ '_',
52
+ '#available',
53
+ '#colorLiteral',
54
+ '#column',
55
+ '#else',
56
+ '#elseif',
57
+ '#endif',
58
+ '#file',
59
+ '#fileLiteral',
60
+ '#function',
61
+ '#if',
62
+ '#imageLiteral',
63
+ '#line',
64
+ '#selector',
65
+ '#sourceLocation',
66
+ 'associativity',
67
+ 'convenience',
68
+ 'dynamic',
69
+ 'didSet',
70
+ 'final',
71
+ 'get',
72
+ 'infix',
73
+ 'indirect',
74
+ 'lazy',
75
+ 'left',
76
+ 'mutating',
77
+ 'none',
78
+ 'nonmutating',
79
+ 'optional',
80
+ 'override',
81
+ 'postfix',
82
+ 'precedence',
83
+ 'prefix',
84
+ 'Protocol',
85
+ 'required',
86
+ 'right',
87
+ 'set',
88
+ 'Type',
89
+ 'unowned',
90
+ 'weak',
91
+ 'willSet'
92
+ ];
93
+
94
+ // Operators - ordered longest to shortest
95
+ const operators = [
96
+ '...',
97
+ '..<',
98
+ '->',
99
+ '+',
100
+ '*',
101
+ '/',
102
+ '-',
103
+ '&',
104
+ '|',
105
+ '~',
106
+ '!',
107
+ '%',
108
+ '<',
109
+ '=',
110
+ '>',
111
+ '(',
112
+ ')',
113
+ '{',
114
+ '}',
115
+ '[',
116
+ ']',
117
+ '.',
118
+ ',',
119
+ ':',
120
+ ';',
121
+ '@',
122
+ '#',
123
+ '`',
124
+ '?'
125
+ ];
126
+
127
+ const values = ['self', 'super', 'true', 'false', 'nil'];
128
+ const access = ['fileprivate', 'open', 'private', 'public'];
129
+
130
+ // Access modifiers
131
+ language.push(access, {type: 'access'});
132
+
133
+ // Built-in values
134
+ language.push(values, {type: 'constant'});
135
+
136
+ // Backtick identifiers
137
+ language.push({
138
+ pattern: /`[^`]+`/g,
139
+ type: 'identifier'
140
+ });
141
+
142
+ // String interpolation \(...) - needs special handling
143
+ language.push({
144
+ pattern: /\\\(([^)]*)\)/g,
145
+ klass: 'string'
146
+ });
147
+
148
+ // Types (CamelCase)
149
+ language.push(Rule.camelCaseType);
150
+
151
+ // Keywords
152
+ language.push(keywords, {type: 'keyword'});
153
+
154
+ // Operators
155
+ language.push(operators, {type: 'operator'});
156
+
157
+ // Comments
158
+ language.push(Rule.cStyleComment);
159
+ language.push(Rule.cppStyleComment);
160
+ language.push(Rule.webLink);
161
+
162
+ // Strings
163
+ language.push(Rule.singleQuotedString);
164
+ language.push(Rule.doubleQuotedString);
165
+ language.push(Rule.stringEscape);
166
+
167
+ // Numbers
168
+ language.push(Rule.decimalNumber);
169
+ language.push(Rule.hexNumber);
170
+
171
+ // Functions
172
+ language.push(Rule.cStyleFunction);
173
+
174
+ export default function register(syntax) {
175
+ syntax.register('swift', language);
176
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * XML language syntax highlighting
3
+ * Supports XML tags, attributes, entities, CDATA sections, and comments
4
+ */
5
+ import {Language} from '../Language.js';
6
+ import {Rule} from '../Rule.js';
7
+
8
+ // XML entity pattern: &entity;
9
+ const xmlEntity = {pattern: /&\w+;/, type: 'entity'};
10
+
11
+ // XML percent escape pattern for URL encoding
12
+ const xmlPercentEscape = {
13
+ pattern: /(%[0-9a-f]{2})/i,
14
+ type: 'percent-escape',
15
+ only: ['string']
16
+ };
17
+
18
+ // Create xml-tag language for highlighting XML tag internals
19
+ const xmlTagLanguage = new Language('xml-tag');
20
+
21
+ // Match opening/closing tags with optional namespace
22
+ // Pattern: <tag>, <ns:tag>, </tag>, </ns:tag>, <tag/>, etc.
23
+ xmlTagLanguage.push({
24
+ pattern: /<\/?((?:[^:\s>]+:)?)([^\s>]+)(\s[^>]*)?\/?>/,
25
+ matches: Rule.extractMatches({type: 'namespace'}, {type: 'tag-name'})
26
+ });
27
+
28
+ // Match attributes with values
29
+ // Pattern: attribute="value", attribute='value', attribute=value
30
+ xmlTagLanguage.push({
31
+ pattern: /([^=\s]+)=(".*?"|'.*?'|[^\s>]+)/,
32
+ matches: Rule.extractMatches(
33
+ {type: 'attribute', only: ['tag']},
34
+ {type: 'string', only: ['tag']}
35
+ )
36
+ });
37
+
38
+ xmlTagLanguage.push(xmlEntity);
39
+ xmlTagLanguage.push(xmlPercentEscape);
40
+ xmlTagLanguage.push(Rule.singleQuotedString);
41
+ xmlTagLanguage.push(Rule.doubleQuotedString);
42
+
43
+ // Create main XML language
44
+
45
+ const language = new Language('xml');
46
+
47
+ // Match CDATA sections
48
+ // Pattern: <![CDATA[content]]>
49
+ language.push({
50
+ pattern: /(<!(\[CDATA\[)([\s\S]*?)(\]\])>)/m,
51
+ matches: Rule.extractMatches(
52
+ {type: 'cdata', allow: ['cdata-content', 'cdata-tag']},
53
+ {type: 'cdata-tag'},
54
+ {type: 'cdata-content'},
55
+ {type: 'cdata-tag'}
56
+ )
57
+ });
58
+
59
+ // XML comments
60
+ language.push(Rule.xmlComment);
61
+
62
+ // Match all XML tags and delegate to xml-tag language
63
+ language.push({
64
+ pattern: /<[^>\-\s]([^>'"!\/;\?@\[\]^`\{\}\|]|"[^"]*"|'[^']')*[\/?]?>/,
65
+ language: 'xml-tag'
66
+ });
67
+
68
+ language.push(xmlEntity);
69
+ language.push(xmlPercentEscape);
70
+ language.push(Rule.webLink);
71
+
72
+ // Register both languages
73
+ export default function register(syntax) {
74
+ syntax.register('xml-tag', xmlTagLanguage);
75
+ syntax.register('xml', language);
76
+ }
@@ -0,0 +1,33 @@
1
+ import {Language} from '../Language.js';
2
+ import {Rule} from '../Rule.js';
3
+
4
+ const language = new Language('xrb');
5
+
6
+ // Embedded Ruby processing instruction: <?r ... ?>
7
+ language.push({
8
+ pattern: /(<\?r)([\s\S]*?)(\?>)/m,
9
+ matches: Rule.extractMatches(
10
+ {type: 'keyword'},
11
+ {language: 'ruby'},
12
+ {type: 'keyword'}
13
+ )
14
+ });
15
+
16
+ // Ruby interpolation within text: #{ ... }
17
+ language.push({
18
+ pattern: /(#{)([\s\S]*?)(})/m,
19
+ matches: Rule.extractMatches(
20
+ {type: 'keyword'},
21
+ {language: 'ruby'},
22
+ {type: 'keyword'}
23
+ )
24
+ });
25
+
26
+ // Derive XML so tags/attributes/entities are highlighted around embedded Ruby
27
+ language.derives('xml');
28
+
29
+ export default function register(syntax) {
30
+ syntax.register('xrb', language);
31
+ // Alias 'trenni' if used historically
32
+ syntax.alias('xrb', ['trenni']);
33
+ }
@@ -0,0 +1,29 @@
1
+ import {Language} from '../Language.js';
2
+ import {Rule} from '../Rule.js';
3
+
4
+ const language = new Language('yaml');
5
+
6
+ language.push({
7
+ pattern: /^\s*#.*$/m,
8
+ type: 'comment',
9
+ allow: ['href']
10
+ });
11
+
12
+ language.push(Rule.singleQuotedString);
13
+ language.push(Rule.doubleQuotedString);
14
+
15
+ language.push({
16
+ pattern: /(&|\*)[a-z0-9]+/i,
17
+ type: 'constant'
18
+ });
19
+
20
+ language.push({
21
+ pattern: /(.*?):/i,
22
+ matches: Rule.extractMatches({type: 'keyword'})
23
+ });
24
+
25
+ language.push(Rule.webLink);
26
+
27
+ export default function register(syntax) {
28
+ syntax.register('yaml', language);
29
+ }
@@ -0,0 +1,276 @@
1
+ import {Match} from './Match.js';
2
+ import {Rule} from './Rule.js';
3
+ import {LanguageNotFoundError, RuleApplyError} from './Errors.js';
4
+
5
+ export class Language {
6
+ // Public properties that define the language
7
+ name;
8
+
9
+ // A list of processes that may be run after extracting matches.
10
+ processes = {};
11
+
12
+ // A sequential list of rules for extracting matches.
13
+ #rules = [];
14
+
15
+ // A list of all the parent languages this language derives from.
16
+ #parents = [];
17
+
18
+ constructor(name = null) {
19
+ // The primary class of this language. Must be unique.
20
+ this.name = name;
21
+ }
22
+
23
+ /**
24
+ * Add a parent to the language. This language should be loaded as a dependency.
25
+ */
26
+ derives(name) {
27
+ this.#parents.push(name);
28
+ this.#rules.push({
29
+ apply: async function (syntax, rule, text) {
30
+ const parentLanguage = await syntax.getLanguage(name);
31
+ return parentLanguage.getMatches(syntax, text);
32
+ }
33
+ });
34
+ }
35
+
36
+ /**
37
+ * Return an array of all classes that the language consists of.
38
+ */
39
+ async allNames(syntax) {
40
+ const names = [this.name];
41
+
42
+ for (const parent of this.#parents) {
43
+ if (syntax) {
44
+ const parentLanguage = await syntax.getLanguage(parent);
45
+ if (parentLanguage) names.push(...(await parentLanguage.allNames(syntax)));
46
+ }
47
+ }
48
+
49
+ return names;
50
+ }
51
+
52
+ /**
53
+ * Push a rule onto the language.
54
+ */
55
+ push(...args) {
56
+ if (Array.isArray(args[0])) {
57
+ const patterns = args[0];
58
+ const rule = args[1];
59
+
60
+ let all = '(';
61
+
62
+ for (let i = 0; i < patterns.length; i += 1) {
63
+ if (i > 0) all += '|';
64
+
65
+ const p = patterns[i];
66
+
67
+ if (p instanceof RegExp) {
68
+ all += p.source;
69
+ } else {
70
+ all += Rule.convertStringToTokenPattern(p, true);
71
+ }
72
+ }
73
+
74
+ all += ')';
75
+
76
+ this.push({
77
+ ...rule,
78
+ pattern: new RegExp(all, rule.options)
79
+ });
80
+ } else {
81
+ const rule = args[0];
82
+
83
+ // Normalize and validate the rule
84
+ const normalized = Rule.normalizeRule(rule, this);
85
+
86
+ if (
87
+ typeof normalized.pattern === 'undefined' ||
88
+ normalized.pattern instanceof RegExp
89
+ ) {
90
+ this.#rules.push(normalized);
91
+ } else {
92
+ console.error('Syntax Error: Malformed rule: ', rule);
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Get matches for a specific rule.
99
+ */
100
+ async getMatchesForRule(syntax, text, rule) {
101
+ let matches = [];
102
+
103
+ // Short circuit (user defined) function:
104
+ if (typeof rule.apply !== 'undefined') {
105
+ try {
106
+ const result = rule.apply(syntax, rule, text);
107
+ matches = result instanceof Promise ? await result : result;
108
+ } catch (error) {
109
+ if (syntax?.defaultOptions?.strict) {
110
+ // If the underlying error is a strict language resolution error, surface it directly:
111
+ if (error instanceof LanguageNotFoundError) throw error;
112
+ throw new RuleApplyError(rule, {cause: error});
113
+ } else {
114
+ console.warn('Syntax Warning: Rule apply failed:', rule, error);
115
+ matches = [];
116
+ }
117
+ }
118
+ }
119
+
120
+ if (rule.debug) {
121
+ console.log('Syntax matches:', rule, text, matches);
122
+ }
123
+
124
+ return matches;
125
+ }
126
+
127
+ /**
128
+ * Get the rule for a specific type.
129
+ */
130
+ getRuleForType(type) {
131
+ for (const rule of this.#rules) {
132
+ if (rule.type === type) {
133
+ return rule;
134
+ }
135
+ }
136
+
137
+ return null;
138
+ }
139
+
140
+ /**
141
+ * Get all matches for the given text.
142
+ */
143
+ async getMatches(syntax, text) {
144
+ const matches = [];
145
+
146
+ for (const rule of this.#rules) {
147
+ matches.push(...(await this.getMatchesForRule(syntax, text, rule)));
148
+ }
149
+
150
+ return matches;
151
+ }
152
+
153
+ /**
154
+ * A helper function for building a tree from a specific rule.
155
+ */
156
+ static async buildTree(syntax, rule, text, offset, additionalMatches) {
157
+ let language;
158
+ try {
159
+ language = await syntax.getLanguage(rule.language);
160
+ } catch (error) {
161
+ if (syntax?.defaultOptions?.strict) {
162
+ // Normalize any load error to LanguageNotFoundError for consistency:
163
+ throw new LanguageNotFoundError(rule.language, {cause: error});
164
+ } else {
165
+ console.warn(
166
+ `Syntax Warning: Failed to load language '${rule.language}' for building tree:`,
167
+ error
168
+ );
169
+ return new Match(offset || 0, text.length, rule, text);
170
+ }
171
+ }
172
+
173
+ if (!language) {
174
+ if (syntax?.defaultOptions?.strict) {
175
+ throw new LanguageNotFoundError(rule.language);
176
+ } else {
177
+ console.warn(
178
+ `Syntax Warning: Language '${rule.language}' not found for building tree.`
179
+ );
180
+ // Return a simple match without children as fallback
181
+ return new Match(offset || 0, text.length, rule, text);
182
+ }
183
+ }
184
+
185
+ const match = await language.buildTree(
186
+ syntax,
187
+ text,
188
+ offset,
189
+ additionalMatches
190
+ );
191
+
192
+ Object.assign(match.expression, rule);
193
+
194
+ return match;
195
+ }
196
+
197
+ /**
198
+ * Build a syntax tree from a given block of text.
199
+ */
200
+ async buildTree(syntax, text, offset, additionalMatches) {
201
+ offset = offset || 0;
202
+
203
+ // Fixes code that uses \r\n for line endings
204
+ text = text.replace(/\r/g, '');
205
+
206
+ const matches = await this.getMatches(syntax, text);
207
+
208
+ // Shift matches if offset is provided
209
+ if (offset && offset > 0) {
210
+ for (const match of matches) {
211
+ match.shift(offset);
212
+ }
213
+ }
214
+
215
+ const top = new Match(
216
+ offset,
217
+ text.length,
218
+ {type: (await this.allNames(syntax)).join(' '), allow: '*', owner: this},
219
+ text
220
+ );
221
+
222
+ // This sort is absolutely key to the tree insertion algorithm
223
+ matches.sort(Match.sort);
224
+
225
+ for (const match of matches) {
226
+ top.insertAtEnd(match);
227
+ }
228
+
229
+ if (additionalMatches) {
230
+ for (const match of additionalMatches) {
231
+ top.insert(match, true);
232
+ }
233
+ }
234
+
235
+ top.complete = true;
236
+
237
+ return top;
238
+ }
239
+
240
+ /**
241
+ * Build a syntax tree and process it into HTML.
242
+ */
243
+ async process(syntax, text, options) {
244
+ const top = await this.buildTree(syntax, text, 0);
245
+
246
+ const lines = top.splitLines();
247
+
248
+ const html = document.createElement('code');
249
+ html.className = 'syntax highlighted';
250
+ html.setAttribute('part', 'code');
251
+
252
+ for (const line of lines) {
253
+ const processedLine = line.reduce(null, (container, match) => {
254
+ if (match.expression) {
255
+ if (match.expression.process) {
256
+ container = match.expression.process(container, match, options);
257
+ }
258
+
259
+ if (match.expression.owner) {
260
+ const process = match.expression.owner.processes[match.expression.type];
261
+ if (process) {
262
+ container = process(container, match, options);
263
+ }
264
+ }
265
+ }
266
+ return container;
267
+ });
268
+
269
+ html.appendChild(processedLine);
270
+ }
271
+
272
+ return html;
273
+ }
274
+ }
275
+
276
+ export default Language;