jsmdcui 0.3.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 (258) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/LICENSE +22 -0
  3. package/README.md +285 -0
  4. package/package.json +17 -0
  5. package/project_structure.txt +35 -0
  6. package/runmd.mjs +240 -0
  7. package/runtime/colorschemes/atom-dark.micro +33 -0
  8. package/runtime/colorschemes/bubblegum.micro +31 -0
  9. package/runtime/colorschemes/cmc-16.micro +47 -0
  10. package/runtime/colorschemes/cmc-tc.micro +43 -0
  11. package/runtime/colorschemes/darcula.micro +34 -0
  12. package/runtime/colorschemes/default.micro +1 -0
  13. package/runtime/colorschemes/dracula-tc.micro +49 -0
  14. package/runtime/colorschemes/dukedark-tc.micro +38 -0
  15. package/runtime/colorschemes/dukelight-tc.micro +38 -0
  16. package/runtime/colorschemes/dukeubuntu-tc.micro +38 -0
  17. package/runtime/colorschemes/geany.micro +29 -0
  18. package/runtime/colorschemes/gotham.micro +29 -0
  19. package/runtime/colorschemes/gruvbox-tc.micro +29 -0
  20. package/runtime/colorschemes/gruvbox.micro +26 -0
  21. package/runtime/colorschemes/material-tc.micro +36 -0
  22. package/runtime/colorschemes/monokai-dark.micro +28 -0
  23. package/runtime/colorschemes/monokai.micro +34 -0
  24. package/runtime/colorschemes/one-dark.micro +39 -0
  25. package/runtime/colorschemes/railscast.micro +37 -0
  26. package/runtime/colorschemes/simple.micro +33 -0
  27. package/runtime/colorschemes/solarized-tc.micro +31 -0
  28. package/runtime/colorschemes/solarized.micro +30 -0
  29. package/runtime/colorschemes/sunny-day.micro +29 -0
  30. package/runtime/colorschemes/twilight.micro +40 -0
  31. package/runtime/colorschemes/zenburn.micro +30 -0
  32. package/runtime/help/actions.md +161 -0
  33. package/runtime/help/cdp.md +119 -0
  34. package/runtime/help/colors.md +421 -0
  35. package/runtime/help/commands.md +161 -0
  36. package/runtime/help/copypaste.md +149 -0
  37. package/runtime/help/defaultkeys.md +148 -0
  38. package/runtime/help/help.md +285 -0
  39. package/runtime/help/keybindings.md +355 -0
  40. package/runtime/help/linter.md +90 -0
  41. package/runtime/help/options.md +709 -0
  42. package/runtime/help/plugins.md +544 -0
  43. package/runtime/help/tutorial.md +112 -0
  44. package/runtime/jsplugins/cdp/cdp-server.js +1161 -0
  45. package/runtime/jsplugins/cdp/cdp.js +192 -0
  46. package/runtime/jsplugins/chapter/chapter.js +108 -0
  47. package/runtime/jsplugins/diff/diff.js +46 -0
  48. package/runtime/jsplugins/example/example.js +110 -0
  49. package/runtime/jsplugins/linter/linter.js +281 -0
  50. package/runtime/plugins/autoclose/autoclose.lua +75 -0
  51. package/runtime/plugins/ftoptions/ftoptions.lua +17 -0
  52. package/runtime/plugins/literate/README.md +5 -0
  53. package/runtime/plugins/literate/literate.lua +55 -0
  54. package/runtime/plugins/status/help/status.md +21 -0
  55. package/runtime/plugins/status/status.lua +62 -0
  56. package/runtime/syntax/LICENSE +22 -0
  57. package/runtime/syntax/PowerShell.yaml +128 -0
  58. package/runtime/syntax/README.md +63 -0
  59. package/runtime/syntax/ada.yaml +43 -0
  60. package/runtime/syntax/apacheconf.yaml +59 -0
  61. package/runtime/syntax/arduino.yaml +101 -0
  62. package/runtime/syntax/asciidoc.yaml +51 -0
  63. package/runtime/syntax/asm.yaml +123 -0
  64. package/runtime/syntax/ats.yaml +99 -0
  65. package/runtime/syntax/awk.yaml +44 -0
  66. package/runtime/syntax/b.yaml +87 -0
  67. package/runtime/syntax/bat.yaml +57 -0
  68. package/runtime/syntax/c.yaml +60 -0
  69. package/runtime/syntax/caddyfile.yaml +23 -0
  70. package/runtime/syntax/cake.yaml +7 -0
  71. package/runtime/syntax/clojure.yaml +38 -0
  72. package/runtime/syntax/cmake.yaml +42 -0
  73. package/runtime/syntax/coffeescript.yaml +56 -0
  74. package/runtime/syntax/colortest.yaml +19 -0
  75. package/runtime/syntax/conky.yaml +17 -0
  76. package/runtime/syntax/cpp.yaml +91 -0
  77. package/runtime/syntax/crontab.yaml +36 -0
  78. package/runtime/syntax/crystal.yaml +72 -0
  79. package/runtime/syntax/csharp.yaml +52 -0
  80. package/runtime/syntax/css.yaml +44 -0
  81. package/runtime/syntax/csx.yaml +8 -0
  82. package/runtime/syntax/cuda.yaml +68 -0
  83. package/runtime/syntax/cython.yaml +52 -0
  84. package/runtime/syntax/d.yaml +121 -0
  85. package/runtime/syntax/dart.yaml +46 -0
  86. package/runtime/syntax/default.yaml +10 -0
  87. package/runtime/syntax/dockerfile.yaml +36 -0
  88. package/runtime/syntax/dot.yaml +29 -0
  89. package/runtime/syntax/elixir.yaml +30 -0
  90. package/runtime/syntax/elm.yaml +38 -0
  91. package/runtime/syntax/erb.yaml +42 -0
  92. package/runtime/syntax/erlang.yaml +45 -0
  93. package/runtime/syntax/fish.yaml +48 -0
  94. package/runtime/syntax/forth.yaml +34 -0
  95. package/runtime/syntax/fortran.yaml +64 -0
  96. package/runtime/syntax/freebsd-kernel.yaml +14 -0
  97. package/runtime/syntax/fsharp.yaml +48 -0
  98. package/runtime/syntax/gdscript.yaml +61 -0
  99. package/runtime/syntax/gemini.yaml +19 -0
  100. package/runtime/syntax/gentoo-ebuild.yaml +48 -0
  101. package/runtime/syntax/gentoo-etc-portage.yaml +23 -0
  102. package/runtime/syntax/git-commit.yaml +35 -0
  103. package/runtime/syntax/git-config.yaml +14 -0
  104. package/runtime/syntax/git-rebase-todo.yaml +19 -0
  105. package/runtime/syntax/gleam.yaml +69 -0
  106. package/runtime/syntax/glsl.yaml +26 -0
  107. package/runtime/syntax/gnuplot.yaml +15 -0
  108. package/runtime/syntax/go.yaml +62 -0
  109. package/runtime/syntax/godoc.yaml +17 -0
  110. package/runtime/syntax/golo.yaml +73 -0
  111. package/runtime/syntax/gomod.yaml +31 -0
  112. package/runtime/syntax/graphql.yaml +47 -0
  113. package/runtime/syntax/groff.yaml +30 -0
  114. package/runtime/syntax/groovy.yaml +111 -0
  115. package/runtime/syntax/haml.yaml +16 -0
  116. package/runtime/syntax/hare.yaml +52 -0
  117. package/runtime/syntax/haskell.yaml +52 -0
  118. package/runtime/syntax/hc.yaml +52 -0
  119. package/runtime/syntax/html.yaml +70 -0
  120. package/runtime/syntax/html4.yaml +25 -0
  121. package/runtime/syntax/html5.yaml +25 -0
  122. package/runtime/syntax/ini.yaml +23 -0
  123. package/runtime/syntax/inputrc.yaml +14 -0
  124. package/runtime/syntax/java.yaml +37 -0
  125. package/runtime/syntax/javascript.yaml +124 -0
  126. package/runtime/syntax/jinja2.yaml +19 -0
  127. package/runtime/syntax/json.yaml +39 -0
  128. package/runtime/syntax/jsonnet.yaml +92 -0
  129. package/runtime/syntax/julia.yaml +57 -0
  130. package/runtime/syntax/justfile.yaml +40 -0
  131. package/runtime/syntax/keymap.yaml +27 -0
  132. package/runtime/syntax/kickstart.yaml +16 -0
  133. package/runtime/syntax/kotlin.yaml +66 -0
  134. package/runtime/syntax/kvlang.yaml +67 -0
  135. package/runtime/syntax/ledger.yaml +14 -0
  136. package/runtime/syntax/lfe.yaml +17 -0
  137. package/runtime/syntax/lilypond.yaml +26 -0
  138. package/runtime/syntax/lisp.yaml +17 -0
  139. package/runtime/syntax/log.yaml +92 -0
  140. package/runtime/syntax/lua.yaml +111 -0
  141. package/runtime/syntax/mail.yaml +25 -0
  142. package/runtime/syntax/makefile.yaml +38 -0
  143. package/runtime/syntax/man.yaml +12 -0
  144. package/runtime/syntax/markdown.yaml +49 -0
  145. package/runtime/syntax/mc.yaml +23 -0
  146. package/runtime/syntax/meson.yaml +51 -0
  147. package/runtime/syntax/micro.yaml +34 -0
  148. package/runtime/syntax/mpdconf.yaml +13 -0
  149. package/runtime/syntax/msbuild.yaml +6 -0
  150. package/runtime/syntax/nanorc.yaml +16 -0
  151. package/runtime/syntax/nftables.yaml +30 -0
  152. package/runtime/syntax/nginx.yaml +22 -0
  153. package/runtime/syntax/nim.yaml +27 -0
  154. package/runtime/syntax/nix.yaml +32 -0
  155. package/runtime/syntax/nu.yaml +114 -0
  156. package/runtime/syntax/objc.yaml +60 -0
  157. package/runtime/syntax/ocaml.yaml +43 -0
  158. package/runtime/syntax/octave.yaml +83 -0
  159. package/runtime/syntax/odin.yaml +64 -0
  160. package/runtime/syntax/pascal.yaml +45 -0
  161. package/runtime/syntax/patch.yaml +14 -0
  162. package/runtime/syntax/peg.yaml +16 -0
  163. package/runtime/syntax/perl.yaml +58 -0
  164. package/runtime/syntax/php.yaml +60 -0
  165. package/runtime/syntax/pkg-config.yaml +12 -0
  166. package/runtime/syntax/po.yaml +12 -0
  167. package/runtime/syntax/pony.yaml +37 -0
  168. package/runtime/syntax/pov.yaml +21 -0
  169. package/runtime/syntax/privoxy-action.yaml +14 -0
  170. package/runtime/syntax/privoxy-config.yaml +10 -0
  171. package/runtime/syntax/privoxy-filter.yaml +12 -0
  172. package/runtime/syntax/proto.yaml +40 -0
  173. package/runtime/syntax/prql.yaml +84 -0
  174. package/runtime/syntax/puppet.yaml +22 -0
  175. package/runtime/syntax/python2.yaml +60 -0
  176. package/runtime/syntax/python3.yaml +80 -0
  177. package/runtime/syntax/r.yaml +32 -0
  178. package/runtime/syntax/raku.yaml +42 -0
  179. package/runtime/syntax/reST.yaml +18 -0
  180. package/runtime/syntax/renpy.yaml +15 -0
  181. package/runtime/syntax/rpmspec.yaml +43 -0
  182. package/runtime/syntax/ruby.yaml +73 -0
  183. package/runtime/syntax/rust.yaml +78 -0
  184. package/runtime/syntax/sage.yaml +60 -0
  185. package/runtime/syntax/scad.yaml +53 -0
  186. package/runtime/syntax/scala.yaml +33 -0
  187. package/runtime/syntax/sed.yaml +13 -0
  188. package/runtime/syntax/sh.yaml +69 -0
  189. package/runtime/syntax/sls.yaml +15 -0
  190. package/runtime/syntax/smalltalk.yaml +55 -0
  191. package/runtime/syntax/solidity.yaml +41 -0
  192. package/runtime/syntax/sql.yaml +35 -0
  193. package/runtime/syntax/stata.yaml +67 -0
  194. package/runtime/syntax/svelte.yaml +27 -0
  195. package/runtime/syntax/swift.yaml +103 -0
  196. package/runtime/syntax/systemd.yaml +16 -0
  197. package/runtime/syntax/tcl.yaml +18 -0
  198. package/runtime/syntax/terraform.yaml +87 -0
  199. package/runtime/syntax/tex.yaml +32 -0
  200. package/runtime/syntax/toml.yaml +56 -0
  201. package/runtime/syntax/twig.yaml +55 -0
  202. package/runtime/syntax/typescript.yaml +49 -0
  203. package/runtime/syntax/v.yaml +80 -0
  204. package/runtime/syntax/vala.yaml +26 -0
  205. package/runtime/syntax/verilog.yaml +60 -0
  206. package/runtime/syntax/vhdl.yaml +37 -0
  207. package/runtime/syntax/vi.yaml +31 -0
  208. package/runtime/syntax/vue.yaml +64 -0
  209. package/runtime/syntax/xml.yaml +37 -0
  210. package/runtime/syntax/xresources.yaml +14 -0
  211. package/runtime/syntax/yaml.yaml +34 -0
  212. package/runtime/syntax/yum.yaml +12 -0
  213. package/runtime/syntax/zig.yaml +52 -0
  214. package/runtime/syntax/zscript.yaml +72 -0
  215. package/runtime/syntax/zsh.yaml +52 -0
  216. package/single-exe/README.md +80 -0
  217. package/single-exe/assetsHelper.js +90 -0
  218. package/single-exe/assetsLoader.mjs +85 -0
  219. package/single-exe/compiled.js +149 -0
  220. package/single-exe/entry.mjs +9 -0
  221. package/single-exe/packAssets.sh +7 -0
  222. package/src/buffer/backup.js +160 -0
  223. package/src/buffer/buffer.js +126 -0
  224. package/src/buffer/fixed3-codec.js +140 -0
  225. package/src/buffer/loc.js +38 -0
  226. package/src/buffer/message.js +29 -0
  227. package/src/config/clean.js +172 -0
  228. package/src/config/colorscheme.js +99 -0
  229. package/src/config/config.js +122 -0
  230. package/src/config/defaults.js +109 -0
  231. package/src/cui/rpc.mjs +259 -0
  232. package/src/cui/server.mjs +116 -0
  233. package/src/display/ansi-style.js +60 -0
  234. package/src/highlight/highlighter.js +243 -0
  235. package/src/highlight/parser.js +225 -0
  236. package/src/index.js +8009 -0
  237. package/src/lua/engine.js +71 -0
  238. package/src/platform/archive.js +50 -0
  239. package/src/platform/clipboard.js +278 -0
  240. package/src/platform/commands.js +137 -0
  241. package/src/plugins/js-bridge.js +983 -0
  242. package/src/plugins/manager.js +674 -0
  243. package/src/runtime/assets.js +90 -0
  244. package/src/runtime/compiled.js +25 -0
  245. package/src/runtime/encodings.js +10 -0
  246. package/src/runtime/registry.js +134 -0
  247. package/src/screen/cell-buffer.js +81 -0
  248. package/src/screen/events.js +263 -0
  249. package/src/screen/screen.js +142 -0
  250. package/src/screen/vt100.js +571 -0
  251. package/src/shell/shell.js +70 -0
  252. package/testapp.md +55 -0
  253. package/tests/backup.test.js +133 -0
  254. package/tests/cmphex3b64.js +95 -0
  255. package/tests/pty-demo.js +492 -0
  256. package/tests/wv-client.js +96 -0
  257. package/tui +5 -0
  258. package/wui +5 -0
