(chunk)
| 291 | * 用它在每个放行点更新 lineStart —— 放行的文本没有换行时沿用之前的状态(prior), |
| 292 | * 于是切在前导空白中间的 chunk 边界也不会让下一个 '{' 失去行首身份。 |
| 293 | * 代价是 index 之前那一串空白的长度,遇到第一个非空白就停。 |
| 294 | * @param {string} text |
| 295 | * @param {number} index |
| 296 | * @param {boolean} [prior=true] - 文本开头之前的行首状态 |
| 297 | * @returns {boolean} |
| 298 | */ |
| 299 | const lineStartBefore = (text, index, prior = true) => { |
| 300 | let i = index - 1; |
| 301 | while (i >= 0 && (text[i] === ' ' || text[i] === '\t' || text[i] === '\r')) i -= 1; |
| 302 | return i < 0 ? prior : text[i] === '\n'; |
| 303 | }; |
| 304 | |
| 305 | /** |
| 306 | * 记录正文当前是否处在代码上下文里。文档里的例子必须保持是例子:``` 围栏内, |
| 307 | * 或同一行反引号数为奇数(行内代码)时,触发器不算触发器。 |
| 308 | * 增量式:只喂**已经放行**的正文,所以流式和整段两条路径可以共用同一套判断。 |
| 309 | */ |
| 310 | const createCodeContextTracker = () => { |
| 311 | let inFence = false; |
| 312 | let ticksOnLine = 0; |
| 313 | let run = 0; |
| 314 | let runAtLineStart = true; // 这串反引号前面,本行是不是只有空白 |
| 315 | let lineIsBlank = true; // 本行到目前为止是不是只有空白 |
| 316 | |
| 317 | // 围栏必须**顶行**(Markdown 的规则)。之前任何位置的三连反引号都会翻转围栏状态, |
| 318 | // 于是 JSON 字符串里的 ``` 也算围栏;一旦错位就再也回不来,后面每个真实调用都被 |
| 319 | // 当成文档静默丢掉 —— 正是这次要消灭的那类无声失败。 |
| 320 | const settle = () => { |
| 321 | if (run === 0) return; |
| 322 | if (run >= 3 && runAtLineStart) { |
| 323 | inFence = !inFence; |
| 324 | ticksOnLine = 0; |
| 325 | } else if (!inFence) { |
| 326 | ticksOnLine += run; |
| 327 | } |
| 328 | run = 0; |
| 329 | }; |
| 330 | |
| 331 | return { |
| 332 | consume: (text) => { |
| 333 | for (let i = 0; i < text.length; i += 1) { |
| 334 | const char = text[i]; |
| 335 | if (char === '`') { |
| 336 | if (run === 0) runAtLineStart = lineIsBlank; |
| 337 | run += 1; |
| 338 | lineIsBlank = false; |
| 339 | continue; |
| 340 | } |
| 341 | settle(); |
| 342 | if (char === '\n') { |
| 343 | ticksOnLine = 0; |
| 344 | lineIsBlank = true; |
| 345 | } else if (char !== ' ' && char !== '\t' && char !== '\r') { |
| 346 | lineIsBlank = false; |
| 347 | } |
| 348 | } |
| 349 | }, |
| 350 | // 反引号可能被切在 chunk 边界上,所以这里结算一份副本,不能动真状态。 |
nothing calls this directly
no test coverage detected