1use std::cell::RefCell;
2use std::rc::Rc;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5use sequoia_buffer::{SpanAttr, StyledSpan};
6use sequoia_core::{Color, Edges, Length, Rect, Size, Vec2};
7use sequoia_input::{
8 CursorIcon, Event, EventPhase, ImeEvent, Key, KeyEvent, Modifiers, PointerButton, PointerEvent,
9};
10use sequoia_layout::{LayoutCtx, LayoutStyle};
11use sequoia_reactive::Signal;
12use sequoia_style::{TextAreaStyle, Theme, VisualState};
13use sequoia_view::{AnyView, EventCtx, FontWeight, PaintCtx, Unit, View, WrapMode};
14
15use crate::buffer::{BufferEngine, StyledDoc};
16use crate::clipboard::SharedClipboard;
17
18#[derive(Clone)]
19pub(crate) enum DocSource {
20 Plain(Signal<String>),
21 Styled(Signal<StyledDoc>),
22}
23
24impl DocSource {
25 fn ensure(&self, engine: &mut BufferEngine) -> bool {
26 match self {
27 DocSource::Plain(s) => engine.ensure(s),
28 DocSource::Styled(s) => engine.ensure_styled(s),
29 }
30 }
31
32 fn commit(&self, engine: &mut BufferEngine) {
33 match self {
34 DocSource::Plain(s) => engine.commit(s),
35 DocSource::Styled(s) => engine.commit_styled(s),
36 }
37 }
38
39 fn get_text(&self) -> String {
40 match self {
41 DocSource::Plain(s) => s.get_untracked(),
42 DocSource::Styled(s) => s.get_untracked().text,
43 }
44 }
45
46 fn set_text(&self, text: String) {
47 match self {
48 DocSource::Plain(s) => s.set(text),
49
50 DocSource::Styled(s) => s.set(StyledDoc {
51 text,
52 spans: Vec::new(),
53 }),
54 }
55 }
56
57 fn track(&self) {
58 match self {
59 DocSource::Plain(s) => {
60 let _ = s.get();
61 }
62 DocSource::Styled(s) => {
63 let _ = s.get();
64 }
65 }
66 }
67}
68
69#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
70struct Caret {
71 offset: usize,
72 anchor: Option<usize>,
73}
74
75impl Caret {
76 fn collapsed(pos: usize) -> Self {
77 Self {
78 offset: pos,
79 anchor: None,
80 }
81 }
82
83 fn ends(&self) -> (usize, usize) {
84 match self.anchor {
85 Some(a) => (a.min(self.offset), a.max(self.offset)),
86 None => (self.offset, self.offset),
87 }
88 }
89
90 fn selected(&self) -> Option<(usize, usize)> {
91 let (a, b) = self.ends();
92 if a < b { Some((a, b)) } else { None }
93 }
94
95 fn move_to(&mut self, pos: usize, extend: bool) {
96 if extend {
97 let anchor = self.anchor.unwrap_or(self.offset);
98 self.anchor = Some(anchor);
99 self.offset = pos;
100 if self.anchor == Some(self.offset) {
101 self.anchor = None;
102 }
103 } else {
104 self.offset = pos;
105 self.anchor = None;
106 }
107 }
108}
109
110fn now_ms() -> u128 {
111 SystemTime::now()
112 .duration_since(UNIX_EPOCH)
113 .map(|d| d.as_millis())
114 .unwrap_or(0)
115}
116
117pub struct TextArea {
118 source: DocSource,
119 placeholder: String,
120 max_length: Option<usize>,
121 max_lines: Option<usize>,
122 min_lines: usize,
123 font_size: Option<f32>,
124 weight: Option<FontWeight>,
125 text_color: Option<Color>,
126 placeholder_color: Option<Color>,
127 background: Option<Color>,
128 border_color: Option<Color>,
129 border_width: Option<f32>,
130 radius: Option<f32>,
131 line_height_factor: Option<f32>,
132 current_line: Option<Color>,
133 highlight_current_line: bool,
134 pad_x: f32,
135 pad_y: f32,
136 indent: String,
137 auto_indent: bool,
138 editable: bool,
139 soft_wrap: bool,
140 wrap_mode: WrapMode,
141 disabled: bool,
142 error: bool,
143 accept_tab: bool,
144 appearance: Option<TextAreaStyle>,
145 style: LayoutStyle,
146 margin: Edges,
147
148 revision: Signal<u32>,
149
150 state: Rc<RefCell<AreaState>>,
151
152 on_edit_start: Option<Rc<dyn Fn()>>,
153 on_edit_end: Option<Rc<dyn Fn(String)>>,
154 on_scroll: Option<Rc<dyn Fn(Vec2)>>,
155}
156
157const DEFAULT_PAD_X: f32 = 6.0;
158const DEFAULT_PAD_Y: f32 = 6.0;
159const DEFAULT_LINE_HEIGHT: f32 = 1.35;
160const DEFAULT_INDENT: &str = " ";
161const DEFAULT_MIN_LINES: usize = 3;
162const CARET_W: f32 = 1.5;
163const SCROLL_MARGIN: f32 = 2.0;
164
165#[derive(Clone)]
166struct AreaState {
167 carets: Vec<Caret>,
168
169 goal_x: Option<f32>,
170 scroll: Vec2,
171 viewport: Size,
172
173 content: Size,
174 layout: Option<Rc<VisualLayout>>,
175 preedit: String,
176 preedit_cursor: Option<(usize, usize)>,
177 caret_rect: Option<Rect>,
178 was_focused: bool,
179 session_text: Option<String>,
180
181 escalate: u8,
182 escalate_base: usize,
183
184 last_click_ms: Option<u128>,
185 last_click_pos: usize,
186 click_count: u32,
187
188 alt_drag: Option<usize>,
189
190 engine: BufferEngine,
191}
192
193impl Default for AreaState {
194 fn default() -> Self {
195 Self {
196 carets: vec![Caret::default()],
197 goal_x: None,
198 scroll: Vec2::default(),
199 viewport: Size::default(),
200 content: Size::default(),
201 layout: None,
202 preedit: String::new(),
203 preedit_cursor: None,
204 caret_rect: None,
205 was_focused: false,
206 session_text: None,
207 escalate: 0,
208 escalate_base: 0,
209 last_click_ms: None,
210 last_click_pos: 0,
211 click_count: 0,
212 alt_drag: None,
213 engine: BufferEngine::new(),
214 }
215 }
216}
217
218fn merge_byte_ranges(ranges: &[(usize, usize)]) -> (Vec<(usize, usize)>, Vec<usize>) {
219 if ranges.is_empty() {
220 return (Vec::new(), Vec::new());
221 }
222 let mut order: Vec<usize> = (0..ranges.len()).collect();
223 order.sort_by_key(|&i| (ranges[i].0, ranges[i].1));
224 let mut merged: Vec<(usize, usize)> = Vec::new();
225 let mut owner = vec![0usize; ranges.len()];
226 for i in order {
227 let (s, e) = ranges[i];
228 if let Some(last) = merged.last_mut()
229 && s <= last.1
230 {
231 last.1 = last.1.max(e);
232 owner[i] = merged.len() - 1;
233 continue;
234 }
235 owner[i] = merged.len();
236 merged.push((s, e));
237 }
238 (merged, owner)
239}
240
241fn map_through_deletes(unions: &[(usize, usize)], x: usize) -> usize {
242 let mut sub = 0usize;
243 for (s, e) in unions {
244 if *e <= x {
245 sub += e - s;
246 } else {
247 break;
248 }
249 }
250 x.saturating_sub(sub)
251}
252
253fn dedupe_carets(carets: &mut Vec<Caret>) {
254 let mut seen = std::collections::HashSet::new();
255 carets.retain(|c| seen.insert(c.offset));
256 if carets.is_empty() {
257 carets.push(Caret::default());
258 }
259}
260
261fn is_word_char(c: char) -> bool {
262 c.is_alphanumeric() || c == '_'
263}
264
265#[derive(Clone, Copy, Debug, PartialEq, Eq)]
266#[non_exhaustive]
267pub(crate) enum StyleFlag {
268 Bold,
269 Italic,
270 Underline,
271 Strike,
272}
273
274impl StyleFlag {
275 fn get(self, a: &SpanAttr) -> bool {
276 match self {
277 StyleFlag::Bold => a.bold,
278 StyleFlag::Italic => a.italic,
279 StyleFlag::Underline => a.underline,
280 StyleFlag::Strike => a.strike,
281 }
282 }
283
284 fn set(self, a: &mut SpanAttr, v: bool) {
285 match self {
286 StyleFlag::Bold => a.bold = v,
287 StyleFlag::Italic => a.italic = v,
288 StyleFlag::Underline => a.underline = v,
289 StyleFlag::Strike => a.strike = v,
290 }
291 }
292}
293
294fn line_starts(chars: &[char]) -> Vec<usize> {
295 let mut starts = vec![0usize];
296 for (i, &c) in chars.iter().enumerate() {
297 if c == '\n' {
298 starts.push(i + 1);
299 }
300 }
301 starts
302}
303
304#[cfg(test)]
305fn line_len(chars: &[char], starts: &[usize], line: usize) -> usize {
306 let start = starts.get(line).copied().unwrap_or(chars.len());
307 match starts.get(line + 1) {
308 Some(&next) => next.saturating_sub(start + 1),
309 None => chars.len().saturating_sub(start),
310 }
311}
312
313fn line_col_of(chars: &[char], starts: &[usize], caret: usize) -> (usize, usize) {
314 let caret = caret.min(chars.len());
315 let line = starts.partition_point(|&s| s <= caret).saturating_sub(1);
316 let line = line.min(starts.len().saturating_sub(1));
317 (line, caret - starts[line])
318}
319
320#[cfg(test)]
321fn caret_of_line_col(chars: &[char], starts: &[usize], line: usize, col: usize) -> usize {
322 let line = line.min(starts.len().saturating_sub(1));
323 starts[line] + col.min(line_len(chars, starts, line))
324}
325
326fn split_lines(text: &str) -> Vec<&str> {
327 text.split('\n').collect()
328}
329
330#[derive(Clone, Copy, Debug, PartialEq, Eq)]
332struct RowSpan {
333 line: usize,
334 start: usize,
335 end: usize,
336}
337
338#[derive(Clone, Debug)]
339struct VisualLayout {
340 text: String,
341 width: f32,
342 size: f32,
343 weight: FontWeight,
344 wrap: bool,
345 mode: WrapMode,
346 lines: Vec<String>,
347 line_starts: Vec<usize>,
348 rows: Vec<RowSpan>,
349}
350
351trait LineMeasure {
352 fn measure_positions(&mut self, text: &str, size: f32, weight: FontWeight) -> Vec<f32>;
353}
354
355impl LineMeasure for EventCtx<'_> {
356 fn measure_positions(&mut self, text: &str, size: f32, weight: FontWeight) -> Vec<f32> {
357 self.caret_positions(text, size, weight)
358 }
359}
360
361impl LineMeasure for PaintCtx<'_> {
362 fn measure_positions(&mut self, text: &str, size: f32, weight: FontWeight) -> Vec<f32> {
363 self.caret_positions(text, size, weight)
364 }
365}
366
367fn build_layout<M: LineMeasure>(
368 m: &mut M,
369 text: String,
370 width: f32,
371 size: f32,
372 weight: FontWeight,
373 wrap: bool,
374 mode: WrapMode,
375) -> VisualLayout {
376 let lines: Vec<String> = text.split('\n').map(|s| s.to_string()).collect();
377 let mut line_starts = Vec::with_capacity(lines.len());
378 let mut acc = 0usize;
379 for line in &lines {
380 line_starts.push(acc);
381 acc += line.chars().count() + 1;
382 }
383
384 let mut rows: Vec<RowSpan> = Vec::with_capacity(lines.len());
385 for (li, line) in lines.iter().enumerate() {
386 let line_start = line_starts[li];
387 let lchars: Vec<char> = line.chars().collect();
388 let count = lchars.len();
389 if !wrap || width <= 0.0 || count == 0 {
390 rows.push(RowSpan {
391 line: li,
392 start: line_start,
393 end: line_start + count,
394 });
395 continue;
396 }
397 let pos = m.measure_positions(line, size, weight);
398 let full = pos.get(count).copied().unwrap_or(0.0);
399 let x_at = |i: usize| pos.get(i).copied().unwrap_or(full);
400 if full <= width {
401 rows.push(RowSpan {
402 line: li,
403 start: line_start,
404 end: line_start + count,
405 });
406 continue;
407 }
408
409 let mut row_start = 0usize;
410 let mut last_break: Option<usize> = None;
411 let mut i = 0usize;
412 while i < count {
413 let row_w = x_at(i + 1) - x_at(row_start);
414 if row_w > width && i > row_start {
415 let break_at = match last_break {
416 Some(b) if matches!(mode, WrapMode::Word) && b > row_start => b,
417 _ => i,
418 };
419 rows.push(RowSpan {
420 line: li,
421 start: line_start + row_start,
422 end: line_start + break_at,
423 });
424 row_start = if break_at == i {
425 i
426 } else {
427 (break_at + 1).min(count)
428 };
429 last_break = None;
430 i = row_start;
431 continue;
432 }
433 if lchars[i] == ' ' {
434 last_break = Some(i);
435 }
436 i += 1;
437 }
438 if row_start < count {
439 rows.push(RowSpan {
440 line: li,
441 start: line_start + row_start,
442 end: line_start + count,
443 });
444 }
445 }
446 if rows.is_empty() {
447 rows.push(RowSpan {
448 line: 0,
449 start: 0,
450 end: 0,
451 });
452 }
453
454 VisualLayout {
455 text,
456 width,
457 size,
458 weight,
459 wrap,
460 mode,
461 lines,
462 line_starts,
463 rows,
464 }
465}
466
467impl VisualLayout {
468 fn row_text(&self, row: &RowSpan) -> String {
469 let base = self.line_starts[row.line];
470 self.lines[row.line]
471 .chars()
472 .skip(row.start - base)
473 .take(row.end - row.start)
474 .collect()
475 }
476
477 fn row_of(&self, caret: usize) -> usize {
478 let mut last = 0usize;
479 for (i, row) in self.rows.iter().enumerate() {
480 last = i;
481 if caret <= row.end {
482 return i;
483 }
484 }
485 last
486 }
487}
488
489fn row_prefix_x<M: LineMeasure>(
490 m: &mut M,
491 layout: &VisualLayout,
492 row: &RowSpan,
493 col: usize,
494 size: f32,
495 weight: FontWeight,
496) -> f32 {
497 let base = row.start - layout.line_starts[row.line];
498 let pos = m.measure_positions(&layout.lines[row.line], size, weight);
499 let x0 = pos.get(base).copied().unwrap_or(0.0);
500 let x1 = pos
501 .get(base + col.min(row.end - row.start))
502 .copied()
503 .unwrap_or(x0);
504 x1 - x0
505}
506
507fn leading_indent_len(line: &str) -> usize {
508 line.chars().take_while(|&c| c == ' ' || c == '\t').count()
509}
510
511fn sanitize_multiline(s: &str) -> String {
512 let mut out = String::with_capacity(s.len());
513 let mut chars = s.chars().peekable();
514 while let Some(c) = chars.next() {
515 match c {
516 '\r' => {
517 out.push('\n');
518 if chars.peek() == Some(&'\n') {
519 chars.next();
520 }
521 }
522 '\n' | '\t' => out.push(c),
523 c if c.is_control() => {}
524 c => out.push(c),
525 }
526 }
527 out
528}
529
530impl TextArea {
531 pub fn new(value: Signal<String>) -> Self {
532 Self::with_source(DocSource::Plain(value))
533 }
534
535 pub fn new_styled(value: Signal<StyledDoc>) -> Self {
536 Self::with_source(DocSource::Styled(value))
537 }
538
539 fn with_source(source: DocSource) -> Self {
540 Self {
541 source,
542 placeholder: String::new(),
543 max_length: None,
544 max_lines: None,
545 min_lines: DEFAULT_MIN_LINES,
546 font_size: None,
547 weight: None,
548 text_color: None,
549 placeholder_color: None,
550 background: None,
551 border_color: None,
552 border_width: None,
553 radius: None,
554 line_height_factor: None,
555 current_line: None,
556 highlight_current_line: false,
557 pad_x: DEFAULT_PAD_X,
558 pad_y: DEFAULT_PAD_Y,
559 indent: DEFAULT_INDENT.to_owned(),
560 auto_indent: true,
561 editable: true,
562 soft_wrap: false,
563 wrap_mode: WrapMode::Word,
564 disabled: false,
565 error: false,
566 accept_tab: true,
567 appearance: None,
568 style: LayoutStyle {
569 width: Length::Fill(1.0),
570 height: Length::Fill(1.0),
571 ..LayoutStyle::default()
572 },
573 margin: Edges::ZERO,
574 revision: Signal::new(0),
575 state: Rc::new(RefCell::new(AreaState::default())),
576 on_edit_start: None,
577 on_edit_end: None,
578 on_scroll: None,
579 }
580 }
581
582 pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
583 self.placeholder = placeholder.into();
584 self
585 }
586
587 pub fn max_length(mut self, max: usize) -> Self {
588 self.max_length = Some(max);
589 self
590 }
591
592 pub fn max_lines(mut self, max: usize) -> Self {
593 self.max_lines = Some(max.max(1));
594 self
595 }
596
597 pub fn min_lines(mut self, min: usize) -> Self {
598 self.min_lines = min.max(1);
599 self
600 }
601
602 pub fn on_edit_start(mut self, f: impl Fn() + 'static) -> Self {
603 self.on_edit_start = Some(Rc::new(f));
604 self
605 }
606
607 pub fn on_edit_end(mut self, f: impl Fn(String) + 'static) -> Self {
608 self.on_edit_end = Some(Rc::new(f));
609 self
610 }
611
612 pub fn on_scroll(mut self, f: impl Fn(Vec2) + 'static) -> Self {
613 self.on_scroll = Some(Rc::new(f));
614 self
615 }
616
617 pub fn font_size(mut self, size: f32) -> Self {
618 self.font_size = Some(size);
619 self
620 }
621
622 pub fn weight(mut self, weight: FontWeight) -> Self {
623 self.weight = Some(weight);
624 self
625 }
626
627 pub fn bold(mut self, bold: bool) -> Self {
628 self.weight = Some(if bold {
629 FontWeight::BOLD
630 } else {
631 FontWeight::NORMAL
632 });
633 self
634 }
635
636 pub fn color(mut self, color: Color) -> Self {
637 self.text_color = Some(color);
638 self
639 }
640
641 pub fn placeholder_color(mut self, color: Color) -> Self {
642 self.placeholder_color = Some(color);
643 self
644 }
645
646 pub fn background(mut self, color: Color) -> Self {
647 self.background = Some(color);
648 self
649 }
650
651 pub fn border_color(mut self, color: Color) -> Self {
652 self.border_color = Some(color);
653 self
654 }
655
656 pub fn border_width(mut self, width: f32) -> Self {
657 self.border_width = Some(width.max(0.0));
658 self
659 }
660
661 pub fn border(mut self, width: f32, color: Color) -> Self {
662 self.border_width = Some(width.max(0.0));
663 self.border_color = Some(color);
664 self
665 }
666
667 pub fn radius(mut self, radius: f32) -> Self {
668 self.radius = Some(radius.max(0.0));
669 self
670 }
671
672 pub fn line_height_factor(mut self, factor: f32) -> Self {
673 self.line_height_factor = Some(factor);
674 self
675 }
676
677 pub fn current_line(mut self, color: Color) -> Self {
678 self.current_line = Some(color);
679 self.highlight_current_line = true;
680 self
681 }
682
683 pub fn highlight_current_line(mut self, on: bool) -> Self {
684 self.highlight_current_line = on;
685 self
686 }
687
688 pub fn padding(mut self, padding: impl Into<Edges>) -> Self {
689 let p = padding.into();
690 self.pad_x = (p.left + p.right) * 0.5;
691 self.pad_y = (p.top + p.bottom) * 0.5;
692 self
693 }
694
695 pub fn padding_xy(mut self, x: f32, y: f32) -> Self {
696 self.pad_x = x.max(0.0);
697 self.pad_y = y.max(0.0);
698 self
699 }
700
701 pub fn indent(mut self, indent: impl Into<String>) -> Self {
702 self.indent = indent.into();
703 self
704 }
705
706 pub fn auto_indent(mut self, on: bool) -> Self {
707 self.auto_indent = on;
708 self
709 }
710
711 pub fn editable(mut self, editable: bool) -> Self {
712 self.editable = editable;
713 self
714 }
715
716 pub fn readonly(mut self, readonly: bool) -> Self {
718 self.editable = !readonly;
719 self
720 }
721
722 pub fn soft_wrap(mut self, on: bool) -> Self {
724 self.soft_wrap = on;
725 self
726 }
727
728 pub fn wrap_mode(mut self, mode: WrapMode) -> Self {
729 self.wrap_mode = mode;
730 self
731 }
732
733 pub fn disabled(mut self, disabled: bool) -> Self {
734 self.disabled = disabled;
735 self
736 }
737
738 pub fn error(mut self, error: bool) -> Self {
740 self.error = error;
741 self
742 }
743
744 pub fn auto_size(mut self, auto: bool) -> Self {
746 self.style.height = if auto {
747 Length::Fit
748 } else {
749 Length::Fill(1.0)
750 };
751 self
752 }
753
754 pub fn accept_tab(mut self, on: bool) -> Self {
755 self.accept_tab = on;
756 self
757 }
758
759 pub fn appearance(mut self, style: TextAreaStyle) -> Self {
760 self.appearance = Some(style);
761 self
762 }
763
764 pub fn width(mut self, width: impl Into<Length>) -> Self {
765 self.style = self.style.width(width.into());
766 self
767 }
768
769 pub fn height(mut self, height: impl Into<Length>) -> Self {
770 self.style = self.style.height(height.into());
771 self
772 }
773
774 pub fn min_width(mut self, v: f32) -> Self {
775 self.style = self.style.min_width(v);
776 self
777 }
778
779 pub fn max_width(mut self, v: f32) -> Self {
780 self.style = self.style.max_width(v);
781 self
782 }
783
784 pub fn min_height(mut self, v: f32) -> Self {
785 self.style = self.style.min_height(v);
786 self
787 }
788
789 pub fn max_height(mut self, v: f32) -> Self {
790 self.style = self.style.max_height(v);
791 self
792 }
793
794 pub fn flex(mut self, weight: f32) -> Self {
795 self.style = self.style.flex(weight);
796 self
797 }
798
799 pub fn expand(self) -> Self {
800 self.flex(1.0)
801 }
802
803 pub fn margin(mut self, margin: impl Into<Edges>) -> Self {
804 let m = margin.into();
805 self.margin = Edges::only(
806 sanitize_margin(m.left),
807 sanitize_margin(m.top),
808 sanitize_margin(m.right),
809 sanitize_margin(m.bottom),
810 );
811 self
812 }
813
814 pub fn margin_xy(self, x: f32, y: f32) -> Self {
815 self.margin(Edges::xy(x, y))
816 }
817
818 pub fn margin_x(mut self, x: f32) -> Self {
819 let x = sanitize_margin(x);
820 self.margin.left = x;
821 self.margin.right = x;
822 self
823 }
824
825 pub fn margin_y(mut self, y: f32) -> Self {
826 let y = sanitize_margin(y);
827 self.margin.top = y;
828 self.margin.bottom = y;
829 self
830 }
831
832 pub fn margin_left(mut self, v: f32) -> Self {
833 self.margin.left = sanitize_margin(v);
834 self
835 }
836
837 pub fn margin_right(mut self, v: f32) -> Self {
838 self.margin.right = sanitize_margin(v);
839 self
840 }
841
842 pub fn margin_top(mut self, v: f32) -> Self {
843 self.margin.top = sanitize_margin(v);
844 self
845 }
846
847 pub fn margin_bottom(mut self, v: f32) -> Self {
848 self.margin.bottom = sanitize_margin(v);
849 self
850 }
851
852 fn with_state<R>(&self, f: impl FnOnce(&mut AreaState) -> R) -> R {
853 f(&mut self.state.borrow_mut())
854 }
855
856 fn main_mut(s: &mut AreaState) -> &mut Caret {
857 if s.carets.is_empty() {
858 s.carets.push(Caret::default());
859 }
860 &mut s.carets[0]
861 }
862
863 fn main_of(s: &AreaState) -> Caret {
864 s.carets.first().copied().unwrap_or_default()
865 }
866
867 fn reset_escalate(s: &mut AreaState) {
868 s.escalate = 0;
869 s.escalate_base = 0;
870 }
871
872 fn chars_via(source: &DocSource) -> Vec<char> {
873 source.get_text().chars().collect()
874 }
875
876 fn chars(&self) -> Vec<char> {
877 Self::chars_via(&self.source)
878 }
879
880 fn layout_for<M: LineMeasure>(
881 &self,
882 m: &mut M,
883 width: f32,
884 size: f32,
885 weight: FontWeight,
886 ) -> Rc<VisualLayout> {
887 let text = self.source.get_text();
888 if let Some(layout) = self.with_state(|s| s.layout.clone())
889 && layout.text == text
890 && (layout.width - width).abs() < 0.5
891 && (layout.size - size).abs() < 0.01
892 && layout.weight == weight
893 && layout.wrap == self.soft_wrap
894 && layout.mode == self.wrap_mode
895 {
896 return layout;
897 }
898 let layout = Rc::new(build_layout(
899 m,
900 text,
901 width.max(0.0),
902 size,
903 weight,
904 self.soft_wrap,
905 self.wrap_mode,
906 ));
907 self.with_state(|s| s.layout = Some(layout.clone()));
908 layout
909 }
910
911 fn event_layout(&self, ctx: &mut EventCtx) -> Rc<VisualLayout> {
912 let theme = ctx.environment.get::<Theme>().unwrap_or_else(Theme::dark);
913 let size = self.resolved_font_size(&theme);
914 let weight = self.font_weight();
915 let width = (ctx.rect.size.width - self.pad_x * 2.0).max(0.0);
916 self.layout_for(ctx, width, size, weight)
917 }
918
919 fn starts(&self, chars: &[char]) -> Vec<usize> {
920 line_starts(chars)
921 }
922
923 pub fn caret(&self) -> usize {
924 self.with_state(|s| Self::main_of(s).offset)
925 }
926
927 pub fn caret_count(&self) -> usize {
928 self.with_state(|s| s.carets.len().max(1))
929 }
930
931 pub fn set_caret(&self, caret: usize) {
932 let source = self.source.clone();
933 self.with_state(|s| {
934 source.ensure(&mut s.engine);
935 let caret = caret.min(s.engine.len_chars());
936 s.carets = vec![Caret::collapsed(caret)];
937 Self::reset_escalate(s);
938 s.goal_x = None;
939 });
940 self.bump();
941 }
942
943 pub fn selection(&self) -> Option<(usize, usize)> {
944 self.with_state(|s| {
945 let m = Self::main_of(s);
946 m.anchor.map(|a| (a.min(m.offset), a.max(m.offset)))
947 })
948 }
949
950 pub fn selections(&self) -> Vec<(usize, usize)> {
951 self.with_state(|s| s.carets.iter().filter_map(|c| c.selected()).collect())
952 }
953
954 pub fn caret_line_col(&self) -> (usize, usize) {
955 let chars = self.chars();
956 let starts = self.starts(&chars);
957 let caret = self.caret();
958 line_col_of(&chars, &starts, caret)
959 }
960
961 pub fn line_count(&self) -> usize {
962 let chars = self.chars();
963 self.starts(&chars).len()
964 }
965
966 pub fn scroll_offset(&self) -> Vec2 {
967 self.with_state(|s| s.scroll)
968 }
969
970 pub fn scroll_to(&self, offset: Vec2) {
971 let (viewport, content) = self.with_state(|s| (s.viewport, s.content));
972 let next = clamp_scroll(offset, content.width, content.height, viewport, self.pad());
973 let changed = self.with_state(|s| {
974 if s.scroll != next {
975 s.scroll = next;
976 true
977 } else {
978 false
979 }
980 });
981 if changed {
982 if let Some(f) = &self.on_scroll {
983 f(next);
984 }
985 self.bump();
986 }
987 }
988
989 fn pad(&self) -> (f32, f32) {
990 (self.pad_x, self.pad_y)
991 }
992
993 fn begin_session(&self) {
994 let cur = self.source.get_text();
995 self.with_state(|s| {
996 s.session_text = Some(cur);
997 s.was_focused = true;
998 });
999 if let Some(f) = &self.on_edit_start {
1000 f();
1001 }
1002 }
1003
1004 fn note_focus(&self, focused: bool) {
1005 enum Trans {
1006 Start,
1007 End(String),
1008 None,
1009 }
1010 let cur = self.source.get_text();
1011 let trans = self.with_state(|s| {
1012 let was = s.was_focused;
1013 s.was_focused = focused;
1014 if !was && focused {
1015 s.session_text = Some(cur.clone());
1016 Trans::Start
1017 } else if was && !focused {
1018 match s.session_text.take() {
1019 Some(before) if before != cur => Trans::End(cur.clone()),
1020 _ => Trans::None,
1021 }
1022 } else {
1023 Trans::None
1024 }
1025 });
1026 match trans {
1027 Trans::Start => {
1028 if let Some(f) = &self.on_edit_start {
1029 f();
1030 }
1031 }
1032 Trans::End(text) => {
1033 if let Some(f) = &self.on_edit_end {
1034 f(text);
1035 }
1036 }
1037 Trans::None => {}
1038 }
1039 }
1040
1041 fn set_caret_extend(&self, caret: usize, extend: bool) {
1042 let source = self.source.clone();
1043 self.with_state(|s| {
1044 source.ensure(&mut s.engine);
1045 let caret = caret.min(s.engine.len_chars());
1046 let main = Self::main_of(s);
1047 s.carets = vec![main];
1048 Self::main_mut(s).move_to(caret, extend);
1049 Self::reset_escalate(s);
1050 s.goal_x = None;
1051 });
1052 }
1053
1054 fn bump(&self) {
1055 self.revision
1056 .set(self.revision.get_untracked().wrapping_add(1));
1057 }
1058
1059 fn font_weight(&self) -> FontWeight {
1060 self.weight
1061 .or_else(|| {
1062 self.appearance
1063 .and_then(|s| s.font_weight)
1064 .map(FontWeight::new)
1065 })
1066 .unwrap_or_default()
1067 }
1068
1069 fn resolved_font_size(&self, theme: &Theme) -> f32 {
1070 self.font_size
1071 .or_else(|| self.appearance.and_then(|s| s.font_size))
1072 .unwrap_or(theme.font_size)
1073 }
1074
1075 fn line_height(&self) -> f32 {
1076 let size = self
1077 .font_size
1078 .or_else(|| self.appearance.and_then(|s| s.font_size))
1079 .unwrap_or(14.0);
1080 size * self
1081 .line_height_factor
1082 .or_else(|| self.appearance.and_then(|s| s.line_height))
1083 .unwrap_or(DEFAULT_LINE_HEIGHT)
1084 .clamp(1.0, 3.0)
1085 }
1086
1087 fn resolved_line_height(&self, theme: &Theme) -> f32 {
1088 self.resolved_font_size(theme)
1089 * self
1090 .line_height_factor
1091 .or_else(|| self.appearance.and_then(|s| s.line_height))
1092 .unwrap_or(DEFAULT_LINE_HEIGHT)
1093 .clamp(1.0, 3.0)
1094 }
1095
1096 fn resolved_text_color(&self, theme: &Theme) -> Color {
1097 self.text_color
1098 .or_else(|| self.appearance.and_then(|s| s.text))
1099 .unwrap_or(theme.text)
1100 }
1101
1102 fn resolved_placeholder_color(&self, theme: &Theme) -> Color {
1103 self.placeholder_color
1104 .or_else(|| self.appearance.and_then(|s| s.placeholder))
1105 .unwrap_or(theme.text_secondary)
1106 }
1107
1108 fn resolved_background(&self, theme: &Theme) -> Color {
1109 self.background
1110 .or_else(|| self.appearance.and_then(|s| s.background))
1111 .unwrap_or(theme.surface)
1112 }
1113
1114 fn resolved_border(&self, theme: &Theme) -> Option<(f32, Color)> {
1115 let width = self
1116 .border_width
1117 .or_else(|| self.appearance.and_then(|s| s.border_width))
1118 .unwrap_or(1.0)
1119 .max(0.0);
1120 if width <= 0.0 {
1121 return None;
1122 }
1123 let color = self
1124 .border_color
1125 .or_else(|| self.appearance.and_then(|s| s.border))
1126 .unwrap_or(theme.border);
1127 if color.a <= 0.0 {
1128 return None;
1129 }
1130 Some((width, color))
1131 }
1132
1133 fn resolved_radius(&self, theme: &Theme) -> f32 {
1134 self.radius
1135 .or_else(|| self.appearance.and_then(|s| s.radius))
1136 .unwrap_or(theme.radius)
1137 .max(0.0)
1138 }
1139
1140 fn delete_selection(&self) -> bool {
1141 let source = self.source.clone();
1142 let mut deleted = false;
1143 self.with_state(|s| {
1144 source.ensure(&mut s.engine);
1145 let len = s.engine.len_chars();
1146
1147 if s.carets.len() <= 1 {
1148 let m = Self::main_of(s);
1149 let Some((mut a, mut b)) = m.anchor.map(|x| (x.min(m.offset), x.max(m.offset)))
1150 else {
1151 return;
1152 };
1153 if a == b {
1154 Self::main_mut(s).anchor = None;
1155 return;
1156 }
1157 (a, b) = (a.min(len), b.min(len));
1158 let (ab, bb) = (s.engine.to_bytes(a), s.engine.to_bytes(b));
1159 s.engine.delete(ab..bb);
1160 source.commit(&mut s.engine);
1161 let offset = s.engine.to_chars(ab);
1162 let main = Self::main_mut(s);
1163 main.offset = offset;
1164 main.anchor = None;
1165 s.goal_x = None;
1166 Self::reset_escalate(s);
1167 deleted = true;
1168 return;
1169 }
1170
1171 let mut pre: Vec<(usize, usize, usize)> = Vec::new();
1172 for (i, c) in s.carets.iter().enumerate() {
1173 if let Some((a, b)) = c.selected() {
1174 let (a, b) = (a.min(len), b.min(len));
1175 if a < b {
1176 pre.push((i, s.engine.to_bytes(a), s.engine.to_bytes(b)));
1177 }
1178 } else if c.anchor.is_some() {
1179 }
1180 }
1181 for c in s.carets.iter_mut() {
1182 if c.selected().is_none() {
1183 c.anchor = None;
1184 }
1185 }
1186 if pre.is_empty() {
1187 return;
1188 }
1189 let raw: Vec<(usize, usize)> = pre.iter().map(|&(_, a, b)| (a, b)).collect();
1190 let (merged, owner) = merge_byte_ranges(&raw);
1191 let ranges: Vec<std::ops::Range<usize>> = merged.iter().map(|&(a, b)| a..b).collect();
1192 s.engine.delete_many(ranges);
1193 source.commit(&mut s.engine);
1194 let new_len = s.engine.len_chars();
1195
1196 let mut new_offsets = vec![0usize; s.carets.len()];
1197 let mut touched = vec![false; s.carets.len()];
1198 for (k, &(i, _, _)) in pre.iter().enumerate() {
1199 let (us, _) = merged[owner[k]];
1200 let mapped = map_through_deletes(&merged, us);
1201 new_offsets[i] = s.engine.to_chars(mapped).min(new_len);
1202 touched[i] = true;
1203 }
1204 for (i, c) in s.carets.iter_mut().enumerate() {
1205 if touched[i] {
1206 c.offset = new_offsets[i];
1207 c.anchor = None;
1208 }
1209 }
1210
1211 dedupe_carets(&mut s.carets);
1212 s.goal_x = None;
1213 Self::reset_escalate(s);
1214 deleted = true;
1215 });
1216 if deleted {
1217 self.bump();
1218 }
1219 deleted
1220 }
1221
1222 pub fn selected_text(&self) -> Option<String> {
1223 let source = self.source.clone();
1224 self.with_state(|s| {
1225 source.ensure(&mut s.engine);
1226 let len = s.engine.len_chars();
1227 let mut parts = Vec::new();
1228 for c in &s.carets {
1229 if let Some((a, b)) = c.selected() {
1230 let (a, b) = (a.min(len), b.min(len));
1231 if a < b {
1232 let text = s
1233 .engine
1234 .buffer()
1235 .substring(s.engine.to_bytes(a)..s.engine.to_bytes(b));
1236 if !text.is_empty() {
1237 parts.push(text);
1238 }
1239 }
1240 }
1241 }
1242 if parts.is_empty() {
1243 None
1244 } else {
1245 Some(parts.join("\n"))
1246 }
1247 })
1248 }
1249
1250 pub fn select_range(&self, a: usize, b: usize) {
1251 let source = self.source.clone();
1252 self.with_state(|s| {
1253 source.ensure(&mut s.engine);
1254 let len = s.engine.len_chars();
1255 let (a, b) = (a.min(len), b.min(len));
1256
1257 s.carets = vec![Caret {
1258 offset: b,
1259 anchor: if a == b { None } else { Some(a) },
1260 }];
1261 Self::reset_escalate(s);
1262 s.goal_x = None;
1263 });
1264 self.bump();
1265 }
1266
1267 pub fn clear_selection(&self) -> bool {
1268 let had = self.with_state(|s| {
1269 let had = s.carets.iter().any(|c| c.anchor.is_some());
1270 for c in s.carets.iter_mut() {
1271 c.anchor = None;
1272 }
1273 Self::reset_escalate(s);
1274 had
1275 });
1276 if had {
1277 self.bump();
1278 }
1279 had
1280 }
1281
1282 pub fn select_all(&self) {
1283 let source = self.source.clone();
1284 self.with_state(|s| {
1285 source.ensure(&mut s.engine);
1286 let len = s.engine.len_chars();
1287 s.carets = vec![Caret {
1288 offset: len,
1289 anchor: Some(0),
1290 }];
1291 Self::reset_escalate(s);
1292 s.goal_x = None;
1293 });
1294 self.bump();
1295 }
1296
1297 fn insert_str(&self, text: &str) {
1298 if !self.editable || text.is_empty() {
1299 return;
1300 }
1301 let source = self.source.clone();
1302 let single = self.with_state(|s| s.carets.len() <= 1);
1303 if single {
1304 self.with_state(|st| {
1305 source.ensure(&mut st.engine);
1306 let total = st.engine.len_chars();
1307
1308 let sel_bytes = match Self::main_of(st).anchor.map(|a| {
1309 (
1310 a.min(Self::main_of(st).offset),
1311 a.max(Self::main_of(st).offset),
1312 )
1313 }) {
1314 None => None,
1315 Some((a, b)) if a == b => {
1316 Self::main_mut(st).anchor = None;
1317 None
1318 }
1319 Some((a, b)) => {
1320 let (a, b) = (a.min(total), b.min(total));
1321 Some((st.engine.to_bytes(a), st.engine.to_bytes(b)))
1322 }
1323 };
1324
1325 let allowed = match self.max_length {
1326 Some(max) => max.saturating_sub(total),
1327 None => usize::MAX,
1328 };
1329 let ins = BufferEngine::fit_prefix(text, allowed);
1330 let caret_b = match sel_bytes {
1331 Some((ab, bb)) => {
1332 st.engine.replace(ab..bb, ins);
1333 ab + ins.len()
1334 }
1335 None => {
1336 let caret_b = st.engine.to_bytes(Self::main_of(st).offset.min(total));
1337 st.engine.insert(caret_b, ins);
1338 caret_b + ins.len()
1339 }
1340 };
1341 if let Some(max) = self.max_lines {
1342 st.engine.enforce_max_lines(max);
1343 }
1344 source.commit(&mut st.engine);
1345
1346 let offset = st.engine.to_chars(caret_b).min(st.engine.len_chars());
1347 let main = Self::main_mut(st);
1348 main.offset = offset;
1349 main.anchor = None;
1350 st.goal_x = None;
1351 Self::reset_escalate(st);
1352 });
1353 self.bump();
1354 return;
1355 }
1356
1357 self.with_state(|st| {
1358 source.ensure(&mut st.engine);
1359 let total = st.engine.len_chars();
1360 let mut sel_pre: Vec<(usize, usize, usize)> = Vec::new();
1361 for (i, c) in st.carets.iter().enumerate() {
1362 if let Some((a, b)) = c.selected() {
1363 let (a, b) = (a.min(total), b.min(total));
1364 if a < b {
1365 sel_pre.push((i, st.engine.to_bytes(a), st.engine.to_bytes(b)));
1366 }
1367 }
1368 }
1369 let raw: Vec<(usize, usize)> = sel_pre.iter().map(|&(_, a, b)| (a, b)).collect();
1370 let (merged, owner) = merge_byte_ranges(&raw);
1371 if !merged.is_empty() {
1372 let ranges: Vec<std::ops::Range<usize>> =
1373 merged.iter().map(|&(a, b)| a..b).collect();
1374 st.engine.delete_many(ranges);
1375 }
1376
1377 let mut points: Vec<usize> = Vec::with_capacity(st.carets.len());
1378 let mut caret_union: Vec<Option<usize>> = vec![None; st.carets.len()];
1379 for (k, &(i, _, _)) in sel_pre.iter().enumerate() {
1380 caret_union[i] = Some(owner[k]);
1381 }
1382 for (i, c) in st.carets.iter().enumerate() {
1383 let pre_b = match caret_union[i] {
1384 Some(u) => merged[u].0,
1385 None => st.engine.to_bytes(c.offset.min(total)),
1386 };
1387
1388 let mut u_hit = caret_union[i];
1389 if u_hit.is_none() {
1390 for (u, &(us, ue)) in merged.iter().enumerate() {
1391 if pre_b >= us && pre_b < ue {
1392 u_hit = Some(u);
1393 break;
1394 }
1395 }
1396 }
1397 let post_b = match u_hit {
1398 Some(u) => map_through_deletes(&merged, merged[u].0),
1399 None => map_through_deletes(&merged, pre_b),
1400 };
1401 points.push(post_b);
1402 }
1403
1404 let allowed = match self.max_length {
1405 Some(max) => max.saturating_sub(total),
1406 None => usize::MAX,
1407 };
1408 let ins = BufferEngine::fit_prefix(text, allowed);
1409 if ins.is_empty() {
1410 source.commit(&mut st.engine);
1411 let new_len = st.engine.len_chars();
1412 for (i, c) in st.carets.iter_mut().enumerate() {
1413 c.offset = st.engine.to_chars(points[i]).min(new_len);
1414 c.anchor = None;
1415 }
1416 dedupe_carets(&mut st.carets);
1417 st.goal_x = None;
1418 Self::reset_escalate(st);
1419 return;
1420 }
1421
1422 let mut uniq = points.clone();
1423 uniq.sort_unstable();
1424 uniq.dedup();
1425
1426 let mut pcs: Vec<usize> = uniq.iter().map(|&p| st.engine.to_chars(p)).collect();
1427 pcs.sort_unstable();
1428 let ins_chars = ins.chars().count();
1429 let at: Vec<usize> = uniq.clone();
1430 st.engine.insert_at_many(at, ins);
1431 if let Some(max) = self.max_lines {
1432 st.engine.enforce_max_lines(max);
1433 }
1434 source.commit(&mut st.engine);
1435 let new_len = st.engine.len_chars();
1436
1437 let mut new_offsets = vec![0usize; st.carets.len()];
1438 for (i, &p) in points.iter().enumerate() {
1439 let pc = st.engine.to_chars(p);
1440 let below = pcs.iter().filter(|&&q| q < pc).count();
1441 new_offsets[i] = (pc + ins_chars * (1 + below)).min(new_len);
1442 }
1443 for (i, c) in st.carets.iter_mut().enumerate() {
1444 c.offset = new_offsets[i];
1445 c.anchor = None;
1446 }
1447 dedupe_carets(&mut st.carets);
1448 st.goal_x = None;
1449 Self::reset_escalate(st);
1450 });
1451 self.bump();
1452 }
1453
1454 fn delete_range_for(
1455 engine: &BufferEngine,
1456 caret_b: usize,
1457 back: bool,
1458 word: bool,
1459 ) -> (usize, usize) {
1460 let buf = engine.buffer();
1461 if back {
1462 let from_b = if word {
1463 sequoia_buffer::word_start_before(buf, caret_b)
1464 } else {
1465 sequoia_buffer::prev_grapheme(buf, caret_b)
1466 };
1467 (from_b, caret_b)
1468 } else {
1469 let to_b = if word {
1470 sequoia_buffer::word_end_after(buf, caret_b)
1471 } else {
1472 sequoia_buffer::next_grapheme(buf, caret_b)
1473 };
1474 (caret_b, to_b)
1475 }
1476 }
1477
1478 fn range_styled(engine: &BufferEngine, s: usize, e: usize) -> bool {
1479 s < e
1480 && engine
1481 .styled_buffer()
1482 .spans()
1483 .iter()
1484 .any(|sp| sp.start < e && s < sp.end)
1485 }
1486
1487 fn backspace(&self, word: bool) {
1488 if !self.editable {
1489 return;
1490 }
1491 if self.delete_selection() {
1492 return;
1493 }
1494 let source = self.source.clone();
1495
1496 if self.with_state(|s| s.carets.len() <= 1) {
1497 if !word {
1498 let stripped = self.with_state(|s| {
1499 source.ensure(&mut s.engine);
1500 let len = s.engine.len_chars();
1501 let caret = Self::main_of(s).offset.min(len);
1502 let caret_b = s.engine.to_bytes(caret);
1503 let total_b = s.engine.to_bytes(len);
1504 if caret == 0 {
1505 return false;
1506 }
1507 let from_b = sequoia_buffer::prev_grapheme(s.engine.buffer(), caret_b);
1508 if from_b == caret_b || !Self::range_styled(&s.engine, from_b, caret_b) {
1509 return false;
1510 }
1511 let next_styled = if caret_b < total_b {
1512 let to_b = sequoia_buffer::next_grapheme(s.engine.buffer(), caret_b);
1513 Self::range_styled(&s.engine, caret_b, to_b)
1514 } else {
1515 false
1516 };
1517 if next_styled {
1518 return false;
1519 }
1520 s.engine.clear_attr(from_b..caret_b);
1521 source.commit(&mut s.engine);
1522
1523 Self::main_mut(s).offset = s.engine.to_chars(caret_b).min(s.engine.len_chars());
1524 Self::main_mut(s).anchor = None;
1525 s.goal_x = None;
1526 Self::reset_escalate(s);
1527 true
1528 });
1529 if stripped {
1530 self.bump();
1531 return;
1532 }
1533 }
1534 self.with_state(|s| {
1535 source.ensure(&mut s.engine);
1536 let len = s.engine.len_chars();
1537 let caret = Self::main_of(s).offset.min(len);
1538 if caret == 0 {
1539 return;
1540 }
1541 let caret_b = s.engine.to_bytes(caret);
1542 let (from_b, _) = Self::delete_range_for(&s.engine, caret_b, true, word);
1543 s.engine.delete(from_b..caret_b);
1544 source.commit(&mut s.engine);
1545 let offset = s.engine.to_chars(from_b);
1546 let main = Self::main_mut(s);
1547 main.offset = offset;
1548 main.anchor = None;
1549 s.goal_x = None;
1550 Self::reset_escalate(s);
1551 });
1552 self.bump();
1553 return;
1554 }
1555
1556 self.with_state(|s| {
1557 source.ensure(&mut s.engine);
1558 let len = s.engine.len_chars();
1559 let total_b = s.engine.to_bytes(len);
1560
1561 let pre_bytes: Vec<usize> = s
1562 .carets
1563 .iter()
1564 .map(|c| s.engine.to_bytes(c.offset.min(len)))
1565 .collect();
1566 let mut stripped_bytes: Vec<usize> = Vec::new();
1567 if !word {
1568 for c in s.carets.iter() {
1569 debug_assert!(c.selected().is_none());
1570 let caret = c.offset.min(len);
1571 if caret == 0 {
1572 continue;
1573 }
1574 let caret_b = s.engine.to_bytes(caret);
1575 let from_b = sequoia_buffer::prev_grapheme(s.engine.buffer(), caret_b);
1576 if from_b == caret_b || !Self::range_styled(&s.engine, from_b, caret_b) {
1577 continue;
1578 }
1579 let next_styled = if caret_b < total_b {
1580 let to_b = sequoia_buffer::next_grapheme(s.engine.buffer(), caret_b);
1581 Self::range_styled(&s.engine, caret_b, to_b)
1582 } else {
1583 false
1584 };
1585 if next_styled {
1586 continue;
1587 }
1588 s.engine.clear_attr(from_b..caret_b);
1589 stripped_bytes.push(caret_b);
1590 }
1591 if !stripped_bytes.is_empty() {
1592 source.commit(&mut s.engine);
1593 }
1594 }
1595
1596 let mut ranges: Vec<(usize, usize)> = Vec::new();
1597 let mut owners: Vec<usize> = Vec::new();
1598 for (i, c) in s.carets.iter().enumerate() {
1599 let caret = c.offset.min(len);
1600 let caret_b = s.engine.to_bytes(caret);
1601 if stripped_bytes.contains(&caret_b) {
1602 continue;
1603 }
1604 if caret == 0 {
1605 continue;
1606 }
1607 let (from_b, to_b) = Self::delete_range_for(&s.engine, caret_b, true, word);
1608 if from_b < to_b {
1609 ranges.push((from_b, to_b));
1610 owners.push(i);
1611 }
1612 }
1613 if ranges.is_empty() {
1614 let new_len = s.engine.len_chars();
1615 for c in s.carets.iter_mut() {
1616 c.offset = c.offset.min(new_len);
1617 c.anchor = None;
1618 }
1619 Self::reset_escalate(s);
1620 return;
1621 }
1622 let raw = ranges.clone();
1623 let (merged, owner) = merge_byte_ranges(&raw);
1624 let dranges: Vec<std::ops::Range<usize>> = merged.iter().map(|&(a, b)| a..b).collect();
1625 s.engine.delete_many(dranges);
1626 source.commit(&mut s.engine);
1627 let new_len = s.engine.len_chars();
1628
1629 let mut new_offsets = vec![0usize; s.carets.len()];
1630 let mut touched = vec![false; s.carets.len()];
1631 for (k, &i) in owners.iter().enumerate() {
1632 let (us, _) = merged[owner[k]];
1633 new_offsets[i] = s
1634 .engine
1635 .to_chars(map_through_deletes(&merged, us))
1636 .min(new_len);
1637 touched[i] = true;
1638 }
1639 for (i, c) in s.carets.iter_mut().enumerate() {
1640 if !touched[i] {
1641 let pb = pre_bytes[i];
1642 c.offset = s
1643 .engine
1644 .to_chars(map_through_deletes(&merged, pb))
1645 .min(new_len);
1646 } else {
1647 c.offset = new_offsets[i];
1648 }
1649 c.anchor = None;
1650 }
1651 s.goal_x = None;
1652 Self::reset_escalate(s);
1653 dedupe_carets(&mut s.carets);
1654 });
1655 self.bump();
1656 }
1657
1658 fn delete(&self, word: bool) {
1659 if !self.editable {
1660 return;
1661 }
1662 if self.delete_selection() {
1663 return;
1664 }
1665 let source = self.source.clone();
1666 if self.with_state(|s| s.carets.len() <= 1) {
1667 self.with_state(|s| {
1668 source.ensure(&mut s.engine);
1669 let len = s.engine.len_chars();
1670 let caret = Self::main_of(s).offset.min(len);
1671 if caret >= len {
1672 return;
1673 }
1674 let caret_b = s.engine.to_bytes(caret);
1675 let (_, to_b) = Self::delete_range_for(&s.engine, caret_b, false, word);
1676 s.engine.delete(caret_b..to_b);
1677 source.commit(&mut s.engine);
1678 let offset = s.engine.to_chars(caret_b);
1679 let main = Self::main_mut(s);
1680 main.offset = offset;
1681 main.anchor = None;
1682 s.goal_x = None;
1683 Self::reset_escalate(s);
1684 });
1685 self.bump();
1686 return;
1687 }
1688
1689 self.with_state(|s| {
1690 source.ensure(&mut s.engine);
1691 let len = s.engine.len_chars();
1692 let pre_bytes: Vec<usize> = s
1693 .carets
1694 .iter()
1695 .map(|c| s.engine.to_bytes(c.offset.min(len)))
1696 .collect();
1697 let mut ranges: Vec<(usize, usize)> = Vec::new();
1698 let mut owners: Vec<usize> = Vec::new();
1699 for (i, &cb) in pre_bytes.iter().enumerate() {
1700 let caret = s.carets[i].offset.min(len);
1701 if caret >= len {
1702 continue;
1703 }
1704 let (_, to_b) = Self::delete_range_for(&s.engine, cb, false, word);
1705 if cb < to_b {
1706 ranges.push((cb, to_b));
1707 owners.push(i);
1708 }
1709 }
1710 if ranges.is_empty() {
1711 Self::reset_escalate(s);
1712 return;
1713 }
1714 let (merged, owner) = merge_byte_ranges(&ranges);
1715 let dranges: Vec<std::ops::Range<usize>> = merged.iter().map(|&(a, b)| a..b).collect();
1716 s.engine.delete_many(dranges);
1717 source.commit(&mut s.engine);
1718 let new_len = s.engine.len_chars();
1719 let mut new_offsets = vec![0usize; s.carets.len()];
1720 let mut touched = vec![false; s.carets.len()];
1721 for (k, &i) in owners.iter().enumerate() {
1722 let (us, _) = merged[owner[k]];
1723 new_offsets[i] = s
1724 .engine
1725 .to_chars(map_through_deletes(&merged, us))
1726 .min(new_len);
1727 touched[i] = true;
1728 }
1729 for (i, c) in s.carets.iter_mut().enumerate() {
1730 if touched[i] {
1731 c.offset = new_offsets[i];
1732 } else {
1733 c.offset = s
1734 .engine
1735 .to_chars(map_through_deletes(&merged, pre_bytes[i]))
1736 .min(new_len);
1737 }
1738 c.anchor = None;
1739 }
1740 s.goal_x = None;
1741 Self::reset_escalate(s);
1742 dedupe_carets(&mut s.carets);
1743 });
1744 self.bump();
1745 }
1746
1747 fn move_caret(&self, delta: isize, extend: bool) {
1748 let source = self.source.clone();
1749 self.with_state(|s| {
1750 source.ensure(&mut s.engine);
1751 let len = s.engine.len_chars();
1752 for c in s.carets.iter_mut() {
1753 let mut caret_b = s.engine.to_bytes(c.offset.min(len));
1754 let steps = delta.unsigned_abs().max(1);
1755 for _ in 0..steps {
1756 let b = if delta < 0 {
1757 sequoia_buffer::prev_grapheme(s.engine.buffer(), caret_b)
1758 } else {
1759 sequoia_buffer::next_grapheme(s.engine.buffer(), caret_b)
1760 };
1761 if b == caret_b {
1762 break;
1763 }
1764 caret_b = b;
1765 }
1766 c.move_to(s.engine.to_chars(caret_b), extend);
1767 }
1768 Self::reset_escalate(s);
1769 s.goal_x = None;
1770 });
1771 self.bump();
1772 }
1773
1774 fn move_word(&self, dir: isize, extend: bool) {
1775 let source = self.source.clone();
1776 self.with_state(|s| {
1777 source.ensure(&mut s.engine);
1778 let len = s.engine.len_chars();
1779 for c in s.carets.iter_mut() {
1780 let caret_b = s.engine.to_bytes(c.offset.min(len));
1781 let next_b = if dir < 0 {
1782 sequoia_buffer::word_start_before(s.engine.buffer(), caret_b)
1783 } else {
1784 sequoia_buffer::word_end_after(s.engine.buffer(), caret_b)
1785 };
1786 c.move_to(s.engine.to_chars(next_b), extend);
1787 }
1788 Self::reset_escalate(s);
1789 s.goal_x = None;
1790 });
1791 self.bump();
1792 }
1793
1794 fn move_home(&self, extend: bool, doc: bool, ctx: &mut EventCtx) {
1795 let layout = self.event_layout(ctx);
1796 let source = self.source.clone();
1797 self.with_state(|s| {
1798 source.ensure(&mut s.engine);
1799 let len = s.engine.len_chars();
1800 for c in s.carets.iter_mut() {
1801 let next = if doc {
1802 0
1803 } else {
1804 let ri = layout.row_of(c.offset.min(len));
1805 layout.rows.get(ri).map(|r| r.start).unwrap_or(0)
1806 };
1807 c.move_to(next, extend);
1808 }
1809 Self::reset_escalate(s);
1810 s.goal_x = None;
1811 });
1812 self.bump();
1813 }
1814
1815 fn move_end(&self, extend: bool, doc: bool, ctx: &mut EventCtx) {
1816 let layout = self.event_layout(ctx);
1817 let source = self.source.clone();
1818 self.with_state(|s| {
1819 source.ensure(&mut s.engine);
1820 let total = s.engine.len_chars();
1821 for c in s.carets.iter_mut() {
1822 let next = if doc {
1823 total
1824 } else {
1825 let ri = layout.row_of(c.offset.min(total));
1826 layout.rows.get(ri).map(|r| r.end).unwrap_or(total)
1827 };
1828 c.move_to(next, extend);
1829 }
1830 Self::reset_escalate(s);
1831 s.goal_x = None;
1832 });
1833 self.bump();
1834 }
1835
1836 fn row_at_y(line_h: f32, rows: usize, cy: f32) -> usize {
1837 if line_h <= 0.0 || rows == 0 {
1838 return 0;
1839 }
1840 ((cy / line_h).floor() as isize).clamp(0, rows.saturating_sub(1) as isize) as usize
1841 }
1842
1843 fn move_visual(&self, dir: isize, steps: usize, extend: bool, ctx: &mut EventCtx) {
1844 let layout = self.event_layout(ctx);
1845 let theme = ctx.environment.get::<Theme>().unwrap_or_else(Theme::dark);
1846 let size = self.resolved_font_size(&theme);
1847 let weight = self.font_weight();
1848 let total = layout.rows.last().map(|r| r.end).unwrap_or(0);
1849 let mains: Vec<usize> = self.with_state(|s| {
1850 s.carets
1851 .iter()
1852 .map(|c| c.offset.min(total))
1853 .collect()
1854 });
1855 let main_goal = self.with_state(|s| s.goal_x);
1856 let mut main_goal_next = main_goal;
1857 let mut nexts: Vec<Option<usize>> = Vec::with_capacity(mains.len());
1858 let last = layout.rows.len().saturating_sub(1);
1859 for (idx, caret) in mains.iter().enumerate() {
1860 let ri = layout.row_of(*caret);
1861 let target = if dir < 0 {
1862 ri.saturating_sub(steps)
1863 } else {
1864 (ri + steps).min(last)
1865 };
1866 if target == ri || layout.rows.is_empty() {
1867 nexts.push(None);
1868 continue;
1869 }
1870 let current = layout.rows[ri];
1871 let col = caret
1872 .saturating_sub(current.start)
1873 .min(current.end - current.start);
1874 let caret_x = row_prefix_x(ctx, &layout, ¤t, col, size, weight);
1875 let goal = if idx == 0 {
1876 match main_goal {
1877 Some(g) => g,
1878 None => {
1879 main_goal_next = Some(caret_x);
1880 caret_x
1881 }
1882 }
1883 } else {
1884 caret_x
1885 };
1886 let target_row = layout.rows[target];
1887 let text = layout.row_text(&target_row);
1888 let target_col = ctx
1889 .char_index_from_x(&text, size, weight, goal)
1890 .min(target_row.end - target_row.start);
1891 nexts.push(Some(target_row.start + target_col));
1892 }
1893 if nexts.iter().all(|n| n.is_none()) {
1894 if mains.len() == 1 && !extend {
1895 self.with_state(|s| s.goal_x = None);
1896 }
1897 return;
1898 }
1899 self.with_state(|s| {
1900 for (c, next) in s.carets.iter_mut().zip(nexts.iter()) {
1901 if let Some(next) = next {
1902 c.move_to(*next, extend);
1903 }
1904 }
1905 s.goal_x = main_goal_next;
1906 Self::reset_escalate(s);
1907 });
1908 }
1909
1910 fn move_page(
1911 &self,
1912 dir: isize,
1913 extend: bool,
1914 viewport_h: f32,
1915 line_h: f32,
1916 ctx: &mut EventCtx,
1917 ) {
1918 let step = if viewport_h <= 0.0 || line_h <= 0.0 {
1919 1
1920 } else {
1921 ((viewport_h / line_h).floor() as usize).max(1)
1922 };
1923 self.move_visual(dir, step, extend, ctx);
1924 self.with_state(|s| {
1925 let max_y = (s.content.height + self.pad_y * 2.0 - viewport_h).max(0.0);
1926 s.scroll.y = (s.scroll.y + dir as f32 * viewport_h).clamp(0.0, max_y);
1927 });
1928 self.bump();
1929 }
1930
1931 fn on_enter(&self) {
1932 if !self.editable {
1933 return;
1934 }
1935 if self.with_state(|s| s.carets.len() <= 1) {
1936 self.delete_selection();
1937 let source = self.source.clone();
1938 let indent = self.with_state(|s| {
1939 source.ensure(&mut s.engine);
1940 if !self.auto_indent {
1941 return String::new();
1942 }
1943 let caret_b = s
1944 .engine
1945 .to_bytes(Self::main_of(s).offset.min(s.engine.len_chars()));
1946 let lines = s.engine.buffer().lines();
1947 let (ls, le) = lines.line_range(lines.line_of(caret_b));
1948 let line_text = s.engine.buffer().substring(ls..le);
1949 line_text
1950 .chars()
1951 .take(leading_indent_len(&line_text))
1952 .collect()
1953 });
1954 self.insert_str(&format!("\n{indent}"));
1955 return;
1956 }
1957
1958 self.delete_selection();
1959 let source = self.source.clone();
1960 self.with_state(|s| {
1961 source.ensure(&mut s.engine);
1962 let total = s.engine.len_chars();
1963 let mut items: Vec<(usize, String)> = Vec::with_capacity(s.carets.len());
1964 for c in s.carets.iter() {
1965 let caret_b = s.engine.to_bytes(c.offset.min(total));
1966 let indent = if self.auto_indent {
1967 let lines = s.engine.buffer().lines();
1968 let (ls, le) = lines.line_range(lines.line_of(caret_b));
1969 let line_text = s.engine.buffer().substring(ls..le);
1970 line_text
1971 .chars()
1972 .take(leading_indent_len(&line_text))
1973 .collect::<String>()
1974 } else {
1975 String::new()
1976 };
1977 items.push((caret_b, indent));
1978 }
1979
1980 let mut uniq: Vec<usize> = items.iter().map(|(b, _)| *b).collect();
1981 uniq.sort_unstable();
1982 uniq.dedup();
1983 s.engine.insert_at_many(uniq.clone(), "\n");
1984 if let Some(max) = self.max_lines {
1985 s.engine.enforce_max_lines(max);
1986 }
1987
1988 let post_nl = |pb: usize| -> usize { pb + uniq.iter().filter(|&&q| q <= pb).count() };
1989
1990 let cur_chars = s.engine.len_chars();
1991 let indent_total: usize = items.iter().map(|(_, it)| it.chars().count()).sum();
1992 let allow_indent = match self.max_length {
1993 Some(max) => cur_chars + indent_total <= max,
1994 None => true,
1995 };
1996 let mut phase2: Vec<(usize, String)> = items
1997 .iter()
1998 .map(|(pb, indent)| (post_nl(*pb), indent.clone()))
1999 .filter(|(_, indent)| !indent.is_empty())
2000 .collect();
2001 if !allow_indent {
2002 phase2.clear();
2003 }
2004 phase2.sort_by_key(|b| std::cmp::Reverse(b.0));
2005 let mut k = 0usize;
2006 while k < phase2.len() {
2007 let mut j = k + 1;
2008 while j < phase2.len() && phase2[j].1 == phase2[k].1 {
2009 j += 1;
2010 }
2011 let mut pts: Vec<usize> = phase2[k..j].iter().map(|(p, _)| *p).collect();
2012 pts.sort_unstable();
2013 pts.dedup();
2014 let text = phase2[k].1.clone();
2015 s.engine.insert_at_many(pts, &text);
2016 k = j;
2017 }
2018 if let Some(max) = self.max_lines {
2019 s.engine.enforce_max_lines(max);
2020 }
2021 source.commit(&mut s.engine);
2022
2023 let new_len = s.engine.len_chars();
2024 let mut finals: Vec<usize> = Vec::with_capacity(items.len());
2025 for (pb, indent) in &items {
2026 let q = post_nl(*pb);
2027 let own = if allow_indent { indent.len() } else { 0 };
2028 let below: usize = phase2
2029 .iter()
2030 .filter(|(p, _)| *p < q)
2031 .map(|(_, t)| t.len())
2032 .sum();
2033 finals.push(s.engine.to_chars(q + own + below).min(new_len));
2034 }
2035 for (c, f) in s.carets.iter_mut().zip(finals.iter()) {
2036 c.offset = *f;
2037 c.anchor = None;
2038 }
2039 dedupe_carets(&mut s.carets);
2040 Self::reset_escalate(s);
2041 s.goal_x = None;
2042 });
2043 self.bump();
2044 }
2045
2046 fn on_tab(&self, extend_shift: bool) {
2047 if !self.editable {
2048 return;
2049 }
2050 if extend_shift {
2051 self.outdent();
2052 } else if !self.indent_selection() {
2053 let indent = self.indent.clone();
2054 if self.with_state(|s| s.carets.len() <= 1) {
2055 self.insert_str(&indent);
2056 } else {
2057 let source = self.source.clone();
2058 self.with_state(|s| {
2059 source.ensure(&mut s.engine);
2060 let total = s.engine.len_chars();
2061 let mut pts: Vec<usize> = s
2062 .carets
2063 .iter()
2064 .map(|c| s.engine.to_bytes(c.offset.min(total)))
2065 .collect();
2066 pts.sort_unstable();
2067 pts.dedup();
2068 let allowed = match self.max_length {
2069 Some(max) => max.saturating_sub(s.engine.len_chars()),
2070 None => usize::MAX,
2071 };
2072 let ins = BufferEngine::fit_prefix(&indent, allowed / s.carets.len().max(1));
2073 if ins.is_empty() {
2074 return;
2075 }
2076
2077 let mut pcs: Vec<usize> = pts.iter().map(|&p| s.engine.to_chars(p)).collect();
2078 pcs.sort_unstable();
2079 let n = ins.chars().count();
2080
2081 let pre: Vec<usize> = s.carets.iter().map(|c| c.offset.min(total)).collect();
2082 s.engine.insert_at_many(pts, ins);
2083 if let Some(max) = self.max_lines {
2084 s.engine.enforce_max_lines(max);
2085 }
2086 source.commit(&mut s.engine);
2087 let new_len = s.engine.len_chars();
2088 for (c, &old) in s.carets.iter_mut().zip(pre.iter()) {
2089 let below = pcs.iter().filter(|&&q| q < old).count();
2090
2091 c.offset = (old + n * (1 + below)).min(new_len);
2092 c.anchor = None;
2093 }
2094 dedupe_carets(&mut s.carets);
2095 Self::reset_escalate(s);
2096 s.goal_x = None;
2097 });
2098 self.bump();
2099 }
2100 }
2101 }
2102
2103 fn indent_selection(&self) -> bool {
2104 if self.indent.is_empty() {
2105 return false;
2106 }
2107 let source = self.source.clone();
2108 let indent = self.indent.clone();
2109 let per = indent.len();
2110
2111 let take_block = self.with_state(|s| {
2112 source.ensure(&mut s.engine);
2113 let len = s.engine.len_chars();
2114 let buf = s.engine.buffer();
2115 let lines = buf.lines();
2116 let total_b = buf.len_bytes();
2117 let mut touched: std::collections::BTreeSet<usize> = std::collections::BTreeSet::new();
2118 let mut any_multi = false;
2119 for c in s.carets.iter() {
2120 match c.selected() {
2121 Some((a, b)) => {
2122 let (a, b) = (a.min(len), b.min(len));
2123 if a >= b {
2124 continue;
2125 }
2126 let (ab, bb) = (s.engine.to_bytes(a), s.engine.to_bytes(b));
2127 let l0 = lines.line_of(ab);
2128 let (mut l1, c1) = lines.line_col(bb.min(total_b));
2129 if c1 == 0 && l1 > l0 {
2130 l1 -= 1;
2131 }
2132 if l1 > l0 {
2133 any_multi = true;
2134 }
2135 for l in l0..=l1 {
2136 touched.insert(l);
2137 }
2138 }
2139 None => {
2140 let cb = s.engine.to_bytes(c.offset.min(len));
2141 touched.insert(lines.line_of(cb));
2142 }
2143 }
2144 }
2145 if touched.is_empty() {
2146 return false;
2147 }
2148
2149 if s.carets.len() <= 1 && !any_multi {
2150 return false;
2151 }
2152 true
2153 });
2154 if !take_block {
2155 return false;
2156 }
2157
2158 self.with_state(|s| {
2159 source.ensure(&mut s.engine);
2160 let buf = s.engine.buffer();
2161 let lines = buf.lines();
2162 let total_b = buf.len_bytes();
2163 let len = s.engine.len_chars();
2164
2165 let mut touched: std::collections::BTreeSet<usize> = std::collections::BTreeSet::new();
2166 for c in s.carets.iter() {
2167 match c.selected() {
2168 Some((a, b)) => {
2169 let (a, b) = (a.min(len), b.min(len));
2170 if a >= b {
2171 continue;
2172 }
2173 let (ab, bb) = (s.engine.to_bytes(a), s.engine.to_bytes(b));
2174 let l0 = lines.line_of(ab);
2175 let (mut l1, c1) = lines.line_col(bb.min(total_b));
2176 if c1 == 0 && l1 > l0 {
2177 l1 -= 1;
2178 }
2179 for l in l0..=l1 {
2180 touched.insert(l);
2181 }
2182 }
2183 None => {
2184 let cb = s.engine.to_bytes(c.offset.min(len));
2185 touched.insert(lines.line_of(cb));
2186 }
2187 }
2188 }
2189 let at: Vec<usize> = touched.iter().map(|&l| lines.line_start(l)).collect();
2190 let shift = |pos: usize| -> usize {
2191 let mut k = 0usize;
2192 for &l in touched.iter() {
2193 if lines.line_start(l) <= pos {
2194 k += 1;
2195 } else {
2196 break;
2197 }
2198 }
2199 pos + per * k
2200 };
2201
2202 let ends: Vec<(usize, usize)> = s
2203 .carets
2204 .iter()
2205 .map(|c| {
2206 let (a, b) = c.ends();
2207 let (a, b) = (a.min(len), b.min(len));
2208 (shift(s.engine.to_bytes(a)), shift(s.engine.to_bytes(b)))
2209 })
2210 .collect();
2211 s.engine.insert_at_many(at, &indent);
2212 if let Some(max) = self.max_lines {
2213 s.engine.enforce_max_lines(max);
2214 }
2215 source.commit(&mut s.engine);
2216 let new_len = s.engine.len_chars();
2217 for (c, (na, nb)) in s.carets.iter_mut().zip(ends.iter()) {
2218 let (na, nb) = (
2219 s.engine.to_chars(*na).min(new_len),
2220 s.engine.to_chars(*nb).min(new_len),
2221 );
2222
2223 let was_fwd = c.offset >= c.anchor.unwrap_or(c.offset);
2224 if na == nb {
2225 c.offset = na;
2226 c.anchor = None;
2227 } else if was_fwd {
2228 c.offset = nb;
2229 c.anchor = Some(na);
2230 } else {
2231 c.offset = na;
2232 c.anchor = Some(nb);
2233 }
2234 }
2235 dedupe_carets(&mut s.carets);
2236 s.goal_x = None;
2237 Self::reset_escalate(s);
2238 });
2239 self.bump();
2240 true
2241 }
2242
2243 fn outdent(&self) {
2244 let indent_w = self.indent.chars().count().max(1);
2245 let source = self.source.clone();
2246
2247 let take_block = self.with_state(|s| {
2248 source.ensure(&mut s.engine);
2249 let len = s.engine.len_chars();
2250 let buf = s.engine.buffer();
2251 let lines = buf.lines();
2252 let total_b = buf.len_bytes();
2253 let mut touched: std::collections::BTreeSet<usize> = std::collections::BTreeSet::new();
2254 let mut any_multi = false;
2255 for c in s.carets.iter() {
2256 match c.selected() {
2257 Some((a, b)) => {
2258 let (a, b) = (a.min(len), b.min(len));
2259 if a >= b {
2260 continue;
2261 }
2262 let (ab, bb) = (s.engine.to_bytes(a), s.engine.to_bytes(b));
2263 let l0 = lines.line_of(ab);
2264 let (mut l1, c1) = lines.line_col(bb.min(total_b));
2265 if c1 == 0 && l1 > l0 {
2266 l1 -= 1;
2267 }
2268 if l1 > l0 {
2269 any_multi = true;
2270 }
2271 for l in l0..=l1 {
2272 touched.insert(l);
2273 }
2274 }
2275 None => {
2276 let cb = s.engine.to_bytes(c.offset.min(len));
2277 touched.insert(lines.line_of(cb));
2278 }
2279 }
2280 }
2281 s.carets.len() > 1 || any_multi || touched.len() > 1
2282 });
2283 if take_block {
2284 self.with_state(|s| {
2285 source.ensure(&mut s.engine);
2286 let len = s.engine.len_chars();
2287 let buf = s.engine.buffer();
2288 let lines = buf.lines();
2289 let total_b = buf.len_bytes();
2290 let mut touched: std::collections::BTreeSet<usize> =
2291 std::collections::BTreeSet::new();
2292 for c in s.carets.iter() {
2293 match c.selected() {
2294 Some((a, b)) => {
2295 let (a, b) = (a.min(len), b.min(len));
2296 if a >= b {
2297 continue;
2298 }
2299 let (ab, bb) = (s.engine.to_bytes(a), s.engine.to_bytes(b));
2300 let l0 = lines.line_of(ab);
2301 let (mut l1, c1) = lines.line_col(bb.min(total_b));
2302 if c1 == 0 && l1 > l0 {
2303 l1 -= 1;
2304 }
2305 for l in l0..=l1 {
2306 touched.insert(l);
2307 }
2308 }
2309 None => {
2310 let cb = s.engine.to_bytes(c.offset.min(len));
2311 touched.insert(lines.line_of(cb));
2312 }
2313 }
2314 }
2315 let mut ranges = Vec::new();
2316 let mut take_of: std::collections::BTreeMap<usize, usize> =
2317 std::collections::BTreeMap::new();
2318 for &line in touched.iter() {
2319 let (ls, le) = lines.line_range(line);
2320 let head = buf.substring(ls..le.min(ls + indent_w + 1));
2321 let take = head
2322 .chars()
2323 .take_while(|&c| c == ' ' || c == '\t')
2324 .take(indent_w)
2325 .map(|c| c.len_utf8())
2326 .sum::<usize>();
2327 take_of.insert(line, take);
2328 if take > 0 {
2329 ranges.push(ls..ls + take);
2330 }
2331 }
2332
2333 let map = |pos: usize| -> usize {
2334 let (line, _) = lines.line_col(pos.min(total_b));
2335 let mut sub = 0usize;
2336 for (&l, &take) in take_of.iter() {
2337 if l > line {
2338 break;
2339 }
2340 if l < line || lines.line_start(l) + take <= pos {
2341 sub += take;
2342 }
2343 }
2344 pos.saturating_sub(sub)
2345 };
2346
2347 let ends: Vec<(usize, usize)> = s
2348 .carets
2349 .iter()
2350 .map(|c| {
2351 let (a, b) = c.ends();
2352 let (a, b) = (a.min(len), b.min(len));
2353 (map(s.engine.to_bytes(a)), map(s.engine.to_bytes(b)))
2354 })
2355 .collect();
2356 if !ranges.is_empty() {
2357 s.engine.delete_many(ranges);
2358 source.commit(&mut s.engine);
2359 }
2360 let new_len = s.engine.len_chars();
2361 for (c, (na, nb)) in s.carets.iter_mut().zip(ends.iter()) {
2362 let (na, nb) = (
2363 s.engine.to_chars(*na).min(new_len),
2364 s.engine.to_chars(*nb).min(new_len),
2365 );
2366 let was_fwd = c.offset >= c.anchor.unwrap_or(c.offset);
2367 if na == nb {
2368 c.offset = na;
2369 c.anchor = None;
2370 } else if was_fwd {
2371 c.offset = nb;
2372 c.anchor = Some(na);
2373 } else {
2374 c.offset = na;
2375 c.anchor = Some(nb);
2376 }
2377 }
2378 dedupe_carets(&mut s.carets);
2379 s.goal_x = None;
2380 Self::reset_escalate(s);
2381 });
2382 self.bump();
2383 return;
2384 }
2385
2386 let moved = self.with_state(|s| {
2387 source.ensure(&mut s.engine);
2388 let len = s.engine.len_chars();
2389 let caret = Self::main_of(s).offset.min(len);
2390 let caret_b = s.engine.to_bytes(caret);
2391 let lines = s.engine.buffer().lines();
2392 let start = lines.line_start(lines.line_of(caret_b));
2393 let prefix = s.engine.buffer().substring(start..caret_b);
2394 let mut strip = 0usize;
2395 for c in prefix.chars().rev().take(indent_w) {
2396 if c == ' ' || c == '\t' {
2397 strip += c.len_utf8();
2398 } else {
2399 break;
2400 }
2401 }
2402 if strip == 0 || caret_b == start {
2403 return None;
2404 }
2405 s.engine.delete(caret_b - strip..caret_b);
2406 source.commit(&mut s.engine);
2407 Some(s.engine.to_chars(caret_b - strip))
2408 });
2409 if let Some(caret) = moved {
2410 self.with_state(|s| {
2411 Self::main_mut(s).offset = caret;
2412 Self::main_mut(s).anchor = None;
2413 s.goal_x = None;
2414 Self::reset_escalate(s);
2415 });
2416 self.bump();
2417 }
2418 }
2419
2420 pub const LINK_PLACEHOLDER: &'static str = "https://example.com";
2421
2422 fn word_at(engine: &BufferEngine, text: &str, caret: usize) -> Option<(usize, usize)> {
2423 let chars: Vec<char> = text.chars().collect();
2424 let len = chars.len();
2425 let c = caret.min(len);
2426 let on_word = |i: usize| chars.get(i).is_some_and(|c| is_word_char(*c));
2427 if !(on_word(c) || (c > 0 && on_word(c - 1))) {
2428 return None;
2429 }
2430 let buf = engine.buffer();
2431 let b = engine.to_bytes(c);
2432 let sb = sequoia_buffer::word_start_before(buf, b);
2433 let eb = sequoia_buffer::word_end_after(buf, b);
2434 if sb >= eb {
2435 return None;
2436 }
2437 let (a, z) = (engine.to_chars(sb), engine.to_chars(eb));
2438 if a < z { Some((a, z)) } else { None }
2439 }
2440
2441 fn line_at(engine: &BufferEngine, len_chars: usize, caret: usize) -> (usize, usize) {
2442 let cb = engine.to_bytes(caret.min(len_chars));
2443 let lines = engine.buffer().lines();
2444 let (bs, be) = lines.line_range(lines.line_of(cb));
2445 let total_b = engine.to_bytes(len_chars);
2446 let s = engine.to_chars(bs);
2447 let e = if be < total_b {
2448 engine.to_chars(be) + 1
2449 } else {
2450 engine.to_chars(be)
2451 };
2452 (s, e.min(len_chars))
2453 }
2454
2455 fn toggle_flag(&self, flag: StyleFlag) {
2456 if !self.editable {
2457 return;
2458 }
2459 let source = self.source.clone();
2460 self.with_state(|s| {
2461 source.ensure(&mut s.engine);
2462 let len = s.engine.len_chars();
2463 let snapshot = s.engine.styled_buffer().to_string();
2464
2465 let mut ranges: Vec<(usize, usize)> = Vec::with_capacity(s.carets.len());
2466 for c in s.carets.iter() {
2467 let r = match c.selected() {
2468 Some((a, b)) => {
2469 let (a, b) = (a.min(len), b.min(len));
2470 if a < b { Some((a, b)) } else { None }
2471 }
2472 None => Self::word_at(&s.engine, &snapshot, c.offset),
2473 };
2474 if let Some(r) = r {
2475 ranges.push(r);
2476 }
2477 }
2478 if ranges.is_empty() {
2479 return;
2480 }
2481 for (a, b) in ranges {
2482 let sb = s.engine.to_bytes(a);
2483 let eb = s.engine.to_bytes(b);
2484 if sb >= eb {
2485 continue;
2486 }
2487 let spans: Vec<StyledSpan> = s.engine.styled_buffer().spans().to_vec();
2488
2489 let mut covered = sb;
2490 let mut all_set = true;
2491 for sp in spans.iter() {
2492 if sp.end <= covered {
2493 continue;
2494 }
2495 if sp.start > covered {
2496 all_set = false;
2497 break;
2498 }
2499 if !flag.get(&sp.attr) {
2500 all_set = false;
2501 break;
2502 }
2503 covered = covered.max(sp.end);
2504 if covered >= eb {
2505 break;
2506 }
2507 }
2508 if covered < eb {
2509 all_set = false;
2510 }
2511 if all_set {
2512 let pieces: Vec<(usize, usize, SpanAttr)> = spans
2513 .iter()
2514 .filter(|sp| sp.start < eb && sb < sp.end)
2515 .map(|sp| {
2516 let ps = sp.start.max(sb);
2517 let pe = sp.end.min(eb);
2518 let mut attr = sp.attr.clone();
2519 flag.set(&mut attr, false);
2520 (ps, pe, attr)
2521 })
2522 .collect();
2523 for (ps, pe, attr) in pieces {
2524 s.engine.set_attr(ps..pe, attr);
2525 }
2526 } else {
2527 let mut bounds = vec![sb, eb];
2528 for sp in spans.iter() {
2529 if sp.start > sb && sp.start < eb {
2530 bounds.push(sp.start);
2531 }
2532 if sp.end > sb && sp.end < eb {
2533 bounds.push(sp.end);
2534 }
2535 }
2536 bounds.sort_unstable();
2537 bounds.dedup();
2538 for w in bounds.windows(2) {
2539 let (ps, pe) = (w[0], w[1]);
2540 let mut attr = spans
2541 .iter()
2542 .find(|sp| sp.start <= ps && pe <= sp.end)
2543 .map(|sp| sp.attr.clone())
2544 .unwrap_or_else(SpanAttr::plain);
2545 flag.set(&mut attr, true);
2546 s.engine.set_attr(ps..pe, attr);
2547 }
2548 }
2549 }
2550 source.commit(&mut s.engine);
2551
2552 let new_len = s.engine.len_chars();
2553 for c in s.carets.iter_mut() {
2554 c.offset = c.offset.min(new_len);
2555 if let Some(an) = c.anchor {
2556 c.anchor = Some(an.min(new_len));
2557 if c.anchor == Some(c.offset) {
2558 c.anchor = None;
2559 }
2560 }
2561 }
2562 Self::reset_escalate(s);
2563 s.goal_x = None;
2564 });
2565 self.bump();
2566 }
2567
2568 pub fn toggle_bold(&self) {
2569 self.toggle_flag(StyleFlag::Bold);
2570 }
2571
2572 pub fn toggle_italic(&self) {
2573 self.toggle_flag(StyleFlag::Italic);
2574 }
2575
2576 pub fn toggle_underline(&self) {
2577 self.toggle_flag(StyleFlag::Underline);
2578 }
2579
2580 pub fn toggle_strike(&self) {
2581 self.toggle_flag(StyleFlag::Strike);
2582 }
2583
2584 pub fn apply_link(&self) {
2585 self.apply_link_url(Self::LINK_PLACEHOLDER);
2586 }
2587
2588 pub fn apply_link_url(&self, url: &str) {
2589 if !self.editable {
2590 return;
2591 }
2592 let source = self.source.clone();
2593 let url = url.to_string();
2594 self.with_state(|s| {
2595 source.ensure(&mut s.engine);
2596 let len = s.engine.len_chars();
2597 let snapshot = s.engine.styled_buffer().to_string();
2598 let mut ranges: Vec<(usize, usize)> = Vec::new();
2599 for c in s.carets.iter() {
2600 let r = match c.selected() {
2601 Some((a, b)) => {
2602 let (a, b) = (a.min(len), b.min(len));
2603 if a < b { Some((a, b)) } else { None }
2604 }
2605 None => Self::word_at(&s.engine, &snapshot, c.offset),
2606 };
2607 if let Some(r) = r {
2608 ranges.push(r);
2609 }
2610 }
2611 if ranges.is_empty() {
2612 return;
2613 }
2614 let mut mids: Vec<usize> = Vec::with_capacity(ranges.len());
2615 for (a, b) in &ranges {
2616 let sb = s.engine.to_bytes(*a);
2617 let eb = s.engine.to_bytes(*b);
2618 if sb < eb {
2619 s.engine.set_attr(sb..eb, SpanAttr::link(url.clone()));
2620 }
2621 mids.push(a + (b - a) / 2);
2622 }
2623 source.commit(&mut s.engine);
2624 let new_len = s.engine.len_chars();
2625 for (c, m) in s.carets.iter_mut().zip(mids.iter()) {
2626 c.offset = (*m).min(new_len);
2627 c.anchor = None;
2628 }
2629 dedupe_carets(&mut s.carets);
2630 Self::reset_escalate(s);
2631 s.goal_x = None;
2632 });
2633 self.bump();
2634 }
2635
2636 fn on_style_shortcut(&self, key: &KeyEvent) -> bool {
2637 let cmd =
2638 key.modifiers.contains(Modifiers::CONTROL) || key.modifiers.contains(Modifiers::SUPER);
2639 if !cmd || key.modifiers.contains(Modifiers::ALT) {
2640 return false;
2641 }
2642 let Some(c) = key.key.char().map(|c| c.to_ascii_lowercase()) else {
2643 return false;
2644 };
2645 match c {
2646 'b' => {
2647 if self.editable {
2648 self.toggle_bold();
2649 }
2650 true
2651 }
2652 'i' => {
2653 if self.editable {
2654 self.toggle_italic();
2655 }
2656 true
2657 }
2658 'u' => {
2659 if self.editable {
2660 self.toggle_underline();
2661 }
2662 true
2663 }
2664 'k' => {
2665 if self.editable {
2666 self.apply_link();
2667 }
2668 true
2669 }
2670 _ => false,
2671 }
2672 }
2673
2674 fn try_auto_close(&self, c: char) -> bool {
2675 let (open, close): (&str, &str) = match c {
2676 '*' => ("**", "**"),
2677 '_' => ("_", "_"),
2678 '`' => ("`", "`"),
2679 _ => return false,
2680 };
2681 if !self.editable {
2682 return false;
2683 }
2684 let source = self.source.clone();
2685 enum Role {
2686 Hop(usize),
2687 Wrap(usize, usize, usize),
2688 Plain(usize),
2689 }
2690 let plan = self.with_state(|s| {
2691 source.ensure(&mut s.engine);
2692 let len = s.engine.len_chars();
2693 let snapshot = s.engine.styled_buffer().to_string();
2694 let snap_chars: Vec<char> = snapshot.chars().collect();
2695 let mut roles: Vec<Role> = Vec::with_capacity(s.carets.len());
2696 for (i, caret) in s.carets.iter().enumerate() {
2697 if let Some((a, b)) = caret.selected() {
2698 let (a, b) = (a.min(len), b.min(len));
2699 if a < b {
2700 roles.push(Role::Wrap(i, a, b));
2701 continue;
2702 }
2703 }
2704 let p = caret.offset.min(len);
2705
2706 let tail: String = snap_chars[p..].iter().collect();
2707 if tail.starts_with(close) {
2708 roles.push(Role::Hop(i));
2709 continue;
2710 }
2711 match Self::word_at(&s.engine, &snapshot, p) {
2712 Some((w0, w1)) => roles.push(Role::Wrap(i, w0, w1)),
2713 None => roles.push(Role::Plain(i)),
2714 }
2715 }
2716 let _ = open;
2717 Some(roles)
2718 });
2719 let Some(roles) = plan else { return false };
2720 let has_wrap = roles.iter().any(|r| matches!(r, Role::Wrap(..)));
2721 let has_hop = roles.iter().any(|r| matches!(r, Role::Hop(_)));
2722 if !has_wrap && !has_hop {
2723 return false;
2724 }
2725 let single = c.to_string();
2726 self.with_state(|s| {
2727 source.ensure(&mut s.engine);
2728 let len = s.engine.len_chars();
2729 if !has_wrap {
2730 let snapshot = s.engine.styled_buffer().to_string();
2731 let snap_chars: Vec<char> = snapshot.chars().collect();
2732 for (i, caret) in s.carets.iter_mut().enumerate() {
2733 let still_hop = roles.iter().any(|r| matches!(r, Role::Hop(j) if *j == i));
2734 if !still_hop {
2735 continue;
2736 }
2737 let p = caret.offset.min(len);
2738 let tail: String = snap_chars[p..].iter().collect();
2739 let step = if tail.starts_with(close) {
2740 close.chars().count()
2741 } else {
2742 0
2743 };
2744 caret.offset = (p + step).min(len);
2745 caret.anchor = None;
2746 }
2747 Self::reset_escalate(s);
2748 s.goal_x = None;
2749 return;
2750 }
2751
2752 let mut pairs: Vec<(usize, String, usize)> = Vec::new();
2753
2754 let mut bases: Vec<(usize, usize)> = Vec::with_capacity(roles.len());
2755 for r in roles.iter() {
2756 match r {
2757 Role::Wrap(i, a, b) => {
2758 let ab = s.engine.to_bytes((*a).min(len));
2759 let bb = s.engine.to_bytes((*b).min(len));
2760 pairs.push((ab, open.to_string(), *a));
2761 pairs.push((bb, close.to_string(), *b));
2762 bases.push((*i, *b));
2763 }
2764 Role::Plain(i) => {
2765 let c = s.carets[*i].offset.min(len);
2766 pairs.push((s.engine.to_bytes(c), single.clone(), c));
2767 bases.push((*i, c));
2768 }
2769 Role::Hop(i) => {
2770 bases.push((*i, s.carets[*i].offset.min(len)));
2771 }
2772 }
2773 }
2774
2775 let added: usize = pairs.iter().map(|(_, t, _)| t.chars().count()).sum();
2776 if let Some(max) = self.max_length
2777 && s.engine.len_chars() + added > max
2778 {
2779 return;
2780 }
2781
2782 let mut byte_to_char: std::collections::HashMap<usize, usize> =
2783 std::collections::HashMap::new();
2784 for (pb, _, pc) in pairs.iter() {
2785 byte_to_char.entry(*pb).or_insert(*pc);
2786 }
2787
2788 let mut ordered = pairs.clone();
2789 ordered.sort_by_key(|b| std::cmp::Reverse(b.0));
2790
2791 let mut applied: Vec<(usize, usize)> = Vec::new();
2792 let mut used: std::collections::HashSet<usize> = std::collections::HashSet::new();
2793 let mut k = 0usize;
2794 while k < ordered.len() {
2795 let mut j = k + 1;
2796 while j < ordered.len() && ordered[j].1 == ordered[k].1 {
2797 j += 1;
2798 }
2799 let mut pts: Vec<usize> = ordered[k..j].iter().map(|(p, _, _)| *p).collect();
2800 pts.sort_unstable();
2801 pts.dedup();
2802 let fresh: Vec<usize> = pts.into_iter().filter(|p| used.insert(*p)).collect();
2803 if !fresh.is_empty() {
2804 let text = ordered[k].1.clone();
2805 let clen = text.chars().count();
2806 for p in fresh.iter() {
2807 if let Some(&pc) = byte_to_char.get(p) {
2808 applied.push((pc, clen));
2809 }
2810 }
2811 s.engine.insert_at_many(fresh, &text);
2812 }
2813 k = j;
2814 }
2815 if let Some(max) = self.max_lines {
2816 s.engine.enforce_max_lines(max);
2817 }
2818 source.commit(&mut s.engine);
2819
2820 applied.sort_unstable();
2821 let new_len = s.engine.len_chars();
2822 for (i, base_ch) in bases {
2823 let add: usize = applied
2824 .iter()
2825 .filter(|(pc, _)| *pc <= base_ch)
2826 .map(|(_, l)| l)
2827 .sum();
2828 s.carets[i].offset = (base_ch + add).min(new_len);
2829 s.carets[i].anchor = None;
2830 }
2831 dedupe_carets(&mut s.carets);
2832 Self::reset_escalate(s);
2833 s.goal_x = None;
2834 });
2835 self.bump();
2836 true
2837 }
2838
2839 fn undo(&self) {
2840 if !self.editable {
2841 return;
2842 }
2843 let source = self.source.clone();
2844 let restored = self.with_state(|s| {
2845 source.ensure(&mut s.engine);
2846 let text_before = s.engine.styled_buffer().to_string();
2847 match s.engine.undo() {
2848 Some((a, b)) => {
2849 source.commit(&mut s.engine);
2850 if s.engine.styled_buffer().to_string() == text_before {
2851 let len = s.engine.len_chars();
2852 for c in s.carets.iter_mut() {
2853 c.offset = c.offset.min(len);
2854 if let Some(an) = c.anchor {
2855 c.anchor = Some(an.min(len));
2856 if c.anchor == Some(c.offset) {
2857 c.anchor = None;
2858 }
2859 }
2860 }
2861 } else {
2862 let at = s.engine.to_chars(a).min(s.engine.len_chars());
2863 s.carets = vec![Caret::collapsed(at)];
2864 let _ = b;
2865 }
2866 s.goal_x = None;
2867 Self::reset_escalate(s);
2868 true
2869 }
2870 None => false,
2871 }
2872 });
2873 if restored {
2874 self.bump();
2875 }
2876 }
2877
2878 fn redo(&self) {
2879 if !self.editable {
2880 return;
2881 }
2882 let source = self.source.clone();
2883 let restored = self.with_state(|s| {
2884 source.ensure(&mut s.engine);
2885 let text_before = s.engine.styled_buffer().to_string();
2886 match s.engine.redo() {
2887 Some((a, b)) => {
2888 source.commit(&mut s.engine);
2889 if s.engine.styled_buffer().to_string() == text_before {
2890 let len = s.engine.len_chars();
2891 for c in s.carets.iter_mut() {
2892 c.offset = c.offset.min(len);
2893 if let Some(an) = c.anchor {
2894 c.anchor = Some(an.min(len));
2895 if c.anchor == Some(c.offset) {
2896 c.anchor = None;
2897 }
2898 }
2899 }
2900 let _ = a;
2901 } else {
2902 let at = s.engine.to_chars(b).min(s.engine.len_chars());
2903 s.carets = vec![Caret::collapsed(at)];
2904 }
2905 s.goal_x = None;
2906 Self::reset_escalate(s);
2907 true
2908 }
2909 None => false,
2910 }
2911 });
2912 if restored {
2913 self.bump();
2914 }
2915 }
2916
2917 fn escalate_select(&self) {
2918 let source = self.source.clone();
2919 self.with_state(|s| {
2920 source.ensure(&mut s.engine);
2921 let len = s.engine.len_chars();
2922 if s.carets.is_empty() {
2923 s.carets.push(Caret::default());
2924 }
2925 let snapshot = s.engine.styled_buffer().to_string();
2926 let main = s.carets[0];
2927 let cur_sel = main.selected().map(|(a, b)| (a.min(len), b.min(len)));
2928 let (ns, ne, level) = match s.escalate {
2929 0 => match cur_sel {
2930 None => match Self::word_at(&s.engine, &snapshot, main.offset) {
2931 Some((w0, w1)) => (w0, w1, 1),
2932
2933 None => {
2934 let (l0, l1) = Self::line_at(&s.engine, len, main.offset);
2935 (l0, l1, 2)
2936 }
2937 },
2938 Some((a, b)) if a == b => match Self::word_at(&s.engine, &snapshot, a) {
2939 Some((w0, w1)) => (w0, w1, 1),
2940 None => {
2941 let (l0, l1) = Self::line_at(&s.engine, len, a);
2942 (l0, l1, 2)
2943 }
2944 },
2945
2946 Some(_) => (0, len, 3),
2947 },
2948 1 => {
2949 let (l0, l1) = Self::line_at(&s.engine, len, s.escalate_base);
2950 (l0, l1, 2)
2951 }
2952 _ => (0, len, 3),
2953 };
2954 let base = if s.escalate == 0 {
2955 main.offset.min(len)
2956 } else {
2957 s.escalate_base
2958 };
2959 s.carets = vec![Caret {
2960 offset: ne,
2961 anchor: if ns == ne { None } else { Some(ns) },
2962 }];
2963 s.escalate = level;
2964 s.escalate_base = base;
2965 s.goal_x = None;
2966 });
2967 self.bump();
2968 }
2969
2970 fn handle_click(&self, caret: usize, extend: bool, alt: bool, now_ms: u128) {
2971 const TRIPLE_MS: u128 = 500;
2972 let source = self.source.clone();
2973 if alt {
2974 self.with_state(|s| {
2975 source.ensure(&mut s.engine);
2976 let caret = caret.min(s.engine.len_chars());
2977 if let Some(idx) = s.carets.iter().position(|c| c.offset == caret) {
2978 if s.carets.len() > 1 {
2979 s.carets.remove(idx);
2980 if let Some(ad) = s.alt_drag {
2981 if ad == idx {
2982 s.alt_drag = None;
2983 } else if ad > idx {
2984 s.alt_drag = Some(ad - 1);
2985 }
2986 }
2987 } else {
2988 s.carets[0].move_to(caret, extend);
2989 }
2990 } else {
2991 s.carets.push(Caret::collapsed(caret));
2992 s.alt_drag = Some(s.carets.len() - 1);
2993 }
2994 Self::reset_escalate(s);
2995 s.goal_x = None;
2996 });
2997 self.bump();
2998 return;
2999 }
3000 if extend {
3001 self.set_caret_extend(caret, true);
3002 return;
3003 }
3004 self.with_state(|s| {
3005 source.ensure(&mut s.engine);
3006 let len = s.engine.len_chars();
3007 let caret = caret.min(len);
3008 let chained = match s.last_click_ms {
3009 Some(t) => now_ms.saturating_sub(t) <= TRIPLE_MS,
3010 None => false,
3011 };
3012
3013 s.click_count = if chained { s.click_count + 1 } else { 1 };
3014 s.last_click_ms = Some(now_ms);
3015 s.last_click_pos = caret;
3016 s.alt_drag = None;
3017 match s.click_count % 3 {
3018 1 => {
3019 s.carets = vec![Caret::collapsed(caret)];
3020 Self::reset_escalate(s);
3021 }
3022 2 => {
3023 let snapshot = s.engine.styled_buffer().to_string();
3024 match Self::word_at(&s.engine, &snapshot, caret) {
3025 Some((w0, w1)) => {
3026 s.carets = vec![Caret {
3027 offset: w1,
3028 anchor: Some(w0),
3029 }];
3030 s.escalate = 1;
3031 s.escalate_base = caret;
3032 }
3033 None => {
3034 s.carets = vec![Caret::collapsed(caret)];
3035 Self::reset_escalate(s);
3036 }
3037 }
3038 }
3039 _ => {
3040 let (l0, l1) = Self::line_at(&s.engine, len, caret);
3041 s.carets = vec![Caret {
3042 offset: l1,
3043 anchor: if l0 == l1 { None } else { Some(l0) },
3044 }];
3045 s.escalate = 2;
3046 s.escalate_base = caret;
3047 }
3048 }
3049 s.goal_x = None;
3050 });
3051 self.bump();
3052 }
3053
3054 fn on_key(&self, key: &KeyEvent, ctx: &mut EventCtx) {
3055 let extend = key.modifiers.contains(Modifiers::SHIFT);
3056 let composing = self.with_state(|s| !s.preedit.is_empty());
3057 let ctrl =
3058 key.modifiers.contains(Modifiers::CONTROL) || key.modifiers.contains(Modifiers::SUPER);
3059 let alt = key.modifiers.contains(Modifiers::ALT);
3060 match key.key {
3061 Key::BACKSPACE => {
3062 if !ctrl {
3063 self.backspace(false);
3064 } else {
3065 self.backspace(true);
3066 }
3067 self.ensure_caret_visible_ctx(ctx);
3068 ctx.handled();
3069 }
3070 Key::DELETE => {
3071 if !ctrl {
3072 self.delete(false);
3073 } else {
3074 self.delete(true);
3075 }
3076 self.ensure_caret_visible_ctx(ctx);
3077 ctx.handled();
3078 }
3079 Key::ARROW_LEFT => {
3080 if ctrl || alt {
3081 self.move_word(-1, extend);
3082 } else {
3083 self.move_caret(-1, extend);
3084 }
3085 self.ensure_caret_visible_ctx(ctx);
3086 ctx.handled();
3087 }
3088 Key::ARROW_RIGHT => {
3089 if ctrl || alt {
3090 self.move_word(1, extend);
3091 } else {
3092 self.move_caret(1, extend);
3093 }
3094 self.ensure_caret_visible_ctx(ctx);
3095 ctx.handled();
3096 }
3097 Key::ARROW_UP => {
3098 self.move_vertical_ctx(-1, extend, ctx);
3099 self.ensure_caret_visible_ctx(ctx);
3100 ctx.handled();
3101 }
3102 Key::ARROW_DOWN => {
3103 self.move_vertical_ctx(1, extend, ctx);
3104 self.ensure_caret_visible_ctx(ctx);
3105 ctx.handled();
3106 }
3107 Key::HOME => {
3108 self.move_home(extend, ctrl, ctx);
3109 self.ensure_caret_visible_ctx(ctx);
3110 ctx.handled();
3111 }
3112 Key::END => {
3113 self.move_end(extend, ctrl, ctx);
3114 self.ensure_caret_visible_ctx(ctx);
3115 ctx.handled();
3116 }
3117 Key::PAGE_UP => {
3118 let vp = self.with_state(|s| s.viewport);
3119 self.move_page(-1, extend, vp.height, self.line_height(), ctx);
3120 self.ensure_caret_visible_ctx(ctx);
3121 ctx.handled();
3122 }
3123 Key::PAGE_DOWN => {
3124 let vp = self.with_state(|s| s.viewport);
3125 self.move_page(1, extend, vp.height, self.line_height(), ctx);
3126 self.ensure_caret_visible_ctx(ctx);
3127 ctx.handled();
3128 }
3129 Key::ENTER => {
3130 self.on_enter();
3131 self.ensure_caret_visible_ctx(ctx);
3132 ctx.handled();
3133 }
3134 Key::TAB => {
3135 self.on_tab(extend);
3136 self.ensure_caret_visible_ctx(ctx);
3137 ctx.handled();
3138 }
3139 Key::ESCAPE => {
3140 let had = self.with_state(|s| {
3141 let had = s.carets.iter().any(|c| c.anchor.is_some())
3142 || s.carets.len() > 1
3143 || !s.preedit.is_empty();
3144 s.carets.truncate(1);
3145 if s.carets.is_empty() {
3146 s.carets.push(Caret::default());
3147 }
3148 s.carets[0].anchor = None;
3149 s.preedit.clear();
3150 s.goal_x = None;
3151 Self::reset_escalate(s);
3152 had
3153 });
3154 if had {
3155 self.bump();
3156 }
3157 ctx.handled();
3158 }
3159 Key::SPACE => {
3160 if composing {
3161 ctx.handled();
3162 return;
3163 }
3164 if extend || ctrl || alt {
3165 if extend && !ctrl && !alt {
3166 self.move_caret(1, true);
3167 self.ensure_caret_visible_ctx(ctx);
3168 }
3169 ctx.handled();
3170 } else if self.editable {
3171 self.insert_str(" ");
3172 self.ensure_caret_visible_ctx(ctx);
3173 ctx.handled();
3174 } else {
3175 ctx.handled();
3176 }
3177 }
3178 other => {
3179 if let Some(c) = other.char()
3180 && !c.is_control()
3181 {
3182 if ctrl || alt {
3183 ctx.handled();
3184 return;
3185 }
3186 if composing {
3187 ctx.handled();
3188 return;
3189 }
3190 if self.editable {
3191 if !self.try_auto_close(c) {
3192 let mut s = String::new();
3193 s.push(c);
3194 self.insert_str(&s);
3195 }
3196 self.ensure_caret_visible_ctx(ctx);
3197 }
3198 ctx.handled();
3199 } else {
3200 ctx.handled();
3201 }
3202 }
3203 }
3204 }
3205
3206 fn move_vertical_ctx(&self, dir: isize, extend: bool, ctx: &mut EventCtx) {
3207 self.move_visual(dir, 1, extend, ctx);
3208 self.bump();
3209 }
3210
3211 fn ensure_caret_visible_ctx(&self, ctx: &mut EventCtx) {
3212 let theme = ctx.environment.get::<Theme>().unwrap_or_else(Theme::dark);
3213 let size = self.resolved_font_size(&theme);
3214 let weight = self.font_weight();
3215 let line_h = self.resolved_line_height(&theme);
3216 let layout = self.event_layout(ctx);
3217 let caret = self.caret();
3218 let ri = layout.row_of(caret);
3219 let Some(row) = layout.rows.get(ri) else {
3220 return;
3221 };
3222 let col = caret
3223 .saturating_sub(row.start)
3224 .min(row.end - row.start);
3225 let x = row_prefix_x(ctx, &layout, row, col, size, weight);
3226 self.ensure_visible(x, ri, line_h);
3227 }
3228
3229 fn ensure_visible(&self, caret_x: f32, line: usize, line_h: f32) {
3230 let changed = self.with_state(|s| {
3231 if s.viewport.is_empty() || line_h <= 0.0 {
3232 return false;
3233 }
3234 let before = s.scroll;
3235 apply_ensure_visible(s, self.pad_x, self.pad_y, caret_x, line, line_h);
3236 if self.soft_wrap {
3237 s.scroll.x = 0.0;
3238 }
3239 s.scroll != before
3240 });
3241 if changed {
3242 let s = self.with_state(|s| s.scroll);
3243 if let Some(f) = &self.on_scroll {
3244 f(s);
3245 }
3246 self.bump();
3247 }
3248 }
3249
3250 fn on_ime(&self, ime: &ImeEvent) {
3251 if self.disabled {
3252 return;
3253 }
3254 if !self.editable {
3255 if ime == &ImeEvent::Disabled {
3256 let had = self.with_state(|s| {
3257 let had = !s.preedit.is_empty();
3258 s.preedit.clear();
3259 had
3260 });
3261 if had {
3262 self.bump();
3263 }
3264 }
3265 return;
3266 }
3267 match ime {
3268 ImeEvent::Enabled => {}
3269 ImeEvent::Preedit { value, cursor } => {
3270 self.with_state(|s| {
3271 s.preedit = value.clone();
3272 s.preedit_cursor = *cursor;
3273 });
3274 self.bump();
3275 }
3276 ImeEvent::Commit(value) => {
3277 self.with_state(|s| {
3278 s.preedit.clear();
3279 s.preedit_cursor = None;
3280 });
3281 self.insert_str(&sanitize_multiline(value));
3282 }
3283 ImeEvent::Disabled => {
3284 let had = self.with_state(|s| {
3285 let had = !s.preedit.is_empty();
3286 s.preedit.clear();
3287 s.preedit_cursor = None;
3288 had
3289 });
3290 if had {
3291 self.bump();
3292 }
3293 }
3294
3295 _ => {}
3296 }
3297 }
3298
3299 fn on_clipboard_key(&self, key: &KeyEvent, ctx: &mut EventCtx) -> bool {
3300 let cmd =
3301 key.modifiers.contains(Modifiers::CONTROL) || key.modifiers.contains(Modifiers::SUPER);
3302 if !cmd {
3303 return false;
3304 }
3305 let Some(c) = key.key.char().map(|c| c.to_ascii_lowercase()) else {
3306 return false;
3307 };
3308
3309 if c == 'z' && !key.modifiers.contains(Modifiers::SHIFT) {
3310 self.undo();
3311 return true;
3312 }
3313 if c == 'y' || (c == 'z' && key.modifiers.contains(Modifiers::SHIFT)) {
3314 self.redo();
3315 return true;
3316 }
3317 if !matches!(c, 'c' | 'x' | 'v' | 'a') {
3318 return false;
3319 }
3320 if c == 'a' {
3321 self.escalate_select();
3322 return true;
3323 }
3324 let Some(clip) = ctx.environment.get::<SharedClipboard>() else {
3325 return true;
3326 };
3327 match c {
3328 'c' => {
3329 if let Some(sel) = self.selected_text() {
3330 clip.0.set_text(&sel);
3331 } else {
3332 clip.0.set_text(&self.source.get_text());
3333 }
3334 }
3335 'x' => {
3336 if !self.editable {
3337 return true;
3338 }
3339 if let Some(sel) = self.selected_text() {
3340 clip.0.set_text(&sel);
3341 self.delete_selection();
3342 } else {
3343 clip.0.set_text(&self.source.get_text());
3344 self.source.set_text(String::new());
3345 self.with_state(|s| {
3346 s.carets = vec![Caret::collapsed(0)];
3347 s.goal_x = None;
3348 });
3349 self.bump();
3350 }
3351 }
3352 _ => {
3353 if !self.editable {
3354 return true;
3355 }
3356 if let Some(paste) = clip.0.get_text() {
3357 self.insert_str(&sanitize_multiline(&paste));
3358 }
3359 }
3360 }
3361 true
3362 }
3363
3364 fn caret_at_visual(
3365 &self,
3366 ctx: &mut EventCtx,
3367 layout: &VisualLayout,
3368 row_idx: usize,
3369 cx: f32,
3370 size: f32,
3371 weight: FontWeight,
3372 ) -> usize {
3373 let Some(row) = layout.rows.get(row_idx) else {
3374 return 0;
3375 };
3376 let text = layout.row_text(row);
3377 let col = ctx
3378 .char_index_from_x(&text, size, weight, cx)
3379 .min(row.end - row.start);
3380 row.start + col
3381 }
3382}
3383
3384fn clamp_scroll(
3385 scroll: Vec2,
3386 content_w: f32,
3387 content_h: f32,
3388 viewport: Size,
3389 pad: (f32, f32),
3390) -> Vec2 {
3391 let max_x = (content_w + pad.0 * 2.0 - viewport.width).max(0.0);
3392 let max_y = (content_h + pad.1 * 2.0 - viewport.height).max(0.0);
3393 Vec2::new(
3394 if viewport.width <= 0.0 {
3395 scroll.x.max(0.0)
3396 } else {
3397 scroll.x.clamp(0.0, max_x)
3398 },
3399 if viewport.height <= 0.0 {
3400 scroll.y.max(0.0)
3401 } else {
3402 scroll.y.clamp(0.0, max_y)
3403 },
3404 )
3405}
3406
3407fn apply_ensure_visible(
3408 s: &mut AreaState,
3409 pad_x: f32,
3410 pad_y: f32,
3411 caret_x: f32,
3412 line: usize,
3413 line_h: f32,
3414) {
3415 let vp = s.viewport;
3416 if vp.is_empty() {
3417 return;
3418 }
3419 let cx = pad_x + caret_x;
3420 if cx < s.scroll.x + SCROLL_MARGIN {
3421 s.scroll.x = (cx - SCROLL_MARGIN).max(0.0);
3422 } else if cx + CARET_W + SCROLL_MARGIN > s.scroll.x + vp.width {
3423 s.scroll.x = (cx + CARET_W + SCROLL_MARGIN - vp.width).max(0.0);
3424 }
3425 let top = pad_y + line as f32 * line_h;
3426 if top < s.scroll.y {
3427 s.scroll.y = top.max(0.0);
3428 } else if top + line_h > s.scroll.y + vp.height {
3429 s.scroll.y = (top + line_h - vp.height).max(0.0);
3430 }
3431}
3432
3433impl View for TextArea {
3434 fn body(&self) -> impl View + 'static {
3435 Unit
3436 }
3437
3438 fn is_leaf(&self) -> bool {
3439 true
3440 }
3441
3442 fn focusable(&self) -> bool {
3443 !self.disabled
3444 }
3445
3446 fn accepts_text_input(&self) -> bool {
3447 !self.disabled
3448 }
3449
3450 fn accessibility(&self) -> Option<sequoia_view::Accessible> {
3451 let text = self.source.get_text();
3452 let value: String = text.chars().take(4096).collect();
3453 Some(
3454 sequoia_view::Accessible::new(
3455 sequoia_view::AccessibleRole::TextArea,
3456 if self.placeholder.is_empty() {
3457 "pole tekstowe".to_string()
3458 } else {
3459 self.placeholder.clone()
3460 },
3461 )
3462 .value(value)
3463 .caret(Some(self.caret()))
3464 .selection(self.selection())
3465 .disabled(self.disabled),
3466 )
3467 }
3468
3469 fn ime_caret_rect(&self) -> Option<Rect> {
3470 self.with_state(|s| s.caret_rect)
3471 }
3472
3473 fn accepts_tab(&self) -> bool {
3474 self.accept_tab && !self.disabled
3475 }
3476
3477 fn has_visual_state(&self) -> bool {
3478 true
3479 }
3480
3481 fn clip_self(&self) -> bool {
3482 true
3483 }
3484
3485 fn clip_radius(&self) -> f32 {
3486 self.radius
3487 .or_else(|| self.appearance.and_then(|s| s.radius))
3488 .unwrap_or(3.0)
3489 .max(0.0)
3490 }
3491
3492 fn reuse_state(&self, old: &Self) {
3493 if !Rc::ptr_eq(&self.state, &old.state) {
3494 *self.state.borrow_mut() = old.state.borrow().clone();
3495 }
3496 self.revision.set(old.revision.get_untracked());
3497 }
3498
3499 fn keyed_children(&self) -> Option<Vec<(u64, AnyView)>> {
3500 self.source.track();
3501 let _ = self.revision.get();
3502
3503 Some(Vec::new())
3504 }
3505
3506 fn cursor_icon(&self, _state: VisualState) -> CursorIcon {
3507 if self.disabled {
3508 CursorIcon::Default
3509 } else {
3510 CursorIcon::Text
3511 }
3512 }
3513
3514 fn layout_style(&self) -> LayoutStyle {
3515 let mut s = self.style;
3516 s.margin = self.margin;
3517 s
3518 }
3519
3520 fn intrinsic_size(&self, ctx: &mut LayoutCtx) -> Option<Size> {
3521 let size = self
3522 .font_size
3523 .or_else(|| self.appearance.and_then(|s| s.font_size))
3524 .unwrap_or(14.0);
3525 let weight = self.font_weight();
3526 let line_h = self.line_height();
3527 let text = self.source.get_text();
3528 let lines = split_lines(&text);
3529 let mut w: f32 = 0.0;
3530 for line in &lines {
3531 w = w.max(ctx.text_width_weighted(line, size, weight));
3532 }
3533 if !self.placeholder.is_empty() && text.is_empty() {
3534 for line in split_lines(&self.placeholder) {
3535 w = w.max(ctx.text_width_weighted(line, size, weight));
3536 }
3537 }
3538 let mut rows = lines.len().max(self.min_lines).max(1);
3539 if let Some(max) = self.max_lines {
3540 rows = rows.min(max.max(1));
3541 }
3542 let rows = rows as f32;
3543 Some(Size::new(
3544 w + self.pad_x * 2.0,
3545 rows * line_h + self.pad_y * 2.0,
3546 ))
3547 }
3548
3549 fn on_event(&self, event: &Event, ctx: &mut EventCtx) {
3550 if self.disabled {
3551 return;
3552 }
3553 if ctx.phase != EventPhase::Target {
3554 if let Event::Pointer(PointerEvent::Wheel {
3555 delta, modifiers, ..
3556 }) = event
3557 && matches!(ctx.phase, EventPhase::Bubble)
3558 {
3559 self.on_wheel(*delta, *modifiers, ctx);
3560 }
3561 return;
3562 }
3563 match event {
3564 Event::Pointer(PointerEvent::Down {
3565 button: PointerButton::Left,
3566 pos,
3567 modifiers,
3568 ..
3569 }) => {
3570 self.begin_session();
3571 let theme = ctx.environment.get::<Theme>().unwrap_or_else(Theme::dark);
3572 let size = self.resolved_font_size(&theme);
3573 let weight = self.font_weight();
3574 let line_h = self.resolved_line_height(&theme);
3575 let layout = self.event_layout(ctx);
3576 let scroll = self.with_state(|s| s.scroll);
3577 let cx = pos.x - ctx.rect.left() - self.pad_x + scroll.x;
3578 let cy = pos.y - ctx.rect.top() - self.pad_y + scroll.y;
3579 let ri = Self::row_at_y(line_h, layout.rows.len(), cy);
3580 let caret = self.caret_at_visual(ctx, &layout, ri, cx, size, weight);
3581 let extend = modifiers.contains(Modifiers::SHIFT);
3582 let alt = modifiers.contains(Modifiers::ALT);
3583 self.handle_click(caret, extend, alt, now_ms());
3584 self.ensure_caret_visible_ctx(ctx);
3585 self.bump();
3586 ctx.request_focus();
3587
3588 ctx.capture_pointer();
3589 ctx.handled();
3590 }
3591 Event::Pointer(PointerEvent::Move { pos, .. }) => {
3592 if ctx.state.pressed {
3593 let theme = ctx.environment.get::<Theme>().unwrap_or_else(Theme::dark);
3594 let size = self.resolved_font_size(&theme);
3595 let weight = self.font_weight();
3596 let line_h = self.resolved_line_height(&theme);
3597 let layout = self.event_layout(ctx);
3598 let scroll = self.with_state(|s| s.scroll);
3599 let cx = pos.x - ctx.rect.left() - self.pad_x + scroll.x;
3600 let cy = pos.y - ctx.rect.top() - self.pad_y + scroll.y;
3601 let ri = Self::row_at_y(line_h, layout.rows.len(), cy);
3602 let caret = self.caret_at_visual(ctx, &layout, ri, cx, size, weight);
3603 let alt_drag = self.with_state(|s| s.alt_drag);
3604 match alt_drag {
3605 Some(idx) => self.with_state(|s| {
3606 if let Some(c) = s.carets.get_mut(idx) {
3607 c.move_to(caret, true);
3608 }
3609 s.goal_x = None;
3610 }),
3611 None => self.set_caret_extend(caret, true),
3612 }
3613 self.ensure_caret_visible_ctx(ctx);
3614 self.bump();
3615 ctx.handled();
3616 }
3617 }
3618 Event::Pointer(PointerEvent::Wheel {
3619 delta, modifiers, ..
3620 }) => {
3621 self.on_wheel(*delta, *modifiers, ctx);
3622 }
3623 Event::Key(key) => {
3624 if self.on_style_shortcut(key) {
3625 self.ensure_caret_visible_ctx(ctx);
3626 ctx.handled();
3627 } else if !self.on_clipboard_key(key, ctx) {
3628 self.on_key(key, ctx);
3629 } else {
3630 self.ensure_caret_visible_ctx(ctx);
3631 ctx.handled();
3632 }
3633 }
3634 Event::Ime(ime) => {
3635 self.on_ime(ime);
3636 ctx.handled();
3637 }
3638 _ => {}
3639 }
3640 }
3641
3642 fn paint_self(&self, ctx: &mut PaintCtx) {
3643 self.note_focus(ctx.state.focused);
3644 let rect = ctx.rect;
3645 if rect.size.width <= 0.0 || rect.size.height <= 0.0 {
3646 return;
3647 }
3648 let theme = ctx.theme();
3649 let size = self.resolved_font_size(&theme);
3650 let weight = self.font_weight();
3651 let line_h = self.resolved_line_height(&theme);
3652 if line_h <= 0.0 {
3653 return;
3654 }
3655 let text_color = if self.disabled {
3656 theme.disabled
3657 } else {
3658 self.resolved_text_color(&theme)
3659 };
3660 let placeholder_color = if self.disabled {
3661 theme.disabled
3662 } else {
3663 self.resolved_placeholder_color(&theme)
3664 };
3665 let background = self.resolved_background(&theme);
3666 let radius = self.resolved_radius(&theme);
3667
3668 self.with_state(|s| s.viewport = rect.size);
3669
3670 let width = (rect.size.width - self.pad_x * 2.0).max(0.0);
3671 let layout = self.layout_for(ctx, width, size, weight);
3672 let total_chars = layout
3673 .line_starts
3674 .last()
3675 .copied()
3676 .unwrap_or(0)
3677 + layout.lines.last().map(|l| l.chars().count()).unwrap_or(0);
3678 let rows = layout.rows.len();
3679 let content_w = if self.soft_wrap {
3680 width
3681 } else {
3682 layout
3683 .lines
3684 .iter()
3685 .map(|line| ctx.text_width_weighted(line, size, weight))
3686 .fold(0.0f32, f32::max)
3687 };
3688 let content_h = rows as f32 * line_h;
3689
3690 let (caret, anchor, preedit, preedit_cursor) = self.with_state(|s| {
3691 let main = Self::main_of(s);
3692 (
3693 main.offset,
3694 main.anchor,
3695 s.preedit.clone(),
3696 s.preedit_cursor,
3697 )
3698 });
3699 let caret = caret.min(total_chars);
3700 let selection = anchor.map(|a| (a.min(caret), a.max(caret)));
3701 let composing = !preedit.is_empty();
3702 let comp_start = selection.map(|(a, _)| a).unwrap_or(caret);
3703 let comp_end = selection.map(|(_, b)| b).unwrap_or(caret);
3704 let caret_row = layout.row_of(comp_start);
3705 let caret_span = layout
3706 .rows
3707 .get(caret_row)
3708 .copied()
3709 .unwrap_or(RowSpan {
3710 line: 0,
3711 start: 0,
3712 end: 0,
3713 });
3714 let caret_col = comp_start
3715 .saturating_sub(caret_span.start)
3716 .min(caret_span.end - caret_span.start);
3717 let base_x = row_prefix_x(ctx, &layout, &caret_span, caret_col, size, weight);
3718 let caret_x = if composing {
3719 let inside = match preedit_cursor {
3720 Some((cur, _)) => {
3721 let mut i = cur.min(preedit.len());
3722 while i > 0 && !preedit.is_char_boundary(i) {
3723 i -= 1;
3724 }
3725 ctx.text_width_weighted(&preedit[..i], size, weight)
3726 }
3727 None => ctx.text_width_weighted(&preedit, size, weight),
3728 };
3729 base_x + inside
3730 } else {
3731 base_x
3732 };
3733 self.with_state(|s| {
3734 s.content = Size::new(content_w, content_h);
3735 s.scroll = clamp_scroll(
3736 s.scroll,
3737 content_w,
3738 content_h,
3739 rect.size,
3740 (self.pad_x, self.pad_y),
3741 );
3742 apply_ensure_visible(s, self.pad_x, self.pad_y, caret_x, caret_row, line_h);
3743 if self.soft_wrap {
3744 s.scroll.x = 0.0;
3745 }
3746 s.scroll = clamp_scroll(
3747 s.scroll,
3748 content_w,
3749 content_h,
3750 rect.size,
3751 (self.pad_x, self.pad_y),
3752 );
3753 });
3754 let scroll = self.with_state(|s| s.scroll);
3755
3756 let border = if self.error {
3757 Some((1.5, sequoia_style::FORM_ERROR))
3758 } else {
3759 self.resolved_border(&theme)
3760 };
3761 if background.a > 0.0 {
3762 match border {
3763 Some((w, color)) => {
3764 ctx.bordered_quad(rect, background, radius, w, color);
3765 }
3766 None => {
3767 ctx.quad(rect, background, radius);
3768 }
3769 }
3770 }
3771
3772 let ox = rect.left() + self.pad_x - scroll.x;
3773 let oy = rect.top() + self.pad_y - scroll.y;
3774 let baseline_at =
3775 |row: usize| -> f32 { oy + row as f32 * line_h + line_h * 0.5 + size * 0.35 };
3776 let top_at = |row: usize| -> f32 { oy + row as f32 * line_h };
3777
3778 let first_row = ((scroll.y - self.pad_y) / line_h).floor().max(0.0) as usize;
3779 let last_row = (((scroll.y - self.pad_y + rect.size.height) / line_h)
3780 .ceil()
3781 .max(0.0) as usize)
3782 .min(rows.saturating_sub(1));
3783
3784 if ctx.state.focused && self.highlight_current_line && anchor.is_none() {
3785 let color = self.current_line.unwrap_or(Color {
3786 r: 1.0,
3787 g: 1.0,
3788 b: 1.0,
3789 a: 0.05,
3790 });
3791 if color.a > 0.0 && caret_row >= first_row && caret_row <= last_row {
3792 ctx.quad(
3793 Rect::from_xywh(rect.left(), top_at(caret_row), rect.size.width, line_h),
3794 color,
3795 0.0,
3796 );
3797 }
3798 }
3799
3800 if let Some(a) = anchor.filter(|_| !composing) {
3801 let (s0, s1) = (a.min(caret).min(total_chars), a.max(caret).min(total_chars));
3802 if s1 > s0 {
3803 for (ri, row) in layout.rows.iter().enumerate() {
3804 if ri < first_row || ri > last_row {
3805 continue;
3806 }
3807 if s1 <= row.start || s0 > row.end {
3808 continue;
3809 }
3810 let row_chars = row.end - row.start;
3811 let col0 = s0.saturating_sub(row.start).min(row_chars);
3812 let x0 = row_prefix_x(ctx, &layout, row, col0, size, weight);
3813 let x1 = if s1 > row.end {
3814 width.max(x0 + 4.0)
3815 } else {
3816 let col1 = s1.saturating_sub(row.start).min(row_chars);
3817 row_prefix_x(ctx, &layout, row, col1, size, weight)
3818 };
3819 if x1 > x0 {
3820 ctx.quad(
3821 Rect::from_xywh(ox + x0, top_at(ri), x1 - x0, line_h),
3822 theme.accent_pressed,
3823 0.0,
3824 );
3825 }
3826 }
3827 }
3828 }
3829
3830 if layout.text.is_empty() && preedit.is_empty() && !self.placeholder.is_empty() {
3831 for (row, line) in split_lines(&self.placeholder).iter().enumerate() {
3832 if row < first_row || row > last_row {
3833 continue;
3834 }
3835 ctx.draw_text_weighted(
3836 line,
3837 (ox, baseline_at(row)),
3838 size,
3839 weight,
3840 placeholder_color,
3841 );
3842 }
3843 } else {
3844 for ri in first_row..=last_row.min(rows.saturating_sub(1)) {
3845 let Some(row) = layout.rows.get(ri) else {
3846 continue;
3847 };
3848 let line = layout.row_text(row);
3849 if line.is_empty() {
3850 continue;
3851 }
3852 if ri == caret_row && composing && ctx.state.focused {
3853 let line_chars = line.chars().count();
3854 let end_col = comp_end.saturating_sub(row.start).min(line_chars);
3855 let before: String = line.chars().take(caret_col.min(line_chars)).collect();
3856 let after: String = line.chars().skip(end_col).collect();
3857 let bx = ctx.text_width_weighted(&before, size, weight);
3858 ctx.draw_text_weighted(
3859 &before,
3860 (ox, baseline_at(ri)),
3861 size,
3862 weight,
3863 text_color,
3864 );
3865 ctx.draw_text_weighted(
3866 &preedit,
3867 (ox + bx, baseline_at(ri)),
3868 size,
3869 weight,
3870 text_color,
3871 );
3872 let pw = ctx.text_width_weighted(&preedit, size, weight);
3873 ctx.draw_text_weighted(
3874 &after,
3875 (ox + bx + pw, baseline_at(ri)),
3876 size,
3877 weight,
3878 text_color,
3879 );
3880 ctx.quad(
3881 Rect::from_xywh(ox + bx, top_at(ri) + line_h - 2.0, pw.max(1.0), 1.5),
3882 text_color,
3883 0.0,
3884 );
3885 } else {
3886 ctx.draw_text_weighted(
3887 &line,
3888 (ox, baseline_at(ri)),
3889 size,
3890 weight,
3891 text_color,
3892 );
3893 }
3894 }
3895 }
3896
3897 let caret_rect = ctx
3898 .state
3899 .focused
3900 .then(|| {
3901 Rect::from_xywh(
3902 ox + caret_x,
3903 baseline_at(caret_row) - size * 0.8,
3904 CARET_W,
3905 size * 1.05,
3906 )
3907 })
3908 .filter(|_| !self.disabled);
3909 self.with_state(|s| s.caret_rect = caret_rect);
3910 if let Some(r) = caret_rect {
3911 ctx.quad(r, text_color, 0.0);
3912 }
3913 }
3914}
3915
3916impl TextArea {
3917 fn on_wheel(&self, delta: Vec2, modifiers: Modifiers, ctx: &mut EventCtx) {
3918 let (mut dx, mut dy) = (delta.x, delta.y);
3919 if modifiers.contains(Modifiers::SHIFT) {
3920 std::mem::swap(&mut dx, &mut dy);
3921 }
3922 let (cur, viewport, content) = self.with_state(|s| (s.scroll, s.viewport, s.content));
3923 let next = clamp_scroll(
3924 Vec2::new(cur.x - dx, cur.y - dy),
3925 content.width,
3926 content.height,
3927 viewport,
3928 self.pad(),
3929 );
3930 if next != cur {
3931 self.with_state(|s| s.scroll = next);
3932 if let Some(f) = &self.on_scroll {
3933 f(next);
3934 }
3935 self.bump();
3936 ctx.handled();
3937 }
3938 }
3939}
3940
3941fn sanitize_margin(v: f32) -> f32 {
3942 if v.is_finite() { v.max(0.0) } else { 0.0 }
3943}
3944
3945#[cfg(test)]
3946mod tests {
3947 use super::*;
3948
3949 fn chars_of(s: &str) -> Vec<char> {
3950 s.chars().collect()
3951 }
3952
3953 #[test]
3954 fn line_starts_cover_empty_and_trailing_newline() {
3955 assert_eq!(line_starts(&chars_of("")), vec![0]);
3956 assert_eq!(line_starts(&chars_of("ab")), vec![0]);
3957 assert_eq!(line_starts(&chars_of("a\nb")), vec![0, 2]);
3958 assert_eq!(line_starts(&chars_of("a\n")), vec![0, 2]);
3959 assert_eq!(line_starts(&chars_of("\n")), vec![0, 1]);
3960 }
3961
3962 #[test]
3963 fn caret_roundtrips_through_line_col() {
3964 let text = "ab\ncde\n\nf";
3965 let chars = chars_of(text);
3966 let starts = line_starts(&chars);
3967 assert_eq!(starts.len(), 4);
3968 for caret in 0..=chars.len() {
3969 let (line, col) = line_col_of(&chars, &starts, caret);
3970 assert_eq!(caret_of_line_col(&chars, &starts, line, col), caret);
3971 }
3972 assert_eq!(line_col_of(&chars, &starts, 0), (0, 0));
3973 assert_eq!(line_col_of(&chars, &starts, 2), (0, 2));
3974 assert_eq!(line_col_of(&chars, &starts, 3), (1, 0));
3975 assert_eq!(line_col_of(&chars, &starts, 7), (2, 0));
3976 assert_eq!(line_col_of(&chars, &starts, 8), (3, 0));
3977 }
3978
3979 #[test]
3980 fn sanitize_multiline_normalizes_newlines() {
3981 assert_eq!(sanitize_multiline("a\r\nb\rc"), "a\nb\nc");
3982 assert_eq!(sanitize_multiline("a\x01b\tc"), "ab\tc");
3983 }
3984
3985 #[test]
3986 fn leading_indent_counts_spaces_and_tabs() {
3987 assert_eq!(leading_indent_len(" foo"), 4);
3988 assert_eq!(leading_indent_len("\t\tfoo"), 2);
3989 assert_eq!(leading_indent_len("foo"), 0);
3990 }
3991}