diff --git a/vlib/v/util/errors.v b/vlib/v/util/errors.v index b434d0320c..be502b0d9b 100644 --- a/vlib/v/util/errors.v +++ b/vlib/v/util/errors.v @@ -13,24 +13,24 @@ import v.token // NB: using only the filename may lead to inability of IDE/editors // to find the source file, when the IDE has a different working folder than // v itself. - +// error_context_before - how many lines of source context to print before the pointer line +// error_context_after - ^^^ same, but after const ( - error_context_before = 2 // how many lines of source context to print before the pointer line - error_context_after = 2 // ^^^ same, but after + error_context_before = 2 + error_context_after = 2 ) +// emanager.support_color - should the error and other messages +// have ANSI terminal escape color codes in them. +// By default, v tries to autodetect, if the terminal supports colors. +// Use -color and -nocolor options to override the detection decision. pub const ( emanager = new_error_manager() ) -// - pub struct EManager { pub mut: - support_color bool // should the error and other messages - // have ANSI terminal escape color codes in them. - // By default, v tries to autodetect, if the terminal supports colors. - // Use -color and -nocolor options to override the detection decision. + support_color bool } pub fn (e &EManager) set_support_color(b bool) { @@ -38,15 +38,18 @@ pub fn (e &EManager) set_support_color(b bool) { } pub fn new_error_manager() &EManager { - return &EManager{ support_color: term.can_show_color_on_stderr() } + return &EManager{ + support_color: term.can_show_color_on_stderr() + } } -pub fn formatted_error(kind string /*error or warn*/, emsg string, filepath string, pos token.Position) string { +// formatted_error - `kind` may be 'error' or 'warn' +pub fn formatted_error(kind, emsg, filepath string, pos token.Position) string { mut path := filepath verror_paths_override := os.getenv('VERROR_PATHS') if verror_paths_override == 'absolute' { - path = os.real_path( path ) - }else{ + path = os.real_path(path) + } else { // Get relative path workdir := os.getwd() + os.path_separator if path.starts_with(workdir) { @@ -55,71 +58,26 @@ pub fn formatted_error(kind string /*error or warn*/, emsg string, filepath stri } // mut source_context := '' - source := util.read_file(filepath) or { '' } - source_lines := source.split_into_lines() - mut p := util.imax(0, util.imin(source.len -1, pos.pos)) - for ; p>=0; p-- { - if source[p] == `\r` || source[p] == `\n` { - break - } + source := read_file(filepath) or { + '' } - column := util.imax(0, pos.pos - p - 1) - position := '${path}:${pos.line_nr+1}:${util.imax(1,column+1)}:' - // - bline := util.imax(0, pos.line_nr - error_context_before) - aline := util.imin(source_lines.len-1, pos.line_nr + error_context_after) - mut clines := []string{} - tab_spaces := ' ' - for iline := bline; iline <= aline; iline++ { - sline := source_lines[iline] - mut cline := '${iline+1:5d}| ' + sline.replace('\t', tab_spaces) - if iline == pos.line_nr && emanager.support_color { - cline = term.red( cline ) - } - clines << cline - // - if iline == pos.line_nr { - // The pointerline should have the same spaces/tabs as the offending - // line, so that it prints the ^ character exactly on the *same spot* - // where it is needed. That is the reason we can not just - // use strings.repeat(` `, col) to form it. - mut pointerline := []string{} - for i, c in sline { - if i < column { - mut x := c - if x == `\t` { - pointerline << tab_spaces - }else{ - x = if x.is_space() { c } else { ` ` } - pointerline << x.str() - } - continue - } - if pos.len > 1 { - max_len := sline.len - pointerline.len // rest of the line - len := if pos.len > max_len { max_len } else { pos.len } - underline := '~'.repeat(len) - pointerline << if emanager.support_color { term.bold(term.blue(underline)) } else { underline } - }else{ - pointerline << if emanager.support_color { term.bold(term.blue('^')) } else { '^' } - } + mut p := imax(0, imin(source.len - 1, pos.pos)) + if source.len > 0 { + for ; p >= 0; p-- { + if source[p] == `\r` || source[p] == `\n` { break } - clines << ' ' + pointerline.join('') } } - source_context += clines.join('\n') - // - final_position := if emanager.support_color { - term.bold(position) - } else { - position - } + column := imax(0, pos.pos - p - 1) + position := '${path}:${pos.line_nr+1}:${util.imax(1,column+1)}:' + source_context += source_context(source, column, pos).join('\n') + final_position := if emanager.support_color { term.bold(position) } else { position } mut final_kind := kind if emanager.support_color { final_kind = if kind.contains('error') { term.bold(term.red(kind)) - }else{ + } else { term.bold(term.bright_blue(kind)) } } @@ -129,10 +87,72 @@ pub fn formatted_error(kind string /*error or warn*/, emsg string, filepath stri return '$final_position $final_kind $final_msg $final_context'.trim_space() } -pub fn verror(kind string, s string) { +pub fn source_context(source string, column int, pos token.Position) []string { + mut clines := []string{} + if source.len == 0 { + return clines + } + source_lines := source.split_into_lines() + bline := imax(0, pos.line_nr - error_context_before) + aline := imax(0, imin(source_lines.len - 1, pos.line_nr + error_context_after)) + tab_spaces := ' ' + for iline := bline; iline <= aline; iline++ { + sline := source_lines[iline] + mut cline := '${iline+1:5d}| ' + sline.replace('\t', tab_spaces) + if iline == pos.line_nr && emanager.support_color { + cline = term.red(cline) + } + clines << cline + // + if iline == pos.line_nr { + // The pointerline should have the same spaces/tabs as the offending + // line, so that it prints the ^ character exactly on the *same spot* + // where it is needed. That is the reason we can not just + // use strings.repeat(` `, col) to form it. + mut pointerline := []string{} + for i, bchar in sline { + if i < column { + mut x := bchar + if x == `\t` { + pointerline << tab_spaces + } else { + x = if x.is_space() { + bchar + } else { + ` ` + } + pointerline << x.str() + } + continue + } + if pos.len > 1 { + max_len := sline.len - pointerline.len // rest of the line + len := if pos.len > max_len { max_len } else { pos.len } + underline := '~'.repeat(len) + pointerline << if emanager.support_color { + term.bold(term.blue(underline)) + } else { + underline + } + } else { + pointerline << if emanager.support_color { + term.bold(term.blue('^')) + } else { + '^' + } + } + break + } + clines << ' ' + pointerline.join('') + } + } + return clines +} + +pub fn verror(kind, s string) { if emanager.support_color { - eprintln( term.bold(term.red(kind)) + ': $s' ) - }else{ + eprintln(term.bold(term.red(kind)) + ': $s') + } else { eprintln('${kind}: $s') } exit(1)