gpui_component/input/
state.rs

1//! A text input field that allows the user to enter text.
2//!
3//! Based on the `Input` example from the `gpui` crate.
4//! https://github.com/zed-industries/zed/blob/main/crates/gpui/examples/input.rs
5use anyhow::Result;
6use gpui::{
7    Action, App, AppContext, Bounds, ClipboardItem, Context, Entity, EntityInputHandler,
8    EventEmitter, FocusHandle, Focusable, InteractiveElement as _, IntoElement, KeyBinding,
9    KeyDownEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _,
10    Pixels, Point, Render, ScrollHandle, ScrollWheelEvent, SharedString, Styled as _, Subscription,
11    Task, UTF16Selection, Window, actions, div, point, prelude::FluentBuilder as _, px,
12};
13use ropey::{Rope, RopeSlice};
14use serde::Deserialize;
15use std::cell::RefCell;
16use std::ops::Range;
17use std::rc::Rc;
18use sum_tree::Bias;
19use unicode_segmentation::*;
20
21use super::{
22    TabSize, blink_cursor::BlinkCursor, change::Change, element::TextElement,
23    mask_pattern::MaskPattern, mode::InputMode, number_input, text_wrapper::TextWrapper,
24};
25use crate::actions::{SelectDown, SelectLeft, SelectRight, SelectUp};
26use crate::input::movement::MoveDirection;
27use crate::input::{
28    HoverDefinition, Lsp, Position,
29    element::RIGHT_MARGIN,
30    popovers::{ContextMenu, DiagnosticPopover, HoverPopover, MouseContextMenu},
31    search::{self, SearchPanel},
32    text_wrapper::LineLayout,
33};
34use crate::input::{RopeExt as _, Selection};
35use crate::{Root, history::History};
36use crate::{highlighter::DiagnosticSet, input::text_wrapper::LineItem};
37
38#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
39#[action(namespace = input, no_json)]
40pub struct Enter {
41    /// Is confirm with secondary.
42    pub secondary: bool,
43}
44
45actions!(
46    input,
47    [
48        Backspace,
49        Delete,
50        DeleteToBeginningOfLine,
51        DeleteToEndOfLine,
52        DeleteToPreviousWordStart,
53        DeleteToNextWordEnd,
54        Indent,
55        Outdent,
56        IndentInline,
57        OutdentInline,
58        MoveUp,
59        MoveDown,
60        MoveLeft,
61        MoveRight,
62        MoveHome,
63        MoveEnd,
64        MovePageUp,
65        MovePageDown,
66        SelectAll,
67        SelectToStartOfLine,
68        SelectToEndOfLine,
69        SelectToStart,
70        SelectToEnd,
71        SelectToPreviousWordStart,
72        SelectToNextWordEnd,
73        ShowCharacterPalette,
74        Copy,
75        Cut,
76        Paste,
77        Undo,
78        Redo,
79        MoveToStartOfLine,
80        MoveToEndOfLine,
81        MoveToStart,
82        MoveToEnd,
83        MoveToPreviousWord,
84        MoveToNextWord,
85        Escape,
86        ToggleCodeActions,
87        Search,
88        GoToDefinition,
89    ]
90);
91
92#[derive(Clone)]
93pub enum InputEvent {
94    Change,
95    PressEnter { secondary: bool },
96    Focus,
97    Blur,
98}
99
100pub(super) const CONTEXT: &str = "Input";
101
102pub(crate) fn init(cx: &mut App) {
103    cx.bind_keys([
104        KeyBinding::new("backspace", Backspace, Some(CONTEXT)),
105        KeyBinding::new("delete", Delete, Some(CONTEXT)),
106        #[cfg(target_os = "macos")]
107        KeyBinding::new("cmd-backspace", DeleteToBeginningOfLine, Some(CONTEXT)),
108        #[cfg(target_os = "macos")]
109        KeyBinding::new("cmd-delete", DeleteToEndOfLine, Some(CONTEXT)),
110        #[cfg(target_os = "macos")]
111        KeyBinding::new("alt-backspace", DeleteToPreviousWordStart, Some(CONTEXT)),
112        #[cfg(not(target_os = "macos"))]
113        KeyBinding::new("ctrl-backspace", DeleteToPreviousWordStart, Some(CONTEXT)),
114        #[cfg(target_os = "macos")]
115        KeyBinding::new("alt-delete", DeleteToNextWordEnd, Some(CONTEXT)),
116        #[cfg(not(target_os = "macos"))]
117        KeyBinding::new("ctrl-delete", DeleteToNextWordEnd, Some(CONTEXT)),
118        KeyBinding::new("enter", Enter { secondary: false }, Some(CONTEXT)),
119        KeyBinding::new("secondary-enter", Enter { secondary: true }, Some(CONTEXT)),
120        KeyBinding::new("escape", Escape, Some(CONTEXT)),
121        KeyBinding::new("up", MoveUp, Some(CONTEXT)),
122        KeyBinding::new("down", MoveDown, Some(CONTEXT)),
123        KeyBinding::new("left", MoveLeft, Some(CONTEXT)),
124        KeyBinding::new("right", MoveRight, Some(CONTEXT)),
125        KeyBinding::new("pageup", MovePageUp, Some(CONTEXT)),
126        KeyBinding::new("pagedown", MovePageDown, Some(CONTEXT)),
127        KeyBinding::new("tab", IndentInline, Some(CONTEXT)),
128        KeyBinding::new("shift-tab", OutdentInline, Some(CONTEXT)),
129        #[cfg(target_os = "macos")]
130        KeyBinding::new("cmd-]", Indent, Some(CONTEXT)),
131        #[cfg(not(target_os = "macos"))]
132        KeyBinding::new("ctrl-]", Indent, Some(CONTEXT)),
133        #[cfg(target_os = "macos")]
134        KeyBinding::new("cmd-[", Outdent, Some(CONTEXT)),
135        #[cfg(not(target_os = "macos"))]
136        KeyBinding::new("ctrl-[", Outdent, Some(CONTEXT)),
137        KeyBinding::new("shift-left", SelectLeft, Some(CONTEXT)),
138        KeyBinding::new("shift-right", SelectRight, Some(CONTEXT)),
139        KeyBinding::new("shift-up", SelectUp, Some(CONTEXT)),
140        KeyBinding::new("shift-down", SelectDown, Some(CONTEXT)),
141        KeyBinding::new("home", MoveHome, Some(CONTEXT)),
142        KeyBinding::new("end", MoveEnd, Some(CONTEXT)),
143        KeyBinding::new("shift-home", SelectToStartOfLine, Some(CONTEXT)),
144        KeyBinding::new("shift-end", SelectToEndOfLine, Some(CONTEXT)),
145        #[cfg(target_os = "macos")]
146        KeyBinding::new("ctrl-shift-a", SelectToStartOfLine, Some(CONTEXT)),
147        #[cfg(target_os = "macos")]
148        KeyBinding::new("ctrl-shift-e", SelectToEndOfLine, Some(CONTEXT)),
149        #[cfg(target_os = "macos")]
150        KeyBinding::new("shift-cmd-left", SelectToStartOfLine, Some(CONTEXT)),
151        #[cfg(target_os = "macos")]
152        KeyBinding::new("shift-cmd-right", SelectToEndOfLine, Some(CONTEXT)),
153        #[cfg(target_os = "macos")]
154        KeyBinding::new("alt-shift-left", SelectToPreviousWordStart, Some(CONTEXT)),
155        #[cfg(not(target_os = "macos"))]
156        KeyBinding::new("ctrl-shift-left", SelectToPreviousWordStart, Some(CONTEXT)),
157        #[cfg(target_os = "macos")]
158        KeyBinding::new("alt-shift-right", SelectToNextWordEnd, Some(CONTEXT)),
159        #[cfg(not(target_os = "macos"))]
160        KeyBinding::new("ctrl-shift-right", SelectToNextWordEnd, Some(CONTEXT)),
161        #[cfg(target_os = "macos")]
162        KeyBinding::new("ctrl-cmd-space", ShowCharacterPalette, Some(CONTEXT)),
163        #[cfg(target_os = "macos")]
164        KeyBinding::new("cmd-a", SelectAll, Some(CONTEXT)),
165        #[cfg(not(target_os = "macos"))]
166        KeyBinding::new("ctrl-a", SelectAll, Some(CONTEXT)),
167        #[cfg(target_os = "macos")]
168        KeyBinding::new("cmd-c", Copy, Some(CONTEXT)),
169        #[cfg(not(target_os = "macos"))]
170        KeyBinding::new("ctrl-c", Copy, Some(CONTEXT)),
171        #[cfg(target_os = "macos")]
172        KeyBinding::new("cmd-x", Cut, Some(CONTEXT)),
173        #[cfg(not(target_os = "macos"))]
174        KeyBinding::new("ctrl-x", Cut, Some(CONTEXT)),
175        #[cfg(target_os = "macos")]
176        KeyBinding::new("cmd-v", Paste, Some(CONTEXT)),
177        #[cfg(not(target_os = "macos"))]
178        KeyBinding::new("ctrl-v", Paste, Some(CONTEXT)),
179        #[cfg(target_os = "macos")]
180        KeyBinding::new("ctrl-a", MoveHome, Some(CONTEXT)),
181        #[cfg(target_os = "macos")]
182        KeyBinding::new("cmd-left", MoveHome, Some(CONTEXT)),
183        #[cfg(target_os = "macos")]
184        KeyBinding::new("ctrl-e", MoveEnd, Some(CONTEXT)),
185        #[cfg(target_os = "macos")]
186        KeyBinding::new("cmd-right", MoveEnd, Some(CONTEXT)),
187        #[cfg(target_os = "macos")]
188        KeyBinding::new("cmd-z", Undo, Some(CONTEXT)),
189        #[cfg(target_os = "macos")]
190        KeyBinding::new("cmd-shift-z", Redo, Some(CONTEXT)),
191        #[cfg(target_os = "macos")]
192        KeyBinding::new("cmd-up", MoveToStart, Some(CONTEXT)),
193        #[cfg(target_os = "macos")]
194        KeyBinding::new("cmd-down", MoveToEnd, Some(CONTEXT)),
195        #[cfg(target_os = "macos")]
196        KeyBinding::new("alt-left", MoveToPreviousWord, Some(CONTEXT)),
197        #[cfg(target_os = "macos")]
198        KeyBinding::new("alt-right", MoveToNextWord, Some(CONTEXT)),
199        #[cfg(not(target_os = "macos"))]
200        KeyBinding::new("ctrl-left", MoveToPreviousWord, Some(CONTEXT)),
201        #[cfg(not(target_os = "macos"))]
202        KeyBinding::new("ctrl-right", MoveToNextWord, Some(CONTEXT)),
203        #[cfg(target_os = "macos")]
204        KeyBinding::new("cmd-shift-up", SelectToStart, Some(CONTEXT)),
205        #[cfg(target_os = "macos")]
206        KeyBinding::new("cmd-shift-down", SelectToEnd, Some(CONTEXT)),
207        #[cfg(not(target_os = "macos"))]
208        KeyBinding::new("ctrl-z", Undo, Some(CONTEXT)),
209        #[cfg(not(target_os = "macos"))]
210        KeyBinding::new("ctrl-y", Redo, Some(CONTEXT)),
211        #[cfg(target_os = "macos")]
212        KeyBinding::new("cmd-.", ToggleCodeActions, Some(CONTEXT)),
213        #[cfg(not(target_os = "macos"))]
214        KeyBinding::new("ctrl-.", ToggleCodeActions, Some(CONTEXT)),
215        #[cfg(target_os = "macos")]
216        KeyBinding::new("cmd-f", Search, Some(CONTEXT)),
217        #[cfg(not(target_os = "macos"))]
218        KeyBinding::new("ctrl-f", Search, Some(CONTEXT)),
219    ]);
220
221    search::init(cx);
222    number_input::init(cx);
223}
224
225#[derive(Clone)]
226pub(super) struct LastLayout {
227    /// The visible range (no wrap) of lines in the viewport, the value is row (0-based) index.
228    pub(super) visible_range: Range<usize>,
229    /// The first visible line top position in scroll viewport.
230    pub(super) visible_top: Pixels,
231    /// The range of byte offset of the visible lines.
232    pub(super) visible_range_offset: Range<usize>,
233    /// The last layout lines (Only have visible lines).
234    pub(super) lines: Rc<Vec<LineLayout>>,
235    /// The line_height of text layout, this will change will InputElement painted.
236    pub(super) line_height: Pixels,
237    /// The wrap width of text layout, this will change will InputElement painted.
238    pub(super) wrap_width: Option<Pixels>,
239    /// The line number area width of text layout, if not line number, this will be 0px.
240    pub(super) line_number_width: Pixels,
241    /// The cursor position (top, left) in pixels.
242    pub(super) cursor_bounds: Option<Bounds<Pixels>>,
243}
244
245impl LastLayout {
246    /// Get the line layout for the given row (0-based).
247    ///
248    /// 0 is the viewport first visible line.
249    ///
250    /// Returns None if the row is out of range.
251    pub(crate) fn line(&self, row: usize) -> Option<&LineLayout> {
252        if row < self.visible_range.start || row >= self.visible_range.end {
253            return None;
254        }
255
256        self.lines.get(row.saturating_sub(self.visible_range.start))
257    }
258}
259
260/// InputState to keep editing state of the [`super::Input`].
261pub struct InputState {
262    pub(super) focus_handle: FocusHandle,
263    pub(super) mode: InputMode,
264    pub(super) text: Rope,
265    pub(super) text_wrapper: TextWrapper,
266    pub(super) history: History<Change>,
267    pub(super) blink_cursor: Entity<BlinkCursor>,
268    pub(super) loading: bool,
269    /// Range in UTF-8 length for the selected text.
270    ///
271    /// - "Hello 世界💝" = 16
272    /// - "💝" = 4
273    pub(super) selected_range: Selection,
274    pub(super) search_panel: Option<Entity<SearchPanel>>,
275    pub(super) searchable: bool,
276    /// Range for save the selected word, use to keep word range when drag move.
277    pub(super) selected_word_range: Option<Selection>,
278    pub(super) selection_reversed: bool,
279    /// The marked range is the temporary insert text on IME typing.
280    pub(super) ime_marked_range: Option<Selection>,
281    pub(super) last_layout: Option<LastLayout>,
282    pub(super) last_cursor: Option<usize>,
283    /// The input container bounds
284    pub(super) input_bounds: Bounds<Pixels>,
285    /// The text bounds
286    pub(super) last_bounds: Option<Bounds<Pixels>>,
287    pub(super) last_selected_range: Option<Selection>,
288    pub(super) selecting: bool,
289    pub(super) disabled: bool,
290    pub(super) masked: bool,
291    pub(super) clean_on_escape: bool,
292    pub(super) soft_wrap: bool,
293    pub(super) pattern: Option<regex::Regex>,
294    pub(super) validate: Option<Box<dyn Fn(&str, &mut Context<Self>) -> bool + 'static>>,
295    pub(crate) scroll_handle: ScrollHandle,
296    /// The deferred scroll offset to apply on next layout.
297    pub(crate) deferred_scroll_offset: Option<Point<Pixels>>,
298    /// The size of the scrollable content.
299    pub(crate) scroll_size: gpui::Size<Pixels>,
300
301    /// The mask pattern for formatting the input text
302    pub(crate) mask_pattern: MaskPattern,
303    pub(super) placeholder: SharedString,
304
305    /// Popover
306    diagnostic_popover: Option<Entity<DiagnosticPopover>>,
307    /// Completion/CodeAction context menu
308    pub(super) context_menu: Option<ContextMenu>,
309    pub(super) mouse_context_menu: Entity<MouseContextMenu>,
310    /// A flag to indicate if we are currently inserting a completion item.
311    pub(super) completion_inserting: bool,
312    pub(super) hover_popover: Option<Entity<HoverPopover>>,
313    /// The LSP definitions locations for "Go to Definition" feature.
314    pub(super) hover_definition: HoverDefinition,
315
316    pub lsp: Lsp,
317
318    /// A flag to indicate if we have a pending update to the text.
319    ///
320    /// If true, will call some update (for example LSP, Syntax Highlight) before render.
321    _pending_update: bool,
322    /// A flag to indicate if we should ignore the next completion event.
323    pub(super) silent_replace_text: bool,
324
325    /// To remember the horizontal column (x-coordinate) of the cursor position for keep column for move up/down.
326    ///
327    /// The first element is the x-coordinate (Pixels), preferred to use this.
328    /// The second element is the column (usize), fallback to use this.
329    pub(super) preferred_column: Option<(Pixels, usize)>,
330    _subscriptions: Vec<Subscription>,
331
332    pub(super) _context_menu_task: Task<Result<()>>,
333}
334
335impl EventEmitter<InputEvent> for InputState {}
336
337impl InputState {
338    /// Create a Input state with default [`InputMode::SingleLine`] mode.
339    ///
340    /// See also: [`Self::multi_line`], [`Self::auto_grow`] to set other mode.
341    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
342        let focus_handle = cx.focus_handle().tab_stop(true);
343        let blink_cursor = cx.new(|_| BlinkCursor::new());
344        let history = History::new().group_interval(std::time::Duration::from_secs(1));
345
346        let _subscriptions = vec![
347            // Observe the blink cursor to repaint the view when it changes.
348            cx.observe(&blink_cursor, |_, _, cx| cx.notify()),
349            // Blink the cursor when the window is active, pause when it's not.
350            cx.observe_window_activation(window, |input, window, cx| {
351                if window.is_window_active() {
352                    let focus_handle = input.focus_handle.clone();
353                    if focus_handle.is_focused(window) {
354                        input.blink_cursor.update(cx, |blink_cursor, cx| {
355                            blink_cursor.start(cx);
356                        });
357                    }
358                }
359            }),
360            cx.on_focus(&focus_handle, window, Self::on_focus),
361            cx.on_blur(&focus_handle, window, Self::on_blur),
362        ];
363
364        let text_style = window.text_style();
365        let mouse_context_menu = MouseContextMenu::new(cx.entity(), window, cx);
366
367        Self {
368            focus_handle: focus_handle.clone(),
369            text: "".into(),
370            text_wrapper: TextWrapper::new(
371                text_style.font(),
372                text_style.font_size.to_pixels(window.rem_size()),
373                None,
374            ),
375            blink_cursor,
376            history,
377            selected_range: Selection::default(),
378            search_panel: None,
379            searchable: false,
380            selected_word_range: None,
381            selection_reversed: false,
382            ime_marked_range: None,
383            input_bounds: Bounds::default(),
384            selecting: false,
385            disabled: false,
386            masked: false,
387            clean_on_escape: false,
388            soft_wrap: true,
389            loading: false,
390            pattern: None,
391            validate: None,
392            mode: InputMode::SingleLine,
393            last_layout: None,
394            last_bounds: None,
395            last_selected_range: None,
396            last_cursor: None,
397            scroll_handle: ScrollHandle::new(),
398            scroll_size: gpui::size(px(0.), px(0.)),
399            deferred_scroll_offset: None,
400            preferred_column: None,
401            placeholder: SharedString::default(),
402            mask_pattern: MaskPattern::default(),
403            lsp: Lsp::default(),
404            diagnostic_popover: None,
405            context_menu: None,
406            mouse_context_menu,
407            completion_inserting: false,
408            hover_popover: None,
409            hover_definition: HoverDefinition::default(),
410            silent_replace_text: false,
411            _subscriptions,
412            _context_menu_task: Task::ready(Ok(())),
413            _pending_update: false,
414        }
415    }
416
417    /// Set Input to use [`InputMode::MultiLine`] mode.
418    ///
419    /// Default rows is 2.
420    pub fn multi_line(mut self) -> Self {
421        self.mode = InputMode::MultiLine {
422            rows: 2,
423            tab: TabSize::default(),
424        };
425        self
426    }
427
428    /// Set Input to use [`InputMode::AutoGrow`] mode with min, max rows limit.
429    pub fn auto_grow(mut self, min_rows: usize, max_rows: usize) -> Self {
430        self.mode = InputMode::AutoGrow {
431            rows: min_rows,
432            min_rows: min_rows,
433            max_rows: max_rows,
434        };
435        self
436    }
437
438    /// Set Input to use [`InputMode::CodeEditor`] mode.
439    ///
440    /// Default options:
441    ///
442    /// - line_number: true
443    /// - tab_size: 2
444    /// - hard_tabs: false
445    /// - height: full
446    ///
447    /// If `highlighter` is None, will use the default highlighter.
448    ///
449    /// Code Editor aim for help used to simple code editing or display, not a full-featured code editor.
450    ///
451    /// ## Features
452    ///
453    /// - Syntax Highlighting
454    /// - Auto Indent
455    /// - Line Number
456    /// - Large Text support, up to 50K lines.
457    pub fn code_editor(mut self, language: impl Into<SharedString>) -> Self {
458        let language: SharedString = language.into();
459        self.mode = InputMode::CodeEditor {
460            rows: 2,
461            tab: TabSize::default(),
462            language,
463            highlighter: Rc::new(RefCell::new(None)),
464            line_number: true,
465            indent_guides: true,
466            diagnostics: DiagnosticSet::new(&Rope::new()),
467        };
468        self.searchable = true;
469        self
470    }
471
472    /// Set this input is searchable, default is false (Default true for Code Editor).
473    pub fn searchable(mut self, searchable: bool) -> Self {
474        debug_assert!(self.mode.is_multi_line());
475        self.searchable = searchable;
476        self
477    }
478
479    /// Set placeholder
480    pub fn placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
481        self.placeholder = placeholder.into();
482        self
483    }
484
485    /// Set enable/disable line number, only for [`InputMode::CodeEditor`] mode.
486    pub fn line_number(mut self, line_number: bool) -> Self {
487        debug_assert!(self.mode.is_code_editor());
488        if let InputMode::CodeEditor { line_number: l, .. } = &mut self.mode {
489            *l = line_number;
490        }
491        self
492    }
493
494    /// Set line number, only for [`InputMode::CodeEditor`] mode.
495    pub fn set_line_number(&mut self, line_number: bool, _: &mut Window, cx: &mut Context<Self>) {
496        debug_assert!(self.mode.is_code_editor());
497        if let InputMode::CodeEditor { line_number: l, .. } = &mut self.mode {
498            *l = line_number;
499        }
500        cx.notify();
501    }
502
503    /// Set the number of rows for the multi-line Textarea.
504    ///
505    /// This is only used when `multi_line` is set to true.
506    ///
507    /// default: 2
508    pub fn rows(mut self, rows: usize) -> Self {
509        match &mut self.mode {
510            InputMode::MultiLine { rows: r, .. } => *r = rows,
511            InputMode::AutoGrow {
512                max_rows: max_r,
513                rows: r,
514                ..
515            } => {
516                *r = rows;
517                *max_r = rows;
518            }
519            _ => {}
520        }
521        self
522    }
523
524    /// Set highlighter language for for [`InputMode::CodeEditor`] mode.
525    pub fn set_highlighter(
526        &mut self,
527        new_language: impl Into<SharedString>,
528        cx: &mut Context<Self>,
529    ) {
530        match &mut self.mode {
531            InputMode::CodeEditor {
532                language,
533                highlighter,
534                ..
535            } => {
536                *language = new_language.into();
537                *highlighter.borrow_mut() = None;
538            }
539            _ => {}
540        }
541        cx.notify();
542    }
543
544    fn reset_highlighter(&mut self, cx: &mut Context<Self>) {
545        match &mut self.mode {
546            InputMode::CodeEditor { highlighter, .. } => {
547                *highlighter.borrow_mut() = None;
548            }
549            _ => {}
550        }
551        cx.notify();
552    }
553
554    #[inline]
555    pub fn diagnostics(&self) -> Option<&DiagnosticSet> {
556        self.mode.diagnostics()
557    }
558
559    #[inline]
560    pub fn diagnostics_mut(&mut self) -> Option<&mut DiagnosticSet> {
561        self.mode.diagnostics_mut()
562    }
563
564    /// Set placeholder
565    pub fn set_placeholder(
566        &mut self,
567        placeholder: impl Into<SharedString>,
568        _: &mut Window,
569        cx: &mut Context<Self>,
570    ) {
571        self.placeholder = placeholder.into();
572        cx.notify();
573    }
574
575    /// Find which line and sub-line the given offset belongs to, along with the position within that sub-line.
576    ///
577    /// Returns:
578    ///
579    /// - The index of the line (zero-based) containing the offset.
580    /// - The index of the sub-line (zero-based) within the line containing the offset.
581    /// - The position of the offset.
582    #[allow(unused)]
583    pub(super) fn line_and_position_for_offset(
584        &self,
585        offset: usize,
586    ) -> (usize, usize, Option<Point<Pixels>>) {
587        let Some(last_layout) = &self.last_layout else {
588            return (0, 0, None);
589        };
590        let line_height = last_layout.line_height;
591
592        let mut prev_lines_offset = last_layout.visible_range_offset.start;
593        let mut y_offset = last_layout.visible_top;
594        for (line_index, line) in last_layout.lines.iter().enumerate() {
595            let local_offset = offset.saturating_sub(prev_lines_offset);
596            if let Some(pos) = line.position_for_index(local_offset, line_height) {
597                let sub_line_index = (pos.y / line_height) as usize;
598                let adjusted_pos = point(pos.x + last_layout.line_number_width, pos.y + y_offset);
599                return (line_index, sub_line_index, Some(adjusted_pos));
600            }
601
602            y_offset += line.size(line_height).height;
603            prev_lines_offset += line.len() + 1;
604        }
605        (0, 0, None)
606    }
607
608    /// Set the text of the input field.
609    ///
610    /// And the selection_range will be reset to 0..0.
611    pub fn set_value(
612        &mut self,
613        value: impl Into<SharedString>,
614        window: &mut Window,
615        cx: &mut Context<Self>,
616    ) {
617        self.history.ignore = true;
618        let was_disabled = self.disabled;
619        self.disabled = false;
620        self.replace_text(value, window, cx);
621        self.disabled = was_disabled;
622        self.history.ignore = false;
623        // Ensure cursor to start when set text
624        if self.mode.is_single_line() {
625            self.selected_range = (self.text.len()..self.text.len()).into();
626        } else {
627            self.selected_range.clear();
628
629            self._pending_update = true;
630            self.lsp.reset();
631        }
632        // Move scroll to top
633        self.scroll_handle.set_offset(point(px(0.), px(0.)));
634
635        cx.notify();
636    }
637
638    /// Insert text at the current cursor position.
639    ///
640    /// And the cursor will be moved to the end of inserted text.
641    pub fn insert(
642        &mut self,
643        text: impl Into<SharedString>,
644        window: &mut Window,
645        cx: &mut Context<Self>,
646    ) {
647        let text: SharedString = text.into();
648        let range_utf16 = self.range_to_utf16(&(self.cursor()..self.cursor()));
649        self.replace_text_in_range_silent(Some(range_utf16), &text, window, cx);
650        self.selected_range = (self.selected_range.end..self.selected_range.end).into();
651    }
652
653    /// Replace text at the current cursor position.
654    ///
655    /// And the cursor will be moved to the end of replaced text.
656    pub fn replace(
657        &mut self,
658        text: impl Into<SharedString>,
659        window: &mut Window,
660        cx: &mut Context<Self>,
661    ) {
662        let text: SharedString = text.into();
663        self.replace_text_in_range_silent(None, &text, window, cx);
664        self.selected_range = (self.selected_range.end..self.selected_range.end).into();
665    }
666
667    fn replace_text(
668        &mut self,
669        text: impl Into<SharedString>,
670        window: &mut Window,
671        cx: &mut Context<Self>,
672    ) {
673        let text: SharedString = text.into();
674        let range = 0..self.text.chars().map(|c| c.len_utf16()).sum();
675        self.replace_text_in_range_silent(Some(range), &text, window, cx);
676        self.reset_highlighter(cx);
677    }
678
679    /// Set with disabled mode.
680    ///
681    /// See also: [`Self::set_disabled`], [`Self::is_disabled`].
682    #[allow(unused)]
683    pub(crate) fn disabled(mut self, disabled: bool) -> Self {
684        self.disabled = disabled;
685        self
686    }
687
688    /// Set with password masked state.
689    ///
690    /// Only for [`InputMode::SingleLine`] mode.
691    pub fn masked(mut self, masked: bool) -> Self {
692        debug_assert!(self.mode.is_single_line());
693        self.masked = masked;
694        self
695    }
696
697    /// Set the password masked state of the input field.
698    ///
699    /// Only for [`InputMode::SingleLine`] mode.
700    pub fn set_masked(&mut self, masked: bool, _: &mut Window, cx: &mut Context<Self>) {
701        debug_assert!(self.mode.is_single_line());
702        self.masked = masked;
703        cx.notify();
704    }
705
706    /// Set true to clear the input by pressing Escape key.
707    pub fn clean_on_escape(mut self) -> Self {
708        self.clean_on_escape = true;
709        self
710    }
711
712    /// Set the soft wrap mode for multi-line input, default is true.
713    pub fn soft_wrap(mut self, wrap: bool) -> Self {
714        debug_assert!(self.mode.is_multi_line());
715        self.soft_wrap = wrap;
716        self
717    }
718
719    /// Update the soft wrap mode for multi-line input, default is true.
720    pub fn set_soft_wrap(&mut self, wrap: bool, _: &mut Window, cx: &mut Context<Self>) {
721        debug_assert!(self.mode.is_multi_line());
722        self.soft_wrap = wrap;
723        if wrap {
724            let wrap_width = self
725                .last_layout
726                .as_ref()
727                .and_then(|b| b.wrap_width)
728                .unwrap_or(self.input_bounds.size.width);
729
730            self.text_wrapper.set_wrap_width(Some(wrap_width), cx);
731
732            // Reset scroll to left 0
733            let mut offset = self.scroll_handle.offset();
734            offset.x = px(0.);
735            self.scroll_handle.set_offset(offset);
736        } else {
737            self.text_wrapper.set_wrap_width(None, cx);
738        }
739        cx.notify();
740    }
741
742    /// Set the regular expression pattern of the input field.
743    ///
744    /// Only for [`InputMode::SingleLine`] mode.
745    pub fn pattern(mut self, pattern: regex::Regex) -> Self {
746        debug_assert!(self.mode.is_single_line());
747        self.pattern = Some(pattern);
748        self
749    }
750
751    /// Set the regular expression pattern of the input field with reference.
752    ///
753    /// Only for [`InputMode::SingleLine`] mode.
754    pub fn set_pattern(
755        &mut self,
756        pattern: regex::Regex,
757        _window: &mut Window,
758        _cx: &mut Context<Self>,
759    ) {
760        debug_assert!(self.mode.is_single_line());
761        self.pattern = Some(pattern);
762    }
763
764    /// Set the validation function of the input field.
765    ///
766    /// Only for [`InputMode::SingleLine`] mode.
767    pub fn validate(mut self, f: impl Fn(&str, &mut Context<Self>) -> bool + 'static) -> Self {
768        debug_assert!(self.mode.is_single_line());
769        self.validate = Some(Box::new(f));
770        self
771    }
772
773    /// Set true to show spinner at the input right.
774    ///
775    /// Only for [`InputMode::SingleLine`] mode.
776    pub fn set_loading(&mut self, loading: bool, _: &mut Window, cx: &mut Context<Self>) {
777        debug_assert!(self.mode.is_single_line());
778        self.loading = loading;
779        cx.notify();
780    }
781
782    /// Set the default value of the input field.
783    pub fn default_value(mut self, value: impl Into<SharedString>) -> Self {
784        let text: SharedString = value.into();
785        self.text = Rope::from(text.as_str());
786        if let Some(diagnostics) = self.mode.diagnostics_mut() {
787            diagnostics.reset(&self.text)
788        }
789        self.text_wrapper.set_default_text(&self.text);
790        self._pending_update = true;
791        self
792    }
793
794    /// Return the value of the input field.
795    pub fn value(&self) -> SharedString {
796        SharedString::new(self.text.to_string())
797    }
798
799    /// Return the value without mask.
800    pub fn unmask_value(&self) -> SharedString {
801        self.mask_pattern.unmask(&self.text.to_string()).into()
802    }
803
804    /// Return the text [`Rope`] of the input field.
805    pub fn text(&self) -> &Rope {
806        &self.text
807    }
808
809    /// Return the (0-based) [`Position`] of the cursor.
810    pub fn cursor_position(&self) -> Position {
811        let offset = self.cursor();
812        self.text.offset_to_position(offset)
813    }
814
815    /// Set (0-based) [`Position`] of the cursor.
816    ///
817    /// This will move the cursor to the specified line and column, and update the selection range.
818    pub fn set_cursor_position(
819        &mut self,
820        position: impl Into<Position>,
821        window: &mut Window,
822        cx: &mut Context<Self>,
823    ) {
824        let position: Position = position.into();
825        let offset = self.text.position_to_offset(&position);
826
827        self.move_to(offset, None, cx);
828        self.update_preferred_column();
829        self.focus(window, cx);
830    }
831
832    /// Focus the input field.
833    pub fn focus(&self, window: &mut Window, cx: &mut Context<Self>) {
834        self.focus_handle.focus(window);
835        self.blink_cursor.update(cx, |cursor, cx| {
836            cursor.start(cx);
837        });
838    }
839
840    pub(super) fn select_left(&mut self, _: &SelectLeft, _: &mut Window, cx: &mut Context<Self>) {
841        self.select_to(self.previous_boundary(self.cursor()), cx);
842    }
843
844    pub(super) fn select_right(&mut self, _: &SelectRight, _: &mut Window, cx: &mut Context<Self>) {
845        self.select_to(self.next_boundary(self.cursor()), cx);
846    }
847
848    pub(super) fn select_up(&mut self, _: &SelectUp, _: &mut Window, cx: &mut Context<Self>) {
849        if self.mode.is_single_line() {
850            return;
851        }
852        let offset = self.start_of_line().saturating_sub(1);
853        self.select_to(self.previous_boundary(offset), cx);
854    }
855
856    pub(super) fn select_down(&mut self, _: &SelectDown, _: &mut Window, cx: &mut Context<Self>) {
857        if self.mode.is_single_line() {
858            return;
859        }
860        let offset = (self.end_of_line() + 1).min(self.text.len());
861        self.select_to(self.next_boundary(offset), cx);
862    }
863
864    pub(super) fn select_all(&mut self, _: &SelectAll, _: &mut Window, cx: &mut Context<Self>) {
865        self.selected_range = (0..self.text.len()).into();
866        cx.notify();
867    }
868
869    pub(super) fn select_to_start(
870        &mut self,
871        _: &SelectToStart,
872        _: &mut Window,
873        cx: &mut Context<Self>,
874    ) {
875        self.select_to(0, cx);
876    }
877
878    pub(super) fn select_to_end(
879        &mut self,
880        _: &SelectToEnd,
881        _: &mut Window,
882        cx: &mut Context<Self>,
883    ) {
884        let end = self.text.len();
885        self.select_to(end, cx);
886    }
887
888    pub(super) fn select_to_start_of_line(
889        &mut self,
890        _: &SelectToStartOfLine,
891        _: &mut Window,
892        cx: &mut Context<Self>,
893    ) {
894        let offset = self.start_of_line();
895        self.select_to(offset, cx);
896    }
897
898    pub(super) fn select_to_end_of_line(
899        &mut self,
900        _: &SelectToEndOfLine,
901        _: &mut Window,
902        cx: &mut Context<Self>,
903    ) {
904        let offset = self.end_of_line();
905        self.select_to(offset, cx);
906    }
907
908    pub(super) fn select_to_previous_word(
909        &mut self,
910        _: &SelectToPreviousWordStart,
911        _: &mut Window,
912        cx: &mut Context<Self>,
913    ) {
914        let offset = self.previous_start_of_word();
915        self.select_to(offset, cx);
916    }
917
918    pub(super) fn select_to_next_word(
919        &mut self,
920        _: &SelectToNextWordEnd,
921        _: &mut Window,
922        cx: &mut Context<Self>,
923    ) {
924        let offset = self.next_end_of_word();
925        self.select_to(offset, cx);
926    }
927
928    /// Return the start offset of the previous word.
929    pub(super) fn previous_start_of_word(&mut self) -> usize {
930        let offset = self.selected_range.start;
931        let offset = self.offset_from_utf16(self.offset_to_utf16(offset));
932        // FIXME: Avoid to_string
933        let left_part = self.text.slice(0..offset).to_string();
934
935        UnicodeSegmentation::split_word_bound_indices(left_part.as_str())
936            .filter(|(_, s)| !s.trim_start().is_empty())
937            .next_back()
938            .map(|(i, _)| i)
939            .unwrap_or(0)
940    }
941
942    /// Return the next end offset of the next word.
943    pub(super) fn next_end_of_word(&mut self) -> usize {
944        let offset = self.cursor();
945        let offset = self.offset_from_utf16(self.offset_to_utf16(offset));
946        let right_part = self.text.slice(offset..self.text.len()).to_string();
947
948        UnicodeSegmentation::split_word_bound_indices(right_part.as_str())
949            .find(|(_, s)| !s.trim_start().is_empty())
950            .map(|(i, s)| offset + i + s.len())
951            .unwrap_or(self.text.len())
952    }
953
954    /// Get start of line byte offset of cursor
955    pub(super) fn start_of_line(&self) -> usize {
956        if self.mode.is_single_line() {
957            return 0;
958        }
959
960        let row = self.text.offset_to_point(self.cursor()).row;
961        self.text.line_start_offset(row)
962    }
963
964    /// Get end of line byte offset of cursor
965    pub(super) fn end_of_line(&self) -> usize {
966        if self.mode.is_single_line() {
967            return self.text.len();
968        }
969
970        let row = self.text.offset_to_point(self.cursor()).row;
971        self.text.line_end_offset(row)
972    }
973
974    /// Get start line of selection start or end (The min value).
975    ///
976    /// This is means is always get the first line of selection.
977    pub(super) fn start_of_line_of_selection(
978        &mut self,
979        window: &mut Window,
980        cx: &mut Context<Self>,
981    ) -> usize {
982        if self.mode.is_single_line() {
983            return 0;
984        }
985
986        let mut offset =
987            self.previous_boundary(self.selected_range.start.min(self.selected_range.end));
988        if self.text.char_at(offset) == Some('\r') {
989            offset += 1;
990        }
991
992        let line = self
993            .text_for_range(self.range_to_utf16(&(0..offset + 1)), &mut None, window, cx)
994            .unwrap_or_default()
995            .rfind('\n')
996            .map(|i| i + 1)
997            .unwrap_or(0);
998        line
999    }
1000
1001    /// Get indent string of next line.
1002    ///
1003    /// To get current and next line indent, to return more depth one.
1004    pub(super) fn indent_of_next_line(&mut self) -> String {
1005        if self.mode.is_single_line() {
1006            return "".into();
1007        }
1008
1009        let mut current_indent = String::new();
1010        let mut next_indent = String::new();
1011        let current_line_start_pos = self.start_of_line();
1012        let next_line_start_pos = self.end_of_line();
1013        for c in self.text.slice(current_line_start_pos..).chars() {
1014            if !c.is_whitespace() {
1015                break;
1016            }
1017            if c == '\n' || c == '\r' {
1018                break;
1019            }
1020            current_indent.push(c);
1021        }
1022
1023        for c in self.text.slice(next_line_start_pos..).chars() {
1024            if !c.is_whitespace() {
1025                break;
1026            }
1027            if c == '\n' || c == '\r' {
1028                break;
1029            }
1030            next_indent.push(c);
1031        }
1032
1033        if next_indent.len() > current_indent.len() {
1034            return next_indent;
1035        } else {
1036            return current_indent;
1037        }
1038    }
1039
1040    pub(super) fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
1041        if self.selected_range.is_empty() {
1042            self.select_to(self.previous_boundary(self.cursor()), cx)
1043        }
1044        self.replace_text_in_range(None, "", window, cx);
1045        self.pause_blink_cursor(cx);
1046    }
1047
1048    pub(super) fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
1049        if self.selected_range.is_empty() {
1050            self.select_to(self.next_boundary(self.cursor()), cx)
1051        }
1052        self.replace_text_in_range(None, "", window, cx);
1053        self.pause_blink_cursor(cx);
1054    }
1055
1056    pub(super) fn delete_to_beginning_of_line(
1057        &mut self,
1058        _: &DeleteToBeginningOfLine,
1059        window: &mut Window,
1060        cx: &mut Context<Self>,
1061    ) {
1062        if !self.selected_range.is_empty() {
1063            self.replace_text_in_range(None, "", window, cx);
1064            self.pause_blink_cursor(cx);
1065            return;
1066        }
1067
1068        let mut offset = self.start_of_line();
1069        if offset == self.cursor() {
1070            offset = offset.saturating_sub(1);
1071        }
1072        self.replace_text_in_range_silent(
1073            Some(self.range_to_utf16(&(offset..self.cursor()))),
1074            "",
1075            window,
1076            cx,
1077        );
1078        self.pause_blink_cursor(cx);
1079    }
1080
1081    pub(super) fn delete_to_end_of_line(
1082        &mut self,
1083        _: &DeleteToEndOfLine,
1084        window: &mut Window,
1085        cx: &mut Context<Self>,
1086    ) {
1087        if !self.selected_range.is_empty() {
1088            self.replace_text_in_range(None, "", window, cx);
1089            self.pause_blink_cursor(cx);
1090            return;
1091        }
1092
1093        let mut offset = self.end_of_line();
1094        if offset == self.cursor() {
1095            offset = (offset + 1).clamp(0, self.text.len());
1096        }
1097        self.replace_text_in_range_silent(
1098            Some(self.range_to_utf16(&(self.cursor()..offset))),
1099            "",
1100            window,
1101            cx,
1102        );
1103        self.pause_blink_cursor(cx);
1104    }
1105
1106    pub(super) fn delete_previous_word(
1107        &mut self,
1108        _: &DeleteToPreviousWordStart,
1109        window: &mut Window,
1110        cx: &mut Context<Self>,
1111    ) {
1112        if !self.selected_range.is_empty() {
1113            self.replace_text_in_range(None, "", window, cx);
1114            self.pause_blink_cursor(cx);
1115            return;
1116        }
1117
1118        let offset = self.previous_start_of_word();
1119        self.replace_text_in_range_silent(
1120            Some(self.range_to_utf16(&(offset..self.cursor()))),
1121            "",
1122            window,
1123            cx,
1124        );
1125        self.pause_blink_cursor(cx);
1126    }
1127
1128    pub(super) fn delete_next_word(
1129        &mut self,
1130        _: &DeleteToNextWordEnd,
1131        window: &mut Window,
1132        cx: &mut Context<Self>,
1133    ) {
1134        if !self.selected_range.is_empty() {
1135            self.replace_text_in_range(None, "", window, cx);
1136            self.pause_blink_cursor(cx);
1137            return;
1138        }
1139
1140        let offset = self.next_end_of_word();
1141        self.replace_text_in_range_silent(
1142            Some(self.range_to_utf16(&(self.cursor()..offset))),
1143            "",
1144            window,
1145            cx,
1146        );
1147        self.pause_blink_cursor(cx);
1148    }
1149
1150    pub(super) fn enter(&mut self, action: &Enter, window: &mut Window, cx: &mut Context<Self>) {
1151        if self.handle_action_for_context_menu(Box::new(action.clone()), window, cx) {
1152            return;
1153        }
1154
1155        if self.mode.is_multi_line() {
1156            // Get current line indent
1157            let indent = if self.mode.is_code_editor() {
1158                self.indent_of_next_line()
1159            } else {
1160                "".to_string()
1161            };
1162
1163            // Add newline and indent
1164            let new_line_text = format!("\n{}", indent);
1165            self.replace_text_in_range_silent(None, &new_line_text, window, cx);
1166            self.pause_blink_cursor(cx);
1167        } else {
1168            // Single line input, just emit the event (e.g.: In a dialog to confirm).
1169            cx.propagate();
1170        }
1171
1172        cx.emit(InputEvent::PressEnter {
1173            secondary: action.secondary,
1174        });
1175    }
1176
1177    pub(super) fn clean(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1178        self.replace_text("", window, cx);
1179        self.selected_range = (0..0).into();
1180        self.scroll_to(0, None, cx);
1181    }
1182
1183    pub(super) fn escape(&mut self, action: &Escape, window: &mut Window, cx: &mut Context<Self>) {
1184        if self.handle_action_for_context_menu(Box::new(action.clone()), window, cx) {
1185            return;
1186        }
1187
1188        if self.ime_marked_range.is_some() {
1189            self.unmark_text(window, cx);
1190        }
1191
1192        if self.clean_on_escape {
1193            return self.clean(window, cx);
1194        }
1195
1196        cx.propagate();
1197    }
1198
1199    pub(super) fn on_mouse_down(
1200        &mut self,
1201        event: &MouseDownEvent,
1202        window: &mut Window,
1203        cx: &mut Context<Self>,
1204    ) {
1205        // If there have IME marked range and is empty (Means pressed Esc to abort IME typing)
1206        // Clear the marked range.
1207        if let Some(ime_marked_range) = &self.ime_marked_range {
1208            if ime_marked_range.len() == 0 {
1209                self.ime_marked_range = None;
1210            }
1211        }
1212
1213        self.selecting = true;
1214        let offset = self.index_for_mouse_position(event.position);
1215
1216        if self.handle_click_hover_definition(event, offset, window, cx) {
1217            return;
1218        }
1219
1220        // Double click to select word
1221        if event.button == MouseButton::Left && event.click_count == 2 {
1222            self.select_word(offset, window, cx);
1223            return;
1224        }
1225
1226        // Show Mouse context menu
1227        if event.button == MouseButton::Right {
1228            self.handle_right_click_menu(event, offset, window, cx);
1229            return;
1230        }
1231
1232        if event.modifiers.shift {
1233            self.select_to(offset, cx);
1234        } else {
1235            self.move_to(offset, None, cx)
1236        }
1237    }
1238
1239    pub(super) fn on_mouse_up(
1240        &mut self,
1241        _: &MouseUpEvent,
1242        _window: &mut Window,
1243        _cx: &mut Context<Self>,
1244    ) {
1245        self.selecting = false;
1246        self.selected_word_range = None;
1247    }
1248
1249    pub(super) fn on_mouse_move(
1250        &mut self,
1251        event: &MouseMoveEvent,
1252        window: &mut Window,
1253        cx: &mut Context<Self>,
1254    ) {
1255        // Show diagnostic popover on mouse move
1256        let offset = self.index_for_mouse_position(event.position);
1257        self.handle_mouse_move(offset, event, window, cx);
1258
1259        if self.mode.is_code_editor() {
1260            if let Some(diagnostic) = self
1261                .mode
1262                .diagnostics()
1263                .and_then(|set| set.for_offset(offset))
1264            {
1265                if let Some(diagnostic_popover) = self.diagnostic_popover.as_ref() {
1266                    if diagnostic_popover.read(cx).diagnostic.range == diagnostic.range {
1267                        diagnostic_popover.update(cx, |this, cx| {
1268                            this.show(cx);
1269                        });
1270
1271                        return;
1272                    }
1273                }
1274
1275                self.diagnostic_popover = Some(DiagnosticPopover::new(diagnostic, cx.entity(), cx));
1276                cx.notify();
1277            } else {
1278                if let Some(diagnostic_popover) = self.diagnostic_popover.as_mut() {
1279                    diagnostic_popover.update(cx, |this, cx| {
1280                        this.check_to_hide(event.position, cx);
1281                    })
1282                }
1283            }
1284        }
1285    }
1286
1287    pub(super) fn on_scroll_wheel(
1288        &mut self,
1289        event: &ScrollWheelEvent,
1290        window: &mut Window,
1291        cx: &mut Context<Self>,
1292    ) {
1293        let line_height = self
1294            .last_layout
1295            .as_ref()
1296            .map(|layout| layout.line_height)
1297            .unwrap_or(window.line_height());
1298        let delta = event.delta.pixel_delta(line_height);
1299
1300        let old_offset = self.scroll_handle.offset();
1301        self.update_scroll_offset(Some(old_offset + delta), cx);
1302
1303        // Only stop propagation if the offset actually changed
1304        if self.scroll_handle.offset() != old_offset {
1305            cx.stop_propagation();
1306        }
1307
1308        self.diagnostic_popover = None;
1309    }
1310
1311    pub(super) fn update_scroll_offset(
1312        &mut self,
1313        offset: Option<Point<Pixels>>,
1314        cx: &mut Context<Self>,
1315    ) {
1316        let mut offset = offset.unwrap_or(self.scroll_handle.offset());
1317
1318        let safe_y_range =
1319            (-self.scroll_size.height + self.input_bounds.size.height).min(px(0.0))..px(0.);
1320        let safe_x_range =
1321            (-self.scroll_size.width + self.input_bounds.size.width).min(px(0.0))..px(0.);
1322
1323        offset.y = if self.mode.is_single_line() {
1324            px(0.)
1325        } else {
1326            offset.y.clamp(safe_y_range.start, safe_y_range.end)
1327        };
1328        offset.x = offset.x.clamp(safe_x_range.start, safe_x_range.end);
1329        self.scroll_handle.set_offset(offset);
1330        cx.notify();
1331    }
1332
1333    /// Scroll to make the given offset visible.
1334    ///
1335    /// If `direction` is Some, will keep edges at the same side.
1336    pub(crate) fn scroll_to(
1337        &mut self,
1338        offset: usize,
1339        direction: Option<MoveDirection>,
1340        cx: &mut Context<Self>,
1341    ) {
1342        let Some(last_layout) = self.last_layout.as_ref() else {
1343            return;
1344        };
1345        let Some(bounds) = self.last_bounds.as_ref() else {
1346            return;
1347        };
1348
1349        let mut scroll_offset = self.scroll_handle.offset();
1350        let was_offset = scroll_offset;
1351        let line_height = last_layout.line_height;
1352
1353        let point = self.text.offset_to_point(offset);
1354
1355        let row = point.row;
1356
1357        let mut row_offset_y = px(0.);
1358        for (ix, wrap_line) in self.text_wrapper.lines.iter().enumerate() {
1359            if ix == row {
1360                break;
1361            }
1362
1363            row_offset_y += wrap_line.height(line_height);
1364        }
1365
1366        if let Some(line) = last_layout
1367            .lines
1368            .get(row.saturating_sub(last_layout.visible_range.start))
1369        {
1370            // Check to scroll horizontally and soft wrap lines
1371            if let Some(pos) = line.position_for_index(point.column, line_height) {
1372                let bounds_width = bounds.size.width - last_layout.line_number_width;
1373                let col_offset_x = pos.x;
1374                row_offset_y += pos.y;
1375                if col_offset_x - RIGHT_MARGIN < -scroll_offset.x {
1376                    // If the position is out of the visible area, scroll to make it visible
1377                    scroll_offset.x = -col_offset_x + RIGHT_MARGIN;
1378                } else if col_offset_x + RIGHT_MARGIN > -scroll_offset.x + bounds_width {
1379                    scroll_offset.x = -(col_offset_x - bounds_width + RIGHT_MARGIN);
1380                }
1381            }
1382        }
1383
1384        // Check if row_offset_y is out of the viewport
1385        // If row offset is not in the viewport, scroll to make it visible
1386        let edge_height = if direction.is_some() && self.mode.is_code_editor() {
1387            3 * line_height
1388        } else {
1389            line_height
1390        };
1391        if row_offset_y - edge_height + line_height < -scroll_offset.y {
1392            // Scroll up
1393            scroll_offset.y = -row_offset_y + edge_height - line_height;
1394        } else if row_offset_y + edge_height > -scroll_offset.y + bounds.size.height {
1395            // Scroll down
1396            scroll_offset.y = -(row_offset_y - bounds.size.height + edge_height);
1397        }
1398
1399        // Avoid necessary scroll, when it was already in the correct position.
1400        if direction == Some(MoveDirection::Up) {
1401            scroll_offset.y = scroll_offset.y.max(was_offset.y);
1402        } else if direction == Some(MoveDirection::Down) {
1403            scroll_offset.y = scroll_offset.y.min(was_offset.y);
1404        }
1405
1406        scroll_offset.x = scroll_offset.x.min(px(0.));
1407        scroll_offset.y = scroll_offset.y.min(px(0.));
1408        self.deferred_scroll_offset = Some(scroll_offset);
1409        cx.notify();
1410    }
1411
1412    pub(super) fn show_character_palette(
1413        &mut self,
1414        _: &ShowCharacterPalette,
1415        window: &mut Window,
1416        _: &mut Context<Self>,
1417    ) {
1418        window.show_character_palette();
1419    }
1420
1421    pub(super) fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
1422        if self.selected_range.is_empty() {
1423            return;
1424        }
1425
1426        let selected_text = self.text.slice(self.selected_range).to_string();
1427        cx.write_to_clipboard(ClipboardItem::new_string(selected_text));
1428    }
1429
1430    pub(super) fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
1431        if self.selected_range.is_empty() {
1432            return;
1433        }
1434
1435        let selected_text = self.text.slice(self.selected_range).to_string();
1436        cx.write_to_clipboard(ClipboardItem::new_string(selected_text));
1437
1438        self.replace_text_in_range_silent(None, "", window, cx);
1439    }
1440
1441    pub(super) fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
1442        if let Some(clipboard) = cx.read_from_clipboard() {
1443            let mut new_text = clipboard.text().unwrap_or_default();
1444            if !self.mode.is_multi_line() {
1445                new_text = new_text.replace('\n', "");
1446            }
1447
1448            self.replace_text_in_range_silent(None, &new_text, window, cx);
1449            self.scroll_to(self.cursor(), None, cx);
1450        }
1451    }
1452
1453    fn push_history(&mut self, text: &Rope, range: &Range<usize>, new_text: &str) {
1454        if self.history.ignore {
1455            return;
1456        }
1457
1458        let old_text = text.slice(range.clone()).to_string();
1459        let new_range = range.start..range.start + new_text.len();
1460
1461        self.history
1462            .push(Change::new(range.clone(), &old_text, new_range, new_text));
1463    }
1464
1465    pub(super) fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
1466        self.history.ignore = true;
1467        if let Some(changes) = self.history.undo() {
1468            for change in changes {
1469                let range_utf16 = self.range_to_utf16(&change.new_range.into());
1470                self.replace_text_in_range_silent(Some(range_utf16), &change.old_text, window, cx);
1471            }
1472        }
1473        self.history.ignore = false;
1474    }
1475
1476    pub(super) fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
1477        self.history.ignore = true;
1478        if let Some(changes) = self.history.redo() {
1479            for change in changes {
1480                let range_utf16 = self.range_to_utf16(&change.old_range.into());
1481                self.replace_text_in_range_silent(Some(range_utf16), &change.new_text, window, cx);
1482            }
1483        }
1484        self.history.ignore = false;
1485    }
1486
1487    /// Get byte offset of the cursor.
1488    ///
1489    /// The offset is the UTF-8 offset.
1490    pub fn cursor(&self) -> usize {
1491        if let Some(ime_marked_range) = &self.ime_marked_range {
1492            return ime_marked_range.end;
1493        }
1494
1495        if self.selection_reversed {
1496            self.selected_range.start
1497        } else {
1498            self.selected_range.end
1499        }
1500    }
1501
1502    pub(crate) fn index_for_mouse_position(&self, position: Point<Pixels>) -> usize {
1503        // If the text is empty, always return 0
1504        if self.text.len() == 0 {
1505            return 0;
1506        }
1507
1508        let (Some(bounds), Some(last_layout)) =
1509            (self.last_bounds.as_ref(), self.last_layout.as_ref())
1510        else {
1511            return 0;
1512        };
1513
1514        let line_height = last_layout.line_height;
1515        let line_number_width = last_layout.line_number_width;
1516
1517        // TIP: About the IBeam cursor
1518        //
1519        // If cursor style is IBeam, the mouse mouse position is in the middle of the cursor (This is special in OS)
1520
1521        // The position is relative to the bounds of the text input
1522        //
1523        // bounds.origin:
1524        //
1525        // - included the input padding.
1526        // - included the scroll offset.
1527        let inner_position = position - bounds.origin - point(line_number_width, px(0.));
1528
1529        let mut index = last_layout.visible_range_offset.start;
1530        let mut y_offset = last_layout.visible_top;
1531        for (ix, line) in self
1532            .text_wrapper
1533            .lines
1534            .iter()
1535            .skip(last_layout.visible_range.start)
1536            .enumerate()
1537        {
1538            let line_origin = self.line_origin_with_y_offset(&mut y_offset, line, line_height);
1539            let pos = inner_position - line_origin;
1540
1541            let Some(line_layout) = last_layout.lines.get(ix) else {
1542                if pos.y < line_origin.y + line_height {
1543                    break;
1544                }
1545
1546                continue;
1547            };
1548
1549            // Return offset by use closest_index_for_x if is single line mode.
1550            if self.mode.is_single_line() {
1551                index = line_layout.closest_index_for_x(pos.x);
1552                break;
1553            }
1554
1555            if let Some(v) = line_layout.closest_index_for_position(pos, line_height) {
1556                index += v;
1557                break;
1558            } else if pos.y < px(0.) {
1559                break;
1560            }
1561
1562            // +1 for `\n`
1563            index += line_layout.len() + 1;
1564        }
1565
1566        let index = if index > self.text.len() {
1567            self.text.len()
1568        } else {
1569            index
1570        };
1571
1572        if self.masked {
1573            // When is masked, the index is char index, need convert to byte index.
1574            self.text.char_index_to_offset(index)
1575        } else {
1576            index
1577        }
1578    }
1579
1580    /// Returns a y offsetted point for the line origin.
1581    fn line_origin_with_y_offset(
1582        &self,
1583        y_offset: &mut Pixels,
1584        line: &LineItem,
1585        line_height: Pixels,
1586    ) -> Point<Pixels> {
1587        // NOTE: About line.wrap_boundaries.len()
1588        //
1589        // If only 1 line, the value is 0
1590        // If have 2 line, the value is 1
1591        if self.mode.is_multi_line() {
1592            let p = point(px(0.), *y_offset);
1593            *y_offset += line.height(line_height);
1594            p
1595        } else {
1596            point(px(0.), px(0.))
1597        }
1598    }
1599
1600    /// Select the text from the current cursor position to the given offset.
1601    ///
1602    /// The offset is the UTF-8 offset.
1603    ///
1604    /// Ensure the offset use self.next_boundary or self.previous_boundary to get the correct offset.
1605    pub(crate) fn select_to(&mut self, offset: usize, cx: &mut Context<Self>) {
1606        let offset = offset.clamp(0, self.text.len());
1607        if self.selection_reversed {
1608            self.selected_range.start = offset
1609        } else {
1610            self.selected_range.end = offset
1611        };
1612
1613        if self.selected_range.end < self.selected_range.start {
1614            self.selection_reversed = !self.selection_reversed;
1615            self.selected_range = (self.selected_range.end..self.selected_range.start).into();
1616        }
1617
1618        // Ensure keep word selected range
1619        if let Some(word_range) = self.selected_word_range.as_ref() {
1620            if self.selected_range.start > word_range.start {
1621                self.selected_range.start = word_range.start;
1622            }
1623            if self.selected_range.end < word_range.end {
1624                self.selected_range.end = word_range.end;
1625            }
1626        }
1627        if self.selected_range.is_empty() {
1628            self.update_preferred_column();
1629        }
1630        cx.notify()
1631    }
1632
1633    /// Unselects the currently selected text.
1634    pub fn unselect(&mut self, _: &mut Window, cx: &mut Context<Self>) {
1635        let offset = self.cursor();
1636        self.selected_range = (offset..offset).into();
1637        cx.notify()
1638    }
1639
1640    #[inline]
1641    pub(super) fn offset_from_utf16(&self, offset: usize) -> usize {
1642        self.text.offset_utf16_to_offset(offset)
1643    }
1644
1645    #[inline]
1646    pub(super) fn offset_to_utf16(&self, offset: usize) -> usize {
1647        self.text.offset_to_offset_utf16(offset)
1648    }
1649
1650    #[inline]
1651    pub(super) fn range_to_utf16(&self, range: &Range<usize>) -> Range<usize> {
1652        self.offset_to_utf16(range.start)..self.offset_to_utf16(range.end)
1653    }
1654
1655    #[inline]
1656    pub(super) fn range_from_utf16(&self, range_utf16: &Range<usize>) -> Range<usize> {
1657        self.offset_from_utf16(range_utf16.start)..self.offset_from_utf16(range_utf16.end)
1658    }
1659
1660    pub(super) fn previous_boundary(&self, offset: usize) -> usize {
1661        let mut offset = self.text.clip_offset(offset.saturating_sub(1), Bias::Left);
1662        if let Some(ch) = self.text.char_at(offset) {
1663            if ch == '\r' {
1664                offset -= 1;
1665            }
1666        }
1667
1668        offset
1669    }
1670
1671    pub(super) fn next_boundary(&self, offset: usize) -> usize {
1672        let mut offset = self.text.clip_offset(offset + 1, Bias::Right);
1673        if let Some(ch) = self.text.char_at(offset) {
1674            if ch == '\r' {
1675                offset += 1;
1676            }
1677        }
1678
1679        offset
1680    }
1681
1682    /// Returns the true to let InputElement to render cursor, when Input is focused and current BlinkCursor is visible.
1683    pub(crate) fn show_cursor(&self, window: &Window, cx: &App) -> bool {
1684        (self.focus_handle.is_focused(window) || self.is_context_menu_open(cx))
1685            && self.blink_cursor.read(cx).visible()
1686            && window.is_window_active()
1687    }
1688
1689    fn on_focus(&mut self, _: &mut Window, cx: &mut Context<Self>) {
1690        self.blink_cursor.update(cx, |cursor, cx| {
1691            cursor.start(cx);
1692        });
1693        cx.emit(InputEvent::Focus);
1694    }
1695
1696    fn on_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1697        if self.is_context_menu_open(cx) {
1698            return;
1699        }
1700
1701        // NOTE: Do not cancel select, when blur.
1702        // Because maybe user want to copy the selected text by AppMenuBar (will take focus handle).
1703
1704        self.hover_popover = None;
1705        self.diagnostic_popover = None;
1706        self.context_menu = None;
1707        self.blink_cursor.update(cx, |cursor, cx| {
1708            cursor.stop(cx);
1709        });
1710        Root::update(window, cx, |root, _, _| {
1711            root.focused_input = None;
1712        });
1713        cx.emit(InputEvent::Blur);
1714        cx.notify();
1715    }
1716
1717    pub(super) fn pause_blink_cursor(&mut self, cx: &mut Context<Self>) {
1718        self.blink_cursor.update(cx, |cursor, cx| {
1719            cursor.pause(cx);
1720        });
1721    }
1722
1723    pub(super) fn on_key_down(&mut self, _: &KeyDownEvent, _: &mut Window, cx: &mut Context<Self>) {
1724        self.pause_blink_cursor(cx);
1725    }
1726
1727    pub(super) fn on_drag_move(
1728        &mut self,
1729        event: &MouseMoveEvent,
1730        window: &mut Window,
1731        cx: &mut Context<Self>,
1732    ) {
1733        if self.text.len() == 0 {
1734            return;
1735        }
1736
1737        if self.last_layout.is_none() {
1738            return;
1739        }
1740
1741        if !self.focus_handle.is_focused(window) {
1742            return;
1743        }
1744
1745        if !self.selecting {
1746            return;
1747        }
1748
1749        let offset = self.index_for_mouse_position(event.position);
1750        self.select_to(offset, cx);
1751    }
1752
1753    fn is_valid_input(&self, new_text: &str, cx: &mut Context<Self>) -> bool {
1754        if new_text.is_empty() {
1755            return true;
1756        }
1757
1758        if let Some(validate) = &self.validate {
1759            if !validate(new_text, cx) {
1760                return false;
1761            }
1762        }
1763
1764        if !self.mask_pattern.is_valid(new_text) {
1765            return false;
1766        }
1767
1768        let Some(pattern) = &self.pattern else {
1769            return true;
1770        };
1771
1772        pattern.is_match(new_text)
1773    }
1774
1775    /// Set the mask pattern for formatting the input text.
1776    ///
1777    /// The pattern can contain:
1778    /// - 9: Any digit or dot
1779    /// - A: Any letter
1780    /// - *: Any character
1781    /// - Other characters will be treated as literal mask characters
1782    ///
1783    /// Example: "(999)999-999" for phone numbers
1784    pub fn mask_pattern(mut self, pattern: impl Into<MaskPattern>) -> Self {
1785        self.mask_pattern = pattern.into();
1786        if let Some(placeholder) = self.mask_pattern.placeholder() {
1787            self.placeholder = placeholder.into();
1788        }
1789        self
1790    }
1791
1792    pub fn set_mask_pattern(
1793        &mut self,
1794        pattern: impl Into<MaskPattern>,
1795        _: &mut Window,
1796        cx: &mut Context<Self>,
1797    ) {
1798        self.mask_pattern = pattern.into();
1799        if let Some(placeholder) = self.mask_pattern.placeholder() {
1800            self.placeholder = placeholder.into();
1801        }
1802        cx.notify();
1803    }
1804
1805    pub(super) fn set_input_bounds(&mut self, new_bounds: Bounds<Pixels>, cx: &mut Context<Self>) {
1806        let wrap_width_changed = self.input_bounds.size.width != new_bounds.size.width;
1807        self.input_bounds = new_bounds;
1808
1809        // Update text_wrapper wrap_width if changed.
1810        if let Some(last_layout) = self.last_layout.as_ref() {
1811            if wrap_width_changed {
1812                let wrap_width = if !self.soft_wrap {
1813                    // None to disable wrapping (will use Pixels::MAX)
1814                    None
1815                } else {
1816                    last_layout.wrap_width
1817                };
1818
1819                self.text_wrapper.set_wrap_width(wrap_width, cx);
1820                self.mode.update_auto_grow(&self.text_wrapper);
1821                cx.notify();
1822            }
1823        }
1824    }
1825
1826    pub(super) fn selected_text(&self) -> RopeSlice<'_> {
1827        let range_utf16 = self.range_to_utf16(&self.selected_range.into());
1828        let range = self.range_from_utf16(&range_utf16);
1829        self.text.slice(range)
1830    }
1831
1832    pub(crate) fn range_to_bounds(&self, range: &Range<usize>) -> Option<Bounds<Pixels>> {
1833        let Some(last_layout) = self.last_layout.as_ref() else {
1834            return None;
1835        };
1836
1837        let Some(last_bounds) = self.last_bounds else {
1838            return None;
1839        };
1840
1841        let (_, _, start_pos) = self.line_and_position_for_offset(range.start);
1842        let (_, _, end_pos) = self.line_and_position_for_offset(range.end);
1843
1844        let Some(start_pos) = start_pos else {
1845            return None;
1846        };
1847        let Some(end_pos) = end_pos else {
1848            return None;
1849        };
1850
1851        Some(Bounds::from_corners(
1852            last_bounds.origin + start_pos,
1853            last_bounds.origin + end_pos + point(px(0.), last_layout.line_height),
1854        ))
1855    }
1856
1857    /// Replace text by [`lsp_types::Range`].
1858    ///
1859    /// See also: [`EntityInputHandler::replace_text_in_range`]
1860    #[allow(unused)]
1861    pub(crate) fn replace_text_in_lsp_range(
1862        &mut self,
1863        lsp_range: &lsp_types::Range,
1864        new_text: &str,
1865        window: &mut Window,
1866        cx: &mut Context<Self>,
1867    ) {
1868        let start = self.text.position_to_offset(&lsp_range.start);
1869        let end = self.text.position_to_offset(&lsp_range.end);
1870        self.replace_text_in_range_silent(
1871            Some(self.range_to_utf16(&(start..end))),
1872            new_text,
1873            window,
1874            cx,
1875        );
1876    }
1877
1878    /// Replace text in range in silent.
1879    ///
1880    /// This will not trigger any UI interaction, such as auto-completion.
1881    pub(crate) fn replace_text_in_range_silent(
1882        &mut self,
1883        range_utf16: Option<Range<usize>>,
1884        new_text: &str,
1885        window: &mut Window,
1886        cx: &mut Context<Self>,
1887    ) {
1888        self.silent_replace_text = true;
1889        self.replace_text_in_range(range_utf16, new_text, window, cx);
1890        self.silent_replace_text = false;
1891    }
1892}
1893
1894impl EntityInputHandler for InputState {
1895    fn text_for_range(
1896        &mut self,
1897        range_utf16: Range<usize>,
1898        adjusted_range: &mut Option<Range<usize>>,
1899        _window: &mut Window,
1900        _cx: &mut Context<Self>,
1901    ) -> Option<String> {
1902        let range = self.range_from_utf16(&range_utf16);
1903        adjusted_range.replace(self.range_to_utf16(&range));
1904        Some(self.text.slice(range).to_string())
1905    }
1906
1907    fn selected_text_range(
1908        &mut self,
1909        _ignore_disabled_input: bool,
1910        _window: &mut Window,
1911        _cx: &mut Context<Self>,
1912    ) -> Option<UTF16Selection> {
1913        Some(UTF16Selection {
1914            range: self.range_to_utf16(&self.selected_range.into()),
1915            reversed: false,
1916        })
1917    }
1918
1919    fn marked_text_range(
1920        &self,
1921        _window: &mut Window,
1922        _cx: &mut Context<Self>,
1923    ) -> Option<Range<usize>> {
1924        self.ime_marked_range
1925            .map(|range| self.range_to_utf16(&range.into()))
1926    }
1927
1928    fn unmark_text(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
1929        self.ime_marked_range = None;
1930    }
1931
1932    /// Replace text in range.
1933    ///
1934    /// - If the new text is invalid, it will not be replaced.
1935    /// - If `range_utf16` is not provided, the current selected range will be used.
1936    fn replace_text_in_range(
1937        &mut self,
1938        range_utf16: Option<Range<usize>>,
1939        new_text: &str,
1940        window: &mut Window,
1941        cx: &mut Context<Self>,
1942    ) {
1943        if self.disabled {
1944            return;
1945        }
1946
1947        self.pause_blink_cursor(cx);
1948
1949        let range = range_utf16
1950            .as_ref()
1951            .map(|range_utf16| self.range_from_utf16(range_utf16))
1952            .or(self.ime_marked_range.map(|range| {
1953                let range = self.range_to_utf16(&(range.start..range.end));
1954                self.range_from_utf16(&range)
1955            }))
1956            .unwrap_or(self.selected_range.into());
1957
1958        let old_text = self.text.clone();
1959        self.text.replace(range.clone(), new_text);
1960
1961        let mut new_offset = (range.start + new_text.len()).min(self.text.len());
1962
1963        if self.mode.is_single_line() {
1964            let pending_text = self.text.to_string();
1965            // Check if the new text is valid
1966            if !self.is_valid_input(&pending_text, cx) {
1967                self.text = old_text;
1968                return;
1969            }
1970
1971            if !self.mask_pattern.is_none() {
1972                let mask_text = self.mask_pattern.mask(&pending_text);
1973                self.text = Rope::from(mask_text.as_str());
1974                let new_text_len =
1975                    (new_text.len() + mask_text.len()).saturating_sub(pending_text.len());
1976                new_offset = (range.start + new_text_len).min(mask_text.len());
1977            }
1978        }
1979
1980        self.push_history(&old_text, &range, &new_text);
1981        self.history.end_grouping();
1982        if let Some(diagnostics) = self.mode.diagnostics_mut() {
1983            diagnostics.reset(&self.text)
1984        }
1985        self.text_wrapper
1986            .update(&self.text, &range, &Rope::from(new_text), cx);
1987        self.mode
1988            .update_highlighter(&range, &self.text, &new_text, true, cx);
1989        self.lsp.update(&self.text, window, cx);
1990        self.selected_range = (new_offset..new_offset).into();
1991        self.ime_marked_range.take();
1992        self.update_preferred_column();
1993        self.update_search(cx);
1994        self.mode.update_auto_grow(&self.text_wrapper);
1995        if !self.silent_replace_text {
1996            self.handle_completion_trigger(&range, &new_text, window, cx);
1997        }
1998        cx.emit(InputEvent::Change);
1999        cx.notify();
2000    }
2001
2002    /// Mark text is the IME temporary insert on typing.
2003    fn replace_and_mark_text_in_range(
2004        &mut self,
2005        range_utf16: Option<Range<usize>>,
2006        new_text: &str,
2007        new_selected_range_utf16: Option<Range<usize>>,
2008        window: &mut Window,
2009        cx: &mut Context<Self>,
2010    ) {
2011        if self.disabled {
2012            return;
2013        }
2014
2015        self.lsp.reset();
2016
2017        let range = range_utf16
2018            .as_ref()
2019            .map(|range_utf16| self.range_from_utf16(range_utf16))
2020            .or(self.ime_marked_range.map(|range| {
2021                let range = self.range_to_utf16(&(range.start..range.end));
2022                self.range_from_utf16(&range)
2023            }))
2024            .unwrap_or(self.selected_range.into());
2025
2026        let old_text = self.text.clone();
2027        self.text.replace(range.clone(), new_text);
2028
2029        if self.mode.is_single_line() {
2030            let pending_text = self.text.to_string();
2031            if !self.is_valid_input(&pending_text, cx) {
2032                self.text = old_text;
2033                return;
2034            }
2035        }
2036
2037        if let Some(diagnostics) = self.mode.diagnostics_mut() {
2038            diagnostics.reset(&self.text)
2039        }
2040        self.text_wrapper
2041            .update(&self.text, &range, &Rope::from(new_text), cx);
2042        self.mode
2043            .update_highlighter(&range, &self.text, &new_text, true, cx);
2044        self.lsp.update(&self.text, window, cx);
2045        if new_text.is_empty() {
2046            // Cancel selection, when cancel IME input.
2047            self.selected_range = (range.start..range.start).into();
2048            self.ime_marked_range = None;
2049        } else {
2050            self.ime_marked_range = Some((range.start..range.start + new_text.len()).into());
2051            self.selected_range = new_selected_range_utf16
2052                .as_ref()
2053                .map(|range_utf16| self.range_from_utf16(range_utf16))
2054                .map(|new_range| new_range.start + range.start..new_range.end + range.end)
2055                .unwrap_or_else(|| range.start + new_text.len()..range.start + new_text.len())
2056                .into();
2057        }
2058        self.mode.update_auto_grow(&self.text_wrapper);
2059        self.history.start_grouping();
2060        self.push_history(&old_text, &range, new_text);
2061        cx.notify();
2062    }
2063
2064    /// Used to position IME candidates.
2065    fn bounds_for_range(
2066        &mut self,
2067        range_utf16: Range<usize>,
2068        bounds: Bounds<Pixels>,
2069        _window: &mut Window,
2070        _cx: &mut Context<Self>,
2071    ) -> Option<Bounds<Pixels>> {
2072        let last_layout = self.last_layout.as_ref()?;
2073        let line_height = last_layout.line_height;
2074        let line_number_width = last_layout.line_number_width;
2075        let range = self.range_from_utf16(&range_utf16);
2076
2077        let mut start_origin = None;
2078        let mut end_origin = None;
2079        let line_number_origin = point(line_number_width, px(0.));
2080        let mut y_offset = last_layout.visible_top;
2081        let mut index_offset = last_layout.visible_range_offset.start;
2082
2083        for line in last_layout.lines.iter() {
2084            if start_origin.is_some() && end_origin.is_some() {
2085                break;
2086            }
2087
2088            if start_origin.is_none() {
2089                if let Some(p) =
2090                    line.position_for_index(range.start.saturating_sub(index_offset), line_height)
2091                {
2092                    start_origin = Some(p + point(px(0.), y_offset));
2093                }
2094            }
2095
2096            if end_origin.is_none() {
2097                if let Some(p) =
2098                    line.position_for_index(range.end.saturating_sub(index_offset), line_height)
2099                {
2100                    end_origin = Some(p + point(px(0.), y_offset));
2101                }
2102            }
2103
2104            index_offset += line.len() + 1;
2105            y_offset += line.size(line_height).height;
2106        }
2107
2108        let start_origin = start_origin.unwrap_or_default();
2109        let mut end_origin = end_origin.unwrap_or_default();
2110        // Ensure at same line.
2111        end_origin.y = start_origin.y;
2112
2113        Some(Bounds::from_corners(
2114            bounds.origin + line_number_origin + start_origin,
2115            // + line_height for show IME panel under the cursor line.
2116            bounds.origin + line_number_origin + point(end_origin.x, end_origin.y + line_height),
2117        ))
2118    }
2119
2120    fn character_index_for_point(
2121        &mut self,
2122        point: gpui::Point<Pixels>,
2123        _window: &mut Window,
2124        _cx: &mut Context<Self>,
2125    ) -> Option<usize> {
2126        let last_layout = self.last_layout.as_ref()?;
2127        let line_height = last_layout.line_height;
2128        let line_point = self.last_bounds?.localize(&point)?;
2129        let offset = last_layout.visible_range_offset.start;
2130
2131        for line in last_layout.lines.iter() {
2132            if let Some(utf8_index) = line.index_for_position(line_point, line_height) {
2133                return Some(self.offset_to_utf16(offset + utf8_index));
2134            }
2135        }
2136
2137        None
2138    }
2139}
2140
2141impl Focusable for InputState {
2142    fn focus_handle(&self, _cx: &App) -> FocusHandle {
2143        self.focus_handle.clone()
2144    }
2145}
2146
2147impl Render for InputState {
2148    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
2149        if self._pending_update {
2150            self.mode
2151                .update_highlighter(&(0..0), &self.text, "", false, cx);
2152            self.lsp.update(&self.text, window, cx);
2153            self._pending_update = false;
2154        }
2155
2156        div()
2157            .id("input-state")
2158            .flex_1()
2159            .when(self.mode.is_multi_line(), |this| this.h_full())
2160            .flex_grow()
2161            .overflow_x_hidden()
2162            .child(TextElement::new(cx.entity().clone()).placeholder(self.placeholder.clone()))
2163            .children(self.diagnostic_popover.clone())
2164            .children(self.context_menu.as_ref().map(|menu| menu.render()))
2165            .children(self.hover_popover.clone())
2166    }
2167}