( lines: string[], cursorLine: number, behavior: CodeBlockBehavior, )
| 33 | * @returns ContextData(命中代码块)或 null |
| 34 | */ |
| 35 | export function detectCodeBlockFromLines( |
| 36 | lines: string[], |
| 37 | cursorLine: number, |
| 38 | behavior: CodeBlockBehavior, |
| 39 | ): ContextData | null { |
| 40 | if (behavior === CodeBlockBehavior.DISABLED) return null; |
| 41 | |
| 42 | const totalLines = lines.length; |
| 43 | |
| 44 | // 阶段一:用栈追踪嵌套层级,找到光标所在的最内层开始围栏。 |
| 45 | // |
| 46 | // 栈为空(不在任何块内): |
| 47 | // 遇到有效围栏行(parseFenceLength >= 3)→ push 进栈 |
| 48 | // 栈非空(在某块内): |
| 49 | // 遇到满足 isClosingFence(长度 >= 栈顶 openLen 的纯反引号行)→ pop |
| 50 | // 遇到带 info string 的围栏行(不是结束围栏)→ push(进入内层块) |
| 51 | // 遇到短于 openLen 的纯反引号行 → 视为内容,忽略 |
| 52 | |
| 53 | const stack: Array<{ fenceStart: number; openLen: number }> = []; |
| 54 | |
| 55 | for (let i = 0; i <= cursorLine; i++) { |
| 56 | const len = parseFenceLength(lines[i]); |
| 57 | if (len === 0) continue; |
| 58 | |
| 59 | if (stack.length === 0) { |
| 60 | stack.push({ fenceStart: i, openLen: len }); |
| 61 | } else { |
| 62 | const { openLen } = stack[stack.length - 1]; |
| 63 | if (isClosingFence(lines[i], openLen)) { |
| 64 | // 满足关闭当前层的条件 |
| 65 | stack.pop(); |
| 66 | } else if (!isClosingFence(lines[i], len)) { |
| 67 | // 有反引号开头,但后面跟了 info string(不是纯围栏行) |
| 68 | // → 内层块的开始围栏 |
| 69 | stack.push({ fenceStart: i, openLen: len }); |
| 70 | } |
| 71 | // 其他情况:纯反引号行但长度不足以关闭当前层 → 内容,忽略 |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | if (stack.length === 0) return null; |
| 76 | |
| 77 | const { fenceStart, openLen } = stack[stack.length - 1]; |
| 78 | |
| 79 | // 光标正好在开始围栏行上,不算「块内」 |
| 80 | if (fenceStart === cursorLine) return null; |
| 81 | |
| 82 | // 阶段二:从 fenceStart+1 向下找匹配的结束围栏 |
| 83 | let fenceEnd = -1; |
| 84 | for (let i = fenceStart + 1; i < totalLines; i++) { |
| 85 | if (isClosingFence(lines[i], openLen)) { |
| 86 | fenceEnd = i; |
| 87 | break; |
| 88 | } |
| 89 | } |
| 90 | if (fenceEnd === -1) return null; |
| 91 | if (cursorLine >= fenceEnd) return null; |
| 92 |
no test coverage detected