mirror of
				https://github.com/Dummi26/mers.git
				synced 2025-11-04 05:16:17 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			602 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			602 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
use std::{
 | 
						|
    fmt::{Debug, Display},
 | 
						|
    rc::Rc,
 | 
						|
    sync::{atomic::AtomicU32, Arc},
 | 
						|
};
 | 
						|
 | 
						|
use line_span::LineSpans;
 | 
						|
 | 
						|
#[cfg(feature = "parse")]
 | 
						|
use crate::parsing::Source;
 | 
						|
use crate::theme::ThemeGen;
 | 
						|
 | 
						|
pub mod themes;
 | 
						|
 | 
						|
#[derive(Clone, Copy, Debug)]
 | 
						|
pub struct SourcePos(pub(crate) usize);
 | 
						|
impl SourcePos {
 | 
						|
    pub fn pos(&self) -> usize {
 | 
						|
        self.0
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#[derive(Clone, Debug)]
 | 
						|
pub struct SourceRange {
 | 
						|
    in_file: Arc<Source>,
 | 
						|
    start: SourcePos,
 | 
						|
    end: SourcePos,
 | 
						|
}
 | 
						|
impl From<(SourcePos, SourcePos, &Arc<Source>)> for SourceRange {
 | 
						|
    fn from(value: (SourcePos, SourcePos, &Arc<Source>)) -> Self {
 | 
						|
        SourceRange {
 | 
						|
            in_file: Arc::clone(value.2),
 | 
						|
            start: value.0,
 | 
						|
            end: value.1,
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
impl SourceRange {
 | 
						|
    pub fn start(&self) -> SourcePos {
 | 
						|
        self.start
 | 
						|
    }
 | 
						|
    pub fn end(&self) -> SourcePos {
 | 
						|
        self.end
 | 
						|
    }
 | 
						|
    pub fn in_file(&self) -> &Arc<Source> {
 | 
						|
        &self.in_file
 | 
						|
    }
 | 
						|
}
 | 
						|
/// To `Display` this, use one of the `display` methods to get a struct with some configuration options.
 | 
						|
/// The `Debug` impl of this is the same as `Display`ing `this.display_term()` or `this.display_notheme()`, depending on if the `ecolor-term` feature is enabled or not.
 | 
						|
/// Since this may use ansi color codes, it should only be used when printing to a terminal, which is why `CheckError` itself has no `Display` implementation, only this one for `Debug`.
 | 
						|
#[derive(Clone)]
 | 
						|
pub struct CheckError(pub Vec<CheckErrorComponent>);
 | 
						|
 | 
						|
#[derive(Clone, Copy)]
 | 
						|
pub enum EColor {
 | 
						|
    Indent(u32),
 | 
						|
 | 
						|
    UnknownVariable,
 | 
						|
    WhitespaceAfterHashtag,
 | 
						|
    HashUnknown,
 | 
						|
    HashIncludeCantLoadFile,
 | 
						|
    HashIncludeNotAString,
 | 
						|
    HashIncludeErrorInIncludedFile,
 | 
						|
    BackslashEscapeUnknown,
 | 
						|
    BackslashEscapeEOF,
 | 
						|
    StringEOF,
 | 
						|
    TypeEOF,
 | 
						|
    /// `&[Int/String` (notice the missing `]`)
 | 
						|
    BracketedRefTypeNoClosingBracket,
 | 
						|
    IfConditionNotBool,
 | 
						|
    ChainWithNonFunction,
 | 
						|
    Function,
 | 
						|
    FunctionArgument,
 | 
						|
    InitFrom,
 | 
						|
    InitTo,
 | 
						|
    AssignFrom,
 | 
						|
    AssignTo,
 | 
						|
    AssignTargetNonReference,
 | 
						|
    AsTypeStatementWithTooBroadType,
 | 
						|
    AsTypeTypeAnnotation,
 | 
						|
    BadCharInTupleType,
 | 
						|
    BadCharInFunctionType,
 | 
						|
    BadTypeFromParsed,
 | 
						|
    TypeAnnotationNoClosingBracket,
 | 
						|
    TryBadSyntax,
 | 
						|
    TryNoFunctionFound,
 | 
						|
    TryNotAFunction,
 | 
						|
    TryUnusedFunction1,
 | 
						|
    TryUnusedFunction2,
 | 
						|
    CustomTypeTestFailed,
 | 
						|
 | 
						|
    StacktraceDescend,
 | 
						|
    StacktraceDescendHashInclude,
 | 
						|
 | 
						|
    MaximumRuntimeExceeded,
 | 
						|
 | 
						|
    InCodePositionLine,
 | 
						|
}
 | 
						|
 | 
						|
pub trait ETheme: ThemeGen<C = EColor, T = String> {}
 | 
						|
impl<T: ThemeGen<C = EColor, T = String>> ETheme for T {}
 | 
						|
 | 
						|
pub fn colorize_str(
 | 
						|
    message: &Vec<(String, Option<EColor>)>,
 | 
						|
    theme: &(impl ETheme + ?Sized),
 | 
						|
) -> String {
 | 
						|
    let mut t = String::new();
 | 
						|
    for (text, color) in message {
 | 
						|
        if let Some(color) = *color {
 | 
						|
            theme.color(text, color, &mut t)
 | 
						|
        } else {
 | 
						|
            theme.nocolor(text, &mut t)
 | 
						|
        }
 | 
						|
    }
 | 
						|
    t
 | 
						|
}
 | 
						|
 | 
						|
#[derive(Clone)]
 | 
						|
pub enum CheckErrorComponent {
 | 
						|
    Message(Vec<(String, Option<EColor>)>),
 | 
						|
    Error(CheckError),
 | 
						|
    ErrorWithDifferentSource(CheckError),
 | 
						|
    Source(Vec<(SourceRange, Option<EColor>)>),
 | 
						|
}
 | 
						|
pub struct CheckErrorHRConfig {
 | 
						|
    color_index_ptr: Rc<AtomicU32>,
 | 
						|
    color_index: u32,
 | 
						|
    theme: Rc<dyn ETheme>,
 | 
						|
    is_inner: bool,
 | 
						|
    style: u8,
 | 
						|
    idt_start: String,
 | 
						|
    idt_default: String,
 | 
						|
    idt_end: String,
 | 
						|
    idt_single: String,
 | 
						|
    /// if true, shows "original" source code, if false, shows source with comments removed (this is what the parser uses internally)
 | 
						|
    show_comments: bool,
 | 
						|
}
 | 
						|
type BorderCharsSet = [[&'static str; 4]; 3];
 | 
						|
pub struct IndentStr<'a>(&'a str, String);
 | 
						|
impl Display for IndentStr<'_> {
 | 
						|
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
						|
        write!(f, "{}{}", self.0, self.1)
 | 
						|
    }
 | 
						|
}
 | 
						|
impl CheckErrorHRConfig {
 | 
						|
    pub const BORDER_STYLE_THIN: u8 = 0;
 | 
						|
    pub const BORDER_STYLE_THICK: u8 = 1;
 | 
						|
    pub const BORDER_STYLE_DOUBLE: u8 = 2;
 | 
						|
    pub const STYLE_DEFAULT: u8 = Self::BORDER_STYLE_THIN;
 | 
						|
    pub const STYLE_DIFFSRC: u8 = Self::BORDER_STYLE_THICK;
 | 
						|
    const CHARS_HORIZONTAL: [&'static str; 3] = ["╶", "╴", "─"];
 | 
						|
    const CHARS: [BorderCharsSet; 3] = [
 | 
						|
        [
 | 
						|
            ["╷", "┌", "┐", "┬"],
 | 
						|
            ["│", "├", "┤", "┼"],
 | 
						|
            ["╵", "└", "┘", "┴"],
 | 
						|
        ],
 | 
						|
        [
 | 
						|
            ["╻", "┎", "┒", "┰"],
 | 
						|
            ["┃", "┠", "┨", "╂"],
 | 
						|
            ["╹", "┖", "┚", "┸"],
 | 
						|
        ],
 | 
						|
        [
 | 
						|
            ["╦", "╓", "╖", "╥"],
 | 
						|
            ["║", "╟", "╢", "╫"],
 | 
						|
            ["╩", "╙", "╜", "╨"],
 | 
						|
        ],
 | 
						|
    ];
 | 
						|
    fn color(&self, s: &str) -> String {
 | 
						|
        let mut t = String::new();
 | 
						|
        self.theme
 | 
						|
            .color(s, EColor::Indent(self.color_index), &mut t);
 | 
						|
        return t;
 | 
						|
    }
 | 
						|
    pub fn indent_start(&self, right: bool) -> IndentStr {
 | 
						|
        IndentStr(
 | 
						|
            &self.idt_start,
 | 
						|
            self.color(
 | 
						|
                Self::CHARS[self.style as usize][0][self.is_inner as usize * 2 + right as usize],
 | 
						|
            ),
 | 
						|
        )
 | 
						|
    }
 | 
						|
    pub fn indent_default(&self, right: bool) -> IndentStr {
 | 
						|
        IndentStr(
 | 
						|
            &self.idt_default,
 | 
						|
            self.color(Self::CHARS[self.style as usize][1][right as usize]),
 | 
						|
        )
 | 
						|
    }
 | 
						|
    pub fn indent_end(&self, right: bool) -> IndentStr {
 | 
						|
        IndentStr(
 | 
						|
            &self.idt_end,
 | 
						|
            self.color(
 | 
						|
                Self::CHARS[self.style as usize][2][self.is_inner as usize * 2 + right as usize],
 | 
						|
            ),
 | 
						|
        )
 | 
						|
    }
 | 
						|
    pub fn indent_single(&self, right: bool) -> IndentStr {
 | 
						|
        IndentStr(
 | 
						|
            &self.idt_single,
 | 
						|
            self.color(if self.is_inner {
 | 
						|
                if right {
 | 
						|
                    Self::CHARS_HORIZONTAL[2] // left+right
 | 
						|
                } else {
 | 
						|
                    Self::CHARS_HORIZONTAL[1] // left only
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                if right {
 | 
						|
                    Self::CHARS_HORIZONTAL[0] // right only
 | 
						|
                } else {
 | 
						|
                    Self::CHARS_HORIZONTAL[1] // left only (so that there is something)
 | 
						|
                }
 | 
						|
            }),
 | 
						|
        )
 | 
						|
    }
 | 
						|
    pub fn for_inner(&self, is_first: bool, is_last: bool, style: u8) -> Self {
 | 
						|
        let color_index = self
 | 
						|
            .color_index_ptr
 | 
						|
            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
 | 
						|
        Self {
 | 
						|
            color_index_ptr: self.color_index_ptr.clone(),
 | 
						|
            color_index,
 | 
						|
            theme: Rc::clone(&self.theme),
 | 
						|
            is_inner: true,
 | 
						|
            style,
 | 
						|
            idt_start: if is_first {
 | 
						|
                self.indent_start(true)
 | 
						|
            } else {
 | 
						|
                self.indent_default(true)
 | 
						|
            }
 | 
						|
            .to_string(),
 | 
						|
            idt_default: self.indent_default(false).to_string(),
 | 
						|
            idt_end: if is_last {
 | 
						|
                self.indent_end(true)
 | 
						|
            } else {
 | 
						|
                self.indent_default(true)
 | 
						|
            }
 | 
						|
            .to_string(),
 | 
						|
            idt_single: match (is_first, is_last) {
 | 
						|
                (false, false) => self.indent_default(true),
 | 
						|
                (false, true) => self.indent_end(true),
 | 
						|
                (true, false) => self.indent_start(true),
 | 
						|
                (true, true) => self.indent_single(true),
 | 
						|
            }
 | 
						|
            .to_string(),
 | 
						|
            show_comments: self.show_comments,
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
#[cfg(feature = "parse")]
 | 
						|
pub struct CheckErrorDisplay<'a> {
 | 
						|
    e: &'a CheckError,
 | 
						|
    theme: Rc<dyn ETheme>,
 | 
						|
    pub show_comments: bool,
 | 
						|
}
 | 
						|
#[cfg(feature = "parse")]
 | 
						|
impl<'a> CheckErrorDisplay<'a> {
 | 
						|
    pub fn show_comments(mut self, show_comments: bool) -> Self {
 | 
						|
        self.show_comments = show_comments;
 | 
						|
        self
 | 
						|
    }
 | 
						|
}
 | 
						|
#[cfg(feature = "parse")]
 | 
						|
impl Display for CheckErrorDisplay<'_> {
 | 
						|
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
						|
        self.e.human_readable(
 | 
						|
            f,
 | 
						|
            &CheckErrorHRConfig {
 | 
						|
                color_index: 0,
 | 
						|
                color_index_ptr: Rc::new(AtomicU32::new(1)),
 | 
						|
                theme: Rc::clone(&self.theme),
 | 
						|
                is_inner: false,
 | 
						|
                style: CheckErrorHRConfig::STYLE_DEFAULT,
 | 
						|
                idt_start: String::new(),
 | 
						|
                idt_default: String::new(),
 | 
						|
                idt_end: String::new(),
 | 
						|
                idt_single: String::new(),
 | 
						|
                show_comments: self.show_comments,
 | 
						|
            },
 | 
						|
        )
 | 
						|
    }
 | 
						|
}
 | 
						|
#[allow(unused)]
 | 
						|
impl CheckError {
 | 
						|
    pub fn new() -> Self {
 | 
						|
        CheckError(vec![])
 | 
						|
    }
 | 
						|
    fn add(mut self, v: CheckErrorComponent) -> Self {
 | 
						|
        self.0.push(v);
 | 
						|
        self
 | 
						|
    }
 | 
						|
    fn add_mut(&mut self, v: CheckErrorComponent) -> &mut Self {
 | 
						|
        self.0.push(v);
 | 
						|
        self
 | 
						|
    }
 | 
						|
    pub(crate) fn msg_str(self, s: String) -> Self {
 | 
						|
        self.add(CheckErrorComponent::Message(vec![(s, None)]))
 | 
						|
    }
 | 
						|
    pub(crate) fn msg_mut_str(&mut self, s: String) -> &mut Self {
 | 
						|
        self.add_mut(CheckErrorComponent::Message(vec![(s, None)]))
 | 
						|
    }
 | 
						|
    pub(crate) fn msg(self, s: Vec<(String, Option<EColor>)>) -> Self {
 | 
						|
        self.add(CheckErrorComponent::Message(s))
 | 
						|
    }
 | 
						|
    pub(crate) fn msg_mut(&mut self, s: Vec<(String, Option<EColor>)>) -> &mut Self {
 | 
						|
        self.add_mut(CheckErrorComponent::Message(s))
 | 
						|
    }
 | 
						|
    pub(crate) fn err(self, e: Self) -> Self {
 | 
						|
        self.add(CheckErrorComponent::Error(e))
 | 
						|
    }
 | 
						|
    pub(crate) fn err_mut(&mut self, e: Self) -> &mut Self {
 | 
						|
        self.add_mut(CheckErrorComponent::Error(e))
 | 
						|
    }
 | 
						|
    pub(crate) fn err_with_diff_src(self, e: CheckError) -> Self {
 | 
						|
        self.add(CheckErrorComponent::ErrorWithDifferentSource(e))
 | 
						|
    }
 | 
						|
    pub(crate) fn err_with_diff_src_mut(&mut self, e: CheckError) -> &mut Self {
 | 
						|
        self.add_mut(CheckErrorComponent::ErrorWithDifferentSource(e))
 | 
						|
    }
 | 
						|
    pub(crate) fn src(self, s: Vec<(SourceRange, Option<EColor>)>) -> Self {
 | 
						|
        self.add(CheckErrorComponent::Source(s))
 | 
						|
    }
 | 
						|
    pub(crate) fn src_mut(&mut self, s: Vec<(SourceRange, Option<EColor>)>) -> &mut Self {
 | 
						|
        self.add_mut(CheckErrorComponent::Source(s))
 | 
						|
    }
 | 
						|
    #[cfg(feature = "parse")]
 | 
						|
    pub fn display<'a>(&'a self, theme: impl ETheme + 'static) -> CheckErrorDisplay<'a> {
 | 
						|
        CheckErrorDisplay {
 | 
						|
            e: self,
 | 
						|
            theme: Rc::new(theme),
 | 
						|
            show_comments: true,
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /// Like `display`, but doesn't use any theme (doesn't colorize its output)
 | 
						|
    #[cfg(feature = "parse")]
 | 
						|
    pub fn display_notheme<'a>(&'a self) -> CheckErrorDisplay<'a> {
 | 
						|
        self.display(themes::NoTheme)
 | 
						|
    }
 | 
						|
    /// Like `display`, but uses the default terminal theme
 | 
						|
    #[cfg(feature = "parse")]
 | 
						|
    #[cfg(feature = "ecolor-term")]
 | 
						|
    pub fn display_term<'a>(&'a self) -> CheckErrorDisplay<'a> {
 | 
						|
        self.display(themes::TermDefaultTheme)
 | 
						|
    }
 | 
						|
    /// will, unless empty, end in a newline
 | 
						|
    #[cfg(feature = "parse")]
 | 
						|
    fn human_readable(
 | 
						|
        &self,
 | 
						|
        f: &mut std::fmt::Formatter<'_>,
 | 
						|
        cfg: &CheckErrorHRConfig,
 | 
						|
    ) -> std::fmt::Result {
 | 
						|
        const ADD_RIGHT_BITS: bool = false;
 | 
						|
        use crate::{parsing::SourceFrom, theme::ThemeTo};
 | 
						|
 | 
						|
        let len = self.0.len();
 | 
						|
        for (i, component) in self.0.iter().enumerate() {
 | 
						|
            let is_first = i == 0;
 | 
						|
            let is_last = i + 1 == len;
 | 
						|
            let i = (); // to see if we use `i` anywhere else
 | 
						|
            macro_rules! indent {
 | 
						|
                ($s:expr, $e:expr, $right:expr) => {
 | 
						|
                    match ($s && is_first, $e && is_last) {
 | 
						|
                        (false, false) => cfg.indent_default($right),
 | 
						|
                        (false, true) => cfg.indent_end($right),
 | 
						|
                        (true, false) => cfg.indent_start($right),
 | 
						|
                        (true, true) => cfg.indent_single($right),
 | 
						|
                    }
 | 
						|
                };
 | 
						|
            }
 | 
						|
            match component {
 | 
						|
                CheckErrorComponent::Message(msg) => {
 | 
						|
                    let msg = colorize_str(msg, cfg.theme.as_ref());
 | 
						|
                    let lines = msg.lines().collect::<Vec<_>>();
 | 
						|
                    let lc = lines.len();
 | 
						|
                    for (i, line) in lines.into_iter().enumerate() {
 | 
						|
                        let s = i == 0;
 | 
						|
                        let e = i + 1 == lc;
 | 
						|
                        writeln!(f, "{}{line}", indent!(s, e, s && ADD_RIGHT_BITS))?
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                CheckErrorComponent::Error(err) => {
 | 
						|
                    let cfg = cfg.for_inner(is_first, is_last, CheckErrorHRConfig::STYLE_DEFAULT);
 | 
						|
                    err.human_readable(f, &cfg)?;
 | 
						|
                }
 | 
						|
                CheckErrorComponent::ErrorWithDifferentSource(err) => {
 | 
						|
                    let cfg = cfg.for_inner(is_first, is_last, CheckErrorHRConfig::STYLE_DIFFSRC);
 | 
						|
                    err.human_readable(f, &cfg)?;
 | 
						|
                }
 | 
						|
                CheckErrorComponent::Source(highlights) => {
 | 
						|
                    if let Some(src) = highlights.first().map(|v| v.0.in_file.as_ref()) {
 | 
						|
                        let start = highlights.iter().map(|v| v.0.start.pos()).min();
 | 
						|
                        let end = highlights.iter().map(|v| v.0.end.pos()).max();
 | 
						|
                        if let (Some(start_in_line), Some(end_in_line)) = (start, end) {
 | 
						|
                            let start = src.get_line_start(start_in_line);
 | 
						|
                            let end = src.get_line_end(end_in_line);
 | 
						|
                            let (start_with_comments, end_with_comments) = (
 | 
						|
                                src.pos_in_og(start_in_line, true),
 | 
						|
                                src.pos_in_og(end_in_line, false),
 | 
						|
                            );
 | 
						|
                            let (mut start, mut end) = if cfg.show_comments {
 | 
						|
                                (src.pos_in_og(start, true), src.pos_in_og(end, false))
 | 
						|
                            } else {
 | 
						|
                                (start, end)
 | 
						|
                            };
 | 
						|
                            let mut first_line_start = 0;
 | 
						|
                            let first_line_nr = src
 | 
						|
                                .src_og()
 | 
						|
                                .line_spans()
 | 
						|
                                .take_while(|l| {
 | 
						|
                                    if l.start() <= start_with_comments {
 | 
						|
                                        first_line_start = l.start();
 | 
						|
                                        true
 | 
						|
                                    } else {
 | 
						|
                                        false
 | 
						|
                                    }
 | 
						|
                                })
 | 
						|
                                .count();
 | 
						|
                            if cfg.show_comments && first_line_start < start {
 | 
						|
                                start = first_line_start;
 | 
						|
                            }
 | 
						|
                            let mut last_line_start = 0;
 | 
						|
                            let last_line_nr = src
 | 
						|
                                .src_og()
 | 
						|
                                .line_spans()
 | 
						|
                                .take_while(|l| {
 | 
						|
                                    if l.start() <= end_with_comments {
 | 
						|
                                        last_line_start = l.start();
 | 
						|
                                        if cfg.show_comments && l.end() > end {
 | 
						|
                                            end = l.end();
 | 
						|
                                        }
 | 
						|
                                        true
 | 
						|
                                    } else {
 | 
						|
                                        false
 | 
						|
                                    }
 | 
						|
                                })
 | 
						|
                                .count();
 | 
						|
                            let src_from = match src.src_from() {
 | 
						|
                                SourceFrom::File(path) => format!(" [{}]", path.to_string_lossy()),
 | 
						|
                                SourceFrom::Unspecified => String::with_capacity(0),
 | 
						|
                            };
 | 
						|
                            if first_line_nr == last_line_nr {
 | 
						|
                                writeln!(
 | 
						|
                                    f,
 | 
						|
                                    "{}{}",
 | 
						|
                                    indent!(true, false, ADD_RIGHT_BITS),
 | 
						|
                                    cfg.theme.color_to(
 | 
						|
                                        &format!(
 | 
						|
                                            "Line {first_line_nr} ({}..{}){}",
 | 
						|
                                            start_with_comments + 1 - first_line_start,
 | 
						|
                                            end_with_comments - last_line_start,
 | 
						|
                                            src_from,
 | 
						|
                                        ),
 | 
						|
                                        EColor::InCodePositionLine
 | 
						|
                                    )
 | 
						|
                                )?;
 | 
						|
                            } else {
 | 
						|
                                writeln!(
 | 
						|
                                    f,
 | 
						|
                                    "{}{}",
 | 
						|
                                    indent!(true, false, ADD_RIGHT_BITS),
 | 
						|
                                    cfg.theme.color_to(
 | 
						|
                                        &format!(
 | 
						|
                                            "Lines {first_line_nr}-{last_line_nr} ({}..{}){}",
 | 
						|
                                            start_with_comments + 1 - first_line_start,
 | 
						|
                                            end_with_comments - last_line_start,
 | 
						|
                                            src_from,
 | 
						|
                                        ),
 | 
						|
                                        EColor::InCodePositionLine
 | 
						|
                                    )
 | 
						|
                                )?;
 | 
						|
                            }
 | 
						|
                            let lines = if cfg.show_comments {
 | 
						|
                                src.src_og()[start..end].line_spans().collect::<Vec<_>>()
 | 
						|
                            } else {
 | 
						|
                                src.src()[start..end].line_spans().collect::<Vec<_>>()
 | 
						|
                            };
 | 
						|
                            let lines_count = lines.len();
 | 
						|
                            for (line_nr_rel, line) in lines.into_iter().enumerate() {
 | 
						|
                                let last_line = line_nr_rel + 1 == lines_count;
 | 
						|
                                let line_start = line.start();
 | 
						|
                                let line_end = line.end();
 | 
						|
                                let line = line.as_str();
 | 
						|
                                let mut line_printed = false;
 | 
						|
                                let mut right = 0;
 | 
						|
                                for (highlight_index, (highlight_pos, color)) in
 | 
						|
                                    highlights.iter().enumerate()
 | 
						|
                                {
 | 
						|
                                    if let Some(color) = *color {
 | 
						|
                                        let (highlight_start, highlight_end) = if cfg.show_comments
 | 
						|
                                        {
 | 
						|
                                            (
 | 
						|
                                                src.pos_in_og(highlight_pos.start.pos(), true),
 | 
						|
                                                src.pos_in_og(highlight_pos.end.pos(), false),
 | 
						|
                                            )
 | 
						|
                                        } else {
 | 
						|
                                            (highlight_pos.start.pos(), highlight_pos.end.pos())
 | 
						|
                                        };
 | 
						|
                                        let highlight_start = highlight_start - start;
 | 
						|
                                        let highlight_end = highlight_end - start;
 | 
						|
                                        if highlight_start < line_end && highlight_end > line_start
 | 
						|
                                        {
 | 
						|
                                            if !line_printed {
 | 
						|
                                                // this isn't the last line (important for indent)
 | 
						|
                                                writeln!(
 | 
						|
                                                    f,
 | 
						|
                                                    "{} {line}",
 | 
						|
                                                    indent!(false, false, false)
 | 
						|
                                                )?;
 | 
						|
                                                line_printed = true;
 | 
						|
                                            }
 | 
						|
                                            // where the highlight starts in this line
 | 
						|
                                            let hl_start =
 | 
						|
                                                highlight_start.saturating_sub(line_start);
 | 
						|
                                            // highlight would be further left than cursor, so we need a new line
 | 
						|
                                            if hl_start < right {
 | 
						|
                                                right = 0;
 | 
						|
                                                writeln!(f)?;
 | 
						|
                                            }
 | 
						|
                                            // length of the highlight
 | 
						|
                                            let hl_len = highlight_end
 | 
						|
                                                .saturating_sub(line_start)
 | 
						|
                                                .saturating_sub(hl_start);
 | 
						|
                                            let hl_space = hl_start - right;
 | 
						|
                                            let print_indent = right == 0;
 | 
						|
                                            let hl_len = hl_len.min(line.len() - right);
 | 
						|
                                            right += hl_space + hl_len;
 | 
						|
                                            if print_indent && right != 0 {
 | 
						|
                                                write!(
 | 
						|
                                                    f,
 | 
						|
                                                    "{} ",
 | 
						|
                                                    indent!(
 | 
						|
                                                        false,
 | 
						|
                                                        // is end if last_line and
 | 
						|
                                                        // all following highlights can be put on this line
 | 
						|
                                                        last_line
 | 
						|
                                                            && highlights
 | 
						|
                                                                .iter()
 | 
						|
                                                                .skip(highlight_index + 1)
 | 
						|
                                                                .try_fold(
 | 
						|
                                                                    // accumulator = end position of previous highlight
 | 
						|
                                                                    highlight_pos.end().pos(),
 | 
						|
                                                                    // success if all highlights start only after the previous highlight ended: a < hl.start
 | 
						|
                                                                    |a, hl| if a < hl
 | 
						|
                                                                        .0
 | 
						|
                                                                        .start()
 | 
						|
                                                                        .pos()
 | 
						|
                                                                    {
 | 
						|
                                                                        Some(hl.0.end().pos())
 | 
						|
                                                                    } else {
 | 
						|
                                                                        None
 | 
						|
                                                                    }
 | 
						|
                                                                )
 | 
						|
                                                                .is_some(),
 | 
						|
                                                        false
 | 
						|
                                                    )
 | 
						|
                                                )?;
 | 
						|
                                            }
 | 
						|
                                            write!(
 | 
						|
                                                f,
 | 
						|
                                                "{}{}",
 | 
						|
                                                " ".repeat(hl_space),
 | 
						|
                                                &cfg.theme.color_to(&"~".repeat(hl_len), color)
 | 
						|
                                            )?;
 | 
						|
                                        }
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                                if !line_printed {
 | 
						|
                                    // may be last line (important for indent)
 | 
						|
                                    writeln!(f, "{} {line}", indent!(false, last_line, false))?;
 | 
						|
                                }
 | 
						|
                                if right != 0 {
 | 
						|
                                    writeln!(f)?;
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        Ok(())
 | 
						|
    }
 | 
						|
}
 | 
						|
impl From<String> for CheckError {
 | 
						|
    fn from(value: String) -> Self {
 | 
						|
        Self::new().msg_str(value)
 | 
						|
    }
 | 
						|
}
 | 
						|
impl From<&str> for CheckError {
 | 
						|
    fn from(value: &str) -> Self {
 | 
						|
        value.to_owned().into()
 | 
						|
    }
 | 
						|
}
 | 
						|
impl Debug for CheckError {
 | 
						|
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
						|
        #[cfg(feature = "ecolor-term")]
 | 
						|
        let e = self.display_term();
 | 
						|
        #[cfg(not(feature = "ecolor-term"))]
 | 
						|
        let e = self.display_notheme();
 | 
						|
        write!(f, "{e}")
 | 
						|
    }
 | 
						|
}
 |