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
use std::fmt;

use serde::{Deserialize, Serialize};

use crate::{formatting::source_lines, sources::Source};

#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct Range {
    pub start: Position,
    pub end: Position,
}

impl Range {
    pub fn from_span(source: &str, (left, right): (usize, usize)) -> Self {
        let start = Position::from_loc(source, left);
        let end = Position::from_loc(source, right);
        Self { start, end }
    }
}

#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct Position {
    pub row: usize,
    pub column: usize,
}

impl Position {
    pub fn from_loc(source: &str, loc: usize) -> Self {
        let (row, column) = crate::lexer::loc_to_pos(source, loc);
        Self { row, column }
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Context {
    pub source: Source,
    pub range: Range,
}

// TODO(gj): temporary hack -- this won't be necessary once `formatting::source_lines` takes a
// `Range` instead of a single `usize` (`loc`).
fn pos_to_loc(src: &str, row: usize, column: usize) -> usize {
    let chars_before_row = src.split('\n').take(row).flat_map(|r| r.chars()).count();
    row + chars_before_row + column
}

impl fmt::Display for Context {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Position { row, column } = self.range.start;
        write!(f, " at line {}, column {}", row + 1, column + 1)?;
        if let Some(ref filename) = self.source.filename {
            write!(f, " of file {}", filename)?;
        }
        let loc = pos_to_loc(&self.source.src, row, column);
        let lines = source_lines(&self.source, loc, 0).replace('\n', "\n\t");
        writeln!(f, ":\n\t{}", lines)?;
        Ok(())
    }
}