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