@@ -0,0 +1,60 @@
1
+ const BASIC = {
2
+ black: 0,
3
+ red: 1,
4
+ green: 2,
5
+ yellow: 3,
6
+ blue: 4,
7
+ magenta: 5,
8
+ cyan: 6,
9
+ white: 7,
10
+ brightblack: 8,
11
+ lightblack: 8,
12
+ brightred: 9,
13
+ lightred: 9,
14
+ brightgreen: 10,
15
+ lightgreen: 10,
16
+ brightyellow: 11,
17
+ lightyellow: 11,
18
+ brightblue: 12,
19
+ lightblue: 12,
20
+ brightmagenta: 13,
21
+ lightmagenta: 13,
22
+ brightcyan: 14,
23
+ lightcyan: 14,
24
+ brightwhite: 15,
25
+ lightwhite: 15,
26
+ };
27
+
28
+ export function styleToAnsi(style = {}) {
29
+ const codes = [0];
30
+ if (style.bold) codes.push(1);
31
+ if (style.italic) codes.push(3);
32
+ if (style.underline) codes.push(4);
33
+ if (style.reverse) codes.push(7);
34
+ codes.push(...colorCodes(style.fg, false));
35
+ codes.push(...colorCodes(style.bg, true));
36
+ return `\x1b[${codes.join(";")}m`;
37
+ }
38
+
39
+ export function resetStyle() {
40
+ return "\x1b[0m";
41
+ }
42
+
43
+ export function styleResetTo(style = {}) {
44
+ return resetStyle() + styleToAnsi(style);
45
+ }
46
+
47
+ function colorCodes(color, background) {
48
+ if (color == null || color === "default") return [];
49
+ const base = background ? 48 : 38;
50
+ if (typeof color === "number") return [base, 5, color];
51
+ const value = String(color).toLowerCase();
52
+ if (/^#[0-9a-f]{6}$/.test(value)) {
53
+ const r = Number.parseInt(value.slice(1, 3), 16);
54
+ const g = Number.parseInt(value.slice(3, 5), 16);
55
+ const b = Number.parseInt(value.slice(5, 7), 16);
56
+ return [base, 2, r, g, b];
57
+ }
58
+ if (value in BASIC) return [base, 5, BASIC[value]];
59
+ return [];
60
+ }
@@ -0,0 +1,243 @@
1
+ export class Highlighter {
2
+ constructor(definition, definitions = []) {
3
+ this.definition = definition;
4
+ this.definitions = definitions;
5
+ this.rules = resolveRules(definition?.rules ?? [], definitions, new Set());
6
+ }
7
+
8
+ highlightLine(line, state = null, progress = null) {
9
+ const text = String(line);
10
+ const changes = new Map([[0, state?.group ?? "default"]]);
11
+ const nextState = state
12
+ ? scanRegion(changes, text, 0, state, this.rules, progress)
13
+ : scanTop(changes, text, 0, this.rules, progress);
14
+ return {
15
+ changes: normalizeChanges(changes, text.length),
16
+ state: nextState,
17
+ };
18
+ }
19
+
20
+ highlightString(input) {
21
+ const matches = [];
22
+ let state = null;
23
+ for (const line of String(input).split("\n")) {
24
+ const result = this.highlightLine(line, state);
25
+ matches.push(result.changes);
26
+ state = result.state;
27
+ }
28
+ return matches;
29
+ }
30
+ }
31
+
32
+ export function resolveRules(rules, definitions, seen) {
33
+ const out = [];
34
+ for (const rule of rules) {
35
+ if (rule.type === "include") {
36
+ if (seen.has(rule.include)) continue;
37
+ seen.add(rule.include);
38
+ const def = definitions.find((candidate) => candidate.filetype === rule.include);
39
+ if (def) out.push(...resolveRules(def.rules, definitions, seen));
40
+ } else if (rule.type === "region") {
41
+ out.push({ ...rule, rules: resolveRules(rule.rules ?? [], definitions, new Set(seen)) });
42
+ } else {
43
+ out.push(rule);
44
+ }
45
+ }
46
+ return out;
47
+ }
48
+
49
+ export function flattenRules(rules, definitions, seen) {
50
+ return resolveRules(rules, definitions, seen).flatMap((rule) => {
51
+ if (rule.type === "region") return flattenRules(rule.rules ?? [], definitions, new Set(seen));
52
+ return rule.type === "pattern" ? [rule] : [];
53
+ });
54
+ }
55
+
56
+ function scanTop(changes, line, pos, rules, progress = null) {
57
+ let cursor = pos;
58
+ while (cursor <= line.length) {
59
+ progress?.(cursor);
60
+ const regionStart = findFirstRegion(rules, line, cursor, progress);
61
+ if (!regionStart) {
62
+ applyPatterns(changes, line, cursor, line.length, rules, "default", progress);
63
+ changes.set(line.length, "default");
64
+ progress?.(line.length);
65
+ return null;
66
+ }
67
+
68
+ applyPatterns(changes, line, cursor, regionStart.start, rules, "default", progress);
69
+ changes.set(regionStart.start, regionStart.region.limitGroup ?? "default");
70
+ changes.set(regionStart.end, regionStart.region.group);
71
+ progress?.(regionStart.end);
72
+
73
+ const state = scanRegion(changes, line, regionStart.end, regionStart.region, rules, progress);
74
+ if (state) return state;
75
+ return null;
76
+ }
77
+ return null;
78
+ }
79
+
80
+ function scanRegion(changes, line, pos, region, rootRules, progress = null) {
81
+ let cursor = pos;
82
+ const rules = region.rules ?? [];
83
+ while (cursor <= line.length) {
84
+ progress?.(cursor);
85
+ const end = findMatch(region.end, line, cursor, region.skip, progress);
86
+ const nested = findFirstRegion(rules, line, cursor, progress);
87
+
88
+ if (end && (!nested || end.start <= nested.start)) {
89
+ applyPatterns(changes, line, cursor, end.start, rules, region.group, progress);
90
+ changes.set(end.start, region.limitGroup ?? region.group);
91
+ changes.set(end.end, parentGroup(region));
92
+ progress?.(end.end);
93
+ if (region.parent) return scanRegion(changes, line, end.end, region.parent, rootRules, progress);
94
+ return scanTop(changes, line, Math.max(end.end, end.start + 1), rootRules, progress);
95
+ }
96
+
97
+ if (nested) {
98
+ applyPatterns(changes, line, cursor, nested.start, rules, region.group, progress);
99
+ changes.set(nested.start, nested.region.limitGroup ?? region.group);
100
+ const child = { ...nested.region, parent: region };
101
+ changes.set(nested.end, child.group);
102
+ progress?.(nested.end);
103
+ const state = scanRegion(changes, line, nested.end, child, rootRules, progress);
104
+ if (state) return state;
105
+ cursor = Math.max(nested.end + 1, nextChangeAfter(changes, nested.end));
106
+ continue;
107
+ }
108
+
109
+ applyPatterns(changes, line, cursor, line.length, rules, region.group, progress);
110
+ changes.set(line.length, region.group);
111
+ progress?.(line.length);
112
+ return region;
113
+ }
114
+ return region;
115
+ }
116
+
117
+ function applyPatterns(changes, line, start, end, rules, fallbackGroup, progress = null) {
118
+ if (end < start) return;
119
+ const length = end - start;
120
+ if (length === 0) {
121
+ if (!changes.has(start)) changes.set(start, fallbackGroup);
122
+ return;
123
+ }
124
+ // Mirror Go's fullHighlights approach: later rules overwrite all positions in their match range,
125
+ // preventing earlier rules (e.g. symbol.operator matching "!") from leaking into spans that a
126
+ // later rule (e.g. the shebang comment pattern) should own entirely.
127
+ const fullHighlights = new Array(length).fill(null);
128
+ for (const rule of rules) {
129
+ if (rule.type !== "pattern" || !rule.regex) continue;
130
+ for (const match of findAllMatches(rule.regex, line, start, end, progress)) {
131
+ if (match.start === match.end) continue;
132
+ const lo = Math.max(match.start, start) - start;
133
+ const hi = Math.min(match.end, end) - start;
134
+ for (let i = lo; i < hi; i++) fullHighlights[i] = rule.group;
135
+ }
136
+ progress?.(end);
137
+ }
138
+ // Emit only color-change boundaries into the changes map (same as Go's transition loop).
139
+ let prev = changes.get(start) ?? fallbackGroup;
140
+ for (let i = 0; i < length; i++) {
141
+ const g = fullHighlights[i] ?? fallbackGroup;
142
+ if (g !== prev) { changes.set(start + i, g); prev = g; }
143
+ }
144
+ if (!changes.has(start)) changes.set(start, fullHighlights[0] ?? fallbackGroup);
145
+ if (!changes.has(end)) changes.set(end, fallbackGroup);
146
+ }
147
+
148
+ function findFirstRegion(rules, line, pos, progress = null) {
149
+ let best = null;
150
+ for (const region of rules.filter((rule) => rule.type === "region" && rule.start)) {
151
+ const match = findMatch(region.start, line, pos, region.skip, progress);
152
+ if (!match) continue;
153
+ if (!best || match.start < best.start) best = { ...match, region };
154
+ }
155
+ return best;
156
+ }
157
+
158
+ function findMatch(regex, line, pos, skip, progress = null) {
159
+ if (!regex) return null;
160
+ const masked = skip ? maskSkipped(line, skip) : line;
161
+ const global = globalize(regex);
162
+ global.lastIndex = pos;
163
+ let match;
164
+ if (!progress) {
165
+ match = global.exec(masked);
166
+ if (!match) return null;
167
+ const start = match.index ?? 0;
168
+ return { start, end: start + match[0].length };
169
+ }
170
+ while ((match = global.exec(masked)) !== null) {
171
+ const start = match.index ?? 0;
172
+ const end = start + match[0].length;
173
+ progress?.(start);
174
+ if (start >= pos) return { start, end };
175
+ if (match[0].length === 0) global.lastIndex++;
176
+ }
177
+ progress?.(line.length);
178
+ return null;
179
+ }
180
+
181
+ function findAllMatches(regex, line, start, end, progress = null) {
182
+ const out = [];
183
+ const global = globalize(regex);
184
+ global.lastIndex = start;
185
+ let match;
186
+ if (!progress) {
187
+ while ((match = global.exec(line)) !== null) {
188
+ const matchStart = match.index ?? 0;
189
+ if (matchStart >= end) break;
190
+ const matchEnd = matchStart + match[0].length;
191
+ if (match[0].length === 0) {
192
+ global.lastIndex++;
193
+ continue;
194
+ }
195
+ if (matchEnd > start) out.push({ start: Math.max(matchStart, start), end: Math.min(matchEnd, end) });
196
+ }
197
+ return out;
198
+ }
199
+ while ((match = global.exec(line)) !== null) {
200
+ const matchStart = match.index ?? 0;
201
+ const matchEnd = matchStart + match[0].length;
202
+ if (matchStart >= end) break;
203
+ progress?.(matchStart);
204
+ if (match[0].length === 0) {
205
+ global.lastIndex++;
206
+ continue;
207
+ }
208
+ if (matchEnd <= start || matchStart >= end) continue;
209
+ out.push({ start: Math.max(matchStart, start), end: Math.min(matchEnd, end) });
210
+ }
211
+ progress?.(end);
212
+ return out;
213
+ }
214
+
215
+ function maskSkipped(line, skip) {
216
+ return line.replace(globalize(skip), (value) => "\0".repeat(value.length));
217
+ }
218
+
219
+ function normalizeChanges(changes, lineLength) {
220
+ const normalized = new Map([...changes.entries()]
221
+ .filter(([index]) => index >= 0 && index <= lineLength)
222
+ .sort(([a], [b]) => a - b));
223
+ if (!normalized.has(0)) normalized.set(0, "default");
224
+ if (!normalized.has(lineLength)) normalized.set(lineLength, normalized.get([...normalized.keys()].at(-1)) ?? "default");
225
+ return new Map([...normalized.entries()].sort(([a], [b]) => a - b));
226
+ }
227
+
228
+ function parentGroup(region) {
229
+ return region.parent?.group ?? "default";
230
+ }
231
+
232
+ function nextChangeAfter(changes, index) {
233
+ for (const key of [...changes.keys()].sort((a, b) => a - b)) {
234
+ if (key > index) return key;
235
+ }
236
+ return index + 1;
237
+ }
238
+
239
+ function globalize(regex) {
240
+ const flags = new Set(regex.flags.split(""));
241
+ flags.add("g");
242
+ return new RegExp(regex.source, [...flags].join(""));
243
+ }
@@ -0,0 +1,225 @@
1
+ import { basename } from "node:path";
2
+
3
+ export class SyntaxHeader {
4
+ constructor({ filetype = "", filename = "", header = "", signature = "" } = {}) {
5
+ this.filetype = filetype;
6
+ this.filename = filename;
7
+ this.header = header;
8
+ this.signature = signature;
9
+ this.fileNameRegex = compileRegex(filename);
10
+ this.headerRegex = compileRegex(header);
11
+ this.signatureRegex = compileRegex(signature);
12
+ }
13
+
14
+ matchFileName(path) {
15
+ return !!this.fileNameRegex?.test(basename(path));
16
+ }
17
+
18
+ matchHeader(firstLine) {
19
+ return !!this.headerRegex?.test(firstLine);
20
+ }
21
+
22
+ hasSignature() {
23
+ return !!this.signatureRegex;
24
+ }
25
+
26
+ matchSignature(line) {
27
+ return !!this.signatureRegex?.test(line);
28
+ }
29
+ }
30
+
31
+ export class SyntaxDefinition {
32
+ constructor(header, source, text = "") {
33
+ this.header = header;
34
+ this.filetype = header.filetype;
35
+ this.source = source;
36
+ this.rules = parseRules(source.rules ?? []);
37
+ this.autocompleteWords = scanAutocompleteWordsFromText(text);
38
+ }
39
+ }
40
+
41
+ export async function loadSyntaxDefinitions(runtime) {
42
+ const headers = new Map();
43
+ const headerPromises = runtime.list(4).map(async (file) => {
44
+ try {
45
+ const text = await file.text();
46
+ headers.set(file.name, parseHeaderFile(text));
47
+ } catch {}
48
+ });
49
+ await Promise.allSettled(headerPromises);
50
+
51
+ const defPromises = runtime.list(1).map(async (file) => {
52
+ let text = "";
53
+ let activeFile = file;
54
+ let source = null;
55
+ let usedFallback = false;
56
+ try {
57
+ text = await file.text();
58
+ source = Bun.YAML.parse(text);
59
+ } catch (e) {
60
+ const fallback = file.real ? runtime.fallback?.(1, file.name) : null;
61
+ if (fallback) {
62
+ try {
63
+ text = await fallback.text();
64
+ source = Bun.YAML.parse(text);
65
+ activeFile = fallback;
66
+ usedFallback = true;
67
+ console.error("Failed to load user syntax yaml, using built-in fallback:", file.name);
68
+ } catch {}
69
+ }
70
+ }
71
+ const header = headers.get(activeFile.name) ?? (source ? parseHeaderYaml(source) : parseHeaderTextFallback(text, activeFile.name));
72
+ if (!source) {
73
+ console.error("Failed to load syntax yaml:", file.name);
74
+ console.error(" Will not highlight this kind of file");
75
+ console.error(" @ loadSyntaxDefinitions ");
76
+ } else if (usedFallback) {
77
+ // keep the fallback path visible in logs, but do not fail the load
78
+ }
79
+ return new SyntaxDefinition(header, source ?? { rules: [] }, text);
80
+ });
81
+
82
+ const definitions = await Promise.allSettled(defPromises);
83
+ return definitions
84
+ .filter((result) => result.status === "fulfilled")
85
+ .map((result) => result.value);
86
+ }
87
+
88
+ export function detectSyntax(definitions, { path = "", firstLine = "", lines = [] } = {}) {
89
+ for (const def of definitions) {
90
+ if (path && def.header.matchFileName(path)) return def;
91
+ }
92
+ for (const def of definitions) {
93
+ if (firstLine && def.header.matchHeader(firstLine)) return def;
94
+ }
95
+ for (const def of definitions) {
96
+ if (!def.header.hasSignature()) continue;
97
+ if (lines.some((line) => def.header.matchSignature(line))) return def;
98
+ }
99
+ return null;
100
+ }
101
+
102
+ export function parseHeaderFile(text) {
103
+ const [filetype = "", filename = "", header = "", signature = ""] = String(text).split(/\r?\n/);
104
+ return new SyntaxHeader({ filetype, filename, header, signature });
105
+ }
106
+
107
+ export function parseHeaderYaml(source) {
108
+ return new SyntaxHeader({
109
+ filetype: source?.filetype ?? "",
110
+ filename: source?.detect?.filename ?? "",
111
+ header: source?.detect?.header ?? "",
112
+ signature: source?.detect?.signature ?? "",
113
+ });
114
+ }
115
+
116
+ function parseHeaderTextFallback(text, fileName = "") {
117
+ const source = String(text ?? "");
118
+ const fallbackType = String(fileName).replace(/\.ya?ml$/i, "");
119
+ const filetype = rawYamlScalar(source.match(/^filetype:[ \t]*(.*)$/m)?.[1]) || fallbackType;
120
+ return new SyntaxHeader({
121
+ filetype,
122
+ filename: rawYamlDetectScalar(source, "filename"),
123
+ header: rawYamlDetectScalar(source, "header"),
124
+ signature: rawYamlDetectScalar(source, "signature"),
125
+ });
126
+ }
127
+
128
+ function rawYamlDetectScalar(text, key) {
129
+ const detect = text.match(/^detect:[ \t]*(?:#.*)?(?:\r?\n)((?:[ \t]+[^\n]*\r?\n?)*)/m)?.[1] ?? "";
130
+ return rawYamlScalar(detect.match(new RegExp(`^[ \t]+${key}:[ \t]*(.*)$`, "m"))?.[1]);
131
+ }
132
+
133
+ function rawYamlScalar(value) {
134
+ if (value == null) return "";
135
+ let out = String(value).trim();
136
+ if (!out) return "";
137
+ if (out.startsWith("\"") && out.endsWith("\"")) {
138
+ try { return JSON.parse(out); } catch { return out.slice(1, -1); }
139
+ }
140
+ if (out.startsWith("'") && out.endsWith("'")) return out.slice(1, -1).replaceAll("''", "'");
141
+ return out;
142
+ }
143
+
144
+ function scanAutocompleteWordsFromText(text) {
145
+ const source = String(text ?? "");
146
+ const words = [];
147
+ const seen = new Set();
148
+ let i = 0;
149
+ while (i < source.length) {
150
+ if (!isSyntaxWordChar(source[i])) { i++; continue; }
151
+ let j = i;
152
+ while (j < source.length && isSyntaxWordChar(source[j])) j++;
153
+ const word = source.slice(i, j);
154
+ if (!seen.has(word)) {
155
+ seen.add(word);
156
+ words.push(word);
157
+ }
158
+ i = j;
159
+ }
160
+ return words;
161
+ }
162
+
163
+ function isSyntaxWordChar(ch) {
164
+ if (!ch) return false;
165
+ const cp = ch.codePointAt(0);
166
+ if ((cp >= 65 && cp <= 90) || (cp >= 97 && cp <= 122) || (cp >= 48 && cp <= 57) || cp === 95) return true;
167
+ if (cp <= 127) return false;
168
+ return /\p{L}|\p{N}/u.test(ch);
169
+ }
170
+
171
+ function parseRules(rules) {
172
+ return rules.map((rule) => parseRule(rule)).filter(Boolean);
173
+ }
174
+
175
+ function parseRule(rule) {
176
+ if (typeof rule === "string") return { type: "include", include: rule };
177
+ if (!rule || typeof rule !== "object") return null;
178
+ const [[group, value]] = Object.entries(rule);
179
+ if (group === "include") return { type: "include", include: value };
180
+ if (typeof value === "string") return { type: "pattern", group, regex: compileRegex(value), source: value };
181
+ if (value && typeof value === "object") {
182
+ return {
183
+ type: "region",
184
+ group,
185
+ start: compileRegex(value.start),
186
+ end: compileRegex(value.end),
187
+ skip: compileRegex(value.skip),
188
+ limitGroup: value["limit-group"] ?? group,
189
+ rules: parseRules(value.rules ?? []),
190
+ source: value,
191
+ };
192
+ }
193
+ return null;
194
+ }
195
+
196
+ function compileRegex(pattern) {
197
+ if (!pattern) return null;
198
+ const translated = translateGoRegexp(pattern);
199
+ try {
200
+ return new RegExp(translated, "u");
201
+ } catch {
202
+ try {
203
+ return new RegExp(translated);
204
+ } catch {
205
+ return null;
206
+ }
207
+ }
208
+ }
209
+
210
+ function translateGoRegexp(pattern) {
211
+ return String(pattern)
212
+ .replaceAll("[[:alnum:]]", "[A-Za-z0-9]")
213
+ .replaceAll("[[:alpha:]]", "[A-Za-z]")
214
+ .replaceAll("[[:ascii:]]", "[\\x00-\\x7F]")
215
+ .replaceAll("[[:blank:]]", "[ \\t]")
216
+ .replaceAll("[[:cntrl:]]", "[\\x00-\\x1F\\x7F]")
217
+ .replaceAll("[[:digit:]]", "[0-9]")
218
+ .replaceAll("[[:graph:]]", "[!-~]")
219
+ .replaceAll("[[:lower:]]", "[a-z]")
220
+ .replaceAll("[[:print:]]", "[ -~]")
221
+ .replaceAll("[[:space:]]", "\\s")
222
+ .replaceAll("[[:upper:]]", "[A-Z]")
223
+ .replaceAll("[[:word:]]", "[A-Za-z0-9_]")
224
+ .replaceAll("[[:xdigit:]]", "[A-Fa-f0-9]");
225
+ }