1use super::{
2 DisplayPoint, DisplayRow, Editor, EditorMode, EditorSettings, EditorStyle, Input, Scroll,
3 Select, SelectPhase, Snapshot, MAX_LINE_LEN,
4};
5use clock::ReplicaId;
6use gpui::{
7 color::Color,
8 geometry::{
9 rect::RectF,
10 vector::{vec2f, Vector2F},
11 PathBuilder,
12 },
13 json::{self, ToJson},
14 keymap::Keystroke,
15 text_layout::{self, RunStyle, TextLayoutCache},
16 AppContext, Axis, Border, Element, Event, EventContext, FontCache, LayoutContext,
17 MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
18};
19use json::json;
20use language::Chunk;
21use smallvec::SmallVec;
22use std::{
23 cmp::{self, Ordering},
24 collections::{BTreeMap, HashMap},
25 fmt::Write,
26 ops::Range,
27};
28use theme::BlockStyle;
29
30pub struct EditorElement {
31 view: WeakViewHandle<Editor>,
32 settings: EditorSettings,
33}
34
35impl EditorElement {
36 pub fn new(view: WeakViewHandle<Editor>, settings: EditorSettings) -> Self {
37 Self { view, settings }
38 }
39
40 fn view<'a>(&self, cx: &'a AppContext) -> &'a Editor {
41 self.view.upgrade(cx).unwrap().read(cx)
42 }
43
44 fn update_view<F, T>(&self, cx: &mut MutableAppContext, f: F) -> T
45 where
46 F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
47 {
48 self.view.upgrade(cx).unwrap().update(cx, f)
49 }
50
51 fn snapshot(&self, cx: &mut MutableAppContext) -> Snapshot {
52 self.update_view(cx, |view, cx| view.snapshot(cx))
53 }
54
55 fn mouse_down(
56 &self,
57 position: Vector2F,
58 alt: bool,
59 shift: bool,
60 mut click_count: usize,
61 layout: &mut LayoutState,
62 paint: &mut PaintState,
63 cx: &mut EventContext,
64 ) -> bool {
65 if paint.gutter_bounds.contains_point(position) {
66 click_count = 3; // Simulate triple-click when clicking the gutter to select lines
67 } else if !paint.text_bounds.contains_point(position) {
68 return false;
69 }
70
71 let snapshot = self.snapshot(cx.app);
72 let (position, overshoot) = paint.point_for_position(&snapshot, layout, position);
73
74 if shift && alt {
75 cx.dispatch_action(Select(SelectPhase::BeginColumnar {
76 position,
77 overshoot,
78 }));
79 } else if shift {
80 cx.dispatch_action(Select(SelectPhase::Extend {
81 position,
82 click_count,
83 }));
84 } else {
85 cx.dispatch_action(Select(SelectPhase::Begin {
86 position,
87 add: alt,
88 click_count,
89 }));
90 }
91
92 true
93 }
94
95 fn mouse_up(&self, _position: Vector2F, cx: &mut EventContext) -> bool {
96 if self.view(cx.app.as_ref()).is_selecting() {
97 cx.dispatch_action(Select(SelectPhase::End));
98 true
99 } else {
100 false
101 }
102 }
103
104 fn mouse_dragged(
105 &self,
106 position: Vector2F,
107 layout: &mut LayoutState,
108 paint: &mut PaintState,
109 cx: &mut EventContext,
110 ) -> bool {
111 let view = self.view(cx.app.as_ref());
112
113 if view.is_selecting() {
114 let rect = paint.text_bounds;
115 let mut scroll_delta = Vector2F::zero();
116
117 let vertical_margin = layout.line_height.min(rect.height() / 3.0);
118 let top = rect.origin_y() + vertical_margin;
119 let bottom = rect.lower_left().y() - vertical_margin;
120 if position.y() < top {
121 scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y()))
122 }
123 if position.y() > bottom {
124 scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom))
125 }
126
127 let horizontal_margin = layout.line_height.min(rect.width() / 3.0);
128 let left = rect.origin_x() + horizontal_margin;
129 let right = rect.upper_right().x() - horizontal_margin;
130 if position.x() < left {
131 scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta(
132 left - position.x(),
133 ))
134 }
135 if position.x() > right {
136 scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta(
137 position.x() - right,
138 ))
139 }
140
141 let font_cache = cx.font_cache.clone();
142 let text_layout_cache = cx.text_layout_cache.clone();
143 let snapshot = self.snapshot(cx.app);
144 let (position, overshoot) = paint.point_for_position(&snapshot, layout, position);
145
146 cx.dispatch_action(Select(SelectPhase::Update {
147 position,
148 overshoot,
149 scroll_position: (snapshot.scroll_position() + scroll_delta).clamp(
150 Vector2F::zero(),
151 layout.scroll_max(&font_cache, &text_layout_cache),
152 ),
153 }));
154 true
155 } else {
156 false
157 }
158 }
159
160 fn key_down(&self, chars: &str, keystroke: &Keystroke, cx: &mut EventContext) -> bool {
161 let view = self.view.upgrade(cx.app).unwrap();
162
163 if view.is_focused(cx.app) {
164 if chars.is_empty() {
165 false
166 } else {
167 if chars.chars().any(|c| c.is_control()) || keystroke.cmd || keystroke.ctrl {
168 false
169 } else {
170 cx.dispatch_action(Input(chars.to_string()));
171 true
172 }
173 }
174 } else {
175 false
176 }
177 }
178
179 fn scroll(
180 &self,
181 position: Vector2F,
182 mut delta: Vector2F,
183 precise: bool,
184 layout: &mut LayoutState,
185 paint: &mut PaintState,
186 cx: &mut EventContext,
187 ) -> bool {
188 if !paint.bounds.contains_point(position) {
189 return false;
190 }
191
192 let snapshot = self.snapshot(cx.app);
193 let font_cache = &cx.font_cache;
194 let layout_cache = &cx.text_layout_cache;
195 let max_glyph_width = layout.em_width;
196 if !precise {
197 delta *= vec2f(max_glyph_width, layout.line_height);
198 }
199
200 let scroll_position = snapshot.scroll_position();
201 let x = (scroll_position.x() * max_glyph_width - delta.x()) / max_glyph_width;
202 let y = (scroll_position.y() * layout.line_height - delta.y()) / layout.line_height;
203 let scroll_position = vec2f(x, y).clamp(
204 Vector2F::zero(),
205 layout.scroll_max(font_cache, layout_cache),
206 );
207
208 cx.dispatch_action(Scroll(scroll_position));
209
210 true
211 }
212
213 fn paint_background(
214 &self,
215 gutter_bounds: RectF,
216 text_bounds: RectF,
217 layout: &LayoutState,
218 cx: &mut PaintContext,
219 ) {
220 let bounds = gutter_bounds.union_rect(text_bounds);
221 let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
222 let start_row = layout.snapshot.scroll_position().y() as u32;
223 let editor = self.view(cx.app);
224 let style = &self.settings.style;
225 cx.scene.push_quad(Quad {
226 bounds: gutter_bounds,
227 background: Some(style.gutter_background),
228 border: Border::new(0., Color::transparent_black()),
229 corner_radius: 0.,
230 });
231 cx.scene.push_quad(Quad {
232 bounds: text_bounds,
233 background: Some(style.background),
234 border: Border::new(0., Color::transparent_black()),
235 corner_radius: 0.,
236 });
237
238 if let EditorMode::Full = editor.mode {
239 let mut active_rows = layout.active_rows.iter().peekable();
240 while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
241 let mut end_row = *start_row;
242 while active_rows.peek().map_or(false, |r| {
243 *r.0 == end_row + 1 && r.1 == contains_non_empty_selection
244 }) {
245 active_rows.next().unwrap();
246 end_row += 1;
247 }
248
249 if !contains_non_empty_selection {
250 let origin = vec2f(
251 bounds.origin_x(),
252 bounds.origin_y() + (layout.line_height * *start_row as f32) - scroll_top,
253 );
254 let size = vec2f(
255 bounds.width(),
256 layout.line_height * (end_row - start_row + 1) as f32,
257 );
258 cx.scene.push_quad(Quad {
259 bounds: RectF::new(origin, size),
260 background: Some(style.active_line_background),
261 border: Border::default(),
262 corner_radius: 0.,
263 });
264 }
265 }
266 }
267
268 // Draw block backgrounds
269 for (ixs, block_style) in &layout.block_layouts {
270 let row = start_row + ixs.start;
271 let offset = vec2f(0., row as f32 * layout.line_height - scroll_top);
272 let height = ixs.len() as f32 * layout.line_height;
273 cx.scene.push_quad(Quad {
274 bounds: RectF::new(
275 text_bounds.origin() + offset,
276 vec2f(text_bounds.width(), height),
277 ),
278 background: block_style.background,
279 border: block_style
280 .border
281 .map_or(Default::default(), |color| Border {
282 width: 1.,
283 color,
284 overlay: true,
285 top: true,
286 right: false,
287 bottom: true,
288 left: false,
289 }),
290 corner_radius: 0.,
291 });
292 cx.scene.push_quad(Quad {
293 bounds: RectF::new(
294 gutter_bounds.origin() + offset,
295 vec2f(gutter_bounds.width(), height),
296 ),
297 background: block_style.gutter_background,
298 border: block_style
299 .gutter_border
300 .map_or(Default::default(), |color| Border {
301 width: 1.,
302 color,
303 overlay: true,
304 top: true,
305 right: false,
306 bottom: true,
307 left: false,
308 }),
309 corner_radius: 0.,
310 });
311 }
312 }
313
314 fn paint_gutter(
315 &mut self,
316 bounds: RectF,
317 visible_bounds: RectF,
318 layout: &LayoutState,
319 cx: &mut PaintContext,
320 ) {
321 let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
322 for (ix, line) in layout.line_number_layouts.iter().enumerate() {
323 if let Some(line) = line {
324 let line_origin = bounds.origin()
325 + vec2f(
326 bounds.width() - line.width() - layout.gutter_padding,
327 ix as f32 * layout.line_height - (scroll_top % layout.line_height),
328 );
329 line.paint(line_origin, visible_bounds, layout.line_height, cx);
330 }
331 }
332 }
333
334 fn paint_text(
335 &mut self,
336 bounds: RectF,
337 visible_bounds: RectF,
338 layout: &LayoutState,
339 cx: &mut PaintContext,
340 ) {
341 let view = self.view(cx.app);
342 let style = &self.settings.style;
343 let local_replica_id = view.replica_id(cx);
344 let scroll_position = layout.snapshot.scroll_position();
345 let start_row = scroll_position.y() as u32;
346 let scroll_top = scroll_position.y() * layout.line_height;
347 let end_row = ((scroll_top + bounds.height()) / layout.line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen
348 let max_glyph_width = layout.em_width;
349 let scroll_left = scroll_position.x() * max_glyph_width;
350
351 cx.scene.push_layer(Some(bounds));
352
353 // Draw selections
354 let corner_radius = 2.5;
355 let mut cursors = SmallVec::<[Cursor; 32]>::new();
356
357 let content_origin = bounds.origin() + layout.text_offset;
358
359 for (replica_id, selections) in &layout.selections {
360 let style_ix = *replica_id as usize % (style.guest_selections.len() + 1);
361 let style = if style_ix == 0 {
362 &style.selection
363 } else {
364 &style.guest_selections[style_ix - 1]
365 };
366
367 for selection in selections {
368 if selection.start != selection.end {
369 let range_start = cmp::min(selection.start, selection.end);
370 let range_end = cmp::max(selection.start, selection.end);
371 let row_range = if range_end.column() == 0 {
372 cmp::max(range_start.row(), start_row)..cmp::min(range_end.row(), end_row)
373 } else {
374 cmp::max(range_start.row(), start_row)
375 ..cmp::min(range_end.row() + 1, end_row)
376 };
377
378 let selection = Selection {
379 color: style.selection,
380 line_height: layout.line_height,
381 start_y: content_origin.y() + row_range.start as f32 * layout.line_height
382 - scroll_top,
383 lines: row_range
384 .into_iter()
385 .map(|row| {
386 let line_layout = &layout.line_layouts[(row - start_row) as usize];
387 SelectionLine {
388 start_x: if row == range_start.row() {
389 content_origin.x()
390 + line_layout.x_for_index(range_start.column() as usize)
391 - scroll_left
392 } else {
393 content_origin.x() - scroll_left
394 },
395 end_x: if row == range_end.row() {
396 content_origin.x()
397 + line_layout.x_for_index(range_end.column() as usize)
398 - scroll_left
399 } else {
400 content_origin.x()
401 + line_layout.width()
402 + corner_radius * 2.0
403 - scroll_left
404 },
405 }
406 })
407 .collect(),
408 };
409
410 selection.paint(bounds, cx.scene);
411 }
412
413 if view.show_local_cursors() || *replica_id != local_replica_id {
414 let cursor_position = selection.end;
415 if (start_row..end_row).contains(&cursor_position.row()) {
416 let cursor_row_layout =
417 &layout.line_layouts[(selection.end.row() - start_row) as usize];
418 let x = cursor_row_layout.x_for_index(selection.end.column() as usize)
419 - scroll_left;
420 let y = selection.end.row() as f32 * layout.line_height - scroll_top;
421 cursors.push(Cursor {
422 color: style.cursor,
423 origin: content_origin + vec2f(x, y),
424 line_height: layout.line_height,
425 });
426 }
427 }
428 }
429 }
430
431 if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) {
432 // Draw glyphs
433 for (ix, line) in layout.line_layouts.iter().enumerate() {
434 let row = start_row + ix as u32;
435 line.paint(
436 content_origin
437 + vec2f(-scroll_left, row as f32 * layout.line_height - scroll_top),
438 visible_text_bounds,
439 layout.line_height,
440 cx,
441 );
442 }
443 }
444
445 cx.scene.push_layer(Some(bounds));
446 for cursor in cursors {
447 cursor.paint(cx);
448 }
449 cx.scene.pop_layer();
450
451 cx.scene.pop_layer();
452 }
453
454 fn max_line_number_width(&self, snapshot: &Snapshot, cx: &LayoutContext) -> f32 {
455 let digit_count = (snapshot.buffer_row_count() as f32).log10().floor() as usize + 1;
456 let style = &self.settings.style;
457
458 cx.text_layout_cache
459 .layout_str(
460 "1".repeat(digit_count).as_str(),
461 style.text.font_size,
462 &[(
463 digit_count,
464 RunStyle {
465 font_id: style.text.font_id,
466 color: Color::black(),
467 underline: None,
468 },
469 )],
470 )
471 .width()
472 }
473
474 fn layout_rows(
475 &self,
476 rows: Range<u32>,
477 active_rows: &BTreeMap<u32, bool>,
478 snapshot: &Snapshot,
479 cx: &LayoutContext,
480 ) -> (
481 Vec<Option<text_layout::Line>>,
482 Vec<(Range<u32>, BlockStyle)>,
483 ) {
484 let style = &self.settings.style;
485 let include_line_numbers = snapshot.mode == EditorMode::Full;
486 let mut last_block_id = None;
487 let mut blocks = Vec::<(Range<u32>, BlockStyle)>::new();
488 let mut line_number_layouts = Vec::with_capacity(rows.len());
489 let mut line_number = String::new();
490 for (ix, row) in snapshot
491 .buffer_rows(rows.start, cx)
492 .take((rows.end - rows.start) as usize)
493 .enumerate()
494 {
495 let display_row = rows.start + ix as u32;
496 let color = if active_rows.contains_key(&display_row) {
497 style.line_number_active
498 } else {
499 style.line_number
500 };
501 match row {
502 DisplayRow::Buffer(buffer_row) => {
503 if include_line_numbers {
504 line_number.clear();
505 write!(&mut line_number, "{}", buffer_row + 1).unwrap();
506 line_number_layouts.push(Some(cx.text_layout_cache.layout_str(
507 &line_number,
508 style.text.font_size,
509 &[(
510 line_number.len(),
511 RunStyle {
512 font_id: style.text.font_id,
513 color,
514 underline: None,
515 },
516 )],
517 )));
518 }
519 last_block_id = None;
520 }
521 DisplayRow::Block(block_id, style) => {
522 let ix = ix as u32;
523 if last_block_id == Some(block_id) {
524 if let Some((row_range, _)) = blocks.last_mut() {
525 row_range.end += 1;
526 }
527 } else if let Some(style) = style {
528 blocks.push((ix..ix + 1, style));
529 }
530 line_number_layouts.push(None);
531 last_block_id = Some(block_id);
532 }
533 DisplayRow::Wrap => {
534 line_number_layouts.push(None);
535 last_block_id = None;
536 }
537 }
538 }
539
540 (line_number_layouts, blocks)
541 }
542
543 fn layout_lines(
544 &mut self,
545 mut rows: Range<u32>,
546 snapshot: &mut Snapshot,
547 cx: &LayoutContext,
548 ) -> Vec<text_layout::Line> {
549 rows.end = cmp::min(rows.end, snapshot.max_point().row() + 1);
550 if rows.start >= rows.end {
551 return Vec::new();
552 }
553
554 // When the editor is empty and unfocused, then show the placeholder.
555 if snapshot.is_empty() && !snapshot.is_focused() {
556 let placeholder_style = self.settings.style.placeholder_text();
557 let placeholder_text = snapshot.placeholder_text();
558 let placeholder_lines = placeholder_text
559 .as_ref()
560 .map_or("", AsRef::as_ref)
561 .split('\n')
562 .skip(rows.start as usize)
563 .take(rows.len());
564 return placeholder_lines
565 .map(|line| {
566 cx.text_layout_cache.layout_str(
567 line,
568 placeholder_style.font_size,
569 &[(
570 line.len(),
571 RunStyle {
572 font_id: placeholder_style.font_id,
573 color: placeholder_style.color,
574 underline: None,
575 },
576 )],
577 )
578 })
579 .collect();
580 }
581
582 let style = &self.settings.style;
583 let mut prev_font_properties = style.text.font_properties.clone();
584 let mut prev_font_id = style.text.font_id;
585
586 let mut layouts = Vec::with_capacity(rows.len());
587 let mut line = String::new();
588 let mut styles = Vec::new();
589 let mut row = rows.start;
590 let mut line_exceeded_max_len = false;
591 let chunks = snapshot.chunks(rows.clone(), Some(&style.syntax), cx);
592
593 let newline_chunk = Chunk {
594 text: "\n",
595 ..Default::default()
596 };
597 'outer: for chunk in chunks.chain([newline_chunk]) {
598 for (ix, mut line_chunk) in chunk.text.split('\n').enumerate() {
599 if ix > 0 {
600 layouts.push(cx.text_layout_cache.layout_str(
601 &line,
602 style.text.font_size,
603 &styles,
604 ));
605 line.clear();
606 styles.clear();
607 row += 1;
608 line_exceeded_max_len = false;
609 if row == rows.end {
610 break 'outer;
611 }
612 }
613
614 if !line_chunk.is_empty() && !line_exceeded_max_len {
615 let highlight_style =
616 chunk.highlight_style.unwrap_or(style.text.clone().into());
617 // Avoid a lookup if the font properties match the previous ones.
618 let font_id = if highlight_style.font_properties == prev_font_properties {
619 prev_font_id
620 } else {
621 cx.font_cache
622 .select_font(
623 style.text.font_family_id,
624 &highlight_style.font_properties,
625 )
626 .unwrap_or(style.text.font_id)
627 };
628
629 if line.len() + line_chunk.len() > MAX_LINE_LEN {
630 let mut chunk_len = MAX_LINE_LEN - line.len();
631 while !line_chunk.is_char_boundary(chunk_len) {
632 chunk_len -= 1;
633 }
634 line_chunk = &line_chunk[..chunk_len];
635 line_exceeded_max_len = true;
636 }
637
638 let underline = if let Some(severity) = chunk.diagnostic {
639 Some(super::diagnostic_style(severity, true, style).text)
640 } else {
641 highlight_style.underline
642 };
643
644 line.push_str(line_chunk);
645 styles.push((
646 line_chunk.len(),
647 RunStyle {
648 font_id,
649 color: highlight_style.color,
650 underline,
651 },
652 ));
653 prev_font_id = font_id;
654 prev_font_properties = highlight_style.font_properties;
655 }
656 }
657 }
658
659 layouts
660 }
661}
662
663impl Element for EditorElement {
664 type LayoutState = Option<LayoutState>;
665 type PaintState = Option<PaintState>;
666
667 fn layout(
668 &mut self,
669 constraint: SizeConstraint,
670 cx: &mut LayoutContext,
671 ) -> (Vector2F, Self::LayoutState) {
672 let mut size = constraint.max;
673 if size.x().is_infinite() {
674 unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
675 }
676
677 let snapshot = self.snapshot(cx.app);
678 let style = self.settings.style.clone();
679 let line_height = style.text.line_height(cx.font_cache);
680
681 let gutter_padding;
682 let gutter_width;
683 if snapshot.mode == EditorMode::Full {
684 gutter_padding = style.text.em_width(cx.font_cache);
685 gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
686 } else {
687 gutter_padding = 0.0;
688 gutter_width = 0.0
689 };
690
691 let text_width = size.x() - gutter_width;
692 let text_offset = vec2f(-style.text.descent(cx.font_cache), 0.);
693 let em_width = style.text.em_width(cx.font_cache);
694 let em_advance = style.text.em_advance(cx.font_cache);
695 let overscroll = vec2f(em_width, 0.);
696 let wrap_width = text_width - text_offset.x() - overscroll.x() - em_width;
697 let snapshot = self.update_view(cx.app, |view, cx| {
698 if view.set_wrap_width(wrap_width, cx) {
699 view.snapshot(cx)
700 } else {
701 snapshot
702 }
703 });
704
705 let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
706 if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
707 size.set_y(
708 scroll_height
709 .min(constraint.max_along(Axis::Vertical))
710 .max(constraint.min_along(Axis::Vertical))
711 .min(line_height * max_lines as f32),
712 )
713 } else if size.y().is_infinite() {
714 size.set_y(scroll_height);
715 }
716 let gutter_size = vec2f(gutter_width, size.y());
717 let text_size = vec2f(text_width, size.y());
718
719 let (autoscroll_horizontally, mut snapshot) = self.update_view(cx.app, |view, cx| {
720 let autoscroll_horizontally = view.autoscroll_vertically(size.y(), line_height, cx);
721 let snapshot = view.snapshot(cx);
722 (autoscroll_horizontally, snapshot)
723 });
724
725 let scroll_position = snapshot.scroll_position();
726 let start_row = scroll_position.y() as u32;
727 let scroll_top = scroll_position.y() * line_height;
728 let end_row = ((scroll_top + size.y()) / line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen
729
730 let mut selections = HashMap::new();
731 let mut active_rows = BTreeMap::new();
732 self.update_view(cx.app, |view, cx| {
733 for selection_set_id in view.active_selection_sets(cx).collect::<Vec<_>>() {
734 let mut set = Vec::new();
735 for selection in view.selections_in_range(
736 selection_set_id,
737 DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0),
738 cx,
739 ) {
740 set.push(selection.clone());
741 if selection_set_id == view.selection_set_id {
742 let is_empty = selection.start == selection.end;
743 let mut selection_start;
744 let mut selection_end;
745 if selection.start < selection.end {
746 selection_start = selection.start;
747 selection_end = selection.end;
748 } else {
749 selection_start = selection.end;
750 selection_end = selection.start;
751 };
752 selection_start = snapshot.prev_row_boundary(selection_start).0;
753 selection_end = snapshot.next_row_boundary(selection_end).0;
754 for row in cmp::max(selection_start.row(), start_row)
755 ..=cmp::min(selection_end.row(), end_row)
756 {
757 let contains_non_empty_selection =
758 active_rows.entry(row).or_insert(!is_empty);
759 *contains_non_empty_selection |= !is_empty;
760 }
761 }
762 }
763
764 selections.insert(selection_set_id.replica_id, set);
765 }
766 });
767
768 let (line_number_layouts, block_layouts) =
769 self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx);
770
771 let mut max_visible_line_width = 0.0;
772 let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx);
773 for line in &line_layouts {
774 if line.width() > max_visible_line_width {
775 max_visible_line_width = line.width();
776 }
777 }
778
779 let mut layout = LayoutState {
780 size,
781 gutter_size,
782 gutter_padding,
783 text_size,
784 overscroll,
785 text_offset,
786 snapshot,
787 style: self.settings.style.clone(),
788 active_rows,
789 line_layouts,
790 line_number_layouts,
791 block_layouts,
792 line_height,
793 em_width,
794 em_advance,
795 selections,
796 max_visible_line_width,
797 };
798
799 let scroll_max = layout.scroll_max(cx.font_cache, cx.text_layout_cache).x();
800 let scroll_width = layout.scroll_width(cx.text_layout_cache);
801 let max_glyph_width = style.text.em_width(&cx.font_cache);
802 self.update_view(cx.app, |view, cx| {
803 let clamped = view.clamp_scroll_left(scroll_max);
804 let autoscrolled;
805 if autoscroll_horizontally {
806 autoscrolled = view.autoscroll_horizontally(
807 start_row,
808 layout.text_size.x(),
809 scroll_width,
810 max_glyph_width,
811 &layout.line_layouts,
812 cx,
813 );
814 } else {
815 autoscrolled = false;
816 }
817
818 if clamped || autoscrolled {
819 layout.snapshot = view.snapshot(cx);
820 }
821 });
822
823 (size, Some(layout))
824 }
825
826 fn paint(
827 &mut self,
828 bounds: RectF,
829 visible_bounds: RectF,
830 layout: &mut Self::LayoutState,
831 cx: &mut PaintContext,
832 ) -> Self::PaintState {
833 if let Some(layout) = layout {
834 cx.scene.push_layer(Some(bounds));
835
836 let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
837 let text_bounds = RectF::new(
838 bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
839 layout.text_size,
840 );
841
842 self.paint_background(gutter_bounds, text_bounds, layout, cx);
843 if layout.gutter_size.x() > 0. {
844 self.paint_gutter(gutter_bounds, visible_bounds, layout, cx);
845 }
846 self.paint_text(text_bounds, visible_bounds, layout, cx);
847
848 cx.scene.pop_layer();
849
850 Some(PaintState {
851 bounds,
852 gutter_bounds,
853 text_bounds,
854 })
855 } else {
856 None
857 }
858 }
859
860 fn dispatch_event(
861 &mut self,
862 event: &Event,
863 _: RectF,
864 layout: &mut Self::LayoutState,
865 paint: &mut Self::PaintState,
866 cx: &mut EventContext,
867 ) -> bool {
868 if let (Some(layout), Some(paint)) = (layout, paint) {
869 match event {
870 Event::LeftMouseDown {
871 position,
872 alt,
873 shift,
874 click_count,
875 ..
876 } => self.mouse_down(*position, *alt, *shift, *click_count, layout, paint, cx),
877 Event::LeftMouseUp { position } => self.mouse_up(*position, cx),
878 Event::LeftMouseDragged { position } => {
879 self.mouse_dragged(*position, layout, paint, cx)
880 }
881 Event::ScrollWheel {
882 position,
883 delta,
884 precise,
885 } => self.scroll(*position, *delta, *precise, layout, paint, cx),
886 Event::KeyDown {
887 chars, keystroke, ..
888 } => self.key_down(chars, keystroke, cx),
889 _ => false,
890 }
891 } else {
892 false
893 }
894 }
895
896 fn debug(
897 &self,
898 bounds: RectF,
899 _: &Self::LayoutState,
900 _: &Self::PaintState,
901 _: &gpui::DebugContext,
902 ) -> json::Value {
903 json!({
904 "type": "BufferElement",
905 "bounds": bounds.to_json()
906 })
907 }
908}
909
910pub struct LayoutState {
911 size: Vector2F,
912 gutter_size: Vector2F,
913 gutter_padding: f32,
914 text_size: Vector2F,
915 style: EditorStyle,
916 snapshot: Snapshot,
917 active_rows: BTreeMap<u32, bool>,
918 line_layouts: Vec<text_layout::Line>,
919 line_number_layouts: Vec<Option<text_layout::Line>>,
920 block_layouts: Vec<(Range<u32>, BlockStyle)>,
921 line_height: f32,
922 em_width: f32,
923 em_advance: f32,
924 selections: HashMap<ReplicaId, Vec<Range<DisplayPoint>>>,
925 overscroll: Vector2F,
926 text_offset: Vector2F,
927 max_visible_line_width: f32,
928}
929
930impl LayoutState {
931 fn scroll_width(&self, layout_cache: &TextLayoutCache) -> f32 {
932 let row = self.snapshot.longest_row();
933 let longest_line_width = self.layout_line(row, &self.snapshot, layout_cache).width();
934 longest_line_width.max(self.max_visible_line_width) + self.overscroll.x()
935 }
936
937 fn scroll_max(&self, font_cache: &FontCache, layout_cache: &TextLayoutCache) -> Vector2F {
938 let text_width = self.text_size.x();
939 let scroll_width = self.scroll_width(layout_cache);
940 let em_width = self.style.text.em_width(font_cache);
941 let max_row = self.snapshot.max_point().row();
942
943 vec2f(
944 ((scroll_width - text_width) / em_width).max(0.0),
945 max_row.saturating_sub(1) as f32,
946 )
947 }
948
949 pub fn layout_line(
950 &self,
951 row: u32,
952 snapshot: &Snapshot,
953 layout_cache: &TextLayoutCache,
954 ) -> text_layout::Line {
955 let mut line = snapshot.line(row);
956
957 if line.len() > MAX_LINE_LEN {
958 let mut len = MAX_LINE_LEN;
959 while !line.is_char_boundary(len) {
960 len -= 1;
961 }
962 line.truncate(len);
963 }
964
965 layout_cache.layout_str(
966 &line,
967 self.style.text.font_size,
968 &[(
969 snapshot.line_len(row) as usize,
970 RunStyle {
971 font_id: self.style.text.font_id,
972 color: Color::black(),
973 underline: None,
974 },
975 )],
976 )
977 }
978}
979
980pub struct PaintState {
981 bounds: RectF,
982 gutter_bounds: RectF,
983 text_bounds: RectF,
984}
985
986impl PaintState {
987 fn point_for_position(
988 &self,
989 snapshot: &Snapshot,
990 layout: &LayoutState,
991 position: Vector2F,
992 ) -> (DisplayPoint, u32) {
993 let scroll_position = snapshot.scroll_position();
994 let position = position - self.text_bounds.origin();
995 let y = position.y().max(0.0).min(layout.size.y());
996 let row = ((y / layout.line_height) + scroll_position.y()) as u32;
997 let row = cmp::min(row, snapshot.max_point().row());
998 let line = &layout.line_layouts[(row - scroll_position.y() as u32) as usize];
999 let x = position.x() + (scroll_position.x() * layout.em_width);
1000
1001 let column = if x >= 0.0 {
1002 line.index_for_x(x)
1003 .map(|ix| ix as u32)
1004 .unwrap_or_else(|| snapshot.line_len(row))
1005 } else {
1006 0
1007 };
1008 let overshoot = (0f32.max(x - line.width()) / layout.em_advance) as u32;
1009
1010 (DisplayPoint::new(row, column), overshoot)
1011 }
1012}
1013
1014struct Cursor {
1015 origin: Vector2F,
1016 line_height: f32,
1017 color: Color,
1018}
1019
1020impl Cursor {
1021 fn paint(&self, cx: &mut PaintContext) {
1022 cx.scene.push_quad(Quad {
1023 bounds: RectF::new(self.origin, vec2f(2.0, self.line_height)),
1024 background: Some(self.color),
1025 border: Border::new(0., Color::black()),
1026 corner_radius: 0.,
1027 });
1028 }
1029}
1030
1031#[derive(Debug)]
1032struct Selection {
1033 start_y: f32,
1034 line_height: f32,
1035 lines: Vec<SelectionLine>,
1036 color: Color,
1037}
1038
1039#[derive(Debug)]
1040struct SelectionLine {
1041 start_x: f32,
1042 end_x: f32,
1043}
1044
1045impl Selection {
1046 fn paint(&self, bounds: RectF, scene: &mut Scene) {
1047 if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
1048 self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene);
1049 self.paint_lines(
1050 self.start_y + self.line_height,
1051 &self.lines[1..],
1052 bounds,
1053 scene,
1054 );
1055 } else {
1056 self.paint_lines(self.start_y, &self.lines, bounds, scene);
1057 }
1058 }
1059
1060 fn paint_lines(&self, start_y: f32, lines: &[SelectionLine], bounds: RectF, scene: &mut Scene) {
1061 if lines.is_empty() {
1062 return;
1063 }
1064
1065 let mut path = PathBuilder::new();
1066 let corner_radius = 0.15 * self.line_height;
1067 let first_line = lines.first().unwrap();
1068 let last_line = lines.last().unwrap();
1069
1070 let first_top_left = vec2f(first_line.start_x, start_y);
1071 let first_top_right = vec2f(first_line.end_x, start_y);
1072
1073 let curve_height = vec2f(0., corner_radius);
1074 let curve_width = |start_x: f32, end_x: f32| {
1075 let max = (end_x - start_x) / 2.;
1076 let width = if max < corner_radius {
1077 max
1078 } else {
1079 corner_radius
1080 };
1081
1082 vec2f(width, 0.)
1083 };
1084
1085 let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
1086 path.reset(first_top_right - top_curve_width);
1087 path.curve_to(first_top_right + curve_height, first_top_right);
1088
1089 let mut iter = lines.iter().enumerate().peekable();
1090 while let Some((ix, line)) = iter.next() {
1091 let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
1092
1093 if let Some((_, next_line)) = iter.peek() {
1094 let next_top_right = vec2f(next_line.end_x, bottom_right.y());
1095
1096 match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() {
1097 Ordering::Equal => {
1098 path.line_to(bottom_right);
1099 }
1100 Ordering::Less => {
1101 let curve_width = curve_width(next_top_right.x(), bottom_right.x());
1102 path.line_to(bottom_right - curve_height);
1103 path.curve_to(bottom_right - curve_width, bottom_right);
1104 path.line_to(next_top_right + curve_width);
1105 path.curve_to(next_top_right + curve_height, next_top_right);
1106 }
1107 Ordering::Greater => {
1108 let curve_width = curve_width(bottom_right.x(), next_top_right.x());
1109 path.line_to(bottom_right - curve_height);
1110 path.curve_to(bottom_right + curve_width, bottom_right);
1111 path.line_to(next_top_right - curve_width);
1112 path.curve_to(next_top_right + curve_height, next_top_right);
1113 }
1114 }
1115 } else {
1116 let curve_width = curve_width(line.start_x, line.end_x);
1117 path.line_to(bottom_right - curve_height);
1118 path.curve_to(bottom_right - curve_width, bottom_right);
1119
1120 let bottom_left = vec2f(line.start_x, bottom_right.y());
1121 path.line_to(bottom_left + curve_width);
1122 path.curve_to(bottom_left - curve_height, bottom_left);
1123 }
1124 }
1125
1126 if first_line.start_x > last_line.start_x {
1127 let curve_width = curve_width(last_line.start_x, first_line.start_x);
1128 let second_top_left = vec2f(last_line.start_x, start_y + self.line_height);
1129 path.line_to(second_top_left + curve_height);
1130 path.curve_to(second_top_left + curve_width, second_top_left);
1131 let first_bottom_left = vec2f(first_line.start_x, second_top_left.y());
1132 path.line_to(first_bottom_left - curve_width);
1133 path.curve_to(first_bottom_left - curve_height, first_bottom_left);
1134 }
1135
1136 path.line_to(first_top_left + curve_height);
1137 path.curve_to(first_top_left + top_curve_width, first_top_left);
1138 path.line_to(first_top_right - top_curve_width);
1139
1140 scene.push_path(path.build(self.color, Some(bounds)));
1141 }
1142}
1143
1144fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
1145 delta.powf(1.5) / 100.0
1146}
1147
1148fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
1149 delta.powf(1.2) / 300.0
1150}
1151
1152#[cfg(test)]
1153mod tests {
1154 use super::*;
1155 use crate::{
1156 test::sample_text,
1157 {Editor, EditorSettings},
1158 };
1159 use language::Buffer;
1160
1161 #[gpui::test]
1162 fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
1163 let settings = EditorSettings::test(cx);
1164
1165 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
1166 let (window_id, editor) = cx.add_window(Default::default(), |cx| {
1167 Editor::for_buffer(
1168 buffer,
1169 {
1170 let settings = settings.clone();
1171 move |_| settings.clone()
1172 },
1173 cx,
1174 )
1175 });
1176 let element = EditorElement::new(editor.downgrade(), settings);
1177
1178 let (layouts, _) = editor.update(cx, |editor, cx| {
1179 let snapshot = editor.snapshot(cx);
1180 let mut presenter = cx.build_presenter(window_id, 30.);
1181 let mut layout_cx = presenter.build_layout_context(false, cx);
1182 element.layout_rows(0..6, &Default::default(), &snapshot, &mut layout_cx)
1183 });
1184 assert_eq!(layouts.len(), 6);
1185 }
1186}