@pentoshi/clai 3.6.0 → 3.7.3

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 (322) hide show
  1. package/dist/agent/compaction-summary.d.ts +17 -0
  2. package/dist/agent/compaction-summary.js +64 -0
  3. package/dist/agent/compaction-summary.js.map +1 -0
  4. package/dist/agent/confirm-port.d.ts +3 -1
  5. package/dist/agent/confirm-port.js +5 -1
  6. package/dist/agent/confirm-port.js.map +1 -1
  7. package/dist/agent/context-manager.js +45 -22
  8. package/dist/agent/context-manager.js.map +1 -1
  9. package/dist/agent/events.d.ts +6 -0
  10. package/dist/agent/evidence-governor.d.ts +37 -0
  11. package/dist/agent/evidence-governor.js +70 -0
  12. package/dist/agent/evidence-governor.js.map +1 -0
  13. package/dist/agent/loop-guard.d.ts +29 -11
  14. package/dist/agent/loop-guard.js +93 -39
  15. package/dist/agent/loop-guard.js.map +1 -1
  16. package/dist/agent/must-continue.d.ts +56 -0
  17. package/dist/agent/must-continue.js +240 -0
  18. package/dist/agent/must-continue.js.map +1 -0
  19. package/dist/agent/outcomes.d.ts +101 -0
  20. package/dist/agent/outcomes.js +359 -0
  21. package/dist/agent/outcomes.js.map +1 -0
  22. package/dist/agent/plan-decision.d.ts +33 -0
  23. package/dist/agent/plan-decision.js +104 -0
  24. package/dist/agent/plan-decision.js.map +1 -0
  25. package/dist/agent/plan-tool.d.ts +12 -0
  26. package/dist/agent/plan-tool.js +312 -104
  27. package/dist/agent/plan-tool.js.map +1 -1
  28. package/dist/agent/project-root.js +3 -0
  29. package/dist/agent/project-root.js.map +1 -1
  30. package/dist/agent/prompt-composer.d.ts +21 -0
  31. package/dist/agent/prompt-composer.js +46 -0
  32. package/dist/agent/prompt-composer.js.map +1 -0
  33. package/dist/agent/runner.d.ts +11 -1
  34. package/dist/agent/runner.js +1226 -971
  35. package/dist/agent/runner.js.map +1 -1
  36. package/dist/agent/scope-context.d.ts +3 -0
  37. package/dist/agent/scope-context.js +24 -0
  38. package/dist/agent/scope-context.js.map +1 -0
  39. package/dist/agent/scratch-write.d.ts +4 -0
  40. package/dist/agent/scratch-write.js +54 -0
  41. package/dist/agent/scratch-write.js.map +1 -0
  42. package/dist/agent/session-policy.d.ts +7 -0
  43. package/dist/agent/session-policy.js +61 -0
  44. package/dist/agent/session-policy.js.map +1 -1
  45. package/dist/agent/session-state.d.ts +41 -0
  46. package/dist/agent/session-state.js +107 -0
  47. package/dist/agent/session-state.js.map +1 -0
  48. package/dist/agent/step-budget.d.ts +14 -0
  49. package/dist/agent/step-budget.js +26 -0
  50. package/dist/agent/step-budget.js.map +1 -0
  51. package/dist/agent/task-analyzer.d.ts +12 -3
  52. package/dist/agent/task-analyzer.js +263 -23
  53. package/dist/agent/task-analyzer.js.map +1 -1
  54. package/dist/agent/task-evidence.d.ts +111 -29
  55. package/dist/agent/task-evidence.js +519 -91
  56. package/dist/agent/task-evidence.js.map +1 -1
  57. package/dist/agent/task-plan.d.ts +57 -0
  58. package/dist/agent/task-plan.js +147 -1
  59. package/dist/agent/task-plan.js.map +1 -1
  60. package/dist/agent/tool-call-parser.d.ts +4 -12
  61. package/dist/agent/tool-call-parser.js +34 -67
  62. package/dist/agent/tool-call-parser.js.map +1 -1
  63. package/dist/agent/tool-history.d.ts +7 -0
  64. package/dist/agent/tool-history.js +50 -0
  65. package/dist/agent/tool-history.js.map +1 -1
  66. package/dist/agent/tool-output-formatting.d.ts +13 -1
  67. package/dist/agent/tool-output-formatting.js +75 -22
  68. package/dist/agent/tool-output-formatting.js.map +1 -1
  69. package/dist/agent/turn-outcome.d.ts +11 -0
  70. package/dist/agent/turn-outcome.js +17 -0
  71. package/dist/agent/turn-outcome.js.map +1 -0
  72. package/dist/agent/turn-state.d.ts +11 -0
  73. package/dist/agent/turn-state.js +28 -0
  74. package/dist/agent/turn-state.js.map +1 -0
  75. package/dist/agent/workspace-orient.d.ts +11 -0
  76. package/dist/agent/workspace-orient.js +103 -12
  77. package/dist/agent/workspace-orient.js.map +1 -1
  78. package/dist/app/adapters/agent-event-adapter.d.ts +2 -13
  79. package/dist/app/adapters/agent-event-adapter.js +63 -25
  80. package/dist/app/adapters/agent-event-adapter.js.map +1 -1
  81. package/dist/app/adapters/current-agent-adapter.d.ts +0 -5
  82. package/dist/app/adapters/current-agent-adapter.js +4 -7
  83. package/dist/app/adapters/current-agent-adapter.js.map +1 -1
  84. package/dist/app/adapters/current-jobs-adapter.js +1 -0
  85. package/dist/app/adapters/current-jobs-adapter.js.map +1 -1
  86. package/dist/app/adapters/current-terminal-adapter.d.ts +0 -7
  87. package/dist/app/adapters/current-terminal-adapter.js +0 -7
  88. package/dist/app/adapters/current-terminal-adapter.js.map +1 -1
  89. package/dist/app/adapters/current-updates-adapter.d.ts +0 -5
  90. package/dist/app/adapters/current-updates-adapter.js +0 -5
  91. package/dist/app/adapters/current-updates-adapter.js.map +1 -1
  92. package/dist/app/adapters/in-memory-clipboard-adapter.d.ts +0 -5
  93. package/dist/app/adapters/in-memory-clipboard-adapter.js +0 -5
  94. package/dist/app/adapters/in-memory-clipboard-adapter.js.map +1 -1
  95. package/dist/app/commands/command.d.ts +0 -5
  96. package/dist/app/commands/command.js.map +1 -1
  97. package/dist/app/commands/registry.d.ts +0 -14
  98. package/dist/app/commands/registry.js +0 -20
  99. package/dist/app/commands/registry.js.map +1 -1
  100. package/dist/app/controllers/disposable.d.ts +0 -5
  101. package/dist/app/controllers/disposable.js +0 -5
  102. package/dist/app/controllers/disposable.js.map +1 -1
  103. package/dist/app/controllers/job-controller.d.ts +1 -5
  104. package/dist/app/controllers/job-controller.js +0 -4
  105. package/dist/app/controllers/job-controller.js.map +1 -1
  106. package/dist/app/controllers/plan-controller.d.ts +5 -8
  107. package/dist/app/controllers/plan-controller.js +30 -12
  108. package/dist/app/controllers/plan-controller.js.map +1 -1
  109. package/dist/app/controllers/session-controller.d.ts +17 -32
  110. package/dist/app/controllers/session-controller.js +56 -35
  111. package/dist/app/controllers/session-controller.js.map +1 -1
  112. package/dist/app/controllers/turn-controller.d.ts +2 -6
  113. package/dist/app/controllers/turn-controller.js +8 -8
  114. package/dist/app/controllers/turn-controller.js.map +1 -1
  115. package/dist/app/events/app-event.d.ts +4 -7
  116. package/dist/app/events/app-event.js +0 -6
  117. package/dist/app/events/app-event.js.map +1 -1
  118. package/dist/app/events/event-buffer.d.ts +6 -17
  119. package/dist/app/events/event-buffer.js +27 -24
  120. package/dist/app/events/event-buffer.js.map +1 -1
  121. package/dist/app/events/sequencer.d.ts +0 -9
  122. package/dist/app/events/sequencer.js +0 -4
  123. package/dist/app/events/sequencer.js.map +1 -1
  124. package/dist/app/ports/agent-port.d.ts +13 -3
  125. package/dist/app/ports/clipboard-port.d.ts +0 -5
  126. package/dist/app/ports/confirm-port.d.ts +0 -7
  127. package/dist/app/ports/jobs-port.d.ts +2 -5
  128. package/dist/app/ports/persistence-port.d.ts +0 -4
  129. package/dist/app/ports/secret-port.d.ts +0 -5
  130. package/dist/app/ports/terminal-port.d.ts +0 -6
  131. package/dist/app/ports/updates-port.d.ts +0 -4
  132. package/dist/attachments/service.d.ts +29 -0
  133. package/dist/attachments/service.js +47 -0
  134. package/dist/attachments/service.js.map +1 -0
  135. package/dist/commands/providers.js +0 -3
  136. package/dist/commands/providers.js.map +1 -1
  137. package/dist/commands/search-providers.d.ts +0 -33
  138. package/dist/commands/search-providers.js +0 -33
  139. package/dist/commands/search-providers.js.map +1 -1
  140. package/dist/commands/update.js +1 -9
  141. package/dist/commands/update.js.map +1 -1
  142. package/dist/index.js +22 -42
  143. package/dist/index.js.map +1 -1
  144. package/dist/llm/adapters/ollama-tools.d.ts +0 -4
  145. package/dist/llm/adapters/ollama-tools.js +0 -4
  146. package/dist/llm/adapters/ollama-tools.js.map +1 -1
  147. package/dist/llm/capabilities.d.ts +10 -10
  148. package/dist/llm/capabilities.js +43 -17
  149. package/dist/llm/capabilities.js.map +1 -1
  150. package/dist/llm/groq.js +0 -6
  151. package/dist/llm/groq.js.map +1 -1
  152. package/dist/llm/http.d.ts +0 -17
  153. package/dist/llm/http.js +0 -45
  154. package/dist/llm/http.js.map +1 -1
  155. package/dist/llm/router.d.ts +0 -6
  156. package/dist/llm/router.js +0 -15
  157. package/dist/llm/router.js.map +1 -1
  158. package/dist/llm/tool-protocol.d.ts +0 -6
  159. package/dist/llm/tool-protocol.js +3 -7
  160. package/dist/llm/tool-protocol.js.map +1 -1
  161. package/dist/modes/agent.d.ts +2 -1
  162. package/dist/modes/agent.js +4 -2
  163. package/dist/modes/agent.js.map +1 -1
  164. package/dist/modes/ask.d.ts +0 -18
  165. package/dist/modes/ask.js +0 -49
  166. package/dist/modes/ask.js.map +1 -1
  167. package/dist/os/cwd.d.ts +0 -21
  168. package/dist/os/cwd.js +0 -24
  169. package/dist/os/cwd.js.map +1 -1
  170. package/dist/os/pkgmgr.js +0 -6
  171. package/dist/os/pkgmgr.js.map +1 -1
  172. package/dist/prompts/embedded.d.ts +1 -0
  173. package/dist/prompts/embedded.js +9 -0
  174. package/dist/prompts/embedded.js.map +1 -0
  175. package/dist/prompts/index.d.ts +6 -18
  176. package/dist/prompts/index.js +104 -293
  177. package/dist/prompts/index.js.map +1 -1
  178. package/dist/prompts/system.agent.md +228 -0
  179. package/dist/prompts/system.ask.md +56 -0
  180. package/dist/repl/prompt-line.js +0 -36
  181. package/dist/repl/prompt-line.js.map +1 -1
  182. package/dist/repl/slash-commands.d.ts +0 -9
  183. package/dist/repl/slash-commands.js +2 -23
  184. package/dist/repl/slash-commands.js.map +1 -1
  185. package/dist/repl.d.ts +5 -0
  186. package/dist/repl.js +169 -55
  187. package/dist/repl.js.map +1 -1
  188. package/dist/safety/classifier.js +17 -9
  189. package/dist/safety/classifier.js.map +1 -1
  190. package/dist/safety/engagement-policy.d.ts +46 -0
  191. package/dist/safety/engagement-policy.js +198 -0
  192. package/dist/safety/engagement-policy.js.map +1 -0
  193. package/dist/store/engagement.d.ts +115 -0
  194. package/dist/store/engagement.js +278 -0
  195. package/dist/store/engagement.js.map +1 -0
  196. package/dist/store/logs.d.ts +5 -0
  197. package/dist/store/logs.js +52 -2
  198. package/dist/store/logs.js.map +1 -1
  199. package/dist/store/plan.d.ts +57 -5
  200. package/dist/store/plan.js +182 -8
  201. package/dist/store/plan.js.map +1 -1
  202. package/dist/store/scope.d.ts +4 -1
  203. package/dist/store/scope.js +2 -2
  204. package/dist/store/scope.js.map +1 -1
  205. package/dist/tools/definitions.js +70 -1
  206. package/dist/tools/definitions.js.map +1 -1
  207. package/dist/tools/elevated-shell.d.ts +60 -0
  208. package/dist/tools/elevated-shell.js +233 -0
  209. package/dist/tools/elevated-shell.js.map +1 -0
  210. package/dist/tools/file-diff.d.ts +125 -0
  211. package/dist/tools/file-diff.js +495 -0
  212. package/dist/tools/file-diff.js.map +1 -0
  213. package/dist/tools/fs.d.ts +11 -13
  214. package/dist/tools/fs.js +269 -78
  215. package/dist/tools/fs.js.map +1 -1
  216. package/dist/tools/http.d.ts +15 -0
  217. package/dist/tools/http.js +202 -61
  218. package/dist/tools/http.js.map +1 -1
  219. package/dist/tools/jobs.d.ts +71 -10
  220. package/dist/tools/jobs.js +403 -124
  221. package/dist/tools/jobs.js.map +1 -1
  222. package/dist/tools/nmap-runner.d.ts +11 -0
  223. package/dist/tools/nmap-runner.js +115 -74
  224. package/dist/tools/nmap-runner.js.map +1 -1
  225. package/dist/tools/pentest-workflows.d.ts +8 -0
  226. package/dist/tools/pentest-workflows.js +71 -0
  227. package/dist/tools/pentest-workflows.js.map +1 -0
  228. package/dist/tools/registry.d.ts +9 -0
  229. package/dist/tools/registry.js +244 -57
  230. package/dist/tools/registry.js.map +1 -1
  231. package/dist/tools/shell.d.ts +3 -0
  232. package/dist/tools/shell.js +35 -13
  233. package/dist/tools/shell.js.map +1 -1
  234. package/dist/tools/tool-types.d.ts +11 -0
  235. package/dist/tools/web/fetch-core.d.ts +7 -0
  236. package/dist/tools/web/fetch-core.js +40 -1
  237. package/dist/tools/web/fetch-core.js.map +1 -1
  238. package/dist/tools/web/fetch.js +8 -1
  239. package/dist/tools/web/fetch.js.map +1 -1
  240. package/dist/tui-v2/app/App.js +34 -41
  241. package/dist/tui-v2/app/App.js.map +1 -1
  242. package/dist/tui-v2/app/command-handlers.js +20 -1
  243. package/dist/tui-v2/app/command-handlers.js.map +1 -1
  244. package/dist/tui-v2/app/commands/picker-commands.js +3 -0
  245. package/dist/tui-v2/app/commands/picker-commands.js.map +1 -1
  246. package/dist/tui-v2/app/commands/session-commands.js +5 -0
  247. package/dist/tui-v2/app/commands/session-commands.js.map +1 -1
  248. package/dist/tui-v2/app/plan-lifecycle.d.ts +25 -3
  249. package/dist/tui-v2/app/plan-lifecycle.js +87 -24
  250. package/dist/tui-v2/app/plan-lifecycle.js.map +1 -1
  251. package/dist/tui-v2/bootstrap/patch-opentui-text.d.ts +6 -0
  252. package/dist/tui-v2/bootstrap/patch-opentui-text.js +53 -0
  253. package/dist/tui-v2/bootstrap/patch-opentui-text.js.map +1 -0
  254. package/dist/tui-v2/bootstrap/start-tui-v2.js +7 -0
  255. package/dist/tui-v2/bootstrap/start-tui-v2.js.map +1 -1
  256. package/dist/tui-v2/components/jobs/jobs-panel.js +23 -14
  257. package/dist/tui-v2/components/jobs/jobs-panel.js.map +1 -1
  258. package/dist/tui-v2/components/modal/confirm-modal.d.ts +1 -1
  259. package/dist/tui-v2/components/modal/confirm-modal.js +18 -7
  260. package/dist/tui-v2/components/modal/confirm-modal.js.map +1 -1
  261. package/dist/tui-v2/components/overlay/overlay-host.js +1 -1
  262. package/dist/tui-v2/components/overlay/overlay-host.js.map +1 -1
  263. package/dist/tui-v2/components/pager/pager.d.ts +6 -0
  264. package/dist/tui-v2/components/pager/pager.js +313 -67
  265. package/dist/tui-v2/components/pager/pager.js.map +1 -1
  266. package/dist/tui-v2/components/status/status-line.d.ts +9 -0
  267. package/dist/tui-v2/components/status/status-line.js +17 -3
  268. package/dist/tui-v2/components/status/status-line.js.map +1 -1
  269. package/dist/tui-v2/components/transcript/assistant-message.js +1 -1
  270. package/dist/tui-v2/components/transcript/assistant-message.js.map +1 -1
  271. package/dist/tui-v2/components/transcript/compacted-row.js +1 -1
  272. package/dist/tui-v2/components/transcript/compacted-row.js.map +1 -1
  273. package/dist/tui-v2/components/transcript/tool-card.d.ts +9 -1
  274. package/dist/tui-v2/components/transcript/tool-card.js +114 -9
  275. package/dist/tui-v2/components/transcript/tool-card.js.map +1 -1
  276. package/dist/tui-v2/components/transcript/transcript-row.js +2 -2
  277. package/dist/tui-v2/components/transcript/transcript-row.js.map +1 -1
  278. package/dist/tui-v2/composer/composer-editor.js +16 -0
  279. package/dist/tui-v2/composer/composer-editor.js.map +1 -1
  280. package/dist/tui-v2/controllers/overlay-controller.d.ts +16 -2
  281. package/dist/tui-v2/controllers/overlay-controller.js +92 -12
  282. package/dist/tui-v2/controllers/overlay-controller.js.map +1 -1
  283. package/dist/tui-v2/rendering/artifact-pager-source.d.ts +19 -0
  284. package/dist/tui-v2/rendering/artifact-pager-source.js +103 -0
  285. package/dist/tui-v2/rendering/artifact-pager-source.js.map +1 -0
  286. package/dist/tui-v2/rendering/file-diff-view.d.ts +63 -0
  287. package/dist/tui-v2/rendering/file-diff-view.js +263 -0
  288. package/dist/tui-v2/rendering/file-diff-view.js.map +1 -0
  289. package/dist/tui-v2/rendering/open-tool-output.d.ts +9 -1
  290. package/dist/tui-v2/rendering/open-tool-output.js +65 -12
  291. package/dist/tui-v2/rendering/open-tool-output.js.map +1 -1
  292. package/dist/tui-v2/rendering/pager-chrome.d.ts +12 -0
  293. package/dist/tui-v2/rendering/pager-chrome.js +56 -0
  294. package/dist/tui-v2/rendering/pager-chrome.js.map +1 -0
  295. package/dist/tui-v2/rendering/plan-view.js +1 -1
  296. package/dist/tui-v2/rendering/plan-view.js.map +1 -1
  297. package/dist/tui-v2/rendering/syntax-highlight.d.ts +37 -0
  298. package/dist/tui-v2/rendering/syntax-highlight.js +1303 -0
  299. package/dist/tui-v2/rendering/syntax-highlight.js.map +1 -0
  300. package/dist/tui-v2/rendering/theme.d.ts +28 -0
  301. package/dist/tui-v2/rendering/theme.js +28 -0
  302. package/dist/tui-v2/rendering/theme.js.map +1 -1
  303. package/dist/tui-v2/rendering/tool-presenter.d.ts +5 -0
  304. package/dist/tui-v2/rendering/tool-presenter.js +35 -3
  305. package/dist/tui-v2/rendering/tool-presenter.js.map +1 -1
  306. package/dist/tui-v2/state/transcript-hydrate.js +5 -0
  307. package/dist/tui-v2/state/transcript-hydrate.js.map +1 -1
  308. package/dist/tui-v2/state/transcript-reducer.js +12 -2
  309. package/dist/tui-v2/state/transcript-reducer.js.map +1 -1
  310. package/dist/tui-v2/state/transcript-store.d.ts +7 -0
  311. package/dist/tui-v2/state/transcript-store.js +46 -0
  312. package/dist/tui-v2/state/transcript-store.js.map +1 -1
  313. package/dist/tui-v2/state/transcript-types.d.ts +12 -0
  314. package/dist/tui-v2/state/transcript-types.js +9 -0
  315. package/dist/tui-v2/state/transcript-types.js.map +1 -1
  316. package/dist/types.d.ts +17 -1
  317. package/dist/ui/mentions.d.ts +6 -0
  318. package/dist/ui/mentions.js +50 -12
  319. package/dist/ui/mentions.js.map +1 -1
  320. package/dist/ui/plan-pane.js +1 -1
  321. package/dist/ui/plan-pane.js.map +1 -1
  322. package/package.json +18 -12
@@ -0,0 +1,1303 @@
1
+ /**
2
+ * Lightweight terminal syntax highlighter for file-diff cards / pager.
3
+ *
4
+ * Goal: work for **any** common source language without shiki/tree-sitter.
5
+ * Strategy:
6
+ * - Map hundreds of extensions → language families
7
+ * - Per-family keyword sets + comment/string rules
8
+ * - Unknown extensions still get a **generic** highlighter (strings, comments,
9
+ * numbers, punctuation) so every file type gets useful coloring
10
+ */
11
+ export function emptyCarry() {
12
+ return { inBlockComment: false, inTripleString: false };
13
+ }
14
+ // ─── Keyword sets ───────────────────────────────────────────────────────────
15
+ const KW = {
16
+ js: words(`
17
+ as async await break case catch class const continue debugger default delete do else
18
+ enum export extends false finally for from function get if implements import in
19
+ instanceof interface let new null of package private protected public return set static
20
+ super switch this throw true try typeof undefined var void while with yield type keyof
21
+ readonly infer never unknown any boolean number string symbol bigint satisfies asserts
22
+ is namespace module declare abstract override
23
+ `),
24
+ py: words(`
25
+ and as assert async await break class continue def del elif else except False finally
26
+ for from global if import in is lambda None nonlocal not or pass raise return True try
27
+ while with yield match case type
28
+ `),
29
+ go: words(`
30
+ break case chan const continue default defer else fallthrough for func go goto if
31
+ import interface map package range return select struct switch type var true false nil iota
32
+ `),
33
+ rs: words(`
34
+ as async await break const continue crate dyn else enum extern false fn for if impl in
35
+ let loop match mod move mut pub ref return self Self static struct super trait true type
36
+ unsafe use where while async await dyn
37
+ `),
38
+ c: words(`
39
+ auto break case char const continue default do double else enum extern float for goto
40
+ if inline int long register restrict return short signed sizeof static struct switch
41
+ typedef union unsigned void volatile while _Bool _Complex _Imaginary true false NULL
42
+ class public private protected virtual template typename namespace using new delete this
43
+ try catch throw const_cast static_cast dynamic_cast reinterpret_cast constexpr noexcept
44
+ override final nullptr bool wchar_t
45
+ `),
46
+ csharp: words(`
47
+ abstract as base bool break byte case catch char checked class const continue decimal
48
+ default delegate do double else enum event explicit extern false finally fixed float for
49
+ foreach goto if implicit in int interface internal is lock long namespace new null object
50
+ operator out override params private protected public readonly ref return sbyte sealed
51
+ short sizeof stackalloc static string struct switch this throw true try typeof uint ulong
52
+ unchecked unsafe ushort using var virtual void volatile while async await nameof record
53
+ init required nint nuint
54
+ `),
55
+ java: words(`
56
+ abstract assert boolean break byte case catch char class const continue default do double
57
+ else enum extends final finally float for goto if implements import instanceof int
58
+ interface long native new package private protected public return short static strictfp
59
+ super switch synchronized this throw throws transient try void volatile while true false
60
+ null var record sealed permits yields yield non-sealed
61
+ `),
62
+ kotlin: words(`
63
+ as as? break class continue do else false for fun if in !in interface is !is null object
64
+ package return super this throw true try typealias typeof val var when while by catch
65
+ constructor delegate dynamic field file finally get import init param property receiver
66
+ set setparam where actual abstract annotation companion const crossinline data enum
67
+ expect external final infix inline inner internal lateinit noinline open operator out
68
+ override private protected public reified sealed suspend tailrec vararg
69
+ `),
70
+ swift: words(`
71
+ associatedtype class deinit enum extension fileprivate func import init inout internal let
72
+ open operator private protocol public rethrows static struct subscript typealias var break
73
+ case continue default defer do else fallthrough for guard if in repeat return switch where
74
+ while as Any catch false is nil super self Self throw true try async await actor some
75
+ `),
76
+ ruby: words(`
77
+ BEGIN END alias and begin break case class def defined? do else elsif end ensure false
78
+ for if in module next nil not or redo rescue retry return self super then true undef
79
+ unless until when while yield
80
+ `),
81
+ php: words(`
82
+ abstract and array as break callable case catch class clone const continue declare default
83
+ do echo else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval exit
84
+ extends final finally fn for foreach function global goto if implements include include_once
85
+ instanceof insteadof interface isset list match namespace new or print private protected
86
+ public readonly require require_once return static switch throw trait try unset use var
87
+ while xor yield true false null
88
+ `),
89
+ sql: words(`
90
+ select from where insert into values update set delete create table drop alter index view
91
+ join left right inner outer on and or not null as order by group having limit offset union
92
+ all distinct case when then else end exists in between like is primary key foreign
93
+ references constraint default unique check cascade grant revoke commit rollback transaction
94
+ begin true false with recursive
95
+ `),
96
+ lua: words(`
97
+ and break do else elseif end false for function goto if in local nil not or repeat return
98
+ then true until while
99
+ `),
100
+ perl: words(`
101
+ __DATA__ __END__ __FILE__ __LINE__ __PACKAGE__ and cmp continue do else elsif eq exp for
102
+ foreach ge gt if le lock lt m ne next no or package qq qr qw qx redo require return sub
103
+ tr unless until while xor y my our use local
104
+ `),
105
+ r: words(`
106
+ if else repeat while function for in next break TRUE FALSE NULL Inf NaN NA NA_integer_
107
+ NA_real_ NA_complex_ NA_character_ ... ..1
108
+ `),
109
+ haskell: words(`
110
+ case class data default deriving do else foreign if import in infix infixl infixr instance
111
+ let module newtype of then type where _
112
+ `),
113
+ erlang: words(`
114
+ after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not
115
+ of or orelse receive rem try when xor
116
+ `),
117
+ fortran: words(`
118
+ assign backspace block call close common continue data dimension do else elseif end endfile
119
+ endif enddo entry equivalence external format function goto if implicit inquire integer
120
+ intrinsic open parameter pause print program read real return rewind save stop subroutine
121
+ then write allocate allocatable allocate deallocate module use contains interface pure
122
+ elemental recursive
123
+ `),
124
+ sh: words(`
125
+ if then else elif fi for while until do done case esac in function select time coproc
126
+ true false return exit export local readonly declare typeset alias unalias set unset
127
+ shift break continue source eval exec
128
+ `),
129
+ css: words(`
130
+ important and or not only from to
131
+ `),
132
+ };
133
+ function words(s) {
134
+ return new Set(s.split(/\s+/).filter(Boolean));
135
+ }
136
+ /** extension (no dot) → family */
137
+ const EXT_FAMILY = {
138
+ // JS / TS
139
+ js: "js",
140
+ mjs: "js",
141
+ cjs: "js",
142
+ jsx: "js",
143
+ ts: "js",
144
+ mts: "js",
145
+ cts: "js",
146
+ tsx: "js",
147
+ vue: "html",
148
+ svelte: "html",
149
+ astro: "html",
150
+ // web
151
+ css: "css",
152
+ scss: "css",
153
+ sass: "css",
154
+ less: "css",
155
+ styl: "css",
156
+ stylus: "css",
157
+ html: "html",
158
+ htm: "html",
159
+ xhtml: "html",
160
+ xml: "html",
161
+ xsl: "html",
162
+ xslt: "html",
163
+ svg: "html",
164
+ jsp: "html",
165
+ asp: "html",
166
+ aspx: "html",
167
+ // data
168
+ json: "json",
169
+ jsonc: "json",
170
+ json5: "json",
171
+ jsonl: "json",
172
+ ndjson: "json",
173
+ yaml: "yaml",
174
+ yml: "yaml",
175
+ toml: "toml",
176
+ ini: "ini",
177
+ cfg: "ini",
178
+ conf: "ini",
179
+ env: "ini",
180
+ properties: "ini",
181
+ // docs
182
+ md: "md",
183
+ mdx: "md",
184
+ markdown: "md",
185
+ rst: "md",
186
+ txt: "generic",
187
+ // shell
188
+ sh: "sh",
189
+ bash: "sh",
190
+ zsh: "sh",
191
+ fish: "sh",
192
+ ksh: "sh",
193
+ csh: "sh",
194
+ tcsh: "sh",
195
+ ps1: "sh",
196
+ psm1: "sh",
197
+ bat: "sh",
198
+ cmd: "sh",
199
+ // python
200
+ py: "py",
201
+ pyw: "py",
202
+ pyi: "py",
203
+ pyx: "py",
204
+ pxd: "py",
205
+ ipynb: "json",
206
+ // ruby
207
+ rb: "ruby",
208
+ rake: "ruby",
209
+ gemspec: "ruby",
210
+ // php
211
+ php: "php",
212
+ phtml: "php",
213
+ // go / rust
214
+ go: "clike",
215
+ rs: "clike",
216
+ // C family
217
+ c: "clike",
218
+ h: "clike",
219
+ cc: "clike",
220
+ cpp: "clike",
221
+ cxx: "clike",
222
+ hpp: "clike",
223
+ hxx: "clike",
224
+ hh: "clike",
225
+ m: "clike",
226
+ mm: "clike",
227
+ cs: "clike",
228
+ java: "clike",
229
+ kt: "clike",
230
+ kts: "clike",
231
+ scala: "clike",
232
+ sc: "clike",
233
+ groovy: "clike",
234
+ gradle: "clike",
235
+ swift: "clike",
236
+ dart: "clike",
237
+ // JVM etc
238
+ clj: "lisp",
239
+ cljs: "lisp",
240
+ cljc: "lisp",
241
+ edn: "lisp",
242
+ lisp: "lisp",
243
+ el: "lisp",
244
+ scm: "lisp",
245
+ ss: "lisp",
246
+ racket: "lisp",
247
+ // functional
248
+ hs: "haskell",
249
+ lhs: "haskell",
250
+ elm: "haskell",
251
+ ml: "clike",
252
+ mli: "clike",
253
+ fs: "clike",
254
+ fsi: "clike",
255
+ fsx: "clike",
256
+ erl: "erlang",
257
+ hrl: "erlang",
258
+ ex: "ruby", // Elixir ~ Ruby-ish # comments
259
+ exs: "ruby",
260
+ // scripting
261
+ pl: "perl",
262
+ pm: "perl",
263
+ t: "perl",
264
+ lua: "lua",
265
+ r: "r",
266
+ R: "r",
267
+ jl: "py", // Julia: # comments like python-ish
268
+ // systems
269
+ zig: "clike",
270
+ nim: "py", // # comments
271
+ v: "clike",
272
+ d: "clike",
273
+ pas: "clike",
274
+ pp: "clike",
275
+ // data / query
276
+ sql: "sql",
277
+ mysql: "sql",
278
+ pgsql: "sql",
279
+ psql: "sql",
280
+ hql: "sql",
281
+ graphql: "js",
282
+ gql: "js",
283
+ prisma: "js",
284
+ // config / devops
285
+ dockerfile: "sh",
286
+ tf: "clike",
287
+ hcl: "clike",
288
+ nix: "clike",
289
+ makefile: "sh",
290
+ mk: "sh",
291
+ cmake: "sh",
292
+ // diff
293
+ diff: "diff",
294
+ patch: "diff",
295
+ // others often in repos
296
+ proto: "clike",
297
+ thrift: "clike",
298
+ avdl: "clike",
299
+ sol: "clike",
300
+ move: "clike",
301
+ wgsl: "clike",
302
+ glsl: "clike",
303
+ vert: "clike",
304
+ frag: "clike",
305
+ hlsl: "clike",
306
+ metal: "clike",
307
+ asm: "generic",
308
+ s: "generic",
309
+ S: "generic",
310
+ f: "fortran",
311
+ f90: "fortran",
312
+ f95: "fortran",
313
+ for: "fortran",
314
+ cob: "generic",
315
+ cbl: "generic",
316
+ vb: "clike",
317
+ vbs: "generic",
318
+ coffee: "js",
319
+ litcoffee: "js",
320
+ ls: "js",
321
+ purescript: "haskell",
322
+ purs: "haskell",
323
+ res: "clike",
324
+ resi: "clike",
325
+ re: "clike",
326
+ rei: "clike",
327
+ mlapp: "generic",
328
+ mat: "generic",
329
+ sas: "generic",
330
+ sparql: "sql",
331
+ rq: "sql",
332
+ turtle: "generic",
333
+ ttl: "generic",
334
+ nq: "generic",
335
+ wasm: "generic",
336
+ wat: "generic",
337
+ csv: "generic",
338
+ tsv: "generic",
339
+ log: "generic",
340
+ lock: "json",
341
+ };
342
+ const BASENAME_FAMILY = {
343
+ dockerfile: "sh",
344
+ containerfile: "sh",
345
+ makefile: "sh",
346
+ gnumakefile: "sh",
347
+ "cmakelists.txt": "sh",
348
+ "cargo.toml": "toml",
349
+ "pyproject.toml": "toml",
350
+ "package.json": "json",
351
+ "tsconfig.json": "json",
352
+ "jsconfig.json": "json",
353
+ "composer.json": "json",
354
+ gemfile: "ruby",
355
+ rakefile: "ruby",
356
+ podfile: "ruby",
357
+ brewfile: "ruby",
358
+ vagrantfile: "ruby",
359
+ procfile: "sh",
360
+ justfile: "sh",
361
+ "go.mod": "clike",
362
+ "go.sum": "generic",
363
+ ".gitignore": "ini",
364
+ ".dockerignore": "ini",
365
+ ".npmrc": "ini",
366
+ ".env": "ini",
367
+ ".env.local": "ini",
368
+ ".env.development": "ini",
369
+ ".env.production": "ini",
370
+ ".eslintrc": "json",
371
+ ".prettierrc": "json",
372
+ ".babelrc": "json",
373
+ };
374
+ function keywordSetFor(pathOrExt, family) {
375
+ const ext = (pathOrExt.split(".").pop() ?? "").toLowerCase();
376
+ if (family === "js")
377
+ return KW.js;
378
+ if (family === "py")
379
+ return KW.py;
380
+ if (family === "ruby")
381
+ return KW.ruby;
382
+ if (family === "php")
383
+ return KW.php;
384
+ if (family === "sql")
385
+ return KW.sql;
386
+ if (family === "lua")
387
+ return KW.lua;
388
+ if (family === "perl")
389
+ return KW.perl;
390
+ if (family === "r")
391
+ return KW.r;
392
+ if (family === "haskell")
393
+ return KW.haskell;
394
+ if (family === "erlang")
395
+ return KW.erlang;
396
+ if (family === "fortran")
397
+ return KW.fortran;
398
+ if (family === "sh")
399
+ return KW.sh;
400
+ if (family === "css")
401
+ return KW.css;
402
+ if (family === "clike") {
403
+ if (ext === "go")
404
+ return KW.go;
405
+ if (ext === "rs")
406
+ return KW.rs;
407
+ if (ext === "cs")
408
+ return KW.csharp;
409
+ if (ext === "java")
410
+ return KW.java;
411
+ if (ext === "kt" || ext === "kts")
412
+ return KW.kotlin;
413
+ if (ext === "swift")
414
+ return KW.swift;
415
+ if (ext === "dart")
416
+ return KW.js; // close enough
417
+ return KW.c;
418
+ }
419
+ return new Set();
420
+ }
421
+ export function languageFromPath(path) {
422
+ const base = (path.split(/[/\\]/).pop() ?? path).toLowerCase();
423
+ if (BASENAME_FAMILY[base])
424
+ return BASENAME_FAMILY[base];
425
+ // multi-dot basenames
426
+ for (const [name, fam] of Object.entries(BASENAME_FAMILY)) {
427
+ if (base === name || base.endsWith(name))
428
+ return fam;
429
+ }
430
+ const dot = base.lastIndexOf(".");
431
+ if (dot < 0) {
432
+ // shebang-less scripts / Makefile already handled
433
+ return "generic";
434
+ }
435
+ // handle .d.ts, .test.ts etc — use last extension
436
+ const ext = base.slice(dot + 1);
437
+ // double extensions like .d.ts
438
+ if (base.endsWith(".d.ts") || base.endsWith(".d.mts") || base.endsWith(".d.cts")) {
439
+ return "js";
440
+ }
441
+ if (base.endsWith(".test.ts") || base.endsWith(".spec.ts") || base.endsWith(".test.tsx")) {
442
+ return "js";
443
+ }
444
+ return EXT_FAMILY[ext] ?? "generic";
445
+ }
446
+ /** @deprecated use languageFromPath */
447
+ export function languageFromPathLegacy(path) {
448
+ return languageFromPath(path);
449
+ }
450
+ // ─── Core helpers ───────────────────────────────────────────────────────────
451
+ function push(spans, kind, text) {
452
+ if (!text)
453
+ return;
454
+ const last = spans[spans.length - 1];
455
+ if (last && last.kind === kind) {
456
+ spans[spans.length - 1] = { kind, text: last.text + text };
457
+ }
458
+ else {
459
+ spans.push({ kind, text });
460
+ }
461
+ }
462
+ function isIdentStart(ch) {
463
+ return /[A-Za-z_$\u00a0-\uffff]/.test(ch);
464
+ }
465
+ function isIdentCont(ch) {
466
+ return /[A-Za-z0-9_$\u00a0-\uffff]/.test(ch);
467
+ }
468
+ /**
469
+ * Highlight one line for any path/language. Unknown types use generic rules.
470
+ */
471
+ export function highlightLine(line, langOrPath, carry = emptyCarry()) {
472
+ const family = typeof langOrPath === "string" &&
473
+ ![
474
+ "clike",
475
+ "js",
476
+ "css",
477
+ "json",
478
+ "html",
479
+ "md",
480
+ "sh",
481
+ "py",
482
+ "ruby",
483
+ "php",
484
+ "sql",
485
+ "lua",
486
+ "perl",
487
+ "r",
488
+ "haskell",
489
+ "lisp",
490
+ "erlang",
491
+ "fortran",
492
+ "yaml",
493
+ "toml",
494
+ "ini",
495
+ "diff",
496
+ "generic",
497
+ ].includes(langOrPath)
498
+ ? languageFromPath(langOrPath)
499
+ : langOrPath;
500
+ switch (family) {
501
+ case "md":
502
+ return highlightMarkdown(line);
503
+ case "json":
504
+ return highlightJsonLike(line);
505
+ case "css":
506
+ return highlightClike(line, KW.css, carry, { regex: false, hashNumber: true });
507
+ case "html":
508
+ return highlightHtml(line, carry);
509
+ case "yaml":
510
+ return highlightYaml(line);
511
+ case "toml":
512
+ case "ini":
513
+ return highlightIniToml(line);
514
+ case "diff":
515
+ return highlightDiff(line);
516
+ case "sh":
517
+ return highlightLineCommentLang(line, "#", KW.sh, carry);
518
+ case "py":
519
+ return highlightPython(line, carry);
520
+ case "ruby":
521
+ return highlightLineCommentLang(line, "#", KW.ruby, carry);
522
+ case "php":
523
+ return highlightPhp(line, carry);
524
+ case "sql":
525
+ return highlightClike(line, KW.sql, carry, {
526
+ lineComment: "--",
527
+ caseInsensitiveKeywords: true,
528
+ });
529
+ case "lua":
530
+ return highlightLua(line, carry);
531
+ case "perl":
532
+ return highlightLineCommentLang(line, "#", KW.perl, carry);
533
+ case "r":
534
+ return highlightLineCommentLang(line, "#", KW.r, carry);
535
+ case "haskell":
536
+ return highlightHaskell(line, carry);
537
+ case "lisp":
538
+ return highlightLisp(line);
539
+ case "erlang":
540
+ return highlightLineCommentLang(line, "%", KW.erlang, carry);
541
+ case "fortran":
542
+ return highlightLineCommentLang(line, "!", KW.fortran, carry, {
543
+ caseInsensitiveKeywords: true,
544
+ });
545
+ case "js":
546
+ return highlightClike(line, KW.js, carry, { regex: true });
547
+ case "clike":
548
+ return highlightClike(line, keywordSetFor("", "clike"), carry, { regex: false });
549
+ case "generic":
550
+ default:
551
+ return highlightGeneric(line, carry);
552
+ }
553
+ }
554
+ export function highlightLineForPath(line, path, carry = emptyCarry()) {
555
+ const family = languageFromPath(path);
556
+ // re-resolve keywords with extension for clike
557
+ if (family === "clike") {
558
+ return highlightClike(line, keywordSetFor(path, "clike"), carry, { regex: false });
559
+ }
560
+ return highlightLine(line, family, carry);
561
+ }
562
+ export function highlightLines(lines, langOrPath) {
563
+ const carry = emptyCarry();
564
+ const isPath = typeof langOrPath === "string" &&
565
+ (langOrPath.includes("/") || langOrPath.includes(".") || langOrPath.includes("\\"));
566
+ return lines.map((line) => isPath
567
+ ? highlightLineForPath(line, langOrPath, carry)
568
+ : highlightLine(line, langOrPath, carry));
569
+ }
570
+ export function clipSpans(spans, maxChars) {
571
+ if (maxChars <= 0)
572
+ return [];
573
+ const out = [];
574
+ let used = 0;
575
+ for (const s of spans) {
576
+ if (used >= maxChars)
577
+ break;
578
+ const room = maxChars - used;
579
+ if (s.text.length <= room) {
580
+ out.push(s);
581
+ used += s.text.length;
582
+ }
583
+ else {
584
+ out.push({ kind: s.kind, text: s.text.slice(0, Math.max(0, room - 1)) + "…" });
585
+ break;
586
+ }
587
+ }
588
+ return out;
589
+ }
590
+ function highlightClike(line, keys, carry, opts = {}) {
591
+ const spans = [];
592
+ const n = line.length;
593
+ let i = 0;
594
+ const lineComment = opts.lineComment ?? "//";
595
+ while (i < n) {
596
+ if (carry.inBlockComment) {
597
+ const end = line.indexOf("*/", i);
598
+ if (end < 0) {
599
+ push(spans, "comment", line.slice(i));
600
+ return spans;
601
+ }
602
+ push(spans, "comment", line.slice(i, end + 2));
603
+ carry.inBlockComment = false;
604
+ i = end + 2;
605
+ continue;
606
+ }
607
+ const ch = line[i];
608
+ const next = line[i + 1];
609
+ // line comment
610
+ if (line.startsWith(lineComment, i)) {
611
+ push(spans, "comment", line.slice(i));
612
+ return spans;
613
+ }
614
+ // block comment
615
+ if (ch === "/" && next === "*") {
616
+ const end = line.indexOf("*/", i + 2);
617
+ if (end < 0) {
618
+ push(spans, "comment", line.slice(i));
619
+ carry.inBlockComment = true;
620
+ return spans;
621
+ }
622
+ push(spans, "comment", line.slice(i, end + 2));
623
+ i = end + 2;
624
+ continue;
625
+ }
626
+ // strings
627
+ if (ch === '"' || ch === "'" || ch === "`") {
628
+ i = readString(line, i, ch, spans);
629
+ continue;
630
+ }
631
+ // regex
632
+ if (opts.regex && ch === "/" && next && next !== "/" && next !== "*") {
633
+ const prev = spans.length ? spans[spans.length - 1].text.trimEnd().slice(-1) : "";
634
+ if (!prev || /[=(:,[\!&|?{;]/.test(prev)) {
635
+ const j = readRegex(line, i);
636
+ if (j > i) {
637
+ push(spans, "regex", line.slice(i, j));
638
+ i = j;
639
+ continue;
640
+ }
641
+ }
642
+ }
643
+ // #hex colors in css
644
+ if (opts.hashNumber && ch === "#" && next && /[0-9a-fA-F]/.test(next)) {
645
+ let j = i + 1;
646
+ while (j < n && /[0-9a-fA-F]/.test(line[j]))
647
+ j += 1;
648
+ push(spans, "number", line.slice(i, j));
649
+ i = j;
650
+ continue;
651
+ }
652
+ // numbers
653
+ if (/\d/.test(ch) || (ch === "." && next && /\d/.test(next))) {
654
+ let j = i;
655
+ while (j < n && /[\d._xXa-fA-FeEpP+-]/.test(line[j]))
656
+ j += 1;
657
+ push(spans, "number", line.slice(i, j));
658
+ i = j;
659
+ continue;
660
+ }
661
+ // identifiers
662
+ if (isIdentStart(ch)) {
663
+ let j = i + 1;
664
+ while (j < n && isIdentCont(line[j]))
665
+ j += 1;
666
+ const word = line.slice(i, j);
667
+ let k = j;
668
+ while (k < n && (line[k] === " " || line[k] === "\t"))
669
+ k += 1;
670
+ const after = line[k];
671
+ const keyLookup = opts.caseInsensitiveKeywords ? word.toLowerCase() : word;
672
+ const keyHit = opts.caseInsensitiveKeywords
673
+ ? [...keys].some((kw) => kw.toLowerCase() === keyLookup)
674
+ : keys.has(word);
675
+ if (keyHit)
676
+ push(spans, "keyword", word);
677
+ else if (after === "(")
678
+ push(spans, "function", word);
679
+ else if (/^[A-Z]/.test(word))
680
+ push(spans, "type", word);
681
+ else if (spans[spans.length - 1]?.text.endsWith("."))
682
+ push(spans, "property", word);
683
+ else
684
+ push(spans, "plain", word);
685
+ i = j;
686
+ continue;
687
+ }
688
+ if (/[{}()\[\];,.]/.test(ch)) {
689
+ push(spans, "punctuation", ch);
690
+ i += 1;
691
+ continue;
692
+ }
693
+ if (/[+\-*/%=<>!&|^~?:@]/.test(ch)) {
694
+ let j = i + 1;
695
+ while (j < n && /[+\-*/%=<>!&|^~?:@]/.test(line[j]))
696
+ j += 1;
697
+ push(spans, "operator", line.slice(i, j));
698
+ i = j;
699
+ continue;
700
+ }
701
+ push(spans, "plain", ch);
702
+ i += 1;
703
+ }
704
+ return spans;
705
+ }
706
+ function readString(line, start, quote, spans) {
707
+ const n = line.length;
708
+ let j = start + 1;
709
+ while (j < n) {
710
+ if (line[j] === "\\") {
711
+ j += 2;
712
+ continue;
713
+ }
714
+ if (line[j] === quote) {
715
+ j += 1;
716
+ break;
717
+ }
718
+ // template ${ } — keep as string for simplicity
719
+ j += 1;
720
+ }
721
+ push(spans, "string", line.slice(start, j));
722
+ return j;
723
+ }
724
+ function readRegex(line, start) {
725
+ const n = line.length;
726
+ let j = start + 1;
727
+ let closed = false;
728
+ while (j < n) {
729
+ if (line[j] === "\\") {
730
+ j += 2;
731
+ continue;
732
+ }
733
+ if (line[j] === "/") {
734
+ j += 1;
735
+ closed = true;
736
+ break;
737
+ }
738
+ if (line[j] === "\n")
739
+ break;
740
+ j += 1;
741
+ }
742
+ if (!closed)
743
+ return start;
744
+ while (j < n && /[gimsuy]/.test(line[j]))
745
+ j += 1;
746
+ return j;
747
+ }
748
+ /** Generic: strings, line/block comments, numbers — any unmapped extension. */
749
+ function highlightGeneric(line, carry) {
750
+ const t = line.trimStart();
751
+ // Prefer # line comments for shell-like / config files when whole-line-ish
752
+ if (t.startsWith("#") && !/^#[0-9a-fA-F]{3,8}\b/.test(t)) {
753
+ return [{ kind: "comment", text: line }];
754
+ }
755
+ return highlightClike(line, new Set(), carry, { regex: false });
756
+ }
757
+ function highlightLineCommentLang(line, commentChar, keys, carry, opts = {}) {
758
+ const caseIns = opts.caseInsensitiveKeywords === true;
759
+ // Reuse clike but with custom line comment by pre-check
760
+ const t = line.trimStart();
761
+ if (t.startsWith(commentChar) && commentChar !== "//") {
762
+ return [{ kind: "comment", text: line }];
763
+ }
764
+ // map # comments: temporarily convert? better custom loop
765
+ if (commentChar === "#") {
766
+ return highlightHashCommentLang(line, keys, caseIns);
767
+ }
768
+ if (commentChar === "%" || commentChar === "!") {
769
+ return highlightHashCommentLang(line, keys, caseIns, commentChar);
770
+ }
771
+ return highlightClike(line, keys, carry, {
772
+ caseInsensitiveKeywords: caseIns,
773
+ });
774
+ }
775
+ function highlightHashCommentLang(line, keys, caseInsensitive = false, commentChar = "#") {
776
+ const spans = [];
777
+ let i = 0;
778
+ const n = line.length;
779
+ while (i < n) {
780
+ if (line[i] === commentChar) {
781
+ push(spans, "comment", line.slice(i));
782
+ return spans;
783
+ }
784
+ if (line[i] === '"' || line[i] === "'" || line[i] === "`") {
785
+ i = readString(line, i, line[i], spans);
786
+ continue;
787
+ }
788
+ if (/\d/.test(line[i])) {
789
+ let j = i;
790
+ while (j < n && /[\d._]/.test(line[j]))
791
+ j += 1;
792
+ push(spans, "number", line.slice(i, j));
793
+ i = j;
794
+ continue;
795
+ }
796
+ if (isIdentStart(line[i])) {
797
+ let j = i + 1;
798
+ while (j < n && isIdentCont(line[j]))
799
+ j += 1;
800
+ const word = line.slice(i, j);
801
+ let k = j;
802
+ while (k < n && /\s/.test(line[k]))
803
+ k += 1;
804
+ const hit = caseInsensitive
805
+ ? [...keys].some((kw) => kw.toLowerCase() === word.toLowerCase())
806
+ : keys.has(word);
807
+ if (hit)
808
+ push(spans, "keyword", word);
809
+ else if (line[k] === "(")
810
+ push(spans, "function", word);
811
+ else
812
+ push(spans, "plain", word);
813
+ i = j;
814
+ continue;
815
+ }
816
+ if (/[{}()\[\];,.]/.test(line[i])) {
817
+ push(spans, "punctuation", line[i]);
818
+ i += 1;
819
+ continue;
820
+ }
821
+ push(spans, "plain", line[i]);
822
+ i += 1;
823
+ }
824
+ return spans;
825
+ }
826
+ function highlightPython(line, carry) {
827
+ if (carry.inTripleString) {
828
+ const q = carry.tripleQuote ?? '"""';
829
+ const end = line.indexOf(q);
830
+ if (end < 0)
831
+ return [{ kind: "string", text: line }];
832
+ carry.inTripleString = false;
833
+ carry.tripleQuote = undefined;
834
+ const spans = [{ kind: "string", text: line.slice(0, end + q.length) }];
835
+ if (end + q.length < line.length) {
836
+ spans.push(...highlightPython(line.slice(end + q.length), carry));
837
+ }
838
+ return spans;
839
+ }
840
+ if (line.includes('"""') || line.includes("'''")) {
841
+ const spans = [];
842
+ let i = 0;
843
+ const n = line.length;
844
+ while (i < n) {
845
+ if (line.startsWith('"""', i) || line.startsWith("'''", i)) {
846
+ const q = line.slice(i, i + 3);
847
+ const end = line.indexOf(q, i + 3);
848
+ if (end < 0) {
849
+ push(spans, "string", line.slice(i));
850
+ carry.inTripleString = true;
851
+ carry.tripleQuote = q;
852
+ return spans;
853
+ }
854
+ push(spans, "string", line.slice(i, end + 3));
855
+ i = end + 3;
856
+ continue;
857
+ }
858
+ if (line[i] === "#") {
859
+ push(spans, "comment", line.slice(i));
860
+ return spans;
861
+ }
862
+ // fall through one char via rest of hash highlighter
863
+ const rest = highlightHashCommentLang(line.slice(i), KW.py);
864
+ // only take until we'd re-enter — simpler: process char by char for non-triple
865
+ const sub = rest[0];
866
+ if (!sub) {
867
+ i += 1;
868
+ continue;
869
+ }
870
+ // process rest of line with hash highlighter but need to stop at triple
871
+ const piece = line.slice(i);
872
+ const triple = piece.search(/"""|'''/);
873
+ if (triple < 0) {
874
+ spans.push(...highlightHashCommentLang(piece, KW.py));
875
+ return spans;
876
+ }
877
+ if (triple > 0)
878
+ spans.push(...highlightHashCommentLang(piece.slice(0, triple), KW.py));
879
+ i += triple;
880
+ }
881
+ return spans;
882
+ }
883
+ return highlightHashCommentLang(line, KW.py);
884
+ }
885
+ function highlightPhp(line, carry) {
886
+ // PHP uses // /* # and $variables
887
+ const spans = highlightClike(line, KW.php, carry, { regex: false });
888
+ // color $ident as property
889
+ const out = [];
890
+ for (const s of spans) {
891
+ if (s.kind !== "plain") {
892
+ out.push(s);
893
+ continue;
894
+ }
895
+ const re = /(\$[A-Za-z_][A-Za-z0-9_]*)/g;
896
+ let last = 0;
897
+ let m;
898
+ let any = false;
899
+ while ((m = re.exec(s.text))) {
900
+ any = true;
901
+ if (m.index > last)
902
+ push(out, "plain", s.text.slice(last, m.index));
903
+ push(out, "property", m[1]);
904
+ last = m.index + m[1].length;
905
+ }
906
+ if (any) {
907
+ if (last < s.text.length)
908
+ push(out, "plain", s.text.slice(last));
909
+ }
910
+ else {
911
+ out.push(s);
912
+ }
913
+ }
914
+ return out;
915
+ }
916
+ function highlightLua(line, carry) {
917
+ // -- line comments, --[[ block ]]
918
+ if (carry.inBlockComment) {
919
+ const end = line.indexOf("]]");
920
+ if (end < 0)
921
+ return [{ kind: "comment", text: line }];
922
+ carry.inBlockComment = false;
923
+ const spans = [{ kind: "comment", text: line.slice(0, end + 2) }];
924
+ if (end + 2 < line.length)
925
+ spans.push(...highlightLua(line.slice(end + 2), carry));
926
+ return spans;
927
+ }
928
+ if (line.includes("--[[")) {
929
+ const start = line.indexOf("--[[");
930
+ const spans = [];
931
+ if (start > 0)
932
+ spans.push(...highlightHashCommentLang(line.slice(0, start), KW.lua));
933
+ // abuse hash with --
934
+ const end = line.indexOf("]]", start + 4);
935
+ if (end < 0) {
936
+ carry.inBlockComment = true;
937
+ spans.push({ kind: "comment", text: line.slice(start) });
938
+ return spans;
939
+ }
940
+ spans.push({ kind: "comment", text: line.slice(start, end + 2) });
941
+ if (end + 2 < line.length)
942
+ spans.push(...highlightLua(line.slice(end + 2), carry));
943
+ return spans;
944
+ }
945
+ // line --
946
+ const idx = line.indexOf("--");
947
+ if (idx >= 0) {
948
+ const before = line.slice(0, idx);
949
+ const spans = before ? highlightHashCommentLang(before, KW.lua) : [];
950
+ spans.push({ kind: "comment", text: line.slice(idx) });
951
+ return spans;
952
+ }
953
+ return highlightHashCommentLang(line, KW.lua);
954
+ }
955
+ function highlightHaskell(line, carry) {
956
+ if (carry.inBlockComment) {
957
+ const end = line.indexOf("-}");
958
+ if (end < 0)
959
+ return [{ kind: "comment", text: line }];
960
+ carry.inBlockComment = false;
961
+ const spans = [{ kind: "comment", text: line.slice(0, end + 2) }];
962
+ if (end + 2 < line.length)
963
+ spans.push(...highlightHaskell(line.slice(end + 2), carry));
964
+ return spans;
965
+ }
966
+ if (line.includes("{-")) {
967
+ const start = line.indexOf("{-");
968
+ const spans = [];
969
+ if (start > 0) {
970
+ // -- style via custom
971
+ const before = line.slice(0, start);
972
+ spans.push(...highlightHaskellLine(before));
973
+ }
974
+ const end = line.indexOf("-}", start + 2);
975
+ if (end < 0) {
976
+ carry.inBlockComment = true;
977
+ spans.push({ kind: "comment", text: line.slice(start) });
978
+ return spans;
979
+ }
980
+ spans.push({ kind: "comment", text: line.slice(start, end + 2) });
981
+ if (end + 2 < line.length)
982
+ spans.push(...highlightHaskell(line.slice(end + 2), carry));
983
+ return spans;
984
+ }
985
+ return highlightHaskellLine(line);
986
+ }
987
+ function highlightHaskellLine(line) {
988
+ const idx = line.indexOf("--");
989
+ if (idx >= 0) {
990
+ const spans = idx > 0 ? highlightHashCommentLang(line.slice(0, idx), KW.haskell) : [];
991
+ // wrong comment char — use plain keyword pass
992
+ spans.push({ kind: "comment", text: line.slice(idx) });
993
+ return spans;
994
+ }
995
+ return highlightHashCommentLang(line, KW.haskell);
996
+ }
997
+ function highlightLisp(line) {
998
+ const t = line.trimStart();
999
+ if (t.startsWith(";"))
1000
+ return [{ kind: "comment", text: line }];
1001
+ const spans = [];
1002
+ let i = 0;
1003
+ const n = line.length;
1004
+ while (i < n) {
1005
+ if (line[i] === ";") {
1006
+ push(spans, "comment", line.slice(i));
1007
+ return spans;
1008
+ }
1009
+ if (line[i] === '"') {
1010
+ i = readString(line, i, '"', spans);
1011
+ continue;
1012
+ }
1013
+ if (line[i] === "(" || line[i] === ")" || line[i] === "[" || line[i] === "]") {
1014
+ push(spans, "punctuation", line[i]);
1015
+ i += 1;
1016
+ continue;
1017
+ }
1018
+ if (isIdentStart(line[i]) || line[i] === ":" || line[i] === "*") {
1019
+ let j = i + 1;
1020
+ while (j < n && /[^\s()\[\]";]/.test(line[j]))
1021
+ j += 1;
1022
+ const word = line.slice(i, j);
1023
+ // first symbol after ( is function-ish
1024
+ const prev = spans[spans.length - 1]?.text;
1025
+ if (prev === "(")
1026
+ push(spans, "function", word);
1027
+ else if (word.startsWith(":"))
1028
+ push(spans, "property", word);
1029
+ else
1030
+ push(spans, "plain", word);
1031
+ i = j;
1032
+ continue;
1033
+ }
1034
+ if (/\d/.test(line[i])) {
1035
+ let j = i;
1036
+ while (j < n && /[\d./]/.test(line[j]))
1037
+ j += 1;
1038
+ push(spans, "number", line.slice(i, j));
1039
+ i = j;
1040
+ continue;
1041
+ }
1042
+ push(spans, "plain", line[i]);
1043
+ i += 1;
1044
+ }
1045
+ return spans;
1046
+ }
1047
+ function highlightMarkdown(line) {
1048
+ const t = line.trimStart();
1049
+ if (t.startsWith("#")) {
1050
+ return [{ kind: "keyword", text: line }];
1051
+ }
1052
+ if (t.startsWith("```") || t.startsWith("~~~")) {
1053
+ return [{ kind: "punctuation", text: line }];
1054
+ }
1055
+ if (t.startsWith(">")) {
1056
+ return [{ kind: "comment", text: line }];
1057
+ }
1058
+ if (t.startsWith("- ") || t.startsWith("* ") || /^\d+\.\s/.test(t)) {
1059
+ const spans = [];
1060
+ const indent = line.length - t.length;
1061
+ if (indent)
1062
+ push(spans, "plain", line.slice(0, indent));
1063
+ const m = t.match(/^([-*]|\d+\.)\s/);
1064
+ if (m) {
1065
+ push(spans, "keyword", m[0]);
1066
+ push(spans, "plain", t.slice(m[0].length));
1067
+ }
1068
+ else {
1069
+ push(spans, "plain", t);
1070
+ }
1071
+ return spans;
1072
+ }
1073
+ // inline `code` and **bold** lightly
1074
+ const spans = [];
1075
+ let i = 0;
1076
+ const n = line.length;
1077
+ while (i < n) {
1078
+ if (line[i] === "`") {
1079
+ const end = line.indexOf("`", i + 1);
1080
+ if (end > i) {
1081
+ push(spans, "string", line.slice(i, end + 1));
1082
+ i = end + 1;
1083
+ continue;
1084
+ }
1085
+ }
1086
+ if (line.startsWith("**", i)) {
1087
+ const end = line.indexOf("**", i + 2);
1088
+ if (end > i) {
1089
+ push(spans, "keyword", line.slice(i, end + 2));
1090
+ i = end + 2;
1091
+ continue;
1092
+ }
1093
+ }
1094
+ if (line[i] === "[") {
1095
+ const end = line.indexOf(")", i);
1096
+ if (end > i && line.includes("](", i)) {
1097
+ push(spans, "property", line.slice(i, end + 1));
1098
+ i = end + 1;
1099
+ continue;
1100
+ }
1101
+ }
1102
+ push(spans, "plain", line[i]);
1103
+ i += 1;
1104
+ }
1105
+ return spans;
1106
+ }
1107
+ function highlightJsonLike(line) {
1108
+ const spans = [];
1109
+ let i = 0;
1110
+ const n = line.length;
1111
+ while (i < n) {
1112
+ const ch = line[i];
1113
+ if (ch === '"') {
1114
+ let j = i + 1;
1115
+ while (j < n) {
1116
+ if (line[j] === "\\") {
1117
+ j += 2;
1118
+ continue;
1119
+ }
1120
+ if (line[j] === '"') {
1121
+ j += 1;
1122
+ break;
1123
+ }
1124
+ j += 1;
1125
+ }
1126
+ let k = j;
1127
+ while (k < n && /\s/.test(line[k]))
1128
+ k += 1;
1129
+ push(spans, line[k] === ":" ? "property" : "string", line.slice(i, j));
1130
+ i = j;
1131
+ continue;
1132
+ }
1133
+ if (/\d/.test(ch) || (ch === "-" && line[i + 1] && /\d/.test(line[i + 1]))) {
1134
+ let j = i + 1;
1135
+ while (j < n && /[\d.eE+-]/.test(line[j]))
1136
+ j += 1;
1137
+ push(spans, "number", line.slice(i, j));
1138
+ i = j;
1139
+ continue;
1140
+ }
1141
+ if (line.startsWith("true", i) || line.startsWith("false", i) || line.startsWith("null", i)) {
1142
+ const w = line.startsWith("false", i) ? "false" : line.startsWith("true", i) ? "true" : "null";
1143
+ push(spans, "keyword", w);
1144
+ i += w.length;
1145
+ continue;
1146
+ }
1147
+ if (/[{}\[\]:,]/.test(ch)) {
1148
+ push(spans, "punctuation", ch);
1149
+ i += 1;
1150
+ continue;
1151
+ }
1152
+ push(spans, "plain", ch);
1153
+ i += 1;
1154
+ }
1155
+ return spans;
1156
+ }
1157
+ function highlightHtml(line, carry) {
1158
+ if (carry.inBlockComment || line.includes("<!--")) {
1159
+ if (carry.inBlockComment) {
1160
+ const end = line.indexOf("-->");
1161
+ if (end < 0)
1162
+ return [{ kind: "comment", text: line }];
1163
+ carry.inBlockComment = false;
1164
+ const spans = [{ kind: "comment", text: line.slice(0, end + 3) }];
1165
+ if (end + 3 < line.length)
1166
+ spans.push(...highlightHtml(line.slice(end + 3), carry));
1167
+ return spans;
1168
+ }
1169
+ const start = line.indexOf("<!--");
1170
+ if (start >= 0) {
1171
+ const spans = [];
1172
+ if (start > 0)
1173
+ spans.push(...highlightHtml(line.slice(0, start), carry));
1174
+ const end = line.indexOf("-->", start + 4);
1175
+ if (end < 0) {
1176
+ carry.inBlockComment = true;
1177
+ spans.push({ kind: "comment", text: line.slice(start) });
1178
+ return spans;
1179
+ }
1180
+ spans.push({ kind: "comment", text: line.slice(start, end + 3) });
1181
+ if (end + 3 < line.length)
1182
+ spans.push(...highlightHtml(line.slice(end + 3), carry));
1183
+ return spans;
1184
+ }
1185
+ }
1186
+ const spans = [];
1187
+ let i = 0;
1188
+ const n = line.length;
1189
+ while (i < n) {
1190
+ if (line[i] === "<") {
1191
+ const close = line.indexOf(">", i);
1192
+ const end = close < 0 ? n : close + 1;
1193
+ spans.push(...highlightTag(line.slice(i, end)));
1194
+ i = end;
1195
+ continue;
1196
+ }
1197
+ let j = i + 1;
1198
+ while (j < n && line[j] !== "<")
1199
+ j += 1;
1200
+ push(spans, "plain", line.slice(i, j));
1201
+ i = j;
1202
+ }
1203
+ return spans;
1204
+ }
1205
+ function highlightTag(tag) {
1206
+ const spans = [];
1207
+ let i = 0;
1208
+ const n = tag.length;
1209
+ while (i < n) {
1210
+ const ch = tag[i];
1211
+ if (ch === "<" || ch === ">" || ch === "/") {
1212
+ push(spans, "punctuation", ch);
1213
+ i += 1;
1214
+ continue;
1215
+ }
1216
+ if (ch === '"' || ch === "'") {
1217
+ i = readString(tag, i, ch, spans);
1218
+ continue;
1219
+ }
1220
+ if (isIdentStart(ch)) {
1221
+ let j = i + 1;
1222
+ while (j < n && /[A-Za-z0-9_:-]/.test(tag[j]))
1223
+ j += 1;
1224
+ const word = tag.slice(i, j);
1225
+ const prev = spans[spans.length - 1]?.text;
1226
+ if (prev === "<" || prev === "/")
1227
+ push(spans, "keyword", word);
1228
+ else
1229
+ push(spans, "property", word);
1230
+ i = j;
1231
+ continue;
1232
+ }
1233
+ push(spans, "plain", ch);
1234
+ i += 1;
1235
+ }
1236
+ return spans;
1237
+ }
1238
+ function highlightYaml(line) {
1239
+ const trimmed = line.trimStart();
1240
+ if (trimmed.startsWith("#"))
1241
+ return [{ kind: "comment", text: line }];
1242
+ const spans = [];
1243
+ const indent = line.length - trimmed.length;
1244
+ if (indent)
1245
+ push(spans, "plain", line.slice(0, indent));
1246
+ if (trimmed.startsWith("- ")) {
1247
+ push(spans, "punctuation", "-");
1248
+ push(spans, "plain", " ");
1249
+ spans.push(...highlightYaml(trimmed.slice(2)));
1250
+ // fix indent double — simpler:
1251
+ return [
1252
+ ...(indent ? [{ kind: "plain", text: line.slice(0, indent) }] : []),
1253
+ { kind: "punctuation", text: "-" },
1254
+ { kind: "plain", text: " " },
1255
+ ...highlightJsonLike(trimmed.slice(2)),
1256
+ ];
1257
+ }
1258
+ const colon = trimmed.indexOf(":");
1259
+ if (colon > 0) {
1260
+ push(spans, "property", trimmed.slice(0, colon));
1261
+ push(spans, "punctuation", ":");
1262
+ const rest = trimmed.slice(colon + 1);
1263
+ if (rest)
1264
+ spans.push(...highlightJsonLike(rest));
1265
+ return spans;
1266
+ }
1267
+ spans.push(...highlightJsonLike(trimmed));
1268
+ return spans;
1269
+ }
1270
+ function highlightIniToml(line) {
1271
+ const t = line.trimStart();
1272
+ if (t.startsWith("#") || t.startsWith(";"))
1273
+ return [{ kind: "comment", text: line }];
1274
+ if (t.startsWith("[") && t.includes("]")) {
1275
+ return [{ kind: "keyword", text: line }];
1276
+ }
1277
+ const eq = line.indexOf("=");
1278
+ if (eq > 0) {
1279
+ return [
1280
+ { kind: "property", text: line.slice(0, eq) },
1281
+ { kind: "operator", text: "=" },
1282
+ ...highlightJsonLike(line.slice(eq + 1)),
1283
+ ];
1284
+ }
1285
+ return highlightJsonLike(line);
1286
+ }
1287
+ function highlightDiff(line) {
1288
+ if (line.startsWith("+++") || line.startsWith("---") || line.startsWith("diff ") || line.startsWith("index ")) {
1289
+ return [{ kind: "keyword", text: line }];
1290
+ }
1291
+ if (line.startsWith("@@"))
1292
+ return [{ kind: "function", text: line }];
1293
+ if (line.startsWith("+"))
1294
+ return [{ kind: "string", text: line }];
1295
+ if (line.startsWith("-"))
1296
+ return [{ kind: "regex", text: line }];
1297
+ return [{ kind: "plain", text: line }];
1298
+ }
1299
+ /** List of extensions we explicitly map (for tests / docs). */
1300
+ export function supportedExtensions() {
1301
+ return Object.keys(EXT_FAMILY).sort();
1302
+ }
1303
+ //# sourceMappingURL=syntax-highlight.js.map