1use std::iter::Extend;
2use crate::{
3 Position,
4 Span,
5 Metrics
6};
7
8pub struct Layout<M: Metrics> {
13 lines: Vec<usize>,
15
16 span: Span,
18
19 metrics: M,
21
22 len: usize
24}
25
26impl<M: Metrics> Layout<M> {
27 pub fn new(metrics: M) -> Layout<M> {
29 Layout {
30 lines: vec![0],
31 span: Span::default(),
32 metrics,
33 len: 0
34 }
35 }
36
37 pub fn span(&self) -> Span {
39 self.span
40 }
41
42 pub fn from<Chars: Iterator<Item=char>>(chars: Chars, metrics: M) -> Layout<M> {
44 let mut layout = Layout::new(metrics);
45 layout.extend(chars);
46 layout
47 }
48
49 pub fn try_from<E, Chars: Iterator<Item=Result<char, E>>>(chars: Chars, metrics: M) -> Result<Layout<M>, E> {
51 let mut layout = Layout::new(metrics);
52
53 for c in chars {
54 layout.push(c?)
55 }
56
57 Ok(layout)
58 }
59
60 pub fn push(&mut self, c: char) {
62 self.span.push(c, &self.metrics);
63 self.len += c.len_utf8();
64 if c == '\n' {
65 self.lines.push(self.len)
66 }
67 }
68
69 pub fn byte_index(&self, str: &str, position: Position) -> Option<usize> {
75 if let Some(line_offset) = self.lines.get(position.line) {
76 let mut column = 0;
77 for (i, c) in str[*line_offset..].char_indices() {
78 if column == position.column {
79 return Some(line_offset + i)
80 }
81
82 if c == '\n' {
83 return None
84 }
85
86 column += self.metrics.char_width(c)
87 }
88 }
89
90 None
91 }
92
93 pub fn span_slice<'a>(&self, str: &'a str, span: Span) -> &'a str {
95 let start = match self.byte_index(str, span.start) {
96 Some(index) => index,
97 None => 0
98 };
99
100 let end = match self.byte_index(str, span.end) {
101 Some(index) => index,
102 None => str.len()
103 };
104
105 &str[start..end]
106 }
107}
108
109impl<M: Metrics> Extend<char> for Layout<M> {
110 fn extend<Chars: IntoIterator<Item=char>>(&mut self, chars: Chars) {
111 for c in chars {
112 self.push(c)
113 }
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn get_byte_index1() {
123 let str = "Hello World!";
124 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
125
126 assert_eq!(layout.byte_index(str, Position::new(0, 2)), Some(2));
127 }
128
129 #[test]
130 fn get_byte_index2() {
131 let str = "Hello\nWorld!";
132 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
133
134 assert_eq!(layout.byte_index(str, Position::new(1, 0)), Some(6));
135 }
136
137 #[test]
138 fn get_byte_index3() {
139 let str = "Hel\nlo\nWorld!";
140 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
141
142 assert_eq!(layout.byte_index(str, Position::new(2, 0)), Some(7));
143 }
144
145 #[test]
146 fn get_byte_index_out_of_bounds1() {
147 let str = "Hel\nlo\nWorld!";
148 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
149
150 assert_eq!(layout.byte_index(str, Position::new(3, 0)), None);
151 }
152
153 #[test]
154 fn get_byte_index_out_of_bounds2() {
155 let str = "Hel\nlo\nWorld!";
156 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
157
158 assert_eq!(layout.byte_index(str, Position::new(1, 3)), None);
159 }
160
161 #[test]
162 fn get_span_slice1() {
163 let str = "Hello\nWorld!";
164 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
165
166 assert_eq!(layout.span_slice(str, layout.span), str);
167 }
168
169 #[test]
170 fn get_span_slice2() {
171 let str = "Hel\nlo\nWorld!";
172 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
173
174 let span = Span::new(Position::new(0, 0), Position::new(0, 3), Position::new(1, 0));
175 assert_eq!(layout.span_slice(str, span), "Hel\n");
176 }
177
178 #[test]
179 fn get_span_slice3() {
180 let str = "Hel\nlo\nWorld!";
181 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
182
183 let span = Span::new(Position::new(1, 0), Position::new(1, 2), Position::new(2, 0));
184 assert_eq!(layout.span_slice(str, span), "lo\n");
185 }
186
187 #[test]
188 fn get_span_slice4() {
189 let str = "Hel\nlo\nWorld!";
190 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
191
192 let span = Span::new(Position::new(2, 0), Position::new(2, 5), Position::new(2, 6));
193 assert_eq!(layout.span_slice(str, span), "World!");
194 }
195
196 #[test]
197 fn get_span_slice5() {
198 let str = "Hel\nlo\nWorld!";
199 let layout = Layout::from(str.chars(), crate::DEFAULT_METRICS);
200
201 let span = Span::new(Position::new(0, 2), Position::new(2, 2), Position::new(2, 3));
202 assert_eq!(layout.span_slice(str, span), "l\nlo\nWor");
203 }
204}