| 637 | * arrives as `42\t```` and cannot close the block early. |
| 638 | */ |
| 639 | export function attributeSourceBytes(finalText: string): Map<string, number> { |
| 640 | const out = new Map<string, number>(); |
| 641 | if (!finalText) return out; |
| 642 | const lines = finalText.split('\n'); |
| 643 | let current: string | null = null; |
| 644 | let inFence = false; |
| 645 | let acc: string[] = []; |
| 646 | const flush = () => { |
| 647 | if (current && acc.length > 0) { |
| 648 | out.set(current, (out.get(current) ?? 0) + acc.join('\n').length); |
| 649 | } |
| 650 | acc = []; |
| 651 | }; |
| 652 | for (const line of lines) { |
| 653 | if (!inFence) { |
| 654 | const header = /^\*\*`([^`]+)`\*\*/.exec(line); |
| 655 | if (header) { |
| 656 | current = header[1]!; |
| 657 | continue; |
| 658 | } |
| 659 | if (current && line.startsWith('```')) { |
| 660 | inFence = true; |
| 661 | continue; |
| 662 | } |
| 663 | continue; |
| 664 | } |
| 665 | if (line === '```') { |
| 666 | inFence = false; |
| 667 | flush(); |
| 668 | continue; |
| 669 | } |
| 670 | acc.push(line); |
| 671 | } |
| 672 | // Unterminated fence (final-ceiling truncation cut mid-block): count it. |
| 673 | if (inFence) flush(); |
| 674 | return out; |
| 675 | } |
| 676 | |
| 677 | /** Human-readable stderr rendering of the JSON report. */ |
| 678 | export function renderTable(report: ExploreDiagnosticReport): string { |