1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
use crate::parser::Rule; use ansi_term::{Color, Style}; use pest::iterators::Pair; use std::fmt; use crate::Locker; #[derive(Debug, Clone)] pub struct Source { text: String, location: String, } impl Source { pub fn new(text: String, location: String) -> Self { Self { text, location } } } #[derive(Debug, Clone)] pub struct SourcePosition { start_pos: usize, end_pos: usize, text: Locker<Source>, } impl SourcePosition { pub fn new(start_pos: usize, end_pos: usize, text: Locker<Source>) -> Self { Self { start_pos, end_pos, text, } } pub fn from_pair(pair: &Pair<'_, Rule>, source: &Locker<Source>) -> Self { Self::new( pair.as_span().start_pos().pos(), pair.as_span().end_pos().pos(), source.clone(), ) } pub fn location(&self) -> Option<String> { match self.text.read() { Ok(text) => Some(text.location.clone()), Err(_) => None, } } } impl fmt::Display for SourcePosition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let source = match self.text.read() { Ok(text) => text, Err(_) => return Err(fmt::Error), }; let line_number = &source.text[0..self.start_pos] .chars() .fold(0, |acc, c| match c == '\n' { true => acc + 1, false => acc, }) + 1; let lines = source.text.split('\n'); let mut relevant_lines_formatted: Vec<(usize, String)> = Vec::new(); let mut chars_seen = 0; for (i, line) in lines.enumerate() { let eol_pos = chars_seen + line.len() + 1; if self.start_pos < eol_pos && self.end_pos > chars_seen { let mut inner_start_pos: isize = self.start_pos as isize - chars_seen as isize; if inner_start_pos < 0 { inner_start_pos = 0; } let mut inner_end_pos = self.end_pos - chars_seen; if inner_end_pos > line.len() { inner_end_pos = line.len(); } if inner_start_pos as usize != inner_end_pos && !line.is_empty() { relevant_lines_formatted.push(( i + 1, format!( "{}{}{}", &line[0..inner_start_pos as usize], Color::Purple .paint(&line[inner_start_pos as usize..inner_end_pos as usize]), &line[inner_end_pos..] ), )); } } chars_seen += line.len() + 1; } fn indent(n: usize) -> String { String::from_utf8(vec![b' '; n]).unwrap() } let mut indentation = format!("{}", line_number).len() + 2; if indentation < 6 { indentation = 6; } writeln!( f, "{}{} {}", indent(indentation), Color::Blue.bold().paint("├"), Style::default() .dimmed() .paint(format!("{}:{} ↴", source.location, line_number)), )?; for (line_no, line) in relevant_lines_formatted { let line_no_str = format!("{}", line_no); let line_no_indentation = indent(indentation - line_no_str.len() - 1); writeln!( f, "{}{} {}", line_no_indentation, Color::Blue.bold().paint(format!("{} │", line_no_str)), line )?; } write!(f, "") } } impl Source { pub fn location(&self) -> &str { &self.location } }