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
use super::Span;
use std::fmt;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::io::{self, Read, Write};
pub struct FileText {
path: PathBuf,
input_str: String,
newlines: Vec<usize>,
}
impl FileText {
pub fn from_path(path: PathBuf) -> io::Result<FileText> {
let mut input_str = String::new();
let mut f = try!(File::open(&path));
try!(f.read_to_string(&mut input_str));
Ok(FileText::new(path, input_str))
}
fn new(path: PathBuf, input_str: String) -> FileText {
let newline_indices: Vec<usize> = {
let input_indices =
input_str.as_bytes().iter()
.enumerate()
.filter(|&(_, &b)| b == ('\n' as u8))
.map(|(i, _)| i + 1);
Some(0).into_iter()
.chain(input_indices)
.collect()
};
FileText { path: path, input_str: input_str, newlines: newline_indices }
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn text(&self) -> &String {
&self.input_str
}
pub fn line_col(&self, pos: usize) -> (usize, usize) {
let num_lines = self.newlines.len();
let line =
(0..num_lines)
.filter(|&i| self.newlines[i] > pos)
.map(|i| i-1)
.next()
.unwrap_or(num_lines - 1);
let line_offset = self.newlines[line];
let col = pos - line_offset;
(line, col)
}
fn line_text(&self, line_num: usize) -> &str {
let start_offset = self.newlines[line_num];
if line_num == self.newlines.len() - 1 {
&self.input_str[start_offset..]
} else {
let end_offset = self.newlines[line_num + 1];
&self.input_str[start_offset..end_offset-1]
}
}
pub fn highlight(&self, span: Span, out: &mut Write) -> io::Result<()> {
let (start_line, start_col) = self.line_col(span.0);
let (end_line, end_col) = self.line_col(span.1);
if start_line == end_line {
let text = self.line_text(start_line);
try!(writeln!(out, " {}", text));
if end_col - start_col <= 1 {
try!(writeln!(out, " {}^", Repeat(' ', start_col)));
} else {
let width = end_col - start_col;
try!(writeln!(out, " {}~{}~",
Repeat(' ', start_col),
Repeat('~', width.saturating_sub(2))));
}
} else {
let line_strs: Vec<_> = (start_line..end_line+1).map(|i| self.line_text(i)).collect();
let max_len = line_strs.iter().map(|l| l.len()).max().unwrap();
try!(writeln!(out, " {}{}~+",
Repeat(' ', start_col),
Repeat('~', max_len - start_col)));
for line in &line_strs[..line_strs.len()-1] {
try!(writeln!(out, "| {0:<1$} |", line, max_len));
}
try!(writeln!(out, "| {}", line_strs[line_strs.len()-1]));
try!(writeln!(out, "+~{}", Repeat('~', end_col)));
}
Ok(())
}
}
struct Repeat(char, usize);
impl fmt::Display for Repeat {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
for _ in 0..self.1 {
try!(write!(fmt, "{}", self.0));
}
Ok(())
}
}