1mod fold_map;
2mod tab_map;
3mod wrap_map;
4
5use super::{buffer, Anchor, Bias, Buffer, Point, Settings, ToOffset, ToPoint};
6use fold_map::FoldMap;
7use gpui::{AppContext, ModelHandle};
8use postage::prelude::Stream;
9use std::ops::Range;
10use tab_map::TabMap;
11pub use wrap_map::BufferRows;
12use wrap_map::WrapMap;
13
14pub struct DisplayMap {
15 buffer: ModelHandle<Buffer>,
16 fold_map: FoldMap,
17 tab_map: TabMap,
18 wrap_map: WrapMap,
19}
20
21impl DisplayMap {
22 pub fn new(
23 buffer: ModelHandle<Buffer>,
24 settings: Settings,
25 wrap_width: Option<f32>,
26 cx: &AppContext,
27 ) -> Self {
28 let (fold_map, snapshot) = FoldMap::new(buffer.clone(), cx);
29 let (tab_map, snapshot) = TabMap::new(snapshot, settings.tab_size);
30 let wrap_map = WrapMap::new(snapshot, settings, wrap_width, cx);
31 DisplayMap {
32 buffer,
33 fold_map,
34 tab_map,
35 wrap_map,
36 }
37 }
38
39 pub fn snapshot(&self, cx: &AppContext) -> DisplayMapSnapshot {
40 let (folds_snapshot, edits) = self.fold_map.read(cx);
41 let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits);
42 let wraps_snapshot = self.wrap_map.sync(tabs_snapshot.clone(), edits, cx);
43 DisplayMapSnapshot {
44 buffer_snapshot: self.buffer.read(cx).snapshot(),
45 folds_snapshot,
46 tabs_snapshot,
47 wraps_snapshot,
48 }
49 }
50
51 pub fn fold<T: ToOffset>(
52 &mut self,
53 ranges: impl IntoIterator<Item = Range<T>>,
54 cx: &AppContext,
55 ) {
56 let (mut fold_map, snapshot, edits) = self.fold_map.write(cx);
57 let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
58 self.wrap_map.sync(snapshot, edits, cx);
59 let (snapshot, edits) = fold_map.fold(ranges, cx);
60 let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
61 self.wrap_map.sync(snapshot, edits, cx);
62 }
63
64 pub fn unfold<T: ToOffset>(
65 &mut self,
66 ranges: impl IntoIterator<Item = Range<T>>,
67 cx: &AppContext,
68 ) {
69 let (mut fold_map, snapshot, edits) = self.fold_map.write(cx);
70 let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
71 self.wrap_map.sync(snapshot, edits, cx);
72 let (snapshot, edits) = fold_map.unfold(ranges, cx);
73 let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
74 self.wrap_map.sync(snapshot, edits, cx);
75 }
76
77 pub fn set_wrap_width(&self, width: Option<f32>) {
78 self.wrap_map.set_wrap_width(width);
79 }
80
81 pub fn notifications(&self) -> impl Stream<Item = ()> {
82 self.wrap_map.notifications()
83 }
84}
85
86pub struct DisplayMapSnapshot {
87 buffer_snapshot: buffer::Snapshot,
88 folds_snapshot: fold_map::Snapshot,
89 tabs_snapshot: tab_map::Snapshot,
90 wraps_snapshot: wrap_map::Snapshot,
91}
92
93impl DisplayMapSnapshot {
94 pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
95 self.wraps_snapshot.buffer_rows(start_row)
96 }
97
98 pub fn max_point(&self) -> DisplayPoint {
99 DisplayPoint(self.wraps_snapshot.max_point())
100 }
101
102 pub fn chunks_at(&self, point: DisplayPoint) -> wrap_map::Chunks {
103 self.wraps_snapshot.chunks_at(point.0)
104 }
105
106 pub fn highlighted_chunks_for_rows(&mut self, rows: Range<u32>) -> wrap_map::HighlightedChunks {
107 self.wraps_snapshot.highlighted_chunks_for_rows(rows)
108 }
109
110 pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
111 self.chunks_at(point).flat_map(str::chars)
112 }
113
114 pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
115 let mut count = 0;
116 let mut column = 0;
117 for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
118 if column >= target {
119 break;
120 }
121 count += 1;
122 column += c.len_utf8() as u32;
123 }
124 count
125 }
126
127 pub fn column_from_chars(&self, display_row: u32, char_count: u32) -> u32 {
128 let mut count = 0;
129 let mut column = 0;
130 for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
131 if c == '\n' || count >= char_count {
132 break;
133 }
134 count += 1;
135 column += c.len_utf8() as u32;
136 }
137 column
138 }
139
140 pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
141 DisplayPoint(self.wraps_snapshot.clip_point(point.0, bias))
142 }
143
144 pub fn folds_in_range<'a, T>(
145 &'a self,
146 range: Range<T>,
147 ) -> impl Iterator<Item = &'a Range<Anchor>>
148 where
149 T: ToOffset,
150 {
151 self.folds_snapshot.folds_in_range(range)
152 }
153
154 pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
155 self.folds_snapshot.intersects_fold(offset)
156 }
157
158 pub fn is_line_folded(&self, display_row: u32) -> bool {
159 let wrap_point = DisplayPoint::new(display_row, 0).0;
160 let row = self.wraps_snapshot.to_input_point(wrap_point).row();
161 self.folds_snapshot.is_line_folded(row)
162 }
163
164 pub fn text(&self) -> String {
165 self.chunks_at(DisplayPoint::zero()).collect()
166 }
167
168 pub fn line(&self, display_row: u32) -> String {
169 let mut result = String::new();
170 for chunk in self.chunks_at(DisplayPoint::new(display_row, 0)) {
171 if let Some(ix) = chunk.find('\n') {
172 result.push_str(&chunk[0..ix]);
173 break;
174 } else {
175 result.push_str(chunk);
176 }
177 }
178 result
179 }
180
181 pub fn line_indent(&self, display_row: u32) -> (u32, bool) {
182 let mut indent = 0;
183 let mut is_blank = true;
184 for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
185 if c == ' ' {
186 indent += 1;
187 } else {
188 is_blank = c == '\n';
189 break;
190 }
191 }
192 (indent, is_blank)
193 }
194
195 pub fn line_len(&self, row: u32) -> u32 {
196 self.wraps_snapshot.line_len(row)
197 }
198
199 pub fn longest_row(&self) -> u32 {
200 self.wraps_snapshot.longest_row()
201 }
202
203 pub fn anchor_before(&self, point: DisplayPoint, bias: Bias) -> Anchor {
204 self.buffer_snapshot
205 .anchor_before(point.to_buffer_point(self, bias))
206 }
207
208 pub fn anchor_after(&self, point: DisplayPoint, bias: Bias) -> Anchor {
209 self.buffer_snapshot
210 .anchor_after(point.to_buffer_point(self, bias))
211 }
212}
213
214#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
215pub struct DisplayPoint(wrap_map::OutputPoint);
216
217impl DisplayPoint {
218 pub fn new(row: u32, column: u32) -> Self {
219 Self(wrap_map::OutputPoint::new(row, column))
220 }
221
222 pub fn zero() -> Self {
223 Self::new(0, 0)
224 }
225
226 pub fn row(self) -> u32 {
227 self.0.row()
228 }
229
230 pub fn column(self) -> u32 {
231 self.0.column()
232 }
233
234 pub fn row_mut(&mut self) -> &mut u32 {
235 self.0.row_mut()
236 }
237
238 pub fn column_mut(&mut self) -> &mut u32 {
239 self.0.column_mut()
240 }
241
242 pub fn to_buffer_point(self, map: &DisplayMapSnapshot, bias: Bias) -> Point {
243 let unwrapped_point = map.wraps_snapshot.to_input_point(self.0);
244 let unexpanded_point = map.tabs_snapshot.to_input_point(unwrapped_point, bias).0;
245 map.folds_snapshot.to_input_point(unexpanded_point)
246 }
247
248 pub fn to_buffer_offset(self, map: &DisplayMapSnapshot, bias: Bias) -> usize {
249 let unwrapped_point = map.wraps_snapshot.to_input_point(self.0);
250 let unexpanded_point = map.tabs_snapshot.to_input_point(unwrapped_point, bias).0;
251 map.folds_snapshot.to_input_offset(unexpanded_point)
252 }
253}
254
255impl Point {
256 pub fn to_display_point(self, map: &DisplayMapSnapshot) -> DisplayPoint {
257 let folded_point = map.folds_snapshot.to_output_point(self);
258 let expanded_point = map.tabs_snapshot.to_output_point(folded_point);
259 let wrapped_point = map.wraps_snapshot.to_output_point(expanded_point);
260 DisplayPoint(wrapped_point)
261 }
262}
263
264impl Anchor {
265 pub fn to_display_point(&self, map: &DisplayMapSnapshot) -> DisplayPoint {
266 self.to_point(&map.buffer_snapshot).to_display_point(map)
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273 use crate::{
274 language::{Language, LanguageConfig},
275 settings::Theme,
276 test::*,
277 util::RandomCharIter,
278 };
279 use buffer::History;
280 use rand::prelude::*;
281 use std::{env, sync::Arc};
282
283 #[gpui::test]
284 async fn test_random(mut cx: gpui::TestAppContext) {
285 cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
286 let iterations = env::var("ITERATIONS")
287 .map(|i| i.parse().expect("invalid `ITERATIONS` variable"))
288 .unwrap_or(100);
289 let operations = env::var("OPERATIONS")
290 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
291 .unwrap_or(10);
292 let seed_range = if let Ok(seed) = env::var("SEED") {
293 let seed = seed.parse().expect("invalid `SEED` variable");
294 seed..seed + 1
295 } else {
296 0..iterations
297 };
298 let font_cache = cx.font_cache();
299
300 for seed in seed_range {
301 dbg!(seed);
302 let mut rng = StdRng::seed_from_u64(seed);
303 let settings = Settings {
304 buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
305 ui_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
306 buffer_font_size: 12.0,
307 ui_font_size: 12.0,
308 tab_size: rng.gen_range(1..=4),
309 theme: Arc::new(Theme::default()),
310 };
311
312 let buffer = cx.add_model(|cx| {
313 let len = rng.gen_range(0..10);
314 let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
315 log::info!("Initial buffer text: {:?}", text);
316 Buffer::new(0, text, cx)
317 });
318 let wrap_width = Some(rng.gen_range(20.0..=100.0));
319 let map = cx.read(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx));
320
321 for _op_ix in 0..operations {
322 buffer.update(&mut cx, |buffer, cx| buffer.randomly_mutate(&mut rng, cx));
323 let snapshot = cx.read(|cx| map.snapshot(cx));
324 let expected_buffer_rows = (0..=snapshot.max_point().row())
325 .map(|display_row| {
326 DisplayPoint::new(display_row, 0)
327 .to_buffer_point(&snapshot, Bias::Left)
328 .row
329 })
330 .collect::<Vec<_>>();
331 for start_display_row in 0..expected_buffer_rows.len() {
332 assert_eq!(
333 snapshot
334 .buffer_rows(start_display_row as u32)
335 .collect::<Vec<_>>(),
336 &expected_buffer_rows[start_display_row..],
337 "invalid buffer_rows({}..)",
338 start_display_row
339 );
340 }
341 }
342 }
343 }
344
345 #[gpui::test]
346 async fn test_soft_wraps(mut cx: gpui::TestAppContext) {
347 cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
348
349 let font_cache = cx.font_cache();
350
351 let settings = Settings {
352 buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
353 ui_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
354 buffer_font_size: 12.0,
355 ui_font_size: 12.0,
356 tab_size: 4,
357 theme: Arc::new(Theme::default()),
358 };
359 let wrap_width = Some(64.);
360
361 let text = "one two three four five\nsix seven eight";
362 let buffer = cx.add_model(|cx| Buffer::new(0, text.to_string(), cx));
363 let map = cx.read(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx));
364
365 let snapshot = cx.read(|cx| map.snapshot(cx));
366 assert_eq!(
367 snapshot
368 .chunks_at(DisplayPoint::new(0, 3))
369 .collect::<String>(),
370 " two \nthree four \nfive\nsix seven \neight"
371 );
372
373 buffer.update(&mut cx, |buffer, cx| {
374 let ix = buffer.text().find("seven").unwrap();
375 buffer.edit(vec![ix..ix], "and ", cx);
376 });
377
378 let snapshot = cx.read(|cx| map.snapshot(cx));
379 assert_eq!(
380 snapshot
381 .chunks_at(DisplayPoint::new(1, 0))
382 .collect::<String>(),
383 "three four \nfive\nsix and \nseven eight"
384 );
385 }
386
387 #[gpui::test]
388 fn test_chunks_at(cx: &mut gpui::MutableAppContext) {
389 let text = sample_text(6, 6);
390 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
391 let map = DisplayMap::new(
392 buffer.clone(),
393 Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
394 None,
395 cx.as_ref(),
396 );
397 buffer.update(cx, |buffer, cx| {
398 buffer.edit(
399 vec![
400 Point::new(1, 0)..Point::new(1, 0),
401 Point::new(1, 1)..Point::new(1, 1),
402 Point::new(2, 1)..Point::new(2, 1),
403 ],
404 "\t",
405 cx,
406 )
407 });
408
409 assert_eq!(
410 &map.snapshot(cx.as_ref())
411 .chunks_at(DisplayPoint::new(1, 0))
412 .collect::<String>()[0..10],
413 " b bb"
414 );
415 assert_eq!(
416 &map.snapshot(cx.as_ref())
417 .chunks_at(DisplayPoint::new(1, 2))
418 .collect::<String>()[0..10],
419 " b bbbb"
420 );
421 assert_eq!(
422 &map.snapshot(cx.as_ref())
423 .chunks_at(DisplayPoint::new(1, 6))
424 .collect::<String>()[0..13],
425 " bbbbb\nc c"
426 );
427 }
428
429 #[gpui::test]
430 async fn test_highlighted_chunks_at(mut cx: gpui::TestAppContext) {
431 use unindent::Unindent as _;
432
433 let grammar = tree_sitter_rust::language();
434 let text = r#"
435 fn outer() {}
436
437 mod module {
438 fn inner() {}
439 }"#
440 .unindent();
441 let highlight_query = tree_sitter::Query::new(
442 grammar,
443 r#"
444 (mod_item name: (identifier) body: _ @mod.body)
445 (function_item name: (identifier) @fn.name)"#,
446 )
447 .unwrap();
448 let theme = Theme::parse(
449 r#"
450 [syntax]
451 "mod.body" = 0xff0000
452 "fn.name" = 0x00ff00"#,
453 )
454 .unwrap();
455 let lang = Arc::new(Language {
456 config: LanguageConfig {
457 name: "Test".to_string(),
458 path_suffixes: vec![".test".to_string()],
459 ..Default::default()
460 },
461 grammar: grammar.clone(),
462 highlight_query,
463 brackets_query: tree_sitter::Query::new(grammar, "").unwrap(),
464 theme_mapping: Default::default(),
465 });
466 lang.set_theme(&theme);
467
468 let buffer = cx.add_model(|cx| {
469 Buffer::from_history(0, History::new(text.into()), None, Some(lang), cx)
470 });
471 buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
472
473 let mut map = cx.read(|cx| {
474 DisplayMap::new(
475 buffer,
476 Settings::new(cx.font_cache()).unwrap().with_tab_size(2),
477 None,
478 cx,
479 )
480 });
481 assert_eq!(
482 cx.read(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
483 vec![
484 ("fn ".to_string(), None),
485 ("outer".to_string(), Some("fn.name")),
486 ("() {}\n\nmod module ".to_string(), None),
487 ("{\n fn ".to_string(), Some("mod.body")),
488 ("inner".to_string(), Some("fn.name")),
489 ("() {}\n}".to_string(), Some("mod.body")),
490 ]
491 );
492 assert_eq!(
493 cx.read(|cx| highlighted_chunks(3..5, &map, &theme, cx)),
494 vec![
495 (" fn ".to_string(), Some("mod.body")),
496 ("inner".to_string(), Some("fn.name")),
497 ("() {}\n}".to_string(), Some("mod.body")),
498 ]
499 );
500
501 cx.read(|cx| map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx));
502 assert_eq!(
503 cx.read(|cx| highlighted_chunks(0..2, &map, &theme, cx)),
504 vec![
505 ("fn ".to_string(), None),
506 ("out".to_string(), Some("fn.name")),
507 ("…".to_string(), None),
508 (" fn ".to_string(), Some("mod.body")),
509 ("inner".to_string(), Some("fn.name")),
510 ("() {}\n}".to_string(), Some("mod.body")),
511 ]
512 );
513
514 fn highlighted_chunks<'a>(
515 rows: Range<u32>,
516 map: &DisplayMap,
517 theme: &'a Theme,
518 cx: &AppContext,
519 ) -> Vec<(String, Option<&'a str>)> {
520 let mut chunks: Vec<(String, Option<&str>)> = Vec::new();
521 for (chunk, style_id) in map.snapshot(cx).highlighted_chunks_for_rows(rows) {
522 let style_name = theme.syntax_style_name(style_id);
523 if let Some((last_chunk, last_style_name)) = chunks.last_mut() {
524 if style_name == *last_style_name {
525 last_chunk.push_str(chunk);
526 } else {
527 chunks.push((chunk.to_string(), style_name));
528 }
529 } else {
530 chunks.push((chunk.to_string(), style_name));
531 }
532 }
533 chunks
534 }
535 }
536
537 #[gpui::test]
538 fn test_clip_point(cx: &mut gpui::MutableAppContext) {
539 use Bias::{Left, Right};
540
541 let text = "\n'a', 'α',\t'✋',\t'❎', '🍐'\n";
542 let display_text = "\n'a', 'α', '✋', '❎', '🍐'\n";
543 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
544 let cx = cx.as_ref();
545 let map = DisplayMap::new(
546 buffer.clone(),
547 Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
548 None,
549 cx,
550 );
551 let map = map.snapshot(cx);
552
553 assert_eq!(map.text(), display_text);
554 for (input_column, bias, output_column) in vec![
555 ("'a', '".len(), Left, "'a', '".len()),
556 ("'a', '".len() + 1, Left, "'a', '".len()),
557 ("'a', '".len() + 1, Right, "'a', 'α".len()),
558 ("'a', 'α', ".len(), Left, "'a', 'α',".len()),
559 ("'a', 'α', ".len(), Right, "'a', 'α', ".len()),
560 ("'a', 'α', '".len() + 1, Left, "'a', 'α', '".len()),
561 ("'a', 'α', '".len() + 1, Right, "'a', 'α', '✋".len()),
562 ("'a', 'α', '✋',".len(), Right, "'a', 'α', '✋',".len()),
563 ("'a', 'α', '✋', ".len(), Left, "'a', 'α', '✋',".len()),
564 (
565 "'a', 'α', '✋', ".len(),
566 Right,
567 "'a', 'α', '✋', ".len(),
568 ),
569 ] {
570 assert_eq!(
571 map.clip_point(DisplayPoint::new(1, input_column as u32), bias),
572 DisplayPoint::new(1, output_column as u32),
573 "clip_point(({}, {}))",
574 1,
575 input_column,
576 );
577 }
578 }
579
580 #[gpui::test]
581 fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) {
582 let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
583 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
584 let cx = cx.as_ref();
585 let map = DisplayMap::new(
586 buffer.clone(),
587 Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
588 None,
589 cx,
590 );
591 let map = map.snapshot(cx);
592 assert_eq!(map.text(), "✅ α\nβ \n🏀β γ");
593
594 let point = Point::new(0, "✅\t\t".len() as u32);
595 let display_point = DisplayPoint::new(0, "✅ ".len() as u32);
596 assert_eq!(point.to_display_point(&map), display_point);
597 assert_eq!(display_point.to_buffer_point(&map, Bias::Left), point,);
598
599 let point = Point::new(1, "β\t".len() as u32);
600 let display_point = DisplayPoint::new(1, "β ".len() as u32);
601 assert_eq!(point.to_display_point(&map), display_point);
602 assert_eq!(display_point.to_buffer_point(&map, Bias::Left), point,);
603
604 let point = Point::new(2, "🏀β\t\t".len() as u32);
605 let display_point = DisplayPoint::new(2, "🏀β ".len() as u32);
606 assert_eq!(point.to_display_point(&map), display_point);
607 assert_eq!(display_point.to_buffer_point(&map, Bias::Left), point,);
608
609 // Display points inside of expanded tabs
610 assert_eq!(
611 DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Right),
612 Point::new(0, "✅\t\t".len() as u32),
613 );
614 assert_eq!(
615 DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Left),
616 Point::new(0, "✅\t".len() as u32),
617 );
618 assert_eq!(
619 map.chunks_at(DisplayPoint::new(0, "✅ ".len() as u32))
620 .collect::<String>(),
621 " α\nβ \n🏀β γ"
622 );
623 assert_eq!(
624 DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Right),
625 Point::new(0, "✅\t".len() as u32),
626 );
627 assert_eq!(
628 DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Left),
629 Point::new(0, "✅".len() as u32),
630 );
631 assert_eq!(
632 map.chunks_at(DisplayPoint::new(0, "✅ ".len() as u32))
633 .collect::<String>(),
634 " α\nβ \n🏀β γ"
635 );
636
637 // Clipping display points inside of multi-byte characters
638 assert_eq!(
639 map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Left),
640 DisplayPoint::new(0, 0)
641 );
642 assert_eq!(
643 map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Right),
644 DisplayPoint::new(0, "✅".len() as u32)
645 );
646 }
647
648 #[gpui::test]
649 fn test_max_point(cx: &mut gpui::MutableAppContext) {
650 let buffer = cx.add_model(|cx| Buffer::new(0, "aaa\n\t\tbbb", cx));
651 let map = DisplayMap::new(
652 buffer.clone(),
653 Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
654 None,
655 cx.as_ref(),
656 );
657 assert_eq!(
658 map.snapshot(cx.as_ref()).max_point(),
659 DisplayPoint::new(1, 11)
660 )
661 }
662}