( source: string, start = 0, end: number = source.length, )
| 1 | const range: number = 2 |
| 2 | |
| 3 | export function generateCodeFrame( |
| 4 | source: string, |
| 5 | start = 0, |
| 6 | end: number = source.length, |
| 7 | ): string { |
| 8 | class="cm">// Ensure start and end are within the source length |
| 9 | start = Math.max(0, Math.min(start, source.length)) |
| 10 | end = Math.max(0, Math.min(end, source.length)) |
| 11 | |
| 12 | if (start > end) return class="st">'' |
| 13 | |
| 14 | class="cm">// Split the content into individual lines but capture the newline sequence |
| 15 | class="cm">// that separated each line. This is important because the actual sequence is |
| 16 | class="cm">// needed to properly take into account the full line length for offset |
| 17 | class="cm">// comparison |
| 18 | let lines = source.split(/(\r?\n)/) |
| 19 | |
| 20 | class="cm">// Separate the lines and newline sequences into separate arrays for easier referencing |
| 21 | const newlineSequences = lines.filter((_, idx) => idx % 2 === 1) |
| 22 | lines = lines.filter((_, idx) => idx % 2 === 0) |
| 23 | |
| 24 | let count = 0 |
| 25 | const res: string[] = [] |
| 26 | for (let i = 0; i < lines.length; i++) { |
| 27 | count += |
| 28 | lines[i].length + |
| 29 | ((newlineSequences[i] && newlineSequences[i].length) || 0) |
| 30 | if (count >= start) { |
| 31 | for (let j = i - range; j <= i + range || end > count; j++) { |
| 32 | if (j < 0 || j >= lines.length) continue |
| 33 | const line = j + 1 |
| 34 | res.push( |
| 35 | `${line}${class="st">' '.repeat(Math.max(3 - String(line).length, 0))}| ${ |
| 36 | lines[j] |
| 37 | }`, |
| 38 | ) |
| 39 | const lineLength = lines[j].length |
| 40 | const newLineSeqLength = |
| 41 | (newlineSequences[j] && newlineSequences[j].length) || 0 |
| 42 | |
| 43 | if (j === i) { |
| 44 | class="cm">// push underline |
| 45 | const pad = start - (count - (lineLength + newLineSeqLength)) |
| 46 | const length = Math.max( |
| 47 | 1, |
| 48 | end > count ? lineLength - pad : end - start, |
| 49 | ) |
| 50 | res.push(` | ` + class="st">' '.repeat(pad) + class="st">'^'.repeat(length)) |
| 51 | } else if (j > i) { |
| 52 | if (end > count) { |
| 53 | const length = Math.max(Math.min(end - count, lineLength), 1) |
| 54 | res.push(` | ` + class="st">'^'.repeat(length)) |
| 55 | } |
| 56 | |
| 57 | count += lineLength + newLineSeqLength |
| 58 | } |
| 59 | } |
| 60 | break |
no test coverage detected