mod.rs

  1mod fold_map;
  2
  3use super::{buffer, Anchor, AnchorRangeExt, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
  4use anyhow::Result;
  5pub use fold_map::BufferRows;
  6use fold_map::FoldMap;
  7use gpui::{AppContext, Entity, ModelContext, ModelHandle};
  8use std::ops::Range;
  9
 10#[derive(Copy, Clone)]
 11pub enum Bias {
 12    Left,
 13    Right,
 14}
 15
 16pub struct DisplayMap {
 17    buffer: ModelHandle<Buffer>,
 18    fold_map: FoldMap,
 19    tab_size: usize,
 20}
 21
 22impl Entity for DisplayMap {
 23    type Event = ();
 24}
 25
 26impl DisplayMap {
 27    pub fn new(buffer: ModelHandle<Buffer>, tab_size: usize, ctx: &mut ModelContext<Self>) -> Self {
 28        ctx.subscribe(&buffer, Self::handle_buffer_event);
 29
 30        DisplayMap {
 31            buffer: buffer.clone(),
 32            fold_map: FoldMap::new(buffer, ctx.as_ref()),
 33            tab_size,
 34        }
 35    }
 36
 37    pub fn folds_in_range<T>(&self, range: Range<T>, app: &AppContext) -> Result<&[Range<Anchor>]>
 38    where
 39        T: ToOffset,
 40    {
 41        self.fold_map.folds_in_range(range, app)
 42    }
 43
 44    pub fn fold<T: ToOffset>(
 45        &mut self,
 46        ranges: impl IntoIterator<Item = Range<T>>,
 47        ctx: &mut ModelContext<Self>,
 48    ) -> Result<()> {
 49        self.fold_map.fold(ranges, ctx.as_ref())?;
 50        ctx.notify();
 51        Ok(())
 52    }
 53
 54    pub fn unfold<T: ToOffset>(
 55        &mut self,
 56        ranges: impl IntoIterator<Item = Range<T>>,
 57        ctx: &mut ModelContext<Self>,
 58    ) -> Result<()> {
 59        self.fold_map.unfold(ranges, ctx.as_ref())?;
 60        ctx.notify();
 61        Ok(())
 62    }
 63
 64    pub fn is_line_folded(&self, display_row: u32) -> bool {
 65        self.fold_map.is_line_folded(display_row)
 66    }
 67
 68    pub fn text(&self, app: &AppContext) -> String {
 69        self.chars_at(DisplayPoint::zero(), app).unwrap().collect()
 70    }
 71
 72    pub fn line(&self, display_row: u32, app: &AppContext) -> Result<String> {
 73        let chars = self.chars_at(DisplayPoint::new(display_row, 0), app)?;
 74        Ok(chars.take_while(|c| *c != '\n').collect())
 75    }
 76
 77    pub fn line_indent(&self, display_row: u32, app: &AppContext) -> Result<(u32, bool)> {
 78        let mut indent = 0;
 79        let mut is_blank = true;
 80        for c in self.chars_at(DisplayPoint::new(display_row, 0), app)? {
 81            if c == ' ' {
 82                indent += 1;
 83            } else {
 84                is_blank = c == '\n';
 85                break;
 86            }
 87        }
 88        Ok((indent, is_blank))
 89    }
 90
 91    pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Result<Chars<'a>> {
 92        let column = point.column() as usize;
 93        let (point, to_next_stop) = point.collapse_tabs(self, Bias::Left, app)?;
 94        let mut fold_chars = self.fold_map.chars_at(point, app)?;
 95        if to_next_stop > 0 {
 96            fold_chars.next();
 97        }
 98
 99        Ok(Chars {
100            fold_chars,
101            column,
102            to_next_stop,
103            tab_size: self.tab_size,
104        })
105    }
106
107    pub fn buffer_rows(&self, start_row: u32) -> Result<BufferRows> {
108        self.fold_map.buffer_rows(start_row)
109    }
110
111    pub fn line_len(&self, row: u32, ctx: &AppContext) -> Result<u32> {
112        DisplayPoint::new(row, self.fold_map.line_len(row, ctx)?)
113            .expand_tabs(self, ctx)
114            .map(|point| point.column())
115    }
116
117    pub fn max_point(&self, app: &AppContext) -> DisplayPoint {
118        self.fold_map.max_point().expand_tabs(self, app).unwrap()
119    }
120
121    pub fn rightmost_point(&self) -> DisplayPoint {
122        self.fold_map.rightmost_point()
123    }
124
125    pub fn anchor_before(
126        &self,
127        point: DisplayPoint,
128        bias: Bias,
129        app: &AppContext,
130    ) -> Result<Anchor> {
131        self.buffer
132            .read(app)
133            .anchor_before(point.to_buffer_point(self, bias, app)?)
134    }
135
136    pub fn anchor_after(
137        &self,
138        point: DisplayPoint,
139        bias: Bias,
140        app: &AppContext,
141    ) -> Result<Anchor> {
142        self.buffer
143            .read(app)
144            .anchor_after(point.to_buffer_point(self, bias, app)?)
145    }
146
147    fn handle_buffer_event(&mut self, event: &buffer::Event, ctx: &mut ModelContext<Self>) {
148        match event {
149            buffer::Event::Edited(edits) => self.fold_map.apply_edits(edits, ctx.as_ref()).unwrap(),
150            _ => {}
151        }
152    }
153}
154
155#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
156pub struct DisplayPoint(Point);
157
158impl DisplayPoint {
159    pub fn new(row: u32, column: u32) -> Self {
160        Self(Point::new(row, column))
161    }
162
163    pub fn zero() -> Self {
164        Self::new(0, 0)
165    }
166
167    pub fn row(self) -> u32 {
168        self.0.row
169    }
170
171    pub fn column(self) -> u32 {
172        self.0.column
173    }
174
175    pub fn row_mut(&mut self) -> &mut u32 {
176        &mut self.0.row
177    }
178
179    pub fn column_mut(&mut self) -> &mut u32 {
180        &mut self.0.column
181    }
182
183    pub fn to_buffer_point(self, map: &DisplayMap, bias: Bias, app: &AppContext) -> Result<Point> {
184        Ok(map
185            .fold_map
186            .to_buffer_point(self.collapse_tabs(map, bias, app)?.0))
187    }
188
189    pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, app: &AppContext) -> Result<usize> {
190        map.fold_map
191            .to_buffer_offset(self.collapse_tabs(map, bias, app)?.0, app)
192    }
193
194    fn expand_tabs(mut self, map: &DisplayMap, app: &AppContext) -> Result<Self> {
195        let chars = map
196            .fold_map
197            .chars_at(DisplayPoint(Point::new(self.row(), 0)), app)?;
198        let expanded = expand_tabs(chars, self.column() as usize, map.tab_size);
199        *self.column_mut() = expanded as u32;
200
201        Ok(self)
202    }
203
204    fn collapse_tabs(
205        mut self,
206        map: &DisplayMap,
207        bias: Bias,
208        app: &AppContext,
209    ) -> Result<(Self, usize)> {
210        let chars = map
211            .fold_map
212            .chars_at(DisplayPoint(Point::new(self.0.row, 0)), app)?;
213        let expanded = self.column() as usize;
214        let (collapsed, to_next_stop) = collapse_tabs(chars, expanded, bias, map.tab_size);
215        *self.column_mut() = collapsed as u32;
216
217        Ok((self, to_next_stop))
218    }
219}
220
221impl Point {
222    pub fn to_display_point(self, map: &DisplayMap, app: &AppContext) -> Result<DisplayPoint> {
223        let mut display_point = map.fold_map.to_display_point(self);
224        let chars = map
225            .fold_map
226            .chars_at(DisplayPoint::new(display_point.row(), 0), app)?;
227        *display_point.column_mut() =
228            expand_tabs(chars, display_point.column() as usize, map.tab_size) as u32;
229        Ok(display_point)
230    }
231}
232
233impl Anchor {
234    pub fn to_display_point(&self, map: &DisplayMap, app: &AppContext) -> Result<DisplayPoint> {
235        self.to_point(map.buffer.read(app))?
236            .to_display_point(map, app)
237    }
238}
239
240pub struct Chars<'a> {
241    fold_chars: fold_map::Chars<'a>,
242    column: usize,
243    to_next_stop: usize,
244    tab_size: usize,
245}
246
247impl<'a> Iterator for Chars<'a> {
248    type Item = char;
249
250    fn next(&mut self) -> Option<Self::Item> {
251        if self.to_next_stop > 0 {
252            self.to_next_stop -= 1;
253            self.column += 1;
254            Some(' ')
255        } else {
256            self.fold_chars.next().map(|c| match c {
257                '\t' => {
258                    self.to_next_stop = self.tab_size - self.column % self.tab_size - 1;
259                    self.column += 1;
260                    ' '
261                }
262                '\n' => {
263                    self.column = 0;
264                    c
265                }
266                _ => {
267                    self.column += 1;
268                    c
269                }
270            })
271        }
272    }
273}
274
275pub fn expand_tabs(chars: impl Iterator<Item = char>, column: usize, tab_size: usize) -> usize {
276    let mut expanded = 0;
277    for c in chars.take(column) {
278        if c == '\t' {
279            expanded += tab_size - expanded % tab_size;
280        } else {
281            expanded += 1;
282        }
283    }
284    expanded
285}
286
287pub fn collapse_tabs(
288    mut chars: impl Iterator<Item = char>,
289    column: usize,
290    bias: Bias,
291    tab_size: usize,
292) -> (usize, usize) {
293    let mut expanded = 0;
294    let mut collapsed = 0;
295    while let Some(c) = chars.next() {
296        if expanded == column {
297            break;
298        }
299
300        if c == '\t' {
301            expanded += tab_size - (expanded % tab_size);
302            if expanded > column {
303                return match bias {
304                    Bias::Left => (collapsed, expanded - column),
305                    Bias::Right => (collapsed + 1, 0),
306                };
307            }
308            collapsed += 1;
309        } else {
310            expanded += 1;
311            collapsed += 1;
312        }
313    }
314    (collapsed, 0)
315}
316
317#[cfg(test)]
318mod tests {
319    use super::*;
320    use crate::test::*;
321    use gpui::App;
322
323    #[test]
324    fn test_chars_at() {
325        App::test((), |app| {
326            let text = sample_text(6, 6);
327            let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
328            let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx));
329            buffer
330                .update(app, |buffer, ctx| {
331                    buffer.edit(
332                        vec![
333                            Point::new(1, 0)..Point::new(1, 0),
334                            Point::new(1, 1)..Point::new(1, 1),
335                            Point::new(2, 1)..Point::new(2, 1),
336                        ],
337                        "\t",
338                        Some(ctx),
339                    )
340                })
341                .unwrap();
342
343            let map = map.read(app);
344            assert_eq!(
345                map.chars_at(DisplayPoint::new(1, 0), app.as_ref())
346                    .unwrap()
347                    .take(10)
348                    .collect::<String>(),
349                "    b   bb"
350            );
351            assert_eq!(
352                map.chars_at(DisplayPoint::new(1, 2), app.as_ref())
353                    .unwrap()
354                    .take(10)
355                    .collect::<String>(),
356                "  b   bbbb"
357            );
358            assert_eq!(
359                map.chars_at(DisplayPoint::new(1, 6), app.as_ref())
360                    .unwrap()
361                    .take(13)
362                    .collect::<String>(),
363                "  bbbbb\nc   c"
364            );
365        });
366    }
367
368    #[test]
369    fn test_expand_tabs() {
370        assert_eq!(expand_tabs("\t".chars(), 0, 4), 0);
371        assert_eq!(expand_tabs("\t".chars(), 1, 4), 4);
372        assert_eq!(expand_tabs("\ta".chars(), 2, 4), 5);
373    }
374
375    #[test]
376    fn test_collapse_tabs() {
377        assert_eq!(collapse_tabs("\t".chars(), 0, Bias::Left, 4), (0, 0));
378        assert_eq!(collapse_tabs("\t".chars(), 0, Bias::Right, 4), (0, 0));
379        assert_eq!(collapse_tabs("\t".chars(), 1, Bias::Left, 4), (0, 3));
380        assert_eq!(collapse_tabs("\t".chars(), 1, Bias::Right, 4), (1, 0));
381        assert_eq!(collapse_tabs("\t".chars(), 2, Bias::Left, 4), (0, 2));
382        assert_eq!(collapse_tabs("\t".chars(), 2, Bias::Right, 4), (1, 0));
383        assert_eq!(collapse_tabs("\t".chars(), 3, Bias::Left, 4), (0, 1));
384        assert_eq!(collapse_tabs("\t".chars(), 3, Bias::Right, 4), (1, 0));
385        assert_eq!(collapse_tabs("\t".chars(), 4, Bias::Left, 4), (1, 0));
386        assert_eq!(collapse_tabs("\t".chars(), 4, Bias::Right, 4), (1, 0));
387        assert_eq!(collapse_tabs("\ta".chars(), 5, Bias::Left, 4), (2, 0));
388        assert_eq!(collapse_tabs("\ta".chars(), 5, Bias::Right, 4), (2, 0));
389    }
390
391    #[test]
392    fn test_max_point() {
393        App::test((), |app| {
394            let buffer = app.add_model(|ctx| Buffer::new(0, "aaa\n\t\tbbb", ctx));
395            let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx));
396            assert_eq!(
397                map.read(app).max_point(app.as_ref()),
398                DisplayPoint::new(1, 11)
399            )
400        });
401    }
402}