mod resolver_error; mod source_hierarchy; mod source_symbols; mod unused_symbols; pub use resolver_error::*; pub use source_hierarchy::*; pub use source_symbols::*; pub use unused_symbols::*; use crate::*; pub use log::LogLevel; pub use ansi::*; pub fn report_source_issue(level: LogLevel, context: &Context, message: &str) { // Prepare variables. let in_merged = &context.source.in_merged; let line_num = in_merged.start.line + 1; let digits = line_num.to_string().len(); let w = digits + 3; let arrow = "-->"; let mut string = message.to_string(); macro_rules! push { ($($tokens:tt)*) => { string.push_str(&format!($($tokens)*)) }; } // Format message and locations. push!("{NORMAL}\n"); let location = context.source.location(); push!("{BLUE}{arrow:>w$}{NORMAL} {location}\n", w=w); // Format source context. let left = in_merged.start.column; let right = in_merged.end.column + 1; let source_line = context.source_code.split('\n').nth(in_merged.start.line) .unwrap_or(""); let space = " "; let colour = match level { LogLevel::Info => BLUE, LogLevel::Warn => YELLOW, LogLevel::Error => RED, LogLevel::Fatal => RED, }; // Print source code line. push!("{BLUE} {line_num} | {NORMAL}"); for (i, c) in source_line.chars().enumerate() { if i == left { push!("{colour}") } if i == right { push!("{NORMAL}") } push!("{c}"); } push!("{NORMAL}\n"); // Print source code underline. push!("{BLUE} {space:>w$} | {NORMAL}", w=digits); for _ in 0..left { push!(" "); } push!("{colour}"); for _ in left..right { push!("^"); } push!("{NORMAL}"); // Print the completed message. match level { LogLevel::Info => log::info!( "{}", string), LogLevel::Warn => log::warn!( "{}", string), LogLevel::Error => log::error!("{}", string), LogLevel::Fatal => log::fatal!("{}", string), } }