@miliastry/quasar 1.0.6 → 1.1.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 (273) hide show
  1. package/dist/Visuals/lyne.css +117 -6
  2. package/dist/Visuals/osu.css +36 -5
  3. package/dist/index.js +9664 -3282
  4. package/dist/index.mjs +9535 -3283
  5. package/dist/types/Analysis/Contracts/AnalysisReport.d.ts +22 -0
  6. package/dist/types/Analysis/Contracts/Contribution.d.ts +68 -0
  7. package/dist/types/Analysis/Contracts/Pass.d.ts +59 -0
  8. package/dist/types/Analysis/Contracts/PipelineContext.d.ts +34 -0
  9. package/dist/types/Analysis/Passes/Analysis/ColorUsageAnalyzer.d.ts +56 -0
  10. package/dist/types/Analysis/Passes/Analysis/GradientAnalyzer.d.ts +111 -0
  11. package/dist/types/Analysis/Passes/Analysis/MergeableColorAnalyzer.d.ts +26 -0
  12. package/dist/types/Analysis/Passes/Analysis/RainbowAnalyzer.d.ts +47 -0
  13. package/dist/types/Analysis/Passes/Analysis/SymbolAnalyzer.d.ts +89 -0
  14. package/dist/types/Analysis/Passes/Analysis/WaveAnalyzer.d.ts +44 -0
  15. package/dist/types/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.d.ts +4 -0
  16. package/dist/types/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.d.ts +4 -0
  17. package/dist/types/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.d.ts +4 -0
  18. package/dist/types/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.d.ts +4 -0
  19. package/dist/types/Analysis/Passes/Decision/DefaultDecision.d.ts +30 -0
  20. package/dist/types/Analysis/Passes/Decision/PaletteRemapDecision.d.ts +169 -0
  21. package/dist/types/Analysis/Passes/Decision/__tests__/DefaultDecision.test.d.ts +4 -0
  22. package/dist/types/Analysis/Passes/Transform/CollapseGradientTransform.d.ts +37 -0
  23. package/dist/types/Analysis/Passes/Transform/MergeColorsTransform.d.ts +28 -0
  24. package/dist/types/Analysis/Passes/Transform/RainbowCollapseTransform.d.ts +19 -0
  25. package/dist/types/Analysis/Passes/Transform/WaveCollapseTransform.d.ts +20 -0
  26. package/dist/types/Analysis/Passes/Utility/CharacterCountAnalyzer.d.ts +19 -0
  27. package/dist/types/Analysis/Pipeline/Pipeline.d.ts +69 -0
  28. package/dist/types/Analysis/Pipeline/PipelineBuilder.d.ts +31 -0
  29. package/dist/types/Analysis/Pipeline/PipelineStage.d.ts +17 -0
  30. package/dist/types/Analysis/Utils/color-utils.d.ts +60 -0
  31. package/dist/types/Analysis/__tests__/Integration.test.d.ts +8 -0
  32. package/dist/types/Analysis/__tests__/PaletteRemap.test.d.ts +14 -0
  33. package/dist/types/Analysis/__tests__/Pipeline.test.d.ts +10 -0
  34. package/dist/types/Analysis/__tests__/RoundTrip.test.d.ts +30 -0
  35. package/dist/types/Analysis/__tests__/SymbolAnalyzer.test.d.ts +11 -0
  36. package/dist/types/Analysis/index.d.ts +40 -0
  37. package/dist/types/BBCode/BBCodeDocumentModel.d.ts +92 -0
  38. package/dist/types/BBCode/BBCodeToGreenNode.d.ts +111 -0
  39. package/dist/types/BBCode/Parser.d.ts +78 -0
  40. package/dist/types/BBCode/index.d.ts +4 -0
  41. package/dist/types/Collab/positions.d.ts +55 -0
  42. package/dist/types/Commands/Command.d.ts +37 -0
  43. package/dist/types/Commands/CommandRegistry.d.ts +48 -0
  44. package/dist/types/Commands/DeleteNode.d.ts +7 -0
  45. package/dist/types/Commands/InsertText.d.ts +8 -0
  46. package/dist/types/Commands/SplitMerge.d.ts +9 -0
  47. package/dist/types/Commands/WrapInTag.d.ts +8 -0
  48. package/dist/types/Commands/index.d.ts +6 -0
  49. package/dist/types/Diff/TreeDiffer.d.ts +52 -0
  50. package/dist/types/Diff/__tests__/TreeDiffer.test.d.ts +1 -0
  51. package/dist/types/Diff/index.d.ts +2 -0
  52. package/dist/types/Edits/EditPlan.d.ts +144 -0
  53. package/dist/types/Edits/Optimizer.d.ts +78 -0
  54. package/dist/types/Edits/Rules/Rule.d.ts +91 -0
  55. package/dist/types/Edits/Rules/dropEmptyTags.d.ts +40 -0
  56. package/dist/types/Edits/Rules/dropRedundantNesting.d.ts +59 -0
  57. package/dist/types/Edits/Rules/index.d.ts +9 -0
  58. package/dist/types/Edits/Rules/mergeAdjacent.d.ts +93 -0
  59. package/dist/types/Edits/Rules/reorderWrappers.d.ts +42 -0
  60. package/dist/types/Edits/Rules/shortenHex.d.ts +30 -0
  61. package/dist/types/Edits/Rules/tagValue.d.ts +56 -0
  62. package/dist/types/Edits/Rules/unwrapInvisibleColor.d.ts +47 -0
  63. package/dist/types/Edits/applyEdits.d.ts +36 -0
  64. package/dist/types/Edits/index.d.ts +6 -0
  65. package/dist/types/Events/EventBus.d.ts +103 -0
  66. package/dist/types/Events/index.d.ts +2 -0
  67. package/dist/types/Formatter/Formatter.d.ts +32 -0
  68. package/dist/types/Formatter/index.d.ts +2 -0
  69. package/dist/types/HTML/HTMLDocumentModel.d.ts +13 -0
  70. package/dist/types/HTML/HTMLToGreenNode.d.ts +4 -0
  71. package/dist/types/Incremental/BracketIndex.d.ts +101 -0
  72. package/dist/types/Incremental/ChangeTracker.d.ts +69 -0
  73. package/dist/types/Incremental/IncrementalParser.d.ts +246 -0
  74. package/dist/types/Incremental/__tests__/BracketIndex.test.d.ts +1 -0
  75. package/dist/types/Incremental/__tests__/IncrementalParser.test.d.ts +1 -0
  76. package/dist/types/Incremental/index.d.ts +4 -0
  77. package/dist/types/Lexer/BBCodeLexer.d.ts +74 -0
  78. package/dist/types/Lexer/Lexer.d.ts +54 -0
  79. package/dist/types/Lexer/index.d.ts +4 -0
  80. package/dist/types/Linter/Linter.d.ts +58 -0
  81. package/dist/types/Linter/index.d.ts +2 -0
  82. package/dist/types/Markdown/MarkdownAST.d.ts +122 -0
  83. package/dist/types/Markdown/MarkdownDocumentModel.d.ts +15 -0
  84. package/dist/types/Markdown/MarkdownLexer.d.ts +155 -0
  85. package/dist/types/Markdown/MarkdownParser.d.ts +42 -0
  86. package/dist/types/Markdown/MarkdownToGreenNode.d.ts +6 -0
  87. package/dist/types/MilHibri/MilHibriDocumentModel.d.ts +21 -0
  88. package/dist/types/Model/DocumentModel.d.ts +318 -0
  89. package/dist/types/Model/NodeFactory.d.ts +29 -0
  90. package/dist/types/Model/TagRegistry.d.ts +165 -0
  91. package/dist/types/Model/index.d.ts +5 -0
  92. package/dist/types/Plugins/PluginAPI.d.ts +44 -0
  93. package/dist/types/Plugins/PluginRegistry.d.ts +81 -0
  94. package/dist/types/Plugins/index.d.ts +3 -0
  95. package/dist/types/Queries/QueryEngine.d.ts +32 -0
  96. package/dist/types/Queries/index.d.ts +1 -0
  97. package/dist/types/Reconciler/SurgicalReconciler.d.ts +23 -0
  98. package/dist/types/RenderPipeline/RenderPipeline.d.ts +46 -0
  99. package/dist/types/RenderPipeline/RenderTree.d.ts +55 -0
  100. package/dist/types/RenderPipeline/index.d.ts +4 -0
  101. package/dist/types/Repair/NestingRepair.d.ts +48 -0
  102. package/dist/types/Repair/index.d.ts +1 -0
  103. package/dist/types/Semantic/SemanticAnalyzer.d.ts +321 -0
  104. package/dist/types/Semantic/index.d.ts +2 -0
  105. package/dist/types/Symbols/SymbolTable.d.ts +47 -0
  106. package/dist/types/Symbols/index.d.ts +1 -0
  107. package/dist/types/Syntax/GreenNode.d.ts +154 -0
  108. package/dist/types/Syntax/GreenNodePool.d.ts +134 -0
  109. package/dist/types/Syntax/NodeMatcher.d.ts +75 -0
  110. package/dist/types/Syntax/RedNode.d.ts +280 -0
  111. package/dist/types/Syntax/RedNodeStore.d.ts +116 -0
  112. package/dist/types/Syntax/TreeBuilder.d.ts +54 -0
  113. package/dist/types/Syntax/__tests__/GreenNode.test.d.ts +1 -0
  114. package/dist/types/Syntax/__tests__/RedNode.test.d.ts +1 -0
  115. package/dist/types/Syntax/__tests__/RedNodeStore.test.d.ts +1 -0
  116. package/dist/types/Syntax/greenEdit.d.ts +64 -0
  117. package/dist/types/Syntax/hash.d.ts +17 -0
  118. package/dist/types/Syntax/index.d.ts +8 -0
  119. package/dist/types/Syntax/nodeAttr.d.ts +63 -0
  120. package/dist/types/Syntax/partition.d.ts +67 -0
  121. package/dist/types/Syntax/preserveNodeIds.d.ts +47 -0
  122. package/dist/types/Tokens/TokenResolver.d.ts +25 -0
  123. package/dist/types/Tokens/index.d.ts +1 -0
  124. package/dist/types/Transactions/Transaction.d.ts +36 -0
  125. package/dist/types/Transactions/UndoManager.d.ts +63 -0
  126. package/dist/types/Transactions/index.d.ts +3 -0
  127. package/dist/types/Transformers/ASTOptimizer.d.ts +34 -0
  128. package/dist/types/Transformers/GradientTransformer.d.ts +18 -0
  129. package/dist/types/Transformers/GrowTransformer.d.ts +16 -0
  130. package/dist/types/Transformers/RainbowTransformer.d.ts +17 -0
  131. package/dist/types/Transformers/SineWaveTransformer.d.ts +17 -0
  132. package/dist/types/Transformers/Transformer.d.ts +17 -0
  133. package/dist/types/Types/core.d.ts +104 -0
  134. package/dist/types/Types/diagnostics.d.ts +85 -0
  135. package/dist/types/Types/index.d.ts +11 -0
  136. package/dist/types/Types/operations.d.ts +109 -0
  137. package/dist/types/Types/queries.d.ts +67 -0
  138. package/dist/types/Types/symbols.d.ts +42 -0
  139. package/dist/types/Types/tokens.d.ts +75 -0
  140. package/dist/types/Utils/BBCodeGenerator.d.ts +21 -0
  141. package/dist/types/Utils/ColorMath.d.ts +92 -0
  142. package/dist/types/Utils/EffectMath.d.ts +643 -0
  143. package/dist/types/Utils/TagVocabulary.d.ts +104 -0
  144. package/dist/types/Utils/TagVocabulary.test.d.ts +1 -0
  145. package/dist/types/Utils/color.d.ts +29 -0
  146. package/dist/types/Utils/dom-to-svg.d.ts +28 -0
  147. package/dist/types/Utils/dom-to-svg.test.d.ts +1 -0
  148. package/dist/types/Utils/treeTransformers.d.ts +160 -0
  149. package/dist/types/Visitors/BBBlocksExporter.d.ts +27 -0
  150. package/dist/types/Visitors/BBCodeExporter.d.ts +90 -0
  151. package/dist/types/Visitors/BlockPatcher.d.ts +91 -0
  152. package/dist/types/Visitors/DOMMorpher.d.ts +13 -0
  153. package/dist/types/Visitors/HTMLRenderer.d.ts +380 -0
  154. package/dist/types/Visitors/JSONExporter.d.ts +42 -0
  155. package/dist/types/Visitors/MarkdownExporter.d.ts +28 -0
  156. package/dist/types/Visitors/SVGRenderer.d.ts +16 -0
  157. package/dist/types/Visitors/TiptapExporter.d.ts +40 -0
  158. package/dist/types/Visitors/Visitor.d.ts +35 -0
  159. package/dist/types/Visitors/index.d.ts +9 -0
  160. package/dist/types/Visuals/BoxDrawer.d.ts +49 -0
  161. package/dist/types/Visuals/index.d.ts +38 -0
  162. package/dist/types/index.d.ts +154 -0
  163. package/package.json +14 -10
  164. package/src/Analysis/Passes/Analysis/ColorUsageAnalyzer.ts +103 -0
  165. package/src/Analysis/Passes/Analysis/GradientAnalyzer.ts +294 -20
  166. package/src/Analysis/Passes/Analysis/SymbolAnalyzer.ts +257 -0
  167. package/src/Analysis/Passes/Decision/PaletteRemapDecision.ts +425 -0
  168. package/src/Analysis/Pipeline/Pipeline.ts +15 -2
  169. package/src/Analysis/index.ts +8 -2
  170. package/src/BBCode/BBCodeDocumentModel.ts +3 -0
  171. package/src/BBCode/BBCodeToGreenNode.ts +18 -12
  172. package/src/BBCode/Parser.ts +38 -1
  173. package/src/Edits/EditPlan.ts +290 -0
  174. package/src/Edits/Optimizer.ts +185 -0
  175. package/src/Edits/Rules/Rule.ts +145 -0
  176. package/src/Edits/Rules/dropEmptyTags.ts +110 -0
  177. package/src/Edits/Rules/dropRedundantNesting.ts +120 -0
  178. package/src/Edits/Rules/index.ts +28 -0
  179. package/src/Edits/Rules/mergeAdjacent.ts +224 -0
  180. package/src/Edits/Rules/reorderWrappers.ts +154 -0
  181. package/src/Edits/Rules/shortenHex.ts +79 -0
  182. package/src/Edits/Rules/tagValue.ts +105 -0
  183. package/src/Edits/Rules/unwrapInvisibleColor.ts +93 -0
  184. package/src/Edits/applyEdits.ts +54 -0
  185. package/src/Edits/index.ts +22 -0
  186. package/src/Events/EventBus.ts +18 -0
  187. package/src/HTML/HTMLToGreenNode.ts +185 -37
  188. package/src/Incremental/BracketIndex.ts +249 -0
  189. package/src/Incremental/IncrementalParser.ts +464 -41
  190. package/src/Lexer/BBCodeLexer.ts +25 -3
  191. package/src/Lexer/index.ts +1 -1
  192. package/src/Markdown/MarkdownAST.ts +65 -2
  193. package/src/Markdown/MarkdownLexer.ts +69 -14
  194. package/src/Markdown/MarkdownParser.ts +749 -62
  195. package/src/Markdown/MarkdownToGreenNode.ts +148 -27
  196. package/src/MilHibri/MilHibriDocumentModel.ts +56 -0
  197. package/src/Model/DocumentModel.ts +138 -5
  198. package/src/Model/TagRegistry.ts +129 -134
  199. package/src/Reconciler/SurgicalReconciler.ts +460 -0
  200. package/src/Repair/NestingRepair.ts +116 -0
  201. package/src/Repair/index.ts +1 -0
  202. package/src/Semantic/SemanticAnalyzer.ts +1802 -65
  203. package/src/Syntax/GreenNodePool.ts +12 -12
  204. package/src/Syntax/RedNode.ts +17 -3
  205. package/src/Syntax/RedNodeStore.ts +9 -1
  206. package/src/Syntax/nodeAttr.ts +172 -0
  207. package/src/Tokens/TokenResolver.ts +55 -0
  208. package/src/Tokens/index.ts +1 -0
  209. package/src/Types/core.ts +15 -0
  210. package/src/Utils/ColorMath.ts +276 -55
  211. package/src/Utils/EffectMath.ts +2462 -0
  212. package/src/Utils/TagVocabulary.ts +141 -0
  213. package/src/Utils/dom-to-svg.ts +28 -12
  214. package/src/Visitors/BBBlocksExporter.ts +15 -1
  215. package/src/Visitors/BBCodeExporter.ts +319 -32
  216. package/src/Visitors/BlockPatcher.ts +87 -11
  217. package/src/Visitors/DOMMorpher.ts +15 -5
  218. package/src/Visitors/HTMLRenderer.ts +374 -151
  219. package/src/Visitors/MarkdownExporter.ts +98 -41
  220. package/src/Visitors/TiptapExporter.ts +1 -0
  221. package/src/Visuals/index.ts +7 -4
  222. package/src/Visuals/lyne.css +117 -6
  223. package/src/Visuals/osu.css +36 -5
  224. package/src/index.ts +172 -5
  225. package/dist/index.d.mts +0 -4612
  226. package/dist/index.d.ts +0 -4612
  227. package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
  228. package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
  229. package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
  230. package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
  231. package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
  232. package/src/Analysis/__tests__/Integration.test.ts +0 -162
  233. package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
  234. package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
  235. package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
  236. package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
  237. package/src/Syntax/__tests__/RedNode.test.ts +0 -81
  238. package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
  239. package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
  240. package/src/Tests/BlockPatcher.test.ts +0 -437
  241. package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
  242. package/src/Tests/BoxDrawer.test.ts +0 -217
  243. package/src/Tests/BoxRichTitle.test.ts +0 -105
  244. package/src/Tests/Chars500kBenchmark.test.ts +0 -151
  245. package/src/Tests/Chars500kEdits.test.ts +0 -321
  246. package/src/Tests/CollabPositions.test.ts +0 -146
  247. package/src/Tests/CompilerPathProfiling.test.ts +0 -186
  248. package/src/Tests/DOMMorpher.test.ts +0 -142
  249. package/src/Tests/DomPatchPerf.test.ts +0 -60
  250. package/src/Tests/EffectSegments.snapshot.json +0 -616
  251. package/src/Tests/EffectSegments.test.ts +0 -68
  252. package/src/Tests/FindNodeAtOffset.test.ts +0 -65
  253. package/src/Tests/Fuzzer.test.ts +0 -166
  254. package/src/Tests/GreenNodePool.test.ts +0 -153
  255. package/src/Tests/Lexer.test.ts +0 -238
  256. package/src/Tests/LyneMode.test.ts +0 -291
  257. package/src/Tests/ModelCoherence.test.ts +0 -180
  258. package/src/Tests/Partition.test.ts +0 -238
  259. package/src/Tests/PluginTags.test.ts +0 -150
  260. package/src/Tests/ProblematicSection.test.ts +0 -46
  261. package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
  262. package/src/Tests/RedReuse.test.ts +0 -134
  263. package/src/Tests/ReproDelete20k.test.ts +0 -62
  264. package/src/Tests/SemanticValidators.test.ts +0 -136
  265. package/src/Tests/StableNodeIds.test.ts +0 -210
  266. package/src/Tests/StudioColorBloat.test.ts +0 -25
  267. package/src/Tests/StudioDebugText.test.ts +0 -27
  268. package/src/Tests/StudioTrailingChar.test.ts +0 -25
  269. package/src/Tests/StudioValidText.test.ts +0 -25
  270. package/src/Tests/UrlImgBug.test.ts +0 -23
  271. package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
  272. package/src/Tests/referenceDocument.ts +0 -119
  273. package/src/Utils/dom-to-svg.test.ts +0 -86
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Quasar — `unwrap-invisible-color`
3
+ *
4
+ * Removes a `[color]` whose entire content is whitespace.
5
+ *
6
+ * [color=#6A4C93]BARCA[/color][color=#6F518D] [/color][color=#755687]·[/color]
7
+ * ╰── 24 bytes to colour a space ──╯
8
+ *
9
+ * A space has no glyph, and `[color]` sets `color` and nothing else — no
10
+ * background, no decoration — so painting one is a guaranteed no-op. Deleting
11
+ * the two delimiters turns 24 bytes into 1 and cannot change a pixel.
12
+ *
13
+ * This is the dominant waste in expanded gradients, and it is easy to see why:
14
+ * the tool that expands `[gradient]` into per-character `[color]` spans walks
15
+ * characters without asking which of them draw ink, so every space in the ramp
16
+ * gets its own fully-spelled tag.
17
+ *
18
+ * ## Only colour
19
+ *
20
+ * The same shape is *not* removable for other tags, and the difference is not
21
+ * subtle:
22
+ *
23
+ * - `[u]` / `[s]` draw a line through the gap — visible on a space.
24
+ * - `[mark]` paints a background — visible on a space.
25
+ * - `[size]` and `[font]` change the advance width of the space itself.
26
+ *
27
+ * Only `color` is invisible on whitespace, so only `color` is unwrapped.
28
+ */
29
+ import type { GreenNode } from '../../Syntax/GreenNode';
30
+ import type { PlannedEdit } from '../EditPlan';
31
+ import { type OptimizationRule, type RuleContext } from './Rule';
32
+ export declare const UNWRAP_INVISIBLE_COLOR_PRIORITY = 85;
33
+ /**
34
+ * Whether a node contributes nothing but whitespace.
35
+ *
36
+ * Recursive through nested colours, so `[color=A][color=B] [/color][/color]`
37
+ * counts. `spacing` is whitespace too — a newline inside a colour tag is as
38
+ * invisible as a space.
39
+ */
40
+ export declare function isInvisibleWhitespace(node: GreenNode): boolean;
41
+ export declare class UnwrapInvisibleColorRule implements OptimizationRule {
42
+ readonly id = "unwrap-invisible-color";
43
+ readonly priority = 85;
44
+ readonly label = "Unwrap colour tags around whitespace";
45
+ run(context: RuleContext): PlannedEdit[];
46
+ private scan;
47
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Quasar — applying a resolved edit set to source text
3
+ *
4
+ * The string-rewriting half of the two appliers. Monaco is the other: it takes
5
+ * the very same `SurgicalEdit[]` and resolves the offsets against its model
6
+ * itself, which is why neither side ever re-serializes the document and the
7
+ * two cannot drift.
8
+ */
9
+ import type { SurgicalEdit } from '../Reconciler/SurgicalReconciler';
10
+ /**
11
+ * Apply edits carrying ORIGINAL offsets, in one pass.
12
+ *
13
+ * The obvious implementation splices the string once per edit:
14
+ *
15
+ * for (const edit of backToFront) out = out.slice(0, s) + text + out.slice(e)
16
+ *
17
+ * which is correct and quadratic — every splice copies the whole document, so
18
+ * a 250.000-character page with ~3.800 edits copies roughly a billion
19
+ * characters and takes about half a second. Measured, not guessed. Collecting
20
+ * the untouched spans and joining them once is linear and turns that into a
21
+ * few milliseconds, which is the difference between a minify command that
22
+ * feels instant and one that visibly stalls the editor.
23
+ *
24
+ * Going forwards rather than backwards is what makes the single pass possible,
25
+ * and it costs nothing: each edit is measured against the original string
26
+ * either way.
27
+ *
28
+ * ## Contract
29
+ *
30
+ * The edits must be pairwise conflict-free — run them through
31
+ * `resolveEditConflicts` first. An edit that reaches back into a span already
32
+ * written is a contract violation with no meaningful answer, so it is skipped
33
+ * rather than allowed to interleave into nonsense; the result stays a valid
34
+ * document that is merely missing one change.
35
+ */
36
+ export declare function applyEditsToSource(source: string, edits: readonly SurgicalEdit[]): string;
@@ -0,0 +1,6 @@
1
+ export { classifyOverlap, editsConflict, compareEditPriority, resolveEditConflicts, } from './EditPlan';
2
+ export type { PlannedEdit, RejectedEdit, ResolvedEditPlan, EditRejectionReason, OverlapRelation, } from './EditPlan';
3
+ export { applyEditsToSource } from './applyEdits';
4
+ export { optimizeBBCode, optimizeTree, defaultRules, allRules, } from './Optimizer';
5
+ export type { OptimizationResult, OptimizeOptions, RuleStat } from './Optimizer';
6
+ export * from './Rules';
@@ -0,0 +1,103 @@
1
+ /**
2
+ * DocumentEngine — EventBus
3
+ *
4
+ * Publish/subscribe event system for document changes.
5
+ * Each DocumentModel has its own EventBus instance.
6
+ *
7
+ * Events include:
8
+ * - document_changed (rebuild, edit, undo/redo)
9
+ * - node_changed (insert, delete, update, move)
10
+ * - diagnostic_updated
11
+ * - transaction_applied
12
+ * - cursor_moved
13
+ * - selection_changed
14
+ */
15
+ import type { DocumentChangeKind, NodeId } from '../Types/core';
16
+ import type { Operation } from '../Types/operations';
17
+ import type { NodeMatch } from '../Syntax/NodeMatcher';
18
+ import type { TextChange } from '../Incremental/ChangeTracker';
19
+ export type DocumentEventType = 'document_changed' | 'node_inserted' | 'node_deleted' | 'node_updated' | 'diagnostics_updated' | 'transaction_applied' | 'undo_performed' | 'redo_performed' | 'cursor_moved' | 'selection_changed';
20
+ export interface DocumentEvent {
21
+ type: DocumentEventType;
22
+ kind?: DocumentChangeKind;
23
+ version?: number;
24
+ source?: string;
25
+ nodeId?: NodeId;
26
+ nodeMatch?: NodeMatch | null;
27
+ change?: TextChange;
28
+ operations?: Operation[];
29
+ /**
30
+ * Where the change came from: `'local'` for user editing, anything else for
31
+ * programmatic/synced sources. Lets a collaboration layer ignore the echo
32
+ * of changes it applied itself. Absent on events with no single cause.
33
+ */
34
+ origin?: string;
35
+ /**
36
+ * On `diagnostics_updated`: how the analysis that produced them was
37
+ * obtained — `'full'` (every node revalidated) or `'window'` (only the
38
+ * nodes the edit could have changed; every other node kept its verdict).
39
+ *
40
+ * The `diagnostics` on the event are the WHOLE document's either way, so a
41
+ * subscriber renders them the same in both cases. This is here for the
42
+ * cases where the difference is the point: a performance overlay, a test
43
+ * that must notice the incremental path going away, a log line explaining a
44
+ * slow frame. See `AnalyzeScope`.
45
+ */
46
+ analysisScope?: 'full' | 'window';
47
+ /**
48
+ * On `diagnostics_updated` with `analysisScope === 'window'`: the span of
49
+ * the current source that was re-validated, in current coordinates.
50
+ * `null`/absent when the whole document was.
51
+ */
52
+ analysisWindow?: {
53
+ start: number;
54
+ end: number;
55
+ } | null;
56
+ timestamp: number;
57
+ [key: string]: unknown;
58
+ }
59
+ export type DocumentEventHandler = (event: DocumentEvent) => void;
60
+ export declare class DocumentEventBus {
61
+ private listeners;
62
+ private history;
63
+ private maxHistory;
64
+ /**
65
+ * Record emitted events so `getHistory()` returns them. Off by default:
66
+ * nothing reads the history today, and a retained event pins its `source`
67
+ * string — and, through the lazy `nodeMatch` accessor, the entire previous
68
+ * red/green tree — so an always-on history held megabytes per document
69
+ * for nobody. Turn it on for debugging or replay tooling.
70
+ */
71
+ recordHistory: boolean;
72
+ /**
73
+ * Subscribe to an event type.
74
+ */
75
+ on(type: DocumentEventType, handler: DocumentEventHandler): () => void;
76
+ /**
77
+ * Whether any handler is subscribed to `type` (or to anything, if omitted).
78
+ *
79
+ * Emitters use this to skip building event payloads nobody will see — the
80
+ * common case, since a headless model has no subscribers at all.
81
+ */
82
+ hasListeners(type?: DocumentEventType): boolean;
83
+ /**
84
+ * Subscribe to all events.
85
+ */
86
+ onAny(handler: DocumentEventHandler): () => void;
87
+ /**
88
+ * Emit an event.
89
+ */
90
+ emit(event: DocumentEvent): void;
91
+ /**
92
+ * Remove a specific handler.
93
+ */
94
+ off(type: DocumentEventType, handler: DocumentEventHandler): void;
95
+ /**
96
+ * Get event history.
97
+ */
98
+ getHistory(): DocumentEvent[];
99
+ /**
100
+ * Clear all listeners and history.
101
+ */
102
+ clear(): void;
103
+ }
@@ -0,0 +1,2 @@
1
+ export { DocumentEventBus } from './EventBus';
2
+ export type { DocumentEvent, DocumentEventType, DocumentEventHandler } from './EventBus';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * DocumentEngine — Formatter
3
+ *
4
+ * Formats the document tree into "pretty" BBCode.
5
+ * Normalizes indentation, spacing, and whitespace.
6
+ *
7
+ * Uses the TagRegistry and Visitor pattern.
8
+ * Plugins can register custom formatters for custom tags.
9
+ */
10
+ import { RedNode } from '../Syntax/RedNode';
11
+ export interface FormatOptions {
12
+ /** Whether to add newlines between block elements */
13
+ blockNewlines?: boolean;
14
+ /** Whether to normalize whitespace inside inline elements */
15
+ normalizeInlineWhitespace?: boolean;
16
+ /** Maximum line width (0 = no limit) */
17
+ maxLineWidth?: number;
18
+ /** Whether to collapse consecutive empty lines */
19
+ collapseEmptyLines?: boolean;
20
+ /** Indentation string */
21
+ indent?: string;
22
+ }
23
+ export declare class Formatter {
24
+ /**
25
+ * Format a RedNode tree (extracts text while preserving exact layout/spacing).
26
+ */
27
+ format(root: RedNode): string;
28
+ /**
29
+ * Format a single node at a given depth.
30
+ */
31
+ private formatNode;
32
+ }
@@ -0,0 +1,2 @@
1
+ export { Formatter } from './Formatter';
2
+ export type { FormatOptions } from './Formatter';
@@ -0,0 +1,13 @@
1
+ import { DocumentModel, type DocumentModelOptions } from '../Model/DocumentModel';
2
+ import { GreenNode } from '../Syntax/GreenNode';
3
+ import { RedNode } from '../Syntax/RedNode';
4
+ export interface HTMLDocumentModelOptions extends DocumentModelOptions {
5
+ source?: string;
6
+ language?: string;
7
+ }
8
+ export declare class HTMLDocumentModel extends DocumentModel {
9
+ constructor(options?: HTMLDocumentModelOptions);
10
+ static fromHTML(source: string): HTMLDocumentModel;
11
+ protected parseToGreen(source: string): GreenNode;
12
+ protected buildRedFromGreen(green: GreenNode): RedNode;
13
+ }
@@ -0,0 +1,4 @@
1
+ import { GreenNode } from '../Syntax/GreenNode';
2
+ import { RedNode } from '../Syntax/RedNode';
3
+ export declare function htmlStringToGreenTree(html: string): GreenNode;
4
+ export declare function greenToRedNode(green: GreenNode, parent?: RedNode | null): RedNode;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * DocumentEngine — BracketDepthIndex
3
+ *
4
+ * Answers the incremental parser's one question about the text OUTSIDE its
5
+ * window — "does every `[` before this offset find its `]` before it too?" —
6
+ * without reading the text before the offset.
7
+ *
8
+ * ─── Why the scan had to go ─────────────────────────────────────────────────
9
+ *
10
+ * `bracketsCloseBefore` walked the whole prefix on every keystroke: with the
11
+ * caret at the end of the 547 KB fixture that is 547.000 `charCodeAt` calls
12
+ * per key, measured at 2.2 ms — more than the parse, the splice and the red
13
+ * build of that same keystroke put together. Caching was declined once on the
14
+ * grounds that the reasoning is subtle: an edit can expose a `]` that a
15
+ * deleted `[` used to claim, so any cache keyed on "the prefix has not
16
+ * changed" is wrong the moment it is asked about a prefix that has.
17
+ *
18
+ * ─── Why this is not that cache ─────────────────────────────────────────────
19
+ *
20
+ * The scan computes a CLAMPED running depth — `d ← max(0, d + a)` where `a`
21
+ * is +1 for `[`, −1 for `]`, 0 otherwise — and a clamped running sum has an
22
+ * exact, composable summary. Over any stretch of text with prefix sums
23
+ * P₁…Pₙ, the depth after the stretch is a function of the depth before it:
24
+ *
25
+ * d' = Pₙ + max(d, −min(0, P₁, …, Pₙ))
26
+ *
27
+ * so a stretch is fully described by two integers, its net sum and its lowest
28
+ * dip. The index keeps the source as a sequence of PIECES (a few KB each),
29
+ * each carrying that pair. An edit re-summarises only the piece(s) it touched,
30
+ * from the NEW text, and every other piece keeps a summary that is still
31
+ * exactly true of its (unchanged, merely displaced) characters. A query walks
32
+ * the piece summaries up to the offset and scans only the tail of the piece
33
+ * the offset falls in. Nothing here is a guess about what the edit exposed:
34
+ * the touched piece is re-read verbatim, and composition is arithmetic.
35
+ *
36
+ * Cost per keystroke: one summary of ≤ 8 KB plus one walk over ~140 piece
37
+ * summaries at 547 KB — a few microseconds, and independent of where the
38
+ * caret is. Verified against the plain scan by `BracketIndex.test.ts`: 2.000
39
+ * random edits over documents full of stray brackets, every offset agreeing.
40
+ *
41
+ * The index does not know which document it describes; the parser keys it on
42
+ * the green root it was last synchronised with and rebuilds it (one scan) when
43
+ * the root it is handed is not that one. See `IncrementalParser.reparse`.
44
+ */
45
+ export declare class BracketDepthIndex {
46
+ /** Length of each piece, in characters. Pieces partition `[0, length)`. */
47
+ private lengths;
48
+ /** Net bracket sum of each piece: `[` counts +1, `]` counts −1. */
49
+ private sums;
50
+ /** Lowest prefix sum inside each piece, capped at 0 (see the header). */
51
+ private dips;
52
+ private _length;
53
+ /**
54
+ * Characters read by the last `depthAt` — the tail of one piece, never the
55
+ * prefix. Exposed so a regression back to an O(prefix) scan is something a
56
+ * test can assert on, rather than something a profile has to notice.
57
+ */
58
+ lastScanned: number;
59
+ /** The length of the text this index currently describes. */
60
+ get length(): number;
61
+ /** Number of pieces — for tests; it should stay ~`length / TARGET_PIECE`. */
62
+ get pieceCount(): number;
63
+ /** Describe `source` from scratch. O(n) — one scan, the same the query used to be. */
64
+ rebuild(source: string): void;
65
+ /**
66
+ * Bring the index from the text before an edit to `newSource`, the text
67
+ * after it. `start`/`endOld` bound the replaced region in OLD coordinates;
68
+ * `insertedLength` is the length of the text that replaced it.
69
+ *
70
+ * Only the pieces overlapping the replaced region are re-read. They are
71
+ * re-summarised as one piece over the same span in NEW coordinates, and
72
+ * that piece is split if it has grown past `MAX_PIECE` — so a long paste
73
+ * ends up as several normal pieces, not one giant one that every later
74
+ * query near it would have to scan.
75
+ */
76
+ applyChange(newSource: string, start: number, endOld: number, insertedLength: number): void;
77
+ /**
78
+ * The clamped bracket depth just before `end` — what the plain scan of
79
+ * `[0, end)` would return. `end === 0` is depth 0; `end > length` is
80
+ * clamped to the text.
81
+ */
82
+ depthAt(source: string, end: number): number;
83
+ /** Summarise `[from, to)` of `source` and append it as a piece. */
84
+ private pushPiece;
85
+ /**
86
+ * Replace `count` pieces at `index` with the summaries of `[from, to)` of
87
+ * `source`, cut into pieces of at most `MAX_PIECE`.
88
+ */
89
+ private spliceSummaries;
90
+ /**
91
+ * Merge the piece at `index` (and the one after it) into a neighbour when
92
+ * it has shrunk below `MIN_PIECE` and the pair stays under `MAX_PIECE`.
93
+ *
94
+ * Merging two summaries is the composition from the header applied to the
95
+ * pair: the sum adds, and the dip of the second is measured from the end of
96
+ * the first. No text is read.
97
+ */
98
+ private mergeSmallAround;
99
+ /** Fold piece `i + 1` into piece `i`. */
100
+ private mergePair;
101
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * DocumentEngine — ChangeTracker
3
+ *
4
+ * Tracks text changes made to the document source.
5
+ * Used by IncrementalParser to determine what needs to be re-parsed.
6
+ */
7
+ export interface TextChange {
8
+ start: number;
9
+ end: number;
10
+ text: string;
11
+ }
12
+ /**
13
+ * Source range of a text edit, in BOTH coordinate systems.
14
+ *
15
+ * An edit sits between two documents: `start`/`end` describe the changed
16
+ * region in NEW-source coordinates (the current document), while `endOld` is
17
+ * the end of the replaced region in OLD-source coordinates. The incremental
18
+ * preview (`BlockPatcher`) uses `start`/`endOld` to locate the affected blocks
19
+ * in the PREVIOUS tree (whose offsets are pre-edit) and `start`/`end` in the
20
+ * new one — insertions or deletions between the two make the naive single-`end`
21
+ * wrong, which is exactly why both ends are kept.
22
+ */
23
+ export interface TextChangeRange {
24
+ /** Start of the edited region (identical in both coordinate systems). */
25
+ start: number;
26
+ /** End of the edited region, in new-source coordinates. */
27
+ end: number;
28
+ /** End of the replaced region, in old-source coordinates. */
29
+ endOld: number;
30
+ }
31
+ export interface TextChangeStats {
32
+ totalChanges: number;
33
+ totalInserted: number;
34
+ totalDeleted: number;
35
+ lastChange: TextChange | null;
36
+ }
37
+ export declare class ChangeTracker {
38
+ private changes;
39
+ private maxHistory;
40
+ constructor(maxHistory?: number);
41
+ /**
42
+ * Track a text change.
43
+ */
44
+ track(change: TextChange): void;
45
+ /**
46
+ * Get all tracked changes.
47
+ */
48
+ getAll(): TextChange[];
49
+ /**
50
+ * Get the most recent change.
51
+ */
52
+ getLast(): TextChange | null;
53
+ /**
54
+ * Get the affected range for the last N changes.
55
+ * Returns null if no changes have been tracked.
56
+ */
57
+ getAffectedRange(count?: number): {
58
+ start: number;
59
+ end: number;
60
+ } | null;
61
+ /**
62
+ * Clear change history.
63
+ */
64
+ clear(): void;
65
+ /**
66
+ * Get statistics about tracked changes.
67
+ */
68
+ getStats(): TextChangeStats;
69
+ }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * DocumentEngine — IncrementalParser
3
+ *
4
+ * Re-parses the smallest region a text change can have affected, instead of
5
+ * the whole document.
6
+ *
7
+ * ─── Why this was rewritten (roadmap S5 / point 9) ──────────────────────────
8
+ *
9
+ * The previous implementation spliced RED nodes in place and never touched
10
+ * ranges. Three consequences, all measured:
11
+ *
12
+ * - The re-parsed subtree came from `newSource.slice(start, end)`, so its
13
+ * ranges were based at 0 and were grafted in without rebasing. Every offset
14
+ * inside the edited region was wrong by `start`.
15
+ * - Siblings after the splice and every ancestor kept their old ranges, so
16
+ * the tree silently disagreed with the text it claimed to describe.
17
+ * - It returned `newRootRed.green` — the OLD green root — so the model's
18
+ * `_greenRoot` and `_redRoot` desynchronised permanently after the first
19
+ * incremental edit.
20
+ *
21
+ * Rendering the result and comparing it against a full rebuild of the same
22
+ * final text: 3 of 6 realistic editing scenarios produced DIFFERENT HTML, and
23
+ * one grew 14 phantom nodes (1752 vs 1738).
24
+ *
25
+ * ─── How it works now ───────────────────────────────────────────────────────
26
+ *
27
+ * Everything happens on the GREEN tree, and the red tree is derived from it.
28
+ * That is not a stylistic choice: green nodes carry widths and no position, so
29
+ * a position is something only a red node has — and therefore only a red node
30
+ * can be wrong about.
31
+ *
32
+ * 1. Descend to the deepest node whose inner span (the part between its
33
+ * delimiters) contains the change, refusing to enter the kinds whose
34
+ * children depend on context outside them (see `OPAQUE_KINDS`).
35
+ * 2. Take the run of that node's children the change touches, widened by one
36
+ * on each side, and re-parse just those.
37
+ * 3. Splice the result back over that run and rebuild the ancestor spine,
38
+ * sharing every untouched subtree by reference.
39
+ *
40
+ * The unit is a RUN OF SIBLINGS, not a whole node. Re-parsing a container's
41
+ * entire contents because one character changed inside it meant typing into a
42
+ * 13 KB `[notice]` re-lexed 66% of the document per keystroke — and an edit at
43
+ * document level had no enclosing container at all, so it fell back to a full
44
+ * rebuild, which is exactly where the caret sits while you write the end of a
45
+ * post.
46
+ *
47
+ * Step 1 relies on the partition invariant from point 14: without it, "the
48
+ * part between the delimiters" is not a well-defined range, which is exactly
49
+ * why this repair was blocked on that work.
50
+ *
51
+ * When any precondition fails the parser returns a full rebuild rather than a
52
+ * plausible-looking wrong tree. `path` says which happened, `reason` says why.
53
+ */
54
+ import { RedNode } from '../Syntax/RedNode';
55
+ import { GreenNode } from '../Syntax/GreenNode';
56
+ import type { TextChange } from './ChangeTracker';
57
+ /**
58
+ * A span of the source, `[start, end)`, in the coordinates of the text the
59
+ * result describes.
60
+ */
61
+ export interface SourceSpan {
62
+ start: number;
63
+ end: number;
64
+ }
65
+ export interface EditOperation {
66
+ kind: 'insert' | 'delete' | 'replace';
67
+ start: number;
68
+ end: number;
69
+ text: string;
70
+ /** The minimal range that needs re-parsing */
71
+ affectedStart: number;
72
+ affectedEnd: number;
73
+ }
74
+ /** Options a caller's parse callback must understand. */
75
+ export interface ReparseParseOptions {
76
+ /**
77
+ * Whether the text being parsed is document-level content. Inner spans of
78
+ * containers are not, and must not be grouped into paragraphs.
79
+ */
80
+ normalizeParagraphs: boolean;
81
+ }
82
+ export interface ReparseResult {
83
+ green: GreenNode;
84
+ red: RedNode;
85
+ /** Nodes that were affected by the change */
86
+ affectedNodes: RedNode[];
87
+ /** Time taken in ms (total) */
88
+ duration: number;
89
+ /** Per-phase timing breakdown in ms */
90
+ timings: {
91
+ findAffected: number;
92
+ safeBoundary: number;
93
+ parse: number;
94
+ buildRed: number;
95
+ mutate: number;
96
+ other: number;
97
+ };
98
+ /** Which path was used */
99
+ path: 'incremental' | 'full_rebuild';
100
+ /** When `full_rebuild`, why the incremental path was declined. */
101
+ reason?: FallbackReason;
102
+ /**
103
+ * The span of `newSource` that went through the parser, in NEW coordinates,
104
+ * or `null` after a full rebuild (where the answer is "all of it").
105
+ *
106
+ * This is the contract the incremental semantic analysis is built on. The
107
+ * red tree that comes back is derived from a green tree that shares every
108
+ * subtree outside this span by reference with the previous one, so — with
109
+ * red-subtree reuse — every red node outside the span is the SAME object it
110
+ * was before the edit, and everything anyone computed about it (its
111
+ * diagnostics, above all) is still true of it, up to a shift in position.
112
+ * The nodes inside the span, plus the ancestors on the path down to it
113
+ * (rebuilt because their child lists changed), are the only ones that are
114
+ * new. `SemanticAnalyzer.analyzeWindow` re-validates exactly those.
115
+ *
116
+ * The span is closed on the widened window, not on the edit: the parser
117
+ * re-parses the sibling on each side of the change too (see
118
+ * `findReparseWindow`), and those siblings are new nodes as well.
119
+ */
120
+ window: SourceSpan | null;
121
+ }
122
+ export type FallbackReason =
123
+ /** No sibling window could be formed around the change. */
124
+ 'no-window'
125
+ /** The change touches a container's own delimiter. */
126
+ | 'touches-delimiter'
127
+ /** The region cannot be lexed in isolation — see `regionIsSelfContained`. */
128
+ | 'region-not-isolated'
129
+ /** An unclosed `[` before the region could claim a `]` the edit creates. */
130
+ | 'open-bracket-before'
131
+ /**
132
+ * The window changed what the parser's `autoClosed` set holds when the text
133
+ * after it is read — see `pendingAutoClosePreserved`.
134
+ */
135
+ | 'pending-auto-close'
136
+ /** The container covers so much of the document that a rebuild is cheaper. */
137
+ | 'region-too-large'
138
+ /** The document is small enough that rebuilding it outright costs less. */
139
+ | 'document-too-small'
140
+ /** The tree's ranges disagree with the source it is supposed to describe. */
141
+ | 'stale-ranges';
142
+ export interface IncrementalParserOptions {
143
+ /** Override `MIN_SOURCE_LENGTH`. Set to 0 to always attempt a splice. */
144
+ minSourceLength?: number;
145
+ /** Override `MAX_REGION_FRACTION`. */
146
+ maxRegionFraction?: number;
147
+ }
148
+ export declare class IncrementalParser {
149
+ private readonly minSourceLength;
150
+ private readonly maxRegionFraction;
151
+ /**
152
+ * Bracket-depth summary of the source, for the boundary check below.
153
+ *
154
+ * Keyed on the green root it was last synchronised with: a reparse whose
155
+ * `oldGreen` is that root brings the index across the edit by re-reading a
156
+ * few KB around it; any other root (a `rebuild`, a model handed a foreign
157
+ * tree) rebuilds it with one scan — the same scan every keystroke used to
158
+ * pay. See `BracketDepthIndex` for why the summary is exact.
159
+ */
160
+ private readonly brackets;
161
+ private bracketsRoot;
162
+ /**
163
+ * Whether, within the current `reparse` call, the index has been brought to
164
+ * describe `newSource`. Explicit rather than inferred: a length comparison
165
+ * would confuse an unsynchronised index over an older text of the same
166
+ * length (insert one character, delete one) with a synchronised one.
167
+ */
168
+ private bracketsSynced;
169
+ /**
170
+ * Where the document carries a pending `autoClosed` name, memoised on the
171
+ * green root it was measured over — see {@link PendingSpans}.
172
+ *
173
+ * It survives an incremental splice by construction: the window guard that
174
+ * consults it only lets through edits that leave the crossings on both
175
+ * sides of the window as they were, so the spans need only be moved. Any
176
+ * other outcome drops the key and the next edit pays one walk.
177
+ */
178
+ private pendingRoot;
179
+ private pendingSpans;
180
+ /**
181
+ * The thresholds are constructor options because they are performance
182
+ * tuning, not semantics: the tree that comes out is the same either way, so
183
+ * a caller with a different document profile — or a test that wants to
184
+ * exercise the splice on a two-line document — can move them without
185
+ * changing what the parser means.
186
+ */
187
+ constructor(options?: IncrementalParserOptions);
188
+ /**
189
+ * Characters the last boundary check actually read — the tail of one index
190
+ * piece, never the prefix. Exposed so a test can pin the bound.
191
+ */
192
+ get lastBoundaryScan(): number;
193
+ /**
194
+ * Does every `[` before `end` find its `]` before `end` too?
195
+ *
196
+ * If one does not, the lexer's bracket matching for it scans onward into
197
+ * the region we are about to re-parse — and an edit that adds a `]` there
198
+ * (or deletes a `[` that was keeping the nesting depth up) changes what that
199
+ * OUTSIDE bracket means. The region would be re-parsed correctly and the
200
+ * text before it would silently become something else.
201
+ *
202
+ * A clamped depth count is exact for this question: the lexer pairs
203
+ * brackets with a stack, so a `[` is unmatched precisely when the depth
204
+ * never returns to its level. The count used to be a scan of the whole
205
+ * prefix on every keystroke — 22% of a keystroke with the caret at the end
206
+ * of a post, 1.4 ms on the 547 KB fixture. The index answers it from piece
207
+ * summaries, reading at most one piece of text.
208
+ *
209
+ * The index is brought across the edit here, not earlier: the paths that
210
+ * return before this point never needed it, and on the next call the
211
+ * root-key mismatch simply rebuilds it. The caller re-keys it on whatever
212
+ * green root it returns.
213
+ */
214
+ private bracketsCloseBefore;
215
+ /**
216
+ * Reparse a tree after a text change.
217
+ *
218
+ * Always returns a result — either an incremental splice or a full rebuild.
219
+ * It never returns a tree whose ranges do not describe `newSource`.
220
+ */
221
+ reparse(oldRed: RedNode, oldGreen: GreenNode, change: TextChange, newSource: string, parseCallback: (text: string, options?: ReparseParseOptions) => GreenNode, buildRedCallback: (green: GreenNode) => RedNode): ReparseResult;
222
+ /**
223
+ * Find the run of sibling children a change can have affected.
224
+ *
225
+ * Two steps. First descend to the deepest node whose INNER span contains the
226
+ * change — inner rather than full, so a container's own delimiters never go
227
+ * back through the parser: an edit that touches `[colo|r=red]` changes what
228
+ * that element IS, and is handled by re-parsing it as part of its parent's
229
+ * window instead.
230
+ *
231
+ * Then pick the children that the change touches, widened by one on each
232
+ * side. The widening is what lets a deletion MERGE two nodes: removing the
233
+ * blank line between two paragraphs changes only the node in between, and
234
+ * without a neighbour on each side the re-parse could not see that the two
235
+ * survivors have to become one.
236
+ */
237
+ private findReparseWindow;
238
+ /**
239
+ * Nodes on the path from the root down to the change.
240
+ *
241
+ * Kept because it is part of the public surface and is genuinely useful for
242
+ * callers that want to know what an edit touched; the reparse itself no
243
+ * longer needs it.
244
+ */
245
+ findAffectedNodes(root: RedNode, change: TextChange): RedNode[];
246
+ }