1use {
2 crate::{
3 decoration::{Decoration, DecorationType},
4 layout::{BlockElement, WrappedElement},
5 selection::Affinity,
6 session::{SelectionMode, CodeSession},
7 history::NewGroup,
8 settings::Settings,
9 str::StrExt,
10 text::Position,
11 token::TokenKind,
12 Line, Selection, Token,
13 },
14 makepad_widgets::*,
15 std::fmt::Write,
16 std::{mem, slice::Iter},
17};
18
19live_design! {
20 use link::shaders::*;
21 use link::theme::*;
22 use link::widgets::*;
23
24 TokenColors = {{TokenColors}} {
25 whitespace: #6E6E6E,
26 delimiter: #a,
27 delimiter_highlight: #f,
28 error_decoration: #f00,
29 warning_decoration: #0f0,
30
31 unknown: #C0C0C0,
32 branch_keyword: #C485BE,
33 constant: #CC917B,
34 identifier: #D4D4D4,
35 loop_keyword: #FF8C00,
36 number: #B6CEAA,
37 other_keyword: #5B9BD3,
38 punctuator: #D4D4D4,
39 string: #CC917B,
40 function: #fffcc9,
41 typename: #56C9B1,
42 comment: #638D54,
43 }
44
45 DrawIndentGuide = {{DrawIndentGuide}} {
46 fn pixel(self) -> vec4 {
47 let thickness = 0.8 + self.dpi_dilate * 0.5;
48 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
49 sdf.move_to(1., -1.);
50 sdf.line_to(1., self.rect_size.y + 1.);
51 return sdf.stroke(self.color, thickness);
52 }
53 }
54
55 DrawDecoration = {{DrawDecoration}} {
56 fn pixel(self) -> vec4 {
57 let transformed_pos = vec2(self.pos.x, self.pos.y + 0.03 * sin(self.pos.x * self.rect_size.x));
58 let cx = Sdf2d::viewport(transformed_pos * self.rect_size);
59 cx.move_to(0.0, self.rect_size.y - 1.0);
60 cx.line_to(self.rect_size.x, self.rect_size.y - 1.0);
61 return cx.stroke(self.color, 0.8);
62 }
63 }
64
65 DrawSelection = {{DrawSelection}} {
66 uniform gloopiness: 8.0
67 uniform border_radius: 2.0
68 uniform focus: 1.0
69 fn vertex(self) -> vec4 {
70 let clipped: vec2 = clamp(
71 self.geom_pos * vec2(self.rect_size.x + 16., self.rect_size.y) + self.rect_pos - vec2(8., 0.),
72 self.draw_clip.xy,
73 self.draw_clip.zw
74 );
75 self.pos = (clipped - self.rect_pos) / self.rect_size;
76 return self.camera_projection * (self.camera_view * (
77 self.view_transform * vec4(clipped.x, clipped.y, self.draw_depth + self.draw_zbias, 1.)
78 ));
79 }
80
81 fn pixel(self) -> vec4 {
82 let sdf = Sdf2d::viewport(self.rect_pos + self.pos * self.rect_size);
83 sdf.box(
84 self.rect_pos.x,
85 self.rect_pos.y,
86 self.rect_size.x,
87 self.rect_size.y,
88 self.border_radius
89 );
90 if self.prev_w > 0.0 {
91 sdf.box(
92 self.prev_x,
93 self.rect_pos.y - self.rect_size.y,
94 self.prev_w,
95 self.rect_size.y,
96 self.border_radius
97 );
98 sdf.gloop(self.gloopiness);
99 }
100 if self.next_w > 0.0 {
101 sdf.box(
102 self.next_x,
103 self.rect_pos.y + self.rect_size.y,
104 self.next_w,
105 self.rect_size.y,
106 self.border_radius
107 );
108 sdf.gloop(self.gloopiness);
109 }
110 return sdf.fill(mix(THEME_COLOR_U_1 * 1.1, THEME_COLOR_U_3 * 0.8, self.focus));
111 }
112 }
113
114 DrawCodeText = {{DrawCodeText}} { }
115
116 pub CodeEditor = {{CodeEditor}} {
117 height: Fill, width: Fill,
118 margin: 0,
119 pad_left_top: vec2(10.0,10.0)
120 scroll_bars: <ScrollBars> {}
121 draw_bg: { color: (THEME_COLOR_BG_CONTAINER) }
122 draw_gutter: {
123 draw_depth: 1.0,
124 text_style: <THEME_FONT_CODE> {},
125 color: (THEME_COLOR_LABEL_OUTER),
126 }
127 draw_text: {
128 draw_depth: 1.0,
129 text_style: <THEME_FONT_CODE> {}
130 fn get_brightness(self)->float{
131 return 1.1
132 }
133
134 fn blend_color(self, incol: vec4) -> vec4 {
135 if self.outline < 0.5 {
136 return incol
137 }
138 if self.pos.y < 0.12 {
139 return #f
140 }
141 return incol
142 }
143 }
144 draw_indent_guide: {
145 color: (THEME_COLOR_U_2)
147 }
148 draw_decoration: {
149 }
151 draw_selection: {
152 }
154
155 draw_cursor: {
156 instance focus: 0.0
158 instance blink: 1.0
159 fn pixel(self) -> vec4 {
160 let color = mix(THEME_COLOR_U_HIDDEN, mix(self.color, THEME_COLOR_U_HIDDEN, self.blink),self.focus);
161 return vec4(color.rgb*color.a, color.a);
162 }
163 color: (THEME_COLOR_WHITE),
164 }
165
166
167 draw_cursor_bg: {
168 instance focus: 0.0
169 fn pixel(self) -> vec4 {
170 let color = mix(THEME_COLOR_U_HIDDEN, THEME_COLOR_U_1, self.focus);
171 return vec4(color.rgb * color.a, color.a);
172 }
173 }
174
175 animator: {
176 blink = {
177 default: off
178 off = {
179 from: {all: Forward {duration:0.05}}
180 apply: {
181 draw_cursor: {blink:0.0}
182 }
183 }
184 on = {
185 from: {all: Forward {duration: 0.05}}
186 apply: {
187 draw_cursor: {blink:1.0}
188 }
189 }
190 }
191 focus = {
192 default: off
193 off = {
194 from: {all: Forward {duration:0.05}}
195 apply: {
196 draw_cursor: {focus:0.0}
197 draw_cursor_bg: {focus:0.0}
198 draw_selection: {focus:0.0}
199 }
200 }
201 on = {
202 from: {all: Forward {duration: 0.05}}
203 apply: {
204 draw_cursor: {focus:1.0}
205 draw_cursor_bg: {focus:1.0}
206 draw_selection: {focus:1.0}
207 }
208 }
209 }
210 }
211 }
212
213}
214
215#[derive(Live, LiveHook, LiveRegister)]
216#[repr(C)]
217struct DrawCodeText {
218 #[deref]
219 draw_super: DrawText,
220 #[live]
221 outline: f32,
222}
223
224#[derive(Live, LiveRegister)]
225pub struct CodeEditor {
226 #[walk] walk: Walk,
227 #[live] scroll_bars: ScrollBars,
228 #[live] draw_gutter: DrawText,
229 #[live] draw_text: DrawCodeText,
230 #[live] token_colors: TokenColors,
231 #[live] draw_indent_guide: DrawIndentGuide,
232 #[live] draw_decoration: DrawDecoration,
233 #[live] draw_selection: DrawSelection,
234 #[live] draw_cursor: DrawColor,
235 #[live] draw_cursor_bg: DrawColor,
236 #[live] draw_bg: DrawColor,
237 #[rust(KeepCursorInView::Off)] pub keep_cursor_in_view: KeepCursorInView,
238 #[rust] last_cursor_screen_pos: Option<DVec2>,
239 #[live] pad_left_top: DVec2,
240 #[rust] cell_size: DVec2,
241 #[rust] cell_offset_y: f64,
242 #[rust] gutter_rect: Rect,
243 #[rust] gutter_chars: usize,
244 #[rust] viewport_rect: Rect,
245 #[rust] unscrolled_rect: Rect,
246 #[rust] line_start: usize,
247 #[rust] line_end: usize,
248 #[rust(1.0)] pub height_scale: f64,
249 #[live(true)] word_wrap: bool,
250 #[live(false)] read_only: bool,
251 #[live(true)] show_gutter: bool,
252 #[live(2usize)] gutter_pad: usize,
253 #[live(true)] empty_page_at_end: bool,
254
255
256 #[live(0.5)] blink_speed: f64,
257
258 #[animator] animator: Animator,
259
260 #[rust] blink_timer: Timer,
261}
262
263pub enum KeepCursorInView {
264 Once,
265 Always(DVec2, NextFrame),
266 LockStart,
267 Locked(DVec2),
268 LockedCenter(DVec2, Position, Affinity),
269 FontResize(DVec2),
270 JumpToPosition,
271 Off,
272}
273
274impl KeepCursorInView {
275 fn is_once(&self) -> bool {
276 match self {
277 Self::Once => true,
278 _ => false,
279 }
280 }
281 fn is_locked(&self) -> bool {
282 match self {
283 Self::LockStart | Self::Locked(_) | Self::LockedCenter(_, _, _) => true,
284 _ => false,
285 }
286 }
287}
288impl LiveHook for CodeEditor {
289}
290impl CodeEditor {
330 pub fn redraw(&mut self, cx: &mut Cx) {
331 self.scroll_bars.redraw(cx);
332 }
333
334 pub fn set_visible(&mut self, _cx:&mut Cx, _visible:bool){}
335
336 pub fn visible(&self) -> bool {true}
337
338
339 pub fn area(&self)->Area{
340 self.scroll_bars.area()
341 }
342
343 pub fn walk(&self, _cx:&mut Cx)->Walk{
344 self.walk
345 }
346
347 pub fn uid_to_widget(&self, _uid:WidgetUid)->WidgetRef{
348 WidgetRef::empty()
349 }
350
351 pub fn find_widgets(&self, _path: &[LiveId], _cached: WidgetCache, _results: &mut WidgetSet){
352 }
353
354 pub fn draw_empty_editor(&mut self, cx: &mut Cx2d, walk:Walk) {
355 self.scroll_bars.begin(cx, walk, Layout::default());
356 self.draw_bg.draw_abs(cx, cx.turtle().unscrolled_rect());
357 self.scroll_bars.end(cx);
358 }
359
360 pub fn draw_walk_editor(&mut self, cx: &mut Cx2d, session: &mut CodeSession, walk:Walk) {
361 session.handle_changes();
363
364 let text = self.draw_text.layout(cx, 0.0, 0.0, None, false, Align::default(), "!");
365 let first_row = text.rows.first().unwrap();
366 let first_glyph = first_row.glyphs.first().unwrap();
367 let width_in_lpxs = first_glyph.advance_in_lpxs();
368 let height_in_lpxs = first_glyph.ascender_in_lpxs() - first_glyph.descender_in_lpxs();
369 let line_spacing_in_lpxs = height_in_lpxs * self.draw_text.text_style.line_spacing;
370 self.cell_size = dvec2(
371 width_in_lpxs as f64,
372 line_spacing_in_lpxs as f64
373 );
374 self.cell_offset_y = ((line_spacing_in_lpxs - height_in_lpxs) / 2.0) as f64;
375
376 let last_added_selection =
377 session.selections()[session.last_added_selection_index().unwrap()];
378 let (cursor_x, cursor_y) = session.layout().logical_to_normalized_position(
379 last_added_selection.cursor.position,
380 last_added_selection.cursor.affinity,
381 );
382 let cursor_pos = dvec2(cursor_x, cursor_y) * self.cell_size;
383 self.last_cursor_screen_pos = Some(cursor_pos - self.scroll_bars.get_scroll_pos());
384 match self.keep_cursor_in_view {
385 KeepCursorInView::Once | KeepCursorInView::Always(_, _) => {
386 let pad_above = dvec2(self.cell_size.x * 8.0, self.cell_size.y);
388 let pad_below = dvec2(self.cell_size.x * 8.0, self.cell_size.y * 2.0);
389 let rect = Rect {
390 pos: cursor_pos - pad_above,
391 size: pad_above + pad_below,
392 };
393 self.scroll_bars.scroll_into_view(cx, rect);
395 if self.keep_cursor_in_view.is_once() {
396 self.keep_cursor_in_view = KeepCursorInView::Off
397 }
398 }
399 KeepCursorInView::LockStart => {
400 let screen_pos = cursor_pos - self.scroll_bars.get_scroll_pos();
402 let rect = Rect {
403 pos: dvec2(0.0, 0.0),
404 size: self.viewport_rect.size,
405 };
406 if rect.contains(screen_pos) {
407 self.keep_cursor_in_view = KeepCursorInView::Locked(screen_pos);
408 } else {
409 let center = rect.size * 0.5 + self.unscrolled_rect.pos;
410 let ((pos, aff), _) = self.pick(session, center);
411 let (cursor_x, cursor_y) =
412 session.layout().logical_to_normalized_position(pos, aff);
413 let screen_pos = dvec2(cursor_x, cursor_y) * self.cell_size
414 - self.scroll_bars.get_scroll_pos();
415 self.keep_cursor_in_view = KeepCursorInView::LockedCenter(screen_pos, pos, aff);
416 }
417 }
418 KeepCursorInView::Locked(pos) => {
419 let new_pos = cursor_pos - self.scroll_bars.get_scroll_pos();
421 let delta = pos - new_pos;
422 let new_pos = self.scroll_bars.get_scroll_pos() - dvec2(0.0, delta.y);
423 self.scroll_bars.set_scroll_pos_no_clip(cx, new_pos);
424 }
426 KeepCursorInView::LockedCenter(screen_pos, pos, aff) => {
427 let (cursor_x, cursor_y) =
428 session.layout().logical_to_normalized_position(pos, aff);
429 let new_pos =
430 dvec2(cursor_x, cursor_y) * self.cell_size - self.scroll_bars.get_scroll_pos();
431 let delta = screen_pos - new_pos;
432 let new_pos = self.scroll_bars.get_scroll_pos() - dvec2(0.0, delta.y);
433 self.scroll_bars.set_scroll_pos_no_clip(cx, new_pos);
434 }
436 KeepCursorInView::JumpToPosition => {
437 let padd = dvec2(self.cell_size.x * 10.0, self.cell_size.y * 10.0);
440 self.scroll_bars.scroll_into_view(
441 cx,
442 Rect {
443 pos: cursor_pos - padd,
444 size: 2.0 * padd,
445 },
446 );
447 self.keep_cursor_in_view = KeepCursorInView::Off;
448 }
449 KeepCursorInView::FontResize(last_pos) => {
450 let new_pos = cursor_pos - self.scroll_bars.get_scroll_pos();
451 let delta = last_pos - new_pos;
452 let new_pos = self.scroll_bars.get_scroll_pos() - dvec2(0.0, delta.y);
453 self.scroll_bars.set_scroll_pos_no_clip(cx, new_pos);
454 self.keep_cursor_in_view = KeepCursorInView::Off
455 }
456 KeepCursorInView::Off => {}
457 }
458
459 let height_is_fit = walk.height.is_fit();
461 const MAX_HEIGHT:f64 = 100_0000.0;
465 self.scroll_bars.begin(cx, walk, Layout::default());
466
467 let turtle_rect = cx.turtle().rect();
468
469 let gutter_width = if self.show_gutter{
470 self.gutter_chars = session
471 .document()
472 .as_text()
473 .as_lines()
474 .len()
475 .to_string()
476 .column_count()+1;
477 self.gutter_chars as f64 * self.cell_size.x + self.gutter_pad as f64 * self.cell_size.x
478 }
479 else{
480 self.gutter_chars = 0;
481 0.0
482 };
483 self.gutter_rect = Rect {
484 pos: turtle_rect.pos,
485 size: DVec2 {
486 x: gutter_width,
487 y: if height_is_fit{MAX_HEIGHT}else{turtle_rect.size.y},
488 },
489 };
490 self.viewport_rect = Rect {
491 pos: DVec2 {
492 x: turtle_rect.pos.x + gutter_width,
493 y: turtle_rect.pos.y,
494 },
495 size: DVec2 {
496 x: turtle_rect.size.x - gutter_width,
497 y:if height_is_fit{MAX_HEIGHT}else{turtle_rect.size.y},
498 },
499 };
500
501 self.gutter_rect.pos += self.pad_left_top;
503 self.gutter_rect.size -= self.pad_left_top;
504 self.viewport_rect.pos += self.pad_left_top;
505 self.viewport_rect.size -= self.pad_left_top;
506
507 session.set_wrap_column(if self.word_wrap {
508 Some((self.viewport_rect.size.x / self.cell_size.x) as usize)
509 } else {
510 None
511 });
512
513 let scroll_pos = self.scroll_bars.get_scroll_pos();
514
515 self.line_start = session
516 .layout()
517 .find_first_line_ending_after_y(scroll_pos.y / self.cell_size.y - self.cell_size.y);
518 self.line_end = session.layout().find_first_line_starting_after_y(
519 (scroll_pos.y + self.viewport_rect.size.y) / self.cell_size.y,
520 );
521 self.unscrolled_rect = cx.turtle().unscrolled_rect();
522
523 let bg_rect = Rect {
524 pos: self.unscrolled_rect.pos,
525 size: DVec2 {
526 x: self.unscrolled_rect.size.x,
527 y:if height_is_fit{MAX_HEIGHT}else{self.unscrolled_rect.size.y},
528 },
529 };
530 self.draw_bg.draw_abs(cx, bg_rect);
531 self.draw_cursor.begin_many_instances(cx);
532
533 if self.show_gutter{
534 self.draw_gutter(cx, session);
535 }
536 self.draw_selection_layer(cx, session);
537 self.draw_text_layer(cx, session);
538 self.draw_indent_guide_layer(cx, session);
539 self.draw_decoration_layer(cx, session);
540 self.draw_selection_layer(cx, session);
541 cx.turtle_mut().set_used(
546 session.layout().width() * self.cell_size.x +self.pad_left_top.x,
547 self.height_scale * session.layout().height() * self.cell_size.y +
548 if height_is_fit || !self.empty_page_at_end{0.0} else {self.viewport_rect.size.y}
549 +self.pad_left_top.y * self.height_scale
550 );
551
552 self.draw_cursor.end_many_instances(cx);
553
554 self.scroll_bars.end(cx);
555
556 if session.update_folds() {
557 cx.redraw_area_in_draw(self.scroll_bars.area());
558 } else if self.keep_cursor_in_view.is_locked() {
559 self.keep_cursor_in_view = KeepCursorInView::Off;
560 }
561 }
562
563 pub fn set_key_focus(&mut self, cx: &mut Cx) {
564 cx.set_key_focus(self.scroll_bars.area());
565 }
566
567 pub fn set_cursor_and_scroll(
568 &mut self,
569 cx: &mut Cx,
570 pos: Position,
571 session: &mut CodeSession,
572 ) {
573 session.set_selection(session.clamp_position(pos), Affinity::Before, SelectionMode::Simple, NewGroup::Yes);
574 self.keep_cursor_in_view = KeepCursorInView::JumpToPosition;
575 self.redraw(cx);
576 }
577
578 pub fn set_selection_and_scroll(
579 &mut self,
580 cx: &mut Cx,
581 start: Position,
582 end: Position,
583 session: &mut CodeSession,
584 ) {
585
586 session.set_selection(session.clamp_position(start), Affinity::Before, SelectionMode::Simple, NewGroup::Yes);
587 session.move_to(session.clamp_position(end), Affinity::Before, NewGroup::Yes);
588 self.keep_cursor_in_view = KeepCursorInView::JumpToPosition;
589 self.redraw(cx);
590 }
591
592 pub fn reset_font_size(&mut self) {
593 self.draw_gutter.text_style.font_size = 9.0;
594 self.draw_text.text_style.font_size = 9.0;
595 if let Some(pos) = self.last_cursor_screen_pos {
596 self.keep_cursor_in_view = KeepCursorInView::FontResize(pos);
597 }
598 }
599
600 pub fn decrease_font_size(&mut self) {
601 if self.draw_text.text_style.font_size > 3.0 {
602 self.draw_text.text_style.font_size -= 1.0;
603 self.draw_gutter.text_style.font_size = self.draw_text.text_style.font_size;
604 if let Some(pos) = self.last_cursor_screen_pos {
605 self.keep_cursor_in_view = KeepCursorInView::FontResize(pos);
606 }
607 }
608 }
609
610 pub fn increase_font_size(&mut self) {
611 if self.draw_text.text_style.font_size < 20.0 {
612 self.draw_text.text_style.font_size += 1.0;
613 self.draw_gutter.text_style.font_size = self.draw_text.text_style.font_size;
614 if let Some(pos) = self.last_cursor_screen_pos {
615 self.keep_cursor_in_view = KeepCursorInView::FontResize(pos);
616 }
617 }
618 }
619
620 pub fn reset_cursor_blinker(&mut self, cx: &mut Cx) {
621 if self.read_only{
622 self.animator_cut(cx, id!(blink.off));
623 }
624 else{
625 self.animator_cut(cx, id!(blink.off));
626 cx.stop_timer(self.blink_timer);
627 self.blink_timer = cx.start_timeout(self.blink_speed)
628 }
629 }
630
631 pub fn handle_event(
632 &mut self,
633 cx: &mut Cx,
634 event: &Event,
635 scope: &mut Scope,
636 session: &mut CodeSession,
637 ) -> Vec<CodeEditorAction> {
638 let mut actions = Vec::new();
639
640 self.animator_handle_event(cx, event);
641
642 session.handle_changes();
643
644 if self.scroll_bars.handle_event(cx, event, scope).len()>0{
645 self.redraw(cx);
646 };
647
648 if self.blink_timer.is_event(event).is_some() {
649 if self.animator_in_state(cx, id!(blink.off)) {
650 self.animator_play(cx, id!(blink.on));
651 } else {
652 self.animator_play(cx, id!(blink.off));
653 }
654 self.blink_timer = cx.start_timeout(self.blink_speed)
655 }
656 let mut keyboard_moved_cursor = false;
657 match event.hits(cx, self.scroll_bars.area()) {
658 Hit::KeyFocusLost(_) => {
659 self.animator_play(cx, id!(focus.off));
660 }
661 Hit::KeyFocus(_) => {
662 self.animator_play(cx, id!(focus.on));
663 }
664 Hit::KeyDown(KeyEvent {
665 key_code: KeyCode::Escape,
666 is_repeat: false,
667 ..
668 }) => {
669 session.fold();
670 if !self.keep_cursor_in_view.is_locked() {
671 self.keep_cursor_in_view = KeepCursorInView::LockStart;
672 }
673 self.redraw(cx);
674 }
675 Hit::KeyUp(KeyEvent {
676 key_code: KeyCode::Escape,
677 ..
678 }) => {
679 session.unfold();
680 if !self.keep_cursor_in_view.is_locked() {
681 self.keep_cursor_in_view = KeepCursorInView::LockStart;
682 }
683 self.redraw(cx);
684 }
685 Hit::KeyDown(KeyEvent {
686 key_code: KeyCode::Minus,
687 modifiers: KeyModifiers { control, logo, .. },
688 ..
689 }) => {
690 if control || logo {
691 self.decrease_font_size();
692 self.redraw(cx);
693 }
694 }
695 Hit::KeyDown(KeyEvent {
696 key_code: KeyCode::Key0,
697 modifiers: KeyModifiers { control, logo, .. },
698 ..
699 }) => {
700 if control || logo {
701 self.reset_font_size();
702 self.redraw(cx);
703 }
704 }
705 Hit::KeyDown(KeyEvent {
706 key_code: KeyCode::Equals,
707 modifiers: KeyModifiers { control, logo, .. },
708 ..
709 }) => {
710 if control || logo {
711 self.increase_font_size();
712 self.redraw(cx);
713 }
714 }
715 Hit::KeyDown(KeyEvent {
716 key_code: KeyCode::KeyW,
717 modifiers: KeyModifiers { control, logo, .. },
718 ..
719 }) => {
720 if control || logo {
721 self.word_wrap = !self.word_wrap;
722 self.redraw(cx);
723 }
724 }
725 Hit::KeyDown(KeyEvent {
726 key_code: KeyCode::KeyA,
727 modifiers: KeyModifiers {control, logo, ..},
728 ..
729 }) => {
730 if control || logo {
731 let ((cursor, affinity), _is_in_gutter) = self.pick(session, dvec2(0.0,0.0));
732 session.set_selection(
733 cursor,
734 affinity,
735 SelectionMode::All,
736 NewGroup::Yes
737 );
738 self.reset_cursor_blinker(cx);
739 self.keep_cursor_in_view = KeepCursorInView::Off;
740 self.redraw(cx);
741 }
742 }
743
744 Hit::KeyDown(KeyEvent {
745 key_code: KeyCode::ArrowLeft,
746 modifiers:
747 KeyModifiers {
748 shift,
749 control,
750 logo,
751 ..
752 },
753 ..
754 }) => {
755 if control || logo {
756 } else {
758 session.move_left(!shift);
759 }
760 keyboard_moved_cursor = true;
761 self.redraw(cx);
762 }
763 Hit::KeyDown(KeyEvent {
764 key_code: KeyCode::ArrowRight,
765 modifiers:
766 KeyModifiers {
767 shift,
768 control,
769 logo,
770 ..
771 },
772 ..
773 }) => {
774 if control || logo {
775 } else {
777 session.move_right(!shift);
778 }
779
780 keyboard_moved_cursor = true;
781 self.redraw(cx);
782 }
783 Hit::KeyDown(KeyEvent {
784 key_code: KeyCode::ArrowUp,
785 modifiers: KeyModifiers { shift, .. },
786 ..
787 }) => {
788 session.move_up(!shift);
789 keyboard_moved_cursor = true;
790 self.redraw(cx);
791 }
792 Hit::KeyDown(KeyEvent {
793 key_code: KeyCode::ArrowDown,
794 modifiers: KeyModifiers { shift, .. },
795 ..
796 }) => {
797 session.move_down(!shift);
798 keyboard_moved_cursor = true;
799 self.redraw(cx);
800 }
801 Hit::KeyDown(KeyEvent {
802 key_code: KeyCode::Home,
803 modifiers: KeyModifiers { shift, .. },
804 ..
805 }) => {
806 session.home(!shift);
807 keyboard_moved_cursor = true;
808 self.redraw(cx);
809 }
810 Hit::KeyDown(KeyEvent {
811 key_code: KeyCode::End,
812 modifiers: KeyModifiers { shift, .. },
813 ..
814 }) => {
815 session.end(!shift);
816 keyboard_moved_cursor = true;
817 self.redraw(cx);
818 }
819 Hit::KeyDown(KeyEvent {
820 key_code: KeyCode::PageUp,
821 modifiers: KeyModifiers { shift, .. },
822 ..
823 }) => {
824 for _ in 0..self.line_end - self.line_start - 3 {
825 session.move_up(!shift);
826 }
827 keyboard_moved_cursor = true;
828 self.redraw(cx);
829 }
830 Hit::KeyDown(KeyEvent {
831 key_code: KeyCode::PageDown,
832 modifiers: KeyModifiers { shift, .. },
833 ..
834 }) => {
835 for _ in 0..self.line_end - self.line_start - 3 {
836 session.move_down(!shift);
837 }
838 keyboard_moved_cursor = true;
839 self.redraw(cx);
840 }
841 Hit::TextInput(TextInputEvent {
842 ref input,
843 was_paste: false,
844 ..
845 }) if input.len() > 0 && !self.read_only => {
846 session.insert(input.into());
847 self.redraw(cx);
848 keyboard_moved_cursor = true;
849 actions.push(CodeEditorAction::TextDidChange);
850 }
851 Hit::TextInput(TextInputEvent {
852 ref input,
853 was_paste: true,
854 ..
855 }) if input.len() > 0 && !self.read_only => {
856 session.paste(input.into());
857 self.redraw(cx);
858 keyboard_moved_cursor = true;
859 actions.push(CodeEditorAction::TextDidChange);
860 }
861 Hit::KeyDown(KeyEvent {
862 key_code: KeyCode::ReturnKey,
863 ..
864 }) => {
865 session.enter();
866 self.redraw(cx);
867 keyboard_moved_cursor = true;
868 actions.push(CodeEditorAction::TextDidChange);
869 }
870 Hit::KeyDown(KeyEvent {
871 key_code: KeyCode::Tab,
872 modifiers: KeyModifiers { shift: false, .. },
873 ..
874 }) if !self.read_only => {
875 session.indent();
876 self.redraw(cx);
877 keyboard_moved_cursor = true;
878 actions.push(CodeEditorAction::TextDidChange);
879 }
880 Hit::KeyDown(KeyEvent {
881 key_code: KeyCode::Tab,
882 modifiers: KeyModifiers { shift: true, .. },
883 ..
884 }) if !self.read_only=> {
885 session.outdent();
886 self.redraw(cx);
887 keyboard_moved_cursor = true;
888 actions.push(CodeEditorAction::TextDidChange);
889 }
890 Hit::KeyDown(KeyEvent {
891 key_code: KeyCode::Delete,
892 ..
893 }) if !self.read_only=> {
894 session.delete();
895 self.redraw(cx);
896 keyboard_moved_cursor = true;
897 actions.push(CodeEditorAction::TextDidChange);
898 }
899 Hit::KeyDown(KeyEvent {
900 key_code: KeyCode::Backspace,
901 ..
902 }) if !self.read_only=> {
903 session.backspace();
904 self.redraw(cx);
905 keyboard_moved_cursor = true;
906 actions.push(CodeEditorAction::TextDidChange);
907 }
908 Hit::TextCopy(ce) => {
909 *ce.response.borrow_mut() = Some(session.copy());
910 keyboard_moved_cursor = true;
911 }
912 Hit::TextCut(ce) if !self.read_only=> {
913 *ce.response.borrow_mut() = Some(session.copy());
914 session.delete();
915 keyboard_moved_cursor = true;
916 self.redraw(cx);
917 }
918 Hit::KeyDown(KeyEvent {
919 key_code: KeyCode::KeyZ,
920 modifiers: KeyModifiers{ control, logo, shift, .. },
921 ..
922 }) if (control || logo) && !shift && !self.read_only =>{
923 if session.undo() {
924 cx.redraw_all();
925 actions.push(CodeEditorAction::TextDidChange);
926 keyboard_moved_cursor = true;
927 }
928 }
929 Hit::KeyDown(KeyEvent {
930 key_code: KeyCode::KeyZ,
931 modifiers: KeyModifiers{ control, logo, shift, ..},
932 ..
933 }) if (control || logo) && shift && !self.read_only =>{
934 if session.redo() {
935 self.redraw(cx);
936 actions.push(CodeEditorAction::TextDidChange);
937 keyboard_moved_cursor = true;
938 }
939 }
940 Hit::KeyDown(ke)=>{
941 actions.push(CodeEditorAction::UnhandledKeyDown(ke))
942 }
943 Hit::FingerDown(FingerDownEvent {
944 abs,
945 tap_count,
946 modifiers:
947 KeyModifiers {
948 alt: false,
949 shift: false,
950 ..
951 },
952 ..
953 }) => {
954 self.animator_play(cx, id!(focus.on));
955 cx.set_key_focus(self.scroll_bars.area());
956 let ((cursor, affinity), is_in_gutter) = self.pick(session, abs);
957 session.set_selection(
958 cursor,
959 affinity,
960 if is_in_gutter {
961 SelectionMode::Line
962 } else {
963 match tap_count {
964 1 => SelectionMode::Simple,
965 2 => SelectionMode::Word,
966 _ => SelectionMode::Line,
967 }
969 },
970 NewGroup::Yes
971 );
972 self.reset_cursor_blinker(cx);
973 self.keep_cursor_in_view = KeepCursorInView::Always(abs, cx.new_next_frame());
974 self.redraw(cx);
975 }
976 Hit::FingerDown(FingerDownEvent {
977 abs,
978 tap_count,
979 modifiers:
980 KeyModifiers {
981 alt: true,
982 shift: false,
983 ..
984 },
985 ..
986 }) => {
987 self.animator_play(cx, id!(focus.on));
988 cx.set_key_focus(self.scroll_bars.area());
989 let ((cursor, affinity), is_in_gutter) = self.pick(session, abs);
990 session.add_selection(
991 cursor,
992 affinity,
993 if is_in_gutter {
994 SelectionMode::Line
995 } else {
996 match tap_count {
997 1 => SelectionMode::Simple,
998 2 => SelectionMode::Word,
999 3 => SelectionMode::Line,
1000 _ => SelectionMode::All,
1001 }
1002 },
1003 );
1004 self.reset_cursor_blinker(cx);
1005 self.keep_cursor_in_view = KeepCursorInView::Always(abs, cx.new_next_frame());
1006 self.redraw(cx);
1007 }
1008 Hit::FingerUp(_) => {
1009 self.reset_cursor_blinker(cx);
1010 self.keep_cursor_in_view = KeepCursorInView::Off;
1011 }
1012 Hit::FingerHoverIn(_) | Hit::FingerHoverOver(_) => {
1013 cx.set_cursor(MouseCursor::Text);
1014 }
1015 Hit::FingerDown(FingerDownEvent {
1016 abs,
1017 modifiers: KeyModifiers { shift: true, .. },
1018 ..
1019 })
1020 | Hit::FingerMove(FingerMoveEvent { abs, .. }) => {
1021 self.reset_cursor_blinker(cx);
1022 if let KeepCursorInView::Always(old_abs, _) = &mut self.keep_cursor_in_view {
1023 *old_abs = abs;
1024 }
1025 cx.set_cursor(MouseCursor::Text);
1026 let ((cursor, affinity), _) = self.pick(session, abs);
1027 session.move_to(cursor, affinity, NewGroup::Yes);
1028 self.redraw(cx);
1030 }
1031 _ => {}
1032 }
1033 if keyboard_moved_cursor {
1034 self.keep_cursor_in_view = KeepCursorInView::Once;
1035 self.reset_cursor_blinker(cx);
1036 }
1037 if let KeepCursorInView::Always(abs, next) = &mut self.keep_cursor_in_view {
1038 if next.is_event(event).is_some() {
1039 *next = cx.new_next_frame();
1040 let abs = *abs;
1041 let ((cursor, affinity), _) = self.pick(session, abs);
1042 session.move_to(cursor, affinity, NewGroup::Yes);
1043 self.redraw(cx);
1044 }
1045 }
1046 actions
1047 }
1048
1049 fn draw_gutter(&mut self, cx: &mut Cx2d, session: &CodeSession) {
1050 let mut line_index = self.line_start;
1051 let mut origin_y = session.layout().line(self.line_start).y();
1052 let mut buf = String::new();
1053 for element in session
1054 .layout()
1055 .block_elements(self.line_start, self.line_end)
1056 {
1057 match element {
1058 BlockElement::Line { line, .. } => {
1059 self.draw_gutter.font_scale = line.scale() as f32;
1060 buf.clear();
1061 match self.gutter_chars{
1062 0|1=>write!(buf, "{: >0}", line_index + 1).unwrap(),
1063 2=>write!(buf, "{: >1}", line_index + 1).unwrap(),
1064 3=>write!(buf, "{: >2}", line_index + 1).unwrap(),
1065 4=>write!(buf, "{: >3}", line_index + 1).unwrap(),
1066 5=>write!(buf, "{: >4}", line_index + 1).unwrap(),
1067 _=>write!(buf, "{: >5}", line_index + 1).unwrap(),
1068 }
1069 self.draw_gutter.draw_abs(
1070 cx,
1071 DVec2 {
1072 x: 0.0,
1073 y: origin_y,
1074 } * self.cell_size
1075 + self.gutter_rect.pos
1076 + dvec2(
1077 (1.0 - line.scale()) * -self.cell_size.x + self.gutter_rect.size.x
1078 - line.scale() * self.gutter_rect.size.x,
1079 self.cell_offset_y,
1080 ),
1081 &buf,
1082 );
1083 line_index += 1;
1084 origin_y += line.height();
1085 }
1086 BlockElement::Widget(widget) => {
1087 origin_y += widget.height;
1088 }
1089 }
1090 }
1091 }
1092
1093 fn draw_text_layer(&mut self, cx: &mut Cx2d, session: &CodeSession) {
1094 let highlighted_delimiter_positions = session.highlighted_delimiter_positions();
1095 let mut line_index = self.line_start;
1096 let mut origin_y = session.layout().line(self.line_start).y();
1097 for element in session
1098 .layout()
1099 .block_elements(self.line_start, self.line_end)
1100 {
1101 match element {
1102 BlockElement::Line { line, .. } => {
1103 self.draw_text.font_scale = line.scale() as f32;
1104 let mut token_iter = line.tokens().iter().copied();
1105 let mut token_slot = token_iter.next();
1106 let mut row_index = 0;
1107 let mut byte_index = 0;
1108 let mut column_index = 0;
1109 for element in line.wrapped_elements() {
1110 match element {
1111 WrappedElement::Text {
1112 is_inlay: false,
1113 mut text,
1114 } => {
1115 while !text.is_empty() {
1116 let token = match token_slot {
1117 Some(token) => {
1118 if text.len() < token.len {
1119 token_slot = Some(Token {
1120 len: token.len - text.len(),
1121 kind: token.kind,
1122 });
1123 Token {
1124 len: text.len(),
1125 kind: token.kind,
1126 }
1127 } else {
1128 token_slot = token_iter.next();
1129 token
1130 }
1131 }
1132 None => Token {
1133 len: text.len(),
1134 kind: TokenKind::Unknown,
1135 },
1136 };
1137 let (text_0, text_1) = text.split_at(token.len);
1138 text = text_1;
1139 self.draw_text.color = match token.kind {
1140 TokenKind::Unknown => self.token_colors.unknown,
1141 TokenKind::BranchKeyword => {
1142 self.token_colors.branch_keyword
1143 }
1144 TokenKind::Comment => self.token_colors.comment,
1145 TokenKind::Constant => self.token_colors.constant,
1146 TokenKind::Delimiter => self.token_colors.delimiter,
1147 TokenKind::Identifier => self.token_colors.identifier,
1148 TokenKind::LoopKeyword => self.token_colors.loop_keyword,
1149 TokenKind::Number => self.token_colors.number,
1150 TokenKind::OtherKeyword => self.token_colors.other_keyword,
1151 TokenKind::Punctuator => self.token_colors.punctuator,
1152 TokenKind::String => self.token_colors.string,
1153 TokenKind::Function => self.token_colors.function,
1154 TokenKind::Typename => self.token_colors.typename,
1155 TokenKind::Whitespace => self.token_colors.whitespace,
1156 };
1157 self.draw_text.outline = 0.0;
1158 if let TokenKind::Delimiter = token.kind {
1159 if highlighted_delimiter_positions.contains(&Position {
1160 line_index,
1161 byte_index,
1162 }) {
1163 self.draw_text.outline = 1.0;
1164 self.draw_text.color =
1165 self.token_colors.delimiter_highlight
1166 }
1167 }
1168 for grapheme in text_0.graphemes() {
1169 let (x, y) = line
1170 .grid_to_normalized_position(row_index, column_index);
1171 self.draw_text.draw_abs(
1172 cx,
1173 DVec2 { x, y: origin_y + y } * self.cell_size
1174 + dvec2(
1175 self.viewport_rect.pos.x,
1176 self.viewport_rect.pos.y + self.cell_offset_y
1177 ),
1178 grapheme,
1179 );
1180 byte_index += grapheme.len();
1181 column_index += grapheme.column_count();
1182 }
1183 }
1184 }
1185 WrappedElement::Text {
1186 is_inlay: true,
1187 text,
1188 } => {
1189 let (x, y) =
1190 line.grid_to_normalized_position(row_index, column_index);
1191 self.draw_text.draw_abs(
1192 cx,
1193 DVec2 { x, y: origin_y + y } * self.cell_size
1194 + self.viewport_rect.pos,
1195 text,
1196 );
1197 column_index += text.column_count();
1198 }
1199 WrappedElement::Widget(widget) => {
1200 column_index += widget.column_count;
1201 }
1202 WrappedElement::Wrap => {
1203 column_index = line.wrap_indent_column_count();
1204 row_index += 1;
1205 }
1206 }
1207 }
1208 line_index += 1;
1209 origin_y += line.height();
1210 }
1211 BlockElement::Widget(widget) => {
1212 origin_y += widget.height;
1213 }
1214 }
1215 }
1216 }
1217
1218 fn draw_indent_guide_layer(&mut self, cx: &mut Cx2d, session: &CodeSession) {
1219 let mut origin_y = session.layout().line(self.line_start).y();
1220 for element in session
1221 .layout()
1222 .block_elements(self.line_start, self.line_end)
1223 {
1224 let Settings {
1225 tab_column_count, ..
1226 } = **session.settings();
1227 match element {
1228 BlockElement::Line { line, .. } => {
1229 for row_index in 0..line.row_count() {
1230 for column_index in
1231 (0..line.indent_column_count()).step_by(tab_column_count)
1232 {
1233 let (x, y) = line.grid_to_normalized_position(row_index, column_index);
1234 self.draw_indent_guide.draw_abs(
1235 cx,
1236 Rect {
1237 pos: DVec2 { x, y: origin_y + y } * self.cell_size
1238 + self.viewport_rect.pos,
1239 size: DVec2 {
1240 x: 2.0,
1241 y: line.scale() * self.cell_size.y,
1242 },
1243 },
1244 );
1245 }
1246 }
1247 origin_y += line.height();
1248 }
1249 BlockElement::Widget(widget) => {
1250 origin_y += widget.height;
1251 }
1252 }
1253 }
1254 }
1255
1256 fn draw_decoration_layer(&mut self, cx: &mut Cx2d, session: &CodeSession) {
1257 let mut active_decoration = None;
1258 let decorations = session.document().decorations();
1259 let mut decorations = decorations.iter();
1260 while decorations.as_slice().first().map_or(false, |decoration| {
1261 decoration.end().line_index < self.line_start
1262 }) {
1263 decorations.next().unwrap();
1264 }
1265 if decorations.as_slice().first().map_or(false, |decoration| {
1266 decoration.start().line_index < self.line_start
1267 }) {
1268 active_decoration = Some(ActiveDecoration {
1269 decoration: *decorations.next().unwrap(),
1270 start_x: 0.0,
1271 });
1272 }
1273 DrawDecorationLayer {
1274 code_editor: self,
1275 active_decoration,
1276 decorations,
1277 }
1278 .draw_decoration_layer(cx, session)
1279 }
1280
1281 fn draw_selection_layer(&mut self, cx: &mut Cx2d, session: &CodeSession) {
1282 let mut active_selection = None;
1283 let selections = session.selections();
1284 let mut selections = selections.iter();
1285 while selections.as_slice().first().map_or(false, |selection| {
1286 selection.end().line_index < self.line_start
1287 }) {
1288 selections.next().unwrap();
1289 }
1290 if selections.as_slice().first().map_or(false, |selection| {
1291 selection.start().line_index < self.line_start
1292 }) {
1293 active_selection = Some(ActiveSelection {
1294 selection: *selections.next().unwrap(),
1295 start_x: 0.0,
1296 });
1297 }
1298 DrawSelectionLayer {
1299 code_editor: self,
1300 active_selection,
1301 selections,
1302 }
1303 .draw_selection_layer(cx, session)
1304 }
1305
1306 fn pick(&self, session: &CodeSession, position: DVec2) -> ((Position, Affinity), bool) {
1307 let position = (position - self.viewport_rect.pos) / self.cell_size;
1308
1309 if position.y < 0.0 {
1310 return (
1311 (
1312 Position {
1313 line_index: 0,
1314 byte_index: 0,
1315 },
1316 Affinity::Before,
1317 ),
1318 false,
1319 );
1320 }
1321 let layout = session.layout();
1322 if position.y > session.layout().height() {
1323 let lines = layout.as_text().as_lines();
1324 return (
1325 (
1326 Position {
1327 line_index: lines.len() - 1,
1328 byte_index: lines[lines.len() - 1].len(),
1329 },
1330 Affinity::After,
1331 ),
1332 false,
1333 );
1334 }
1335 let mut line_index = layout.find_first_line_ending_after_y(position.y);
1336 let mut origin_y = layout.line(line_index).y();
1337 for block in layout.block_elements(line_index, line_index + 1) {
1338 match block {
1339 BlockElement::Line {
1340 is_inlay: false,
1341 line,
1342 } => {
1343 let mut byte_index = 0;
1344 let mut row_index = 0;
1345 let mut column_index = 0;
1346 for element in line.wrapped_elements() {
1347 match element {
1348 WrappedElement::Text {
1349 is_inlay: false,
1350 text,
1351 } => {
1352 for grapheme in text.graphemes() {
1353 let (start_x, y) =
1354 line.grid_to_normalized_position(row_index, column_index);
1355 let start_y = origin_y + y;
1356 let (end_x, _) = line.grid_to_normalized_position(
1357 row_index,
1358 column_index + grapheme.column_count(),
1359 );
1360 let end_y = start_y + line.scale();
1361 if (start_y..=end_y).contains(&position.y) {
1362 let mid_x = (start_x + end_x) / 2.0;
1363 if (start_x..=mid_x).contains(&position.x) {
1364 return (
1365 (
1366 Position {
1367 line_index,
1368 byte_index,
1369 },
1370 Affinity::After,
1371 ),
1372 false,
1373 );
1374 }
1375 if (mid_x..=end_x).contains(&position.x) {
1376 return (
1377 (
1378 Position {
1379 line_index,
1380 byte_index: byte_index + grapheme.len(),
1381 },
1382 Affinity::Before,
1383 ),
1384 false,
1385 );
1386 }
1387 }
1388 byte_index += grapheme.len();
1389 column_index += grapheme.column_count();
1390 }
1391 }
1392 WrappedElement::Text {
1393 is_inlay: true,
1394 text,
1395 } => {
1396 let (start_x, y) =
1397 line.grid_to_normalized_position(row_index, column_index);
1398 let start_y = origin_y + y;
1399 let (end_x, _) = line.grid_to_normalized_position(
1400 row_index,
1401 column_index + text.column_count(),
1402 );
1403 let end_y = origin_y + line.scale();
1404 if (start_y..=end_y).contains(&position.y)
1405 && (start_x..=end_x).contains(&position.x)
1406 {
1407 return (
1408 (
1409 Position {
1410 line_index,
1411 byte_index,
1412 },
1413 Affinity::Before,
1414 ),
1415 false,
1416 );
1417 }
1418 column_index += text.column_count();
1419 }
1420 WrappedElement::Widget(widget) => {
1421 column_index += widget.column_count;
1422 }
1423 WrappedElement::Wrap => {
1424 let (_, y) =
1425 line.grid_to_normalized_position(row_index, column_index);
1426 let start_y = origin_y + y;
1427 let end_y = start_y + line.scale();
1428 if (start_y..=end_y).contains(&position.y) {
1429 return if position.x < 0.0 {
1430 (
1431 (
1432 Position {
1433 line_index,
1434 byte_index: 0,
1435 },
1436 Affinity::Before,
1437 ),
1438 true,
1439 )
1440 } else {
1441 (
1442 (
1443 Position {
1444 line_index,
1445 byte_index,
1446 },
1447 Affinity::Before,
1448 ),
1449 false,
1450 )
1451 };
1452 }
1453 column_index = line.wrap_indent_column_count();
1454 row_index += 1;
1455 }
1456 }
1457 }
1458 let (_, y) = line.grid_to_normalized_position(row_index, column_index);
1459 let start_y = origin_y + y;
1460 let end_y = start_y + line.scale();
1461 if (start_y..=end_y).contains(&position.y) {
1462 return if position.x < 0.0 {
1463 (
1464 (
1465 Position {
1466 line_index,
1467 byte_index: 0,
1468 },
1469 Affinity::Before,
1470 ),
1471 true,
1472 )
1473 } else {
1474 (
1475 (
1476 Position {
1477 line_index,
1478 byte_index,
1479 },
1480 Affinity::Before,
1481 ),
1482 false,
1483 )
1484 };
1485 }
1486 line_index += 1;
1487 origin_y += line.height();
1488 }
1489 BlockElement::Line {
1490 is_inlay: true,
1491 line,
1492 } => {
1493 let start_y = origin_y;
1494 let end_y = start_y + line.height();
1495 if (start_y..=end_y).contains(&position.y) {
1496 return (
1497 (
1498 Position {
1499 line_index,
1500 byte_index: 0,
1501 },
1502 Affinity::Before,
1503 ),
1504 false,
1505 );
1506 }
1507 origin_y += line.height();
1508 }
1509 BlockElement::Widget(widget) => {
1510 origin_y += widget.height;
1511 }
1512 }
1513 }
1514 panic!()
1515 }
1516}
1517
1518#[derive(Clone, Copy, Debug, PartialEq, DefaultNone)]
1519pub enum CodeEditorAction {
1520 TextDidChange,
1521 UnhandledKeyDown(KeyEvent),
1522 None
1523}
1524
1525struct DrawDecorationLayer<'a> {
1526 code_editor: &'a mut CodeEditor,
1527 active_decoration: Option<ActiveDecoration>,
1528 decorations: Iter<'a, Decoration>,
1529}
1530
1531impl<'a> DrawDecorationLayer<'a> {
1532 fn draw_decoration_layer(&mut self, cx: &mut Cx2d, session: &CodeSession) {
1533 let mut line_index = self.code_editor.line_start;
1534 let mut origin_y = session.layout().line(line_index).y();
1535 for block in session
1536 .layout()
1537 .block_elements(self.code_editor.line_start, self.code_editor.line_end)
1538 {
1539 match block {
1540 BlockElement::Line {
1541 is_inlay: false,
1542 line,
1543 } => {
1544 let mut byte_index = 0;
1545 let mut row_index = 0;
1546 let mut column_index = 0;
1547 self.handle_event(
1548 cx,
1549 line_index,
1550 line,
1551 byte_index,
1552 Affinity::Before,
1553 origin_y,
1554 row_index,
1555 column_index,
1556 );
1557 for element in line.wrapped_elements() {
1558 match element {
1559 WrappedElement::Text {
1560 is_inlay: false,
1561 text,
1562 } => {
1563 for grapheme in text.graphemes() {
1564 self.handle_event(
1565 cx,
1566 line_index,
1567 line,
1568 byte_index,
1569 Affinity::After,
1570 origin_y,
1571 row_index,
1572 column_index,
1573 );
1574 byte_index += grapheme.len();
1575 column_index += grapheme.column_count();
1576 self.handle_event(
1577 cx,
1578 line_index,
1579 line,
1580 byte_index,
1581 Affinity::Before,
1582 origin_y,
1583 row_index,
1584 column_index,
1585 );
1586 }
1587 }
1588 WrappedElement::Text {
1589 is_inlay: true,
1590 text,
1591 } => {
1592 column_index += text.column_count();
1593 }
1594 WrappedElement::Widget(widget) => {
1595 column_index += widget.column_count;
1596 }
1597 WrappedElement::Wrap => {
1598 if self.active_decoration.is_some() {
1599 self.draw_decoration(
1600 cx,
1601 line,
1602 origin_y,
1603 row_index,
1604 column_index,
1605 );
1606 }
1607 column_index = line.wrap_indent_column_count();
1608 row_index += 1;
1609 }
1610 }
1611 }
1612 self.handle_event(
1613 cx,
1614 line_index,
1615 line,
1616 byte_index,
1617 Affinity::After,
1618 origin_y,
1619 row_index,
1620 column_index,
1621 );
1622 if self.active_decoration.is_some() {
1623 self.draw_decoration(cx, line, origin_y, row_index, column_index);
1624 }
1625 line_index += 1;
1626 origin_y += line.height();
1627 }
1628 BlockElement::Line {
1629 is_inlay: true,
1630 line,
1631 } => {
1632 origin_y += line.height();
1633 }
1634 BlockElement::Widget(widget) => {
1635 origin_y += widget.height;
1636 }
1637 }
1638 }
1639 }
1640
1641 fn handle_event(
1642 &mut self,
1643 cx: &mut Cx2d,
1644 line_index: usize,
1645 line: Line<'_>,
1646 byte_index: usize,
1647 affinity: Affinity,
1648 origin_y: f64,
1649 row_index: usize,
1650 column_index: usize,
1651 ) {
1652 let position = Position {
1653 line_index,
1654 byte_index,
1655 };
1656 if self.active_decoration.as_ref().map_or(false, |decoration| {
1657 decoration.decoration.end() == position && affinity == Affinity::Before
1658 }) {
1659 self.draw_decoration(cx, line, origin_y, row_index, column_index);
1660 self.active_decoration = None;
1661 }
1662 if self
1663 .decorations
1664 .as_slice()
1665 .first()
1666 .map_or(false, |decoration| {
1667 decoration.start() == position && affinity == Affinity::After
1668 })
1669 {
1670 let decoration = *self.decorations.next().unwrap();
1671 if !decoration.is_empty() {
1672 let (start_x, _) = line.grid_to_normalized_position(row_index, column_index);
1673 self.active_decoration = Some(ActiveDecoration {
1674 decoration,
1675 start_x,
1676 });
1677 }
1678 }
1679 }
1680
1681 fn draw_decoration(
1682 &mut self,
1683 cx: &mut Cx2d,
1684 line: Line<'_>,
1685 origin_y: f64,
1686 row_index: usize,
1687 column_index: usize,
1688 ) {
1689 let start_x = mem::take(&mut self.active_decoration.as_mut().unwrap().start_x);
1690 let (x, y) = line.grid_to_normalized_position(row_index, column_index);
1691 self.code_editor.draw_decoration.color =
1692 match self.active_decoration.as_mut().unwrap().decoration.ty {
1693 DecorationType::Warning => self.code_editor.token_colors.warning_decoration,
1694 DecorationType::Error => self.code_editor.token_colors.error_decoration,
1695 };
1696
1697 self.code_editor.draw_decoration.draw_abs(
1698 cx,
1699 Rect {
1700 pos: DVec2 {
1701 x: start_x,
1702 y: origin_y + y,
1703 } * self.code_editor.cell_size
1704 + self.code_editor.viewport_rect.pos,
1705 size: DVec2 {
1706 x: x - start_x,
1707 y: line.scale(),
1708 } * self.code_editor.cell_size,
1709 },
1710 );
1711 }
1712}
1713
1714struct ActiveDecoration {
1715 decoration: Decoration,
1716 start_x: f64,
1717}
1718
1719struct DrawSelectionLayer<'a> {
1720 code_editor: &'a mut CodeEditor,
1721 active_selection: Option<ActiveSelection>,
1722 selections: Iter<'a, Selection>,
1723}
1724
1725impl<'a> DrawSelectionLayer<'a> {
1726 fn draw_selection_layer(&mut self, cx: &mut Cx2d, session: &CodeSession) {
1727 let mut line_index = self.code_editor.line_start;
1728 let mut origin_y = session.layout().line(line_index).y();
1729 for block in session
1730 .layout()
1731 .block_elements(self.code_editor.line_start, self.code_editor.line_end)
1732 {
1733 match block {
1734 BlockElement::Line {
1735 is_inlay: false,
1736 line,
1737 } => {
1738 let mut byte_index = 0;
1739 let mut row_index = 0;
1740 let mut column_index = 0;
1741 self.draw_selection_event(
1742 cx,
1743 line_index,
1744 line,
1745 byte_index,
1746 Affinity::Before,
1747 origin_y,
1748 row_index,
1749 column_index,
1750 );
1751 for element in line.wrapped_elements() {
1752 match element {
1753 WrappedElement::Text {
1754 is_inlay: false,
1755 text,
1756 } => {
1757 for grapheme in text.graphemes() {
1758 self.draw_selection_event(
1759 cx,
1760 line_index,
1761 line,
1762 byte_index,
1763 Affinity::After,
1764 origin_y,
1765 row_index,
1766 column_index,
1767 );
1768 byte_index += grapheme.len();
1769 column_index += grapheme.column_count();
1770 self.draw_selection_event(
1771 cx,
1772 line_index,
1773 line,
1774 byte_index,
1775 Affinity::Before,
1776 origin_y,
1777 row_index,
1778 column_index,
1779 );
1780 }
1781 }
1782 WrappedElement::Text {
1783 is_inlay: true,
1784 text,
1785 } => {
1786 column_index += text.column_count();
1787 }
1788 WrappedElement::Widget(widget) => {
1789 column_index += widget.column_count;
1790 }
1791 WrappedElement::Wrap => {
1792 if self.active_selection.is_some() {
1793 self.draw_selection(
1794 cx,
1795 line,
1796 origin_y,
1797 row_index,
1798 column_index,
1799 );
1800 }
1801 column_index = line.wrap_indent_column_count();
1802 row_index += 1;
1803 }
1804 }
1805 }
1806 self.draw_selection_event(
1807 cx,
1808 line_index,
1809 line,
1810 byte_index,
1811 Affinity::After,
1812 origin_y,
1813 row_index,
1814 column_index,
1815 );
1816 column_index += 1;
1817 if self.active_selection.is_some() {
1818 self.draw_selection(cx, line, origin_y, row_index, column_index);
1819 }
1820 line_index += 1;
1821 origin_y += line.height();
1822 }
1823 BlockElement::Line {
1824 is_inlay: true,
1825 line,
1826 } => {
1827 origin_y += line.height();
1828 }
1829 BlockElement::Widget(widget) => {
1830 origin_y += widget.height;
1831 }
1832 }
1833 }
1834 if self.active_selection.is_some() {
1835 self.code_editor.draw_selection.end(cx);
1836 }
1837 }
1838
1839 fn draw_selection_event(
1840 &mut self,
1841 cx: &mut Cx2d,
1842 line_index: usize,
1843 line: Line<'_>,
1844 byte_index: usize,
1845 affinity: Affinity,
1846 origin_y: f64,
1847 row_index: usize,
1848 column_index: usize,
1849 ) {
1850 let position = Position {
1851 line_index,
1852 byte_index,
1853 };
1854 if self.active_selection.as_ref().map_or(false, |selection| {
1855 selection.selection.end() == position && selection.selection.end_affinity() == affinity
1856 }) {
1857 self.draw_selection(cx, line, origin_y, row_index, column_index);
1858 self.code_editor.draw_selection.end(cx);
1859 let selection = self.active_selection.take().unwrap().selection;
1860 if selection.cursor.position == position && selection.cursor.affinity == affinity {
1861 self.draw_cursor(cx, line, origin_y, row_index, column_index);
1862 }
1863 }
1864 if self
1865 .selections
1866 .as_slice()
1867 .first()
1868 .map_or(false, |selection| {
1869 selection.start() == position && selection.start_affinity() == affinity
1870 })
1871 {
1872 let selection = *self.selections.next().unwrap();
1873 if selection.cursor.position == position && selection.cursor.affinity == affinity {
1874 self.draw_cursor_bg(cx, line, origin_y, row_index, column_index);
1875 self.draw_cursor(cx, line, origin_y, row_index, column_index);
1876 }
1877 if !selection.is_empty() {
1878 let (start_x, _) = line.grid_to_normalized_position(row_index, column_index);
1879 self.active_selection = Some(ActiveSelection { selection, start_x });
1880 }
1881 self.code_editor.draw_selection.begin();
1882 }
1883 }
1884
1885 fn draw_selection(
1886 &mut self,
1887 cx: &mut Cx2d,
1888 line: Line<'_>,
1889 origin_y: f64,
1890 row_index: usize,
1891 column_index: usize,
1892 ) {
1893 let start_x = mem::take(&mut self.active_selection.as_mut().unwrap().start_x);
1894 let (x, y) = line.grid_to_normalized_position(row_index, column_index);
1895 if start_x == x {
1896 return;
1897 }
1898 self.code_editor.draw_selection.draw(
1899 cx,
1900 Rect {
1901 pos: DVec2 {
1902 x: start_x,
1903 y: origin_y + y,
1904 } * self.code_editor.cell_size
1905 + self.code_editor.viewport_rect.pos,
1906 size: DVec2 {
1907 x: x - start_x,
1908 y: line.scale(),
1909 } * self.code_editor.cell_size,
1910 },
1911 );
1912 }
1913
1914 fn draw_cursor(
1915 &mut self,
1916 cx: &mut Cx2d,
1917 line: Line,
1918 origin_y: f64,
1919 row_index: usize,
1920 column_index: usize,
1921 ) {
1922 let (x, y) = line.grid_to_normalized_position(row_index, column_index);
1923
1924 self.code_editor.draw_cursor.draw_abs(
1925 cx,
1926 Rect {
1927 pos: DVec2 { x, y: origin_y + y } * self.code_editor.cell_size
1928 + self.code_editor.viewport_rect.pos,
1929 size: DVec2 {
1930 x: 2.0,
1931 y: line.scale() * self.code_editor.cell_size.y,
1932 },
1933 },
1934 );
1935 }
1936
1937 fn draw_cursor_bg(
1938 &mut self,
1939 cx: &mut Cx2d,
1940 line: Line<'_>,
1941 origin_y: f64,
1942 row_index: usize,
1943 column_index: usize,
1944 ) {
1945 let (_x, y) = line.grid_to_normalized_position(row_index, column_index);
1946
1947 self.code_editor.draw_cursor_bg.draw_abs(
1948 cx,
1949 Rect {
1950 pos: DVec2 {
1951 x: self.code_editor.unscrolled_rect.pos.x,
1952 y: (origin_y + y) * self.code_editor.cell_size.y
1953 + self.code_editor.viewport_rect.pos.y,
1954 },
1955 size: DVec2 {
1956 x: self.code_editor.unscrolled_rect.size.x,
1957 y: line.scale() * self.code_editor.cell_size.y,
1958 },
1959 },
1960 );
1961 }
1962}
1963
1964struct ActiveSelection {
1965 selection: Selection,
1966 start_x: f64,
1967}
1968
1969#[derive(Live, LiveHook, LiveRegister)]
1970struct TokenColors {
1971 #[live]
1972 unknown: Vec4,
1973 #[live]
1974 branch_keyword: Vec4,
1975 #[live]
1976 comment: Vec4,
1977 #[live]
1978 constant: Vec4,
1979 #[live]
1980 delimiter: Vec4,
1981 #[live]
1982 delimiter_highlight: Vec4,
1983 #[live]
1984 identifier: Vec4,
1985 #[live]
1986 loop_keyword: Vec4,
1987 #[live]
1988 number: Vec4,
1989 #[live]
1990 other_keyword: Vec4,
1991 #[live]
1992 function: Vec4,
1993 #[live]
1994 punctuator: Vec4,
1995 #[live]
1996 string: Vec4,
1997 #[live]
1998 typename: Vec4,
1999 #[live]
2000 whitespace: Vec4,
2001 #[live]
2002 error_decoration: Vec4,
2003 #[live]
2004 warning_decoration: Vec4,
2005}
2006
2007#[derive(Live, LiveHook, LiveRegister)]
2008#[repr(C)]
2009pub struct DrawIndentGuide {
2010 #[deref]
2011 draw_super: DrawQuad,
2012 #[live]
2013 color: Vec4,
2014}
2015
2016#[derive(Live, LiveHook, LiveRegister)]
2017struct DrawDecoration {
2018 #[deref]
2019 draw_super: DrawQuad,
2020 #[live]
2021 color: Vec4,
2022}
2023
2024#[derive(Live, LiveHook, LiveRegister)]
2025#[repr(C)]
2026struct DrawSelection {
2027 #[deref]
2028 draw_super: DrawQuad,
2029 #[live]
2030 prev_x: f32,
2031 #[live]
2032 prev_w: f32,
2033 #[live]
2034 next_x: f32,
2035 #[live]
2036 next_w: f32,
2037 #[rust]
2038 prev_prev_rect: Option<Rect>,
2039 #[rust]
2040 prev_rect: Option<Rect>,
2041}
2042
2043impl DrawSelection {
2044 fn begin(&mut self) {
2045 debug_assert!(self.prev_rect.is_none());
2046 }
2047
2048 fn end(&mut self, cx: &mut Cx2d) {
2049 self.draw_rect_internal(cx, None);
2050 self.prev_prev_rect = None;
2051 self.prev_rect = None;
2052 }
2053
2054 fn draw(&mut self, cx: &mut Cx2d, rect: Rect) {
2055 self.draw_rect_internal(cx, Some(rect));
2056 self.prev_prev_rect = self.prev_rect;
2057 self.prev_rect = Some(rect);
2058 }
2059
2060 fn draw_rect_internal(&mut self, cx: &mut Cx2d, rect: Option<Rect>) {
2061 if let Some(prev_rect) = self.prev_rect {
2062 if let Some(prev_prev_rect) = self.prev_prev_rect {
2063 self.prev_x = prev_prev_rect.pos.x as f32;
2064 self.prev_w = prev_prev_rect.size.x as f32;
2065 } else {
2066 self.prev_x = 0.0;
2067 self.prev_w = 0.0;
2068 }
2069 if let Some(rect) = rect {
2070 self.next_x = rect.pos.x as f32;
2071 self.next_w = rect.size.x as f32;
2072 } else {
2073 self.next_x = 0.0;
2074 self.next_w = 0.0;
2075 }
2076 self.draw_abs(cx, prev_rect);
2077 }
2078 }
2079}