highlight_error/
highlight_error.rs

1/// Given a complete source file, highlights an error between two indexes (in bytes).
2pub fn highlight_error(ini_idx: usize, end_idx: usize, file: &str) -> String {
3  // Please do NOT "improve" this by using high-order functions
4
5  // Appends empty spaces to the left of a text
6  fn pad(len: usize, txt: &str) -> String {
7    return format!("{}{}", " ".repeat(std::cmp::max(len - txt.len(), 0)), txt);
8  }
9
10  // Makes sure the end index is lower than the end index
11  assert!(ini_idx <= end_idx);
12
13  // Appends empty spaces until end_idx <= file.len()
14  // This is done this way to avoid allocating a new string
15  let text : &str;
16  let buff : String;
17  if end_idx <= file.len() {
18    text = file;
19  } else {
20    buff = format!("{}{}", file, " ".repeat(end_idx - file.len()));
21    text = &buff;
22  };
23
24  // Terminal colors
25  let color = "\x1b[4m\x1b[31m";
26  let reset = "\x1b[0m";
27
28  // Calculates indices and line numbers
29  let mut cur_lin_idx = 0; // current line index
30  let mut cur_lin_num = 0; // current line number
31  let mut slc_lin_idx = 0; // slice line index
32  let mut slc_lin_num = 0; // slice line number
33  let mut slc_end_idx = 0; // slice end index
34  let mut idx = 0;
35  while idx <= text.len() {
36    let chr = text[idx..].chars().nth(0).unwrap_or('\n');
37    //println!("[{}] | {} {} {} {} {} {} | {}", if chr == '\n' { 'N' } else { chr }, idx, cur_lin_idx, cur_lin_num, slc_lin_idx, slc_lin_num, slc_end_idx, ini_idx);
38    if idx == ini_idx {
39      slc_lin_idx = cur_lin_idx;
40      slc_lin_num = cur_lin_num;
41    }
42    if chr == '\n' {
43      cur_lin_idx = idx + 1;
44      cur_lin_num = cur_lin_num + 1;
45      if idx >= end_idx {
46        slc_end_idx = idx;
47        break;
48      }
49    }
50    idx += chr.len_utf8();
51  }
52  let num_len = format!("{}", cur_lin_idx + 1).len();
53  let slice   = &text[slc_lin_idx .. slc_end_idx];
54  let ini_idx = ini_idx - slc_lin_idx;
55  let end_idx = end_idx - slc_lin_idx;
56
57  // Builds the display text
58  let mut text = String::new();
59  let mut newl = true;
60  let mut high = false;
61  let mut line = slc_lin_num;
62  let mut idx = 0;
63  while idx < slice.len() {
64    let chr = slice[idx..].chars().nth(0).unwrap_or(' ');
65    if newl {
66      text.push_str(reset);
67      text.push_str(&format!(" {} | ", pad(num_len, &format!("{}", line + 1))));
68      if high {
69        text.push_str(color);
70      }
71      newl = false;
72    }
73    if chr == '\n' {
74      newl = true;
75      line = line + 1;
76    }
77    if idx == ini_idx {
78      high = true;
79      text.push_str(color);
80    }
81    if chr == '\n' && high && end_idx - ini_idx == 1 {
82      text.push(' '); // single "\n" highlight
83    }
84    if idx == end_idx {
85      high = false;
86      text.push_str(reset);
87    }
88    text.push(chr);
89    idx += chr.len_utf8();
90  }
91  text.push_str(reset);
92
93  // Returns it
94  return text;
95}