| 1 | module errors |
| 2 | |
| 3 | import os |
| 4 | import v2.token |
| 5 | |
| 6 | // loads the source file and generates the error message including the source |
| 7 | // line and column. offending token is highlighted in the snipped of source code. |
| 8 | // since this is used on errors, loading the source file isnt an issue. |
| 9 | // TODO: probably needs a better name |
| 10 | pub fn details(file &token.File, pos token.Position, row_padding int) string { |
| 11 | if file.name == '' { |
| 12 | return 'at ${pos.line}:${pos.column}' |
| 13 | } |
| 14 | src := os.read_file(file.name) or { return 'at ${pos.line}:${pos.column} in `${file.name}`' } |
| 15 | line_start := if pos.line - row_padding - 1 > 0 { |
| 16 | file.line_start(pos.line - row_padding) |
| 17 | } else { |
| 18 | 0 |
| 19 | } |
| 20 | mut line_end := pos.offset + 1 |
| 21 | for i := 0; line_end < src.len; { |
| 22 | if src[line_end] == `\n` { |
| 23 | i++ |
| 24 | if i == row_padding + 1 { |
| 25 | break |
| 26 | } |
| 27 | } |
| 28 | line_end++ |
| 29 | } |
| 30 | lines_src := src[line_start..line_end].split('\n') |
| 31 | line_no_start, _ := file.find_line_and_column(line_start) |
| 32 | mut lines_src_formatted := []string{} |
| 33 | for i in 0 .. lines_src.len { |
| 34 | line_no := line_no_start + i |
| 35 | line_src := lines_src[i] |
| 36 | line_spaces := line_src.replace('\t', ' ') |
| 37 | lines_src_formatted << '${line_no:5d} | ' + line_spaces |
| 38 | if line_no == pos.line { |
| 39 | space_diff := line_spaces.len - line_src.len |
| 40 | lines_src_formatted << ' ' + ' '.repeat(space_diff + pos.column - 1) + '^' |
| 41 | } |
| 42 | } |
| 43 | return lines_src_formatted.join('\n') |
| 44 | } |
| 45 | |