Skip to main content

sequoia_elements/
text_area.rs

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