1use 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::ops::Range;
16use std::rc::Rc;
17use sum_tree::Bias;
18use unicode_segmentation::*;
19
20use super::{
21 blink_cursor::BlinkCursor, change::Change, element::TextElement, mask_pattern::MaskPattern,
22 mode::InputMode, number_input, text_wrapper::TextWrapper,
23};
24use crate::Size;
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 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 pub(super) visible_range: Range<usize>,
229 pub(super) visible_top: Pixels,
231 pub(super) visible_range_offset: Range<usize>,
233 pub(super) lines: Rc<Vec<LineLayout>>,
235 pub(super) line_height: Pixels,
237 pub(super) wrap_width: Option<Pixels>,
239 pub(super) line_number_width: Pixels,
241 pub(super) cursor_bounds: Option<Bounds<Pixels>>,
243}
244
245impl LastLayout {
246 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
260pub 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 pub(super) selected_range: Selection,
274 pub(super) search_panel: Option<Entity<SearchPanel>>,
275 pub(super) searchable: bool,
276 pub(super) selected_word_range: Option<Selection>,
278 pub(super) selection_reversed: bool,
279 pub(super) ime_marked_range: Option<Selection>,
281 pub(super) last_layout: Option<LastLayout>,
282 pub(super) last_cursor: Option<usize>,
283 pub(super) input_bounds: Bounds<Pixels>,
285 pub(super) last_bounds: Option<Bounds<Pixels>>,
287 pub(super) last_selected_range: Option<Selection>,
288 pub(super) selecting: bool,
289 pub(super) size: Size,
290 pub(super) disabled: bool,
291 pub(super) masked: bool,
292 pub(super) clean_on_escape: bool,
293 pub(super) soft_wrap: bool,
294 pub(super) pattern: Option<regex::Regex>,
295 pub(super) validate: Option<Box<dyn Fn(&str, &mut Context<Self>) -> bool + 'static>>,
296 pub(crate) scroll_handle: ScrollHandle,
297 pub(crate) deferred_scroll_offset: Option<Point<Pixels>>,
299 pub(crate) scroll_size: gpui::Size<Pixels>,
301
302 pub(crate) mask_pattern: MaskPattern,
304 pub(super) placeholder: SharedString,
305
306 diagnostic_popover: Option<Entity<DiagnosticPopover>>,
308 pub(super) context_menu: Option<ContextMenu>,
310 pub(super) mouse_context_menu: Entity<MouseContextMenu>,
311 pub(super) completion_inserting: bool,
313 pub(super) hover_popover: Option<Entity<HoverPopover>>,
314 pub(super) hover_definition: HoverDefinition,
316
317 pub lsp: Lsp,
318
319 _pending_update: bool,
323 pub(super) silent_replace_text: bool,
325
326 pub(super) preferred_column: Option<(Pixels, usize)>,
331 _subscriptions: Vec<Subscription>,
332
333 pub(super) _context_menu_task: Task<Result<()>>,
334}
335
336impl EventEmitter<InputEvent> for InputState {}
337
338impl InputState {
339 pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
343 let focus_handle = cx.focus_handle().tab_stop(true);
344 let blink_cursor = cx.new(|_| BlinkCursor::new());
345 let history = History::new().group_interval(std::time::Duration::from_secs(1));
346
347 let _subscriptions = vec![
348 cx.observe(&blink_cursor, |_, _, cx| cx.notify()),
350 cx.observe_window_activation(window, |input, window, cx| {
352 if window.is_window_active() {
353 let focus_handle = input.focus_handle.clone();
354 if focus_handle.is_focused(window) {
355 input.blink_cursor.update(cx, |blink_cursor, cx| {
356 blink_cursor.start(cx);
357 });
358 }
359 }
360 }),
361 cx.on_focus(&focus_handle, window, Self::on_focus),
362 cx.on_blur(&focus_handle, window, Self::on_blur),
363 ];
364
365 let text_style = window.text_style();
366 let mouse_context_menu = MouseContextMenu::new(cx.entity(), window, cx);
367
368 Self {
369 focus_handle: focus_handle.clone(),
370 text: "".into(),
371 text_wrapper: TextWrapper::new(text_style.font(), window.rem_size(), None),
372 blink_cursor,
373 history,
374 selected_range: Selection::default(),
375 search_panel: None,
376 searchable: false,
377 selected_word_range: None,
378 selection_reversed: false,
379 ime_marked_range: None,
380 input_bounds: Bounds::default(),
381 selecting: false,
382 disabled: false,
383 masked: false,
384 clean_on_escape: false,
385 soft_wrap: true,
386 loading: false,
387 pattern: None,
388 validate: None,
389 mode: InputMode::default(),
390 last_layout: None,
391 last_bounds: None,
392 last_selected_range: None,
393 last_cursor: None,
394 scroll_handle: ScrollHandle::new(),
395 scroll_size: gpui::size(px(0.), px(0.)),
396 deferred_scroll_offset: None,
397 preferred_column: None,
398 placeholder: SharedString::default(),
399 mask_pattern: MaskPattern::default(),
400 lsp: Lsp::default(),
401 diagnostic_popover: None,
402 context_menu: None,
403 mouse_context_menu,
404 completion_inserting: false,
405 hover_popover: None,
406 hover_definition: HoverDefinition::default(),
407 silent_replace_text: false,
408 size: Size::default(),
409 _subscriptions,
410 _context_menu_task: Task::ready(Ok(())),
411 _pending_update: false,
412 }
413 }
414
415 pub fn multi_line(mut self, multi_line: bool) -> Self {
419 self.mode = self.mode.multi_line(multi_line);
420 self
421 }
422
423 pub fn auto_grow(mut self, min_rows: usize, max_rows: usize) -> Self {
425 self.mode = InputMode::auto_grow(min_rows, max_rows);
426 self
427 }
428
429 pub fn code_editor(mut self, language: impl Into<SharedString>) -> Self {
451 let language: SharedString = language.into();
452 self.mode = InputMode::code_editor(language);
453 self.searchable = true;
454 self
455 }
456
457 pub fn searchable(mut self, searchable: bool) -> Self {
459 debug_assert!(self.mode.is_multi_line());
460 self.searchable = searchable;
461 self
462 }
463
464 pub fn placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
466 self.placeholder = placeholder.into();
467 self
468 }
469
470 pub fn line_number(mut self, line_number: bool) -> Self {
472 debug_assert!(self.mode.is_code_editor() && self.mode.is_multi_line());
473 if let InputMode::CodeEditor { line_number: l, .. } = &mut self.mode {
474 *l = line_number;
475 }
476 self
477 }
478
479 pub fn set_line_number(&mut self, line_number: bool, _: &mut Window, cx: &mut Context<Self>) {
481 debug_assert!(self.mode.is_code_editor() && self.mode.is_multi_line());
482 if let InputMode::CodeEditor { line_number: l, .. } = &mut self.mode {
483 *l = line_number;
484 }
485 cx.notify();
486 }
487
488 pub fn rows(mut self, rows: usize) -> Self {
494 match &mut self.mode {
495 InputMode::PlainText { rows: r, .. } | InputMode::CodeEditor { rows: r, .. } => {
496 *r = rows
497 }
498 InputMode::AutoGrow {
499 max_rows: max_r,
500 rows: r,
501 ..
502 } => {
503 *r = rows;
504 *max_r = rows;
505 }
506 }
507 self
508 }
509
510 pub fn set_highlighter(
512 &mut self,
513 new_language: impl Into<SharedString>,
514 cx: &mut Context<Self>,
515 ) {
516 match &mut self.mode {
517 InputMode::CodeEditor {
518 language,
519 highlighter,
520 ..
521 } => {
522 *language = new_language.into();
523 *highlighter.borrow_mut() = None;
524 }
525 _ => {}
526 }
527 cx.notify();
528 }
529
530 fn reset_highlighter(&mut self, cx: &mut Context<Self>) {
531 match &mut self.mode {
532 InputMode::CodeEditor { highlighter, .. } => {
533 *highlighter.borrow_mut() = None;
534 }
535 _ => {}
536 }
537 cx.notify();
538 }
539
540 #[inline]
541 pub fn diagnostics(&self) -> Option<&DiagnosticSet> {
542 self.mode.diagnostics()
543 }
544
545 #[inline]
546 pub fn diagnostics_mut(&mut self) -> Option<&mut DiagnosticSet> {
547 self.mode.diagnostics_mut()
548 }
549
550 pub fn set_placeholder(
552 &mut self,
553 placeholder: impl Into<SharedString>,
554 _: &mut Window,
555 cx: &mut Context<Self>,
556 ) {
557 self.placeholder = placeholder.into();
558 cx.notify();
559 }
560
561 #[allow(unused)]
569 pub(super) fn line_and_position_for_offset(
570 &self,
571 offset: usize,
572 ) -> (usize, usize, Option<Point<Pixels>>) {
573 let Some(last_layout) = &self.last_layout else {
574 return (0, 0, None);
575 };
576 let line_height = last_layout.line_height;
577
578 let mut prev_lines_offset = last_layout.visible_range_offset.start;
579 let mut y_offset = last_layout.visible_top;
580 for (line_index, line) in last_layout.lines.iter().enumerate() {
581 let local_offset = offset.saturating_sub(prev_lines_offset);
582 if let Some(pos) = line.position_for_index(local_offset, line_height) {
583 let sub_line_index = (pos.y / line_height) as usize;
584 let adjusted_pos = point(pos.x + last_layout.line_number_width, pos.y + y_offset);
585 return (line_index, sub_line_index, Some(adjusted_pos));
586 }
587
588 y_offset += line.size(line_height).height;
589 prev_lines_offset += line.len() + 1;
590 }
591 (0, 0, None)
592 }
593
594 pub fn set_value(
598 &mut self,
599 value: impl Into<SharedString>,
600 window: &mut Window,
601 cx: &mut Context<Self>,
602 ) {
603 self.history.ignore = true;
604 let was_disabled = self.disabled;
605 self.disabled = false;
606 self.replace_text(value, window, cx);
607 self.disabled = was_disabled;
608 self.history.ignore = false;
609 if self.mode.is_single_line() {
611 self.selected_range = (self.text.len()..self.text.len()).into();
612 } else {
613 self.selected_range.clear();
614
615 self._pending_update = true;
616 self.lsp.reset();
617 }
618 self.scroll_handle.set_offset(point(px(0.), px(0.)));
620
621 cx.notify();
622 }
623
624 pub fn insert(
628 &mut self,
629 text: impl Into<SharedString>,
630 window: &mut Window,
631 cx: &mut Context<Self>,
632 ) {
633 let text: SharedString = text.into();
634 let range_utf16 = self.range_to_utf16(&(self.cursor()..self.cursor()));
635 self.replace_text_in_range_silent(Some(range_utf16), &text, window, cx);
636 self.selected_range = (self.selected_range.end..self.selected_range.end).into();
637 }
638
639 pub fn replace(
643 &mut self,
644 text: impl Into<SharedString>,
645 window: &mut Window,
646 cx: &mut Context<Self>,
647 ) {
648 let text: SharedString = text.into();
649 self.replace_text_in_range_silent(None, &text, window, cx);
650 self.selected_range = (self.selected_range.end..self.selected_range.end).into();
651 }
652
653 fn replace_text(
654 &mut self,
655 text: impl Into<SharedString>,
656 window: &mut Window,
657 cx: &mut Context<Self>,
658 ) {
659 let text: SharedString = text.into();
660 let range = 0..self.text.chars().map(|c| c.len_utf16()).sum();
661 self.replace_text_in_range_silent(Some(range), &text, window, cx);
662 self.reset_highlighter(cx);
663 }
664
665 #[allow(unused)]
669 pub(crate) fn disabled(mut self, disabled: bool) -> Self {
670 self.disabled = disabled;
671 self
672 }
673
674 pub fn masked(mut self, masked: bool) -> Self {
678 debug_assert!(self.mode.is_single_line());
679 self.masked = masked;
680 self
681 }
682
683 pub fn set_masked(&mut self, masked: bool, _: &mut Window, cx: &mut Context<Self>) {
687 debug_assert!(self.mode.is_single_line());
688 self.masked = masked;
689 cx.notify();
690 }
691
692 pub fn clean_on_escape(mut self) -> Self {
694 self.clean_on_escape = true;
695 self
696 }
697
698 pub fn soft_wrap(mut self, wrap: bool) -> Self {
700 debug_assert!(self.mode.is_multi_line());
701 self.soft_wrap = wrap;
702 self
703 }
704
705 pub fn set_soft_wrap(&mut self, wrap: bool, _: &mut Window, cx: &mut Context<Self>) {
707 debug_assert!(self.mode.is_multi_line());
708 self.soft_wrap = wrap;
709 if wrap {
710 let wrap_width = self
711 .last_layout
712 .as_ref()
713 .and_then(|b| b.wrap_width)
714 .unwrap_or(self.input_bounds.size.width);
715
716 self.text_wrapper.set_wrap_width(Some(wrap_width), cx);
717
718 let mut offset = self.scroll_handle.offset();
720 offset.x = px(0.);
721 self.scroll_handle.set_offset(offset);
722 } else {
723 self.text_wrapper.set_wrap_width(None, cx);
724 }
725 cx.notify();
726 }
727
728 pub fn pattern(mut self, pattern: regex::Regex) -> Self {
732 debug_assert!(self.mode.is_single_line());
733 self.pattern = Some(pattern);
734 self
735 }
736
737 pub fn set_pattern(
741 &mut self,
742 pattern: regex::Regex,
743 _window: &mut Window,
744 _cx: &mut Context<Self>,
745 ) {
746 debug_assert!(self.mode.is_single_line());
747 self.pattern = Some(pattern);
748 }
749
750 pub fn validate(mut self, f: impl Fn(&str, &mut Context<Self>) -> bool + 'static) -> Self {
754 debug_assert!(self.mode.is_single_line());
755 self.validate = Some(Box::new(f));
756 self
757 }
758
759 pub fn set_loading(&mut self, loading: bool, _: &mut Window, cx: &mut Context<Self>) {
763 debug_assert!(self.mode.is_single_line());
764 self.loading = loading;
765 cx.notify();
766 }
767
768 pub fn default_value(mut self, value: impl Into<SharedString>) -> Self {
770 let text: SharedString = value.into();
771 self.text = Rope::from(text.as_str());
772 if let Some(diagnostics) = self.mode.diagnostics_mut() {
773 diagnostics.reset(&self.text)
774 }
775 self.text_wrapper.set_default_text(&self.text);
776 self._pending_update = true;
777 self
778 }
779
780 pub fn value(&self) -> SharedString {
782 SharedString::new(self.text.to_string())
783 }
784
785 pub fn unmask_value(&self) -> SharedString {
787 self.mask_pattern.unmask(&self.text.to_string()).into()
788 }
789
790 pub fn text(&self) -> &Rope {
792 &self.text
793 }
794
795 pub fn cursor_position(&self) -> Position {
797 let offset = self.cursor();
798 self.text.offset_to_position(offset)
799 }
800
801 pub fn set_cursor_position(
805 &mut self,
806 position: impl Into<Position>,
807 window: &mut Window,
808 cx: &mut Context<Self>,
809 ) {
810 let position: Position = position.into();
811 let offset = self.text.position_to_offset(&position);
812
813 self.move_to(offset, None, cx);
814 self.update_preferred_column();
815 self.focus(window, cx);
816 }
817
818 pub fn focus(&self, window: &mut Window, cx: &mut Context<Self>) {
820 self.focus_handle.focus(window);
821 self.blink_cursor.update(cx, |cursor, cx| {
822 cursor.start(cx);
823 });
824 }
825
826 pub(super) fn select_left(&mut self, _: &SelectLeft, _: &mut Window, cx: &mut Context<Self>) {
827 self.select_to(self.previous_boundary(self.cursor()), cx);
828 }
829
830 pub(super) fn select_right(&mut self, _: &SelectRight, _: &mut Window, cx: &mut Context<Self>) {
831 self.select_to(self.next_boundary(self.cursor()), cx);
832 }
833
834 pub(super) fn select_up(&mut self, _: &SelectUp, _: &mut Window, cx: &mut Context<Self>) {
835 if self.mode.is_single_line() {
836 return;
837 }
838 let offset = self.start_of_line().saturating_sub(1);
839 self.select_to(self.previous_boundary(offset), cx);
840 }
841
842 pub(super) fn select_down(&mut self, _: &SelectDown, _: &mut Window, cx: &mut Context<Self>) {
843 if self.mode.is_single_line() {
844 return;
845 }
846 let offset = (self.end_of_line() + 1).min(self.text.len());
847 self.select_to(self.next_boundary(offset), cx);
848 }
849
850 pub(super) fn select_all(&mut self, _: &SelectAll, _: &mut Window, cx: &mut Context<Self>) {
851 self.selected_range = (0..self.text.len()).into();
852 cx.notify();
853 }
854
855 pub(super) fn select_to_start(
856 &mut self,
857 _: &SelectToStart,
858 _: &mut Window,
859 cx: &mut Context<Self>,
860 ) {
861 self.select_to(0, cx);
862 }
863
864 pub(super) fn select_to_end(
865 &mut self,
866 _: &SelectToEnd,
867 _: &mut Window,
868 cx: &mut Context<Self>,
869 ) {
870 let end = self.text.len();
871 self.select_to(end, cx);
872 }
873
874 pub(super) fn select_to_start_of_line(
875 &mut self,
876 _: &SelectToStartOfLine,
877 _: &mut Window,
878 cx: &mut Context<Self>,
879 ) {
880 let offset = self.start_of_line();
881 self.select_to(offset, cx);
882 }
883
884 pub(super) fn select_to_end_of_line(
885 &mut self,
886 _: &SelectToEndOfLine,
887 _: &mut Window,
888 cx: &mut Context<Self>,
889 ) {
890 let offset = self.end_of_line();
891 self.select_to(offset, cx);
892 }
893
894 pub(super) fn select_to_previous_word(
895 &mut self,
896 _: &SelectToPreviousWordStart,
897 _: &mut Window,
898 cx: &mut Context<Self>,
899 ) {
900 let offset = self.previous_start_of_word();
901 self.select_to(offset, cx);
902 }
903
904 pub(super) fn select_to_next_word(
905 &mut self,
906 _: &SelectToNextWordEnd,
907 _: &mut Window,
908 cx: &mut Context<Self>,
909 ) {
910 let offset = self.next_end_of_word();
911 self.select_to(offset, cx);
912 }
913
914 pub(super) fn previous_start_of_word(&mut self) -> usize {
916 let offset = self.selected_range.start;
917 let offset = self.offset_from_utf16(self.offset_to_utf16(offset));
918 let left_part = self.text.slice(0..offset).to_string();
920
921 UnicodeSegmentation::split_word_bound_indices(left_part.as_str())
922 .filter(|(_, s)| !s.trim_start().is_empty())
923 .next_back()
924 .map(|(i, _)| i)
925 .unwrap_or(0)
926 }
927
928 pub(super) fn next_end_of_word(&mut self) -> usize {
930 let offset = self.cursor();
931 let offset = self.offset_from_utf16(self.offset_to_utf16(offset));
932 let right_part = self.text.slice(offset..self.text.len()).to_string();
933
934 UnicodeSegmentation::split_word_bound_indices(right_part.as_str())
935 .find(|(_, s)| !s.trim_start().is_empty())
936 .map(|(i, s)| offset + i + s.len())
937 .unwrap_or(self.text.len())
938 }
939
940 pub(super) fn start_of_line(&self) -> usize {
942 if self.mode.is_single_line() {
943 return 0;
944 }
945
946 let row = self.text.offset_to_point(self.cursor()).row;
947 self.text.line_start_offset(row)
948 }
949
950 pub(super) fn end_of_line(&self) -> usize {
952 if self.mode.is_single_line() {
953 return self.text.len();
954 }
955
956 let row = self.text.offset_to_point(self.cursor()).row;
957 self.text.line_end_offset(row)
958 }
959
960 pub(super) fn start_of_line_of_selection(
964 &mut self,
965 window: &mut Window,
966 cx: &mut Context<Self>,
967 ) -> usize {
968 if self.mode.is_single_line() {
969 return 0;
970 }
971
972 let mut offset =
973 self.previous_boundary(self.selected_range.start.min(self.selected_range.end));
974 if self.text.char_at(offset) == Some('\r') {
975 offset += 1;
976 }
977
978 let line = self
979 .text_for_range(self.range_to_utf16(&(0..offset + 1)), &mut None, window, cx)
980 .unwrap_or_default()
981 .rfind('\n')
982 .map(|i| i + 1)
983 .unwrap_or(0);
984 line
985 }
986
987 pub(super) fn indent_of_next_line(&mut self) -> String {
991 if self.mode.is_single_line() {
992 return "".into();
993 }
994
995 let mut current_indent = String::new();
996 let mut next_indent = String::new();
997 let current_line_start_pos = self.start_of_line();
998 let next_line_start_pos = self.end_of_line();
999 for c in self.text.slice(current_line_start_pos..).chars() {
1000 if !c.is_whitespace() {
1001 break;
1002 }
1003 if c == '\n' || c == '\r' {
1004 break;
1005 }
1006 current_indent.push(c);
1007 }
1008
1009 for c in self.text.slice(next_line_start_pos..).chars() {
1010 if !c.is_whitespace() {
1011 break;
1012 }
1013 if c == '\n' || c == '\r' {
1014 break;
1015 }
1016 next_indent.push(c);
1017 }
1018
1019 if next_indent.len() > current_indent.len() {
1020 return next_indent;
1021 } else {
1022 return current_indent;
1023 }
1024 }
1025
1026 pub(super) fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
1027 if self.selected_range.is_empty() {
1028 self.select_to(self.previous_boundary(self.cursor()), cx)
1029 }
1030 self.replace_text_in_range(None, "", window, cx);
1031 self.pause_blink_cursor(cx);
1032 }
1033
1034 pub(super) fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
1035 if self.selected_range.is_empty() {
1036 self.select_to(self.next_boundary(self.cursor()), cx)
1037 }
1038 self.replace_text_in_range(None, "", window, cx);
1039 self.pause_blink_cursor(cx);
1040 }
1041
1042 pub(super) fn delete_to_beginning_of_line(
1043 &mut self,
1044 _: &DeleteToBeginningOfLine,
1045 window: &mut Window,
1046 cx: &mut Context<Self>,
1047 ) {
1048 if !self.selected_range.is_empty() {
1049 self.replace_text_in_range(None, "", window, cx);
1050 self.pause_blink_cursor(cx);
1051 return;
1052 }
1053
1054 let mut offset = self.start_of_line();
1055 if offset == self.cursor() {
1056 offset = offset.saturating_sub(1);
1057 }
1058 self.replace_text_in_range_silent(
1059 Some(self.range_to_utf16(&(offset..self.cursor()))),
1060 "",
1061 window,
1062 cx,
1063 );
1064 self.pause_blink_cursor(cx);
1065 }
1066
1067 pub(super) fn delete_to_end_of_line(
1068 &mut self,
1069 _: &DeleteToEndOfLine,
1070 window: &mut Window,
1071 cx: &mut Context<Self>,
1072 ) {
1073 if !self.selected_range.is_empty() {
1074 self.replace_text_in_range(None, "", window, cx);
1075 self.pause_blink_cursor(cx);
1076 return;
1077 }
1078
1079 let mut offset = self.end_of_line();
1080 if offset == self.cursor() {
1081 offset = (offset + 1).clamp(0, self.text.len());
1082 }
1083 self.replace_text_in_range_silent(
1084 Some(self.range_to_utf16(&(self.cursor()..offset))),
1085 "",
1086 window,
1087 cx,
1088 );
1089 self.pause_blink_cursor(cx);
1090 }
1091
1092 pub(super) fn delete_previous_word(
1093 &mut self,
1094 _: &DeleteToPreviousWordStart,
1095 window: &mut Window,
1096 cx: &mut Context<Self>,
1097 ) {
1098 if !self.selected_range.is_empty() {
1099 self.replace_text_in_range(None, "", window, cx);
1100 self.pause_blink_cursor(cx);
1101 return;
1102 }
1103
1104 let offset = self.previous_start_of_word();
1105 self.replace_text_in_range_silent(
1106 Some(self.range_to_utf16(&(offset..self.cursor()))),
1107 "",
1108 window,
1109 cx,
1110 );
1111 self.pause_blink_cursor(cx);
1112 }
1113
1114 pub(super) fn delete_next_word(
1115 &mut self,
1116 _: &DeleteToNextWordEnd,
1117 window: &mut Window,
1118 cx: &mut Context<Self>,
1119 ) {
1120 if !self.selected_range.is_empty() {
1121 self.replace_text_in_range(None, "", window, cx);
1122 self.pause_blink_cursor(cx);
1123 return;
1124 }
1125
1126 let offset = self.next_end_of_word();
1127 self.replace_text_in_range_silent(
1128 Some(self.range_to_utf16(&(self.cursor()..offset))),
1129 "",
1130 window,
1131 cx,
1132 );
1133 self.pause_blink_cursor(cx);
1134 }
1135
1136 pub(super) fn enter(&mut self, action: &Enter, window: &mut Window, cx: &mut Context<Self>) {
1137 if self.handle_action_for_context_menu(Box::new(action.clone()), window, cx) {
1138 return;
1139 }
1140
1141 if self.mode.is_multi_line() {
1142 let indent = if self.mode.is_code_editor() {
1144 self.indent_of_next_line()
1145 } else {
1146 "".to_string()
1147 };
1148
1149 let new_line_text = format!("\n{}", indent);
1151 self.replace_text_in_range_silent(None, &new_line_text, window, cx);
1152 self.pause_blink_cursor(cx);
1153 } else {
1154 cx.propagate();
1156 }
1157
1158 cx.emit(InputEvent::PressEnter {
1159 secondary: action.secondary,
1160 });
1161 }
1162
1163 pub(super) fn clean(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1164 self.replace_text("", window, cx);
1165 self.selected_range = (0..0).into();
1166 self.scroll_to(0, None, cx);
1167 }
1168
1169 pub(super) fn escape(&mut self, action: &Escape, window: &mut Window, cx: &mut Context<Self>) {
1170 if self.handle_action_for_context_menu(Box::new(action.clone()), window, cx) {
1171 return;
1172 }
1173
1174 if self.ime_marked_range.is_some() {
1175 self.unmark_text(window, cx);
1176 }
1177
1178 if self.clean_on_escape {
1179 return self.clean(window, cx);
1180 }
1181
1182 cx.propagate();
1183 }
1184
1185 pub(super) fn on_mouse_down(
1186 &mut self,
1187 event: &MouseDownEvent,
1188 window: &mut Window,
1189 cx: &mut Context<Self>,
1190 ) {
1191 if let Some(ime_marked_range) = &self.ime_marked_range {
1194 if ime_marked_range.len() == 0 {
1195 self.ime_marked_range = None;
1196 }
1197 }
1198
1199 self.selecting = true;
1200 let offset = self.index_for_mouse_position(event.position);
1201
1202 if self.handle_click_hover_definition(event, offset, window, cx) {
1203 return;
1204 }
1205
1206 if event.button == MouseButton::Left && event.click_count == 2 {
1208 self.select_word(offset, window, cx);
1209 return;
1210 }
1211
1212 if event.button == MouseButton::Right {
1214 self.handle_right_click_menu(event, offset, window, cx);
1215 return;
1216 }
1217
1218 if event.modifiers.shift {
1219 self.select_to(offset, cx);
1220 } else {
1221 self.move_to(offset, None, cx)
1222 }
1223 }
1224
1225 pub(super) fn on_mouse_up(
1226 &mut self,
1227 _: &MouseUpEvent,
1228 _window: &mut Window,
1229 _cx: &mut Context<Self>,
1230 ) {
1231 if self.selected_range.is_empty() {
1232 self.selection_reversed = false;
1233 }
1234 self.selecting = false;
1235 self.selected_word_range = None;
1236 }
1237
1238 pub(super) fn on_mouse_move(
1239 &mut self,
1240 event: &MouseMoveEvent,
1241 window: &mut Window,
1242 cx: &mut Context<Self>,
1243 ) {
1244 let offset = self.index_for_mouse_position(event.position);
1246 self.handle_mouse_move(offset, event, window, cx);
1247
1248 if self.mode.is_code_editor() {
1249 if let Some(diagnostic) = self
1250 .mode
1251 .diagnostics()
1252 .and_then(|set| set.for_offset(offset))
1253 {
1254 if let Some(diagnostic_popover) = self.diagnostic_popover.as_ref() {
1255 if diagnostic_popover.read(cx).diagnostic.range == diagnostic.range {
1256 diagnostic_popover.update(cx, |this, cx| {
1257 this.show(cx);
1258 });
1259
1260 return;
1261 }
1262 }
1263
1264 self.diagnostic_popover = Some(DiagnosticPopover::new(diagnostic, cx.entity(), cx));
1265 cx.notify();
1266 } else {
1267 if let Some(diagnostic_popover) = self.diagnostic_popover.as_mut() {
1268 diagnostic_popover.update(cx, |this, cx| {
1269 this.check_to_hide(event.position, cx);
1270 })
1271 }
1272 }
1273 }
1274 }
1275
1276 pub(super) fn on_scroll_wheel(
1277 &mut self,
1278 event: &ScrollWheelEvent,
1279 window: &mut Window,
1280 cx: &mut Context<Self>,
1281 ) {
1282 let line_height = self
1283 .last_layout
1284 .as_ref()
1285 .map(|layout| layout.line_height)
1286 .unwrap_or(window.line_height());
1287 let delta = event.delta.pixel_delta(line_height);
1288
1289 let old_offset = self.scroll_handle.offset();
1290 self.update_scroll_offset(Some(old_offset + delta), cx);
1291
1292 if self.scroll_handle.offset() != old_offset {
1294 cx.stop_propagation();
1295 }
1296
1297 self.diagnostic_popover = None;
1298 }
1299
1300 pub(super) fn update_scroll_offset(
1301 &mut self,
1302 offset: Option<Point<Pixels>>,
1303 cx: &mut Context<Self>,
1304 ) {
1305 let mut offset = offset.unwrap_or(self.scroll_handle.offset());
1306
1307 let safe_y_range =
1308 (-self.scroll_size.height + self.input_bounds.size.height).min(px(0.0))..px(0.);
1309 let safe_x_range =
1310 (-self.scroll_size.width + self.input_bounds.size.width).min(px(0.0))..px(0.);
1311
1312 offset.y = if self.mode.is_single_line() {
1313 px(0.)
1314 } else {
1315 offset.y.clamp(safe_y_range.start, safe_y_range.end)
1316 };
1317 offset.x = offset.x.clamp(safe_x_range.start, safe_x_range.end);
1318 self.scroll_handle.set_offset(offset);
1319 cx.notify();
1320 }
1321
1322 pub(crate) fn scroll_to(
1326 &mut self,
1327 offset: usize,
1328 direction: Option<MoveDirection>,
1329 cx: &mut Context<Self>,
1330 ) {
1331 let Some(last_layout) = self.last_layout.as_ref() else {
1332 return;
1333 };
1334 let Some(bounds) = self.last_bounds.as_ref() else {
1335 return;
1336 };
1337
1338 let mut scroll_offset = self.scroll_handle.offset();
1339 let was_offset = scroll_offset;
1340 let line_height = last_layout.line_height;
1341
1342 let point = self.text.offset_to_point(offset);
1343
1344 let row = point.row;
1345
1346 let mut row_offset_y = px(0.);
1347 for (ix, wrap_line) in self.text_wrapper.lines.iter().enumerate() {
1348 if ix == row {
1349 break;
1350 }
1351
1352 row_offset_y += wrap_line.height(line_height);
1353 }
1354
1355 if let Some(line) = last_layout
1356 .lines
1357 .get(row.saturating_sub(last_layout.visible_range.start))
1358 {
1359 if let Some(pos) = line.position_for_index(point.column, line_height) {
1361 let bounds_width = bounds.size.width - last_layout.line_number_width;
1362 let col_offset_x = pos.x;
1363 row_offset_y += pos.y;
1364 if col_offset_x - RIGHT_MARGIN < -scroll_offset.x {
1365 scroll_offset.x = -col_offset_x + RIGHT_MARGIN;
1367 } else if col_offset_x + RIGHT_MARGIN > -scroll_offset.x + bounds_width {
1368 scroll_offset.x = -(col_offset_x - bounds_width + RIGHT_MARGIN);
1369 }
1370 }
1371 }
1372
1373 let edge_height = if direction.is_some() && self.mode.is_code_editor() {
1376 3 * line_height
1377 } else {
1378 line_height
1379 };
1380 if row_offset_y - edge_height + line_height < -scroll_offset.y {
1381 scroll_offset.y = -row_offset_y + edge_height - line_height;
1383 } else if row_offset_y + edge_height > -scroll_offset.y + bounds.size.height {
1384 scroll_offset.y = -(row_offset_y - bounds.size.height + edge_height);
1386 }
1387
1388 if direction == Some(MoveDirection::Up) {
1390 scroll_offset.y = scroll_offset.y.max(was_offset.y);
1391 } else if direction == Some(MoveDirection::Down) {
1392 scroll_offset.y = scroll_offset.y.min(was_offset.y);
1393 }
1394
1395 scroll_offset.x = scroll_offset.x.min(px(0.));
1396 scroll_offset.y = scroll_offset.y.min(px(0.));
1397 self.deferred_scroll_offset = Some(scroll_offset);
1398 cx.notify();
1399 }
1400
1401 pub(super) fn show_character_palette(
1402 &mut self,
1403 _: &ShowCharacterPalette,
1404 window: &mut Window,
1405 _: &mut Context<Self>,
1406 ) {
1407 window.show_character_palette();
1408 }
1409
1410 pub(super) fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
1411 if self.selected_range.is_empty() {
1412 return;
1413 }
1414
1415 let selected_text = self.text.slice(self.selected_range).to_string();
1416 cx.write_to_clipboard(ClipboardItem::new_string(selected_text));
1417 }
1418
1419 pub(super) fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
1420 if self.selected_range.is_empty() {
1421 return;
1422 }
1423
1424 let selected_text = self.text.slice(self.selected_range).to_string();
1425 cx.write_to_clipboard(ClipboardItem::new_string(selected_text));
1426
1427 self.replace_text_in_range_silent(None, "", window, cx);
1428 }
1429
1430 pub(super) fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
1431 if let Some(clipboard) = cx.read_from_clipboard() {
1432 let mut new_text = clipboard.text().unwrap_or_default();
1433 if !self.mode.is_multi_line() {
1434 new_text = new_text.replace('\n', "");
1435 }
1436
1437 self.replace_text_in_range_silent(None, &new_text, window, cx);
1438 self.scroll_to(self.cursor(), None, cx);
1439 }
1440 }
1441
1442 fn push_history(&mut self, text: &Rope, range: &Range<usize>, new_text: &str) {
1443 if self.history.ignore {
1444 return;
1445 }
1446
1447 let old_text = text.slice(range.clone()).to_string();
1448 let new_range = range.start..range.start + new_text.len();
1449
1450 self.history
1451 .push(Change::new(range.clone(), &old_text, new_range, new_text));
1452 }
1453
1454 pub(super) fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
1455 self.history.ignore = true;
1456 if let Some(changes) = self.history.undo() {
1457 for change in changes {
1458 let range_utf16 = self.range_to_utf16(&change.new_range.into());
1459 self.replace_text_in_range_silent(Some(range_utf16), &change.old_text, window, cx);
1460 }
1461 }
1462 self.history.ignore = false;
1463 }
1464
1465 pub(super) fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
1466 self.history.ignore = true;
1467 if let Some(changes) = self.history.redo() {
1468 for change in changes {
1469 let range_utf16 = self.range_to_utf16(&change.old_range.into());
1470 self.replace_text_in_range_silent(Some(range_utf16), &change.new_text, window, cx);
1471 }
1472 }
1473 self.history.ignore = false;
1474 }
1475
1476 pub fn cursor(&self) -> usize {
1480 if let Some(ime_marked_range) = &self.ime_marked_range {
1481 return ime_marked_range.end;
1482 }
1483
1484 if self.selection_reversed {
1485 self.selected_range.start
1486 } else {
1487 self.selected_range.end
1488 }
1489 }
1490
1491 pub(crate) fn index_for_mouse_position(&self, position: Point<Pixels>) -> usize {
1492 if self.text.len() == 0 {
1494 return 0;
1495 }
1496
1497 let (Some(bounds), Some(last_layout)) =
1498 (self.last_bounds.as_ref(), self.last_layout.as_ref())
1499 else {
1500 return 0;
1501 };
1502
1503 let line_height = last_layout.line_height;
1504 let line_number_width = last_layout.line_number_width;
1505
1506 let inner_position = position - bounds.origin - point(line_number_width, px(0.));
1517
1518 let mut index = last_layout.visible_range_offset.start;
1519 let mut y_offset = last_layout.visible_top;
1520 for (ix, line) in self
1521 .text_wrapper
1522 .lines
1523 .iter()
1524 .skip(last_layout.visible_range.start)
1525 .enumerate()
1526 {
1527 let line_origin = self.line_origin_with_y_offset(&mut y_offset, line, line_height);
1528 let pos = inner_position - line_origin;
1529
1530 let Some(line_layout) = last_layout.lines.get(ix) else {
1531 if pos.y < line_origin.y + line_height {
1532 break;
1533 }
1534
1535 continue;
1536 };
1537
1538 if self.mode.is_single_line() {
1540 index = line_layout.closest_index_for_x(pos.x);
1541 break;
1542 }
1543
1544 if let Some(v) = line_layout.closest_index_for_position(pos, line_height) {
1545 index += v;
1546 break;
1547 } else if pos.y < px(0.) {
1548 break;
1549 }
1550
1551 index += line_layout.len() + 1;
1553 }
1554
1555 let index = if index > self.text.len() {
1556 self.text.len()
1557 } else {
1558 index
1559 };
1560
1561 if self.masked {
1562 self.text.char_index_to_offset(index)
1564 } else {
1565 index
1566 }
1567 }
1568
1569 fn line_origin_with_y_offset(
1571 &self,
1572 y_offset: &mut Pixels,
1573 line: &LineItem,
1574 line_height: Pixels,
1575 ) -> Point<Pixels> {
1576 if self.mode.is_multi_line() {
1581 let p = point(px(0.), *y_offset);
1582 *y_offset += line.height(line_height);
1583 p
1584 } else {
1585 point(px(0.), px(0.))
1586 }
1587 }
1588
1589 pub(crate) fn select_to(&mut self, offset: usize, cx: &mut Context<Self>) {
1595 let offset = offset.clamp(0, self.text.len());
1596 if self.selection_reversed {
1597 self.selected_range.start = offset
1598 } else {
1599 self.selected_range.end = offset
1600 };
1601
1602 if self.selected_range.end < self.selected_range.start {
1603 self.selection_reversed = !self.selection_reversed;
1604 self.selected_range = (self.selected_range.end..self.selected_range.start).into();
1605 }
1606
1607 if let Some(word_range) = self.selected_word_range.as_ref() {
1609 if self.selected_range.start > word_range.start {
1610 self.selected_range.start = word_range.start;
1611 }
1612 if self.selected_range.end < word_range.end {
1613 self.selected_range.end = word_range.end;
1614 }
1615 }
1616 if self.selected_range.is_empty() {
1617 self.update_preferred_column();
1618 }
1619 cx.notify()
1620 }
1621
1622 pub fn unselect(&mut self, _: &mut Window, cx: &mut Context<Self>) {
1624 let offset = self.cursor();
1625 self.selected_range = (offset..offset).into();
1626 cx.notify()
1627 }
1628
1629 #[inline]
1630 pub(super) fn offset_from_utf16(&self, offset: usize) -> usize {
1631 self.text.offset_utf16_to_offset(offset)
1632 }
1633
1634 #[inline]
1635 pub(super) fn offset_to_utf16(&self, offset: usize) -> usize {
1636 self.text.offset_to_offset_utf16(offset)
1637 }
1638
1639 #[inline]
1640 pub(super) fn range_to_utf16(&self, range: &Range<usize>) -> Range<usize> {
1641 self.offset_to_utf16(range.start)..self.offset_to_utf16(range.end)
1642 }
1643
1644 #[inline]
1645 pub(super) fn range_from_utf16(&self, range_utf16: &Range<usize>) -> Range<usize> {
1646 self.offset_from_utf16(range_utf16.start)..self.offset_from_utf16(range_utf16.end)
1647 }
1648
1649 pub(super) fn previous_boundary(&self, offset: usize) -> usize {
1650 let mut offset = self.text.clip_offset(offset.saturating_sub(1), Bias::Left);
1651 if let Some(ch) = self.text.char_at(offset) {
1652 if ch == '\r' {
1653 offset -= 1;
1654 }
1655 }
1656
1657 offset
1658 }
1659
1660 pub(super) fn next_boundary(&self, offset: usize) -> usize {
1661 let mut offset = self.text.clip_offset(offset + 1, Bias::Right);
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(crate) fn show_cursor(&self, window: &Window, cx: &App) -> bool {
1673 (self.focus_handle.is_focused(window) || self.is_context_menu_open(cx))
1674 && self.blink_cursor.read(cx).visible()
1675 && window.is_window_active()
1676 }
1677
1678 fn on_focus(&mut self, _: &mut Window, cx: &mut Context<Self>) {
1679 self.blink_cursor.update(cx, |cursor, cx| {
1680 cursor.start(cx);
1681 });
1682 cx.emit(InputEvent::Focus);
1683 }
1684
1685 fn on_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1686 if self.is_context_menu_open(cx) {
1687 return;
1688 }
1689
1690 self.hover_popover = None;
1694 self.diagnostic_popover = None;
1695 self.context_menu = None;
1696 self.blink_cursor.update(cx, |cursor, cx| {
1697 cursor.stop(cx);
1698 });
1699 Root::update(window, cx, |root, _, _| {
1700 root.focused_input = None;
1701 });
1702 cx.emit(InputEvent::Blur);
1703 cx.notify();
1704 }
1705
1706 pub(super) fn pause_blink_cursor(&mut self, cx: &mut Context<Self>) {
1707 self.blink_cursor.update(cx, |cursor, cx| {
1708 cursor.pause(cx);
1709 });
1710 }
1711
1712 pub(super) fn on_key_down(&mut self, _: &KeyDownEvent, _: &mut Window, cx: &mut Context<Self>) {
1713 self.pause_blink_cursor(cx);
1714 }
1715
1716 pub(super) fn on_drag_move(
1717 &mut self,
1718 event: &MouseMoveEvent,
1719 window: &mut Window,
1720 cx: &mut Context<Self>,
1721 ) {
1722 if self.text.len() == 0 {
1723 return;
1724 }
1725
1726 if self.last_layout.is_none() {
1727 return;
1728 }
1729
1730 if !self.focus_handle.is_focused(window) {
1731 return;
1732 }
1733
1734 if !self.selecting {
1735 return;
1736 }
1737
1738 let offset = self.index_for_mouse_position(event.position);
1739 self.select_to(offset, cx);
1740 }
1741
1742 fn is_valid_input(&self, new_text: &str, cx: &mut Context<Self>) -> bool {
1743 if new_text.is_empty() {
1744 return true;
1745 }
1746
1747 if let Some(validate) = &self.validate {
1748 if !validate(new_text, cx) {
1749 return false;
1750 }
1751 }
1752
1753 if !self.mask_pattern.is_valid(new_text) {
1754 return false;
1755 }
1756
1757 let Some(pattern) = &self.pattern else {
1758 return true;
1759 };
1760
1761 pattern.is_match(new_text)
1762 }
1763
1764 pub fn mask_pattern(mut self, pattern: impl Into<MaskPattern>) -> Self {
1774 self.mask_pattern = pattern.into();
1775 if let Some(placeholder) = self.mask_pattern.placeholder() {
1776 self.placeholder = placeholder.into();
1777 }
1778 self
1779 }
1780
1781 pub fn set_mask_pattern(
1782 &mut self,
1783 pattern: impl Into<MaskPattern>,
1784 _: &mut Window,
1785 cx: &mut Context<Self>,
1786 ) {
1787 self.mask_pattern = pattern.into();
1788 if let Some(placeholder) = self.mask_pattern.placeholder() {
1789 self.placeholder = placeholder.into();
1790 }
1791 cx.notify();
1792 }
1793
1794 pub(super) fn set_input_bounds(&mut self, new_bounds: Bounds<Pixels>, cx: &mut Context<Self>) {
1795 let wrap_width_changed = self.input_bounds.size.width != new_bounds.size.width;
1796 self.input_bounds = new_bounds;
1797
1798 if let Some(last_layout) = self.last_layout.as_ref() {
1800 if wrap_width_changed {
1801 let wrap_width = if !self.soft_wrap {
1802 None
1804 } else {
1805 last_layout.wrap_width
1806 };
1807
1808 self.text_wrapper.set_wrap_width(wrap_width, cx);
1809 self.mode.update_auto_grow(&self.text_wrapper);
1810 cx.notify();
1811 }
1812 }
1813 }
1814
1815 pub(super) fn selected_text(&self) -> RopeSlice<'_> {
1816 let range_utf16 = self.range_to_utf16(&self.selected_range.into());
1817 let range = self.range_from_utf16(&range_utf16);
1818 self.text.slice(range)
1819 }
1820
1821 pub(crate) fn range_to_bounds(&self, range: &Range<usize>) -> Option<Bounds<Pixels>> {
1822 let Some(last_layout) = self.last_layout.as_ref() else {
1823 return None;
1824 };
1825
1826 let Some(last_bounds) = self.last_bounds else {
1827 return None;
1828 };
1829
1830 let (_, _, start_pos) = self.line_and_position_for_offset(range.start);
1831 let (_, _, end_pos) = self.line_and_position_for_offset(range.end);
1832
1833 let Some(start_pos) = start_pos else {
1834 return None;
1835 };
1836 let Some(end_pos) = end_pos else {
1837 return None;
1838 };
1839
1840 Some(Bounds::from_corners(
1841 last_bounds.origin + start_pos,
1842 last_bounds.origin + end_pos + point(px(0.), last_layout.line_height),
1843 ))
1844 }
1845
1846 #[allow(unused)]
1850 pub(crate) fn replace_text_in_lsp_range(
1851 &mut self,
1852 lsp_range: &lsp_types::Range,
1853 new_text: &str,
1854 window: &mut Window,
1855 cx: &mut Context<Self>,
1856 ) {
1857 let start = self.text.position_to_offset(&lsp_range.start);
1858 let end = self.text.position_to_offset(&lsp_range.end);
1859 self.replace_text_in_range_silent(
1860 Some(self.range_to_utf16(&(start..end))),
1861 new_text,
1862 window,
1863 cx,
1864 );
1865 }
1866
1867 pub(crate) fn replace_text_in_range_silent(
1871 &mut self,
1872 range_utf16: Option<Range<usize>>,
1873 new_text: &str,
1874 window: &mut Window,
1875 cx: &mut Context<Self>,
1876 ) {
1877 self.silent_replace_text = true;
1878 self.replace_text_in_range(range_utf16, new_text, window, cx);
1879 self.silent_replace_text = false;
1880 }
1881}
1882
1883impl EntityInputHandler for InputState {
1884 fn text_for_range(
1885 &mut self,
1886 range_utf16: Range<usize>,
1887 adjusted_range: &mut Option<Range<usize>>,
1888 _window: &mut Window,
1889 _cx: &mut Context<Self>,
1890 ) -> Option<String> {
1891 let range = self.range_from_utf16(&range_utf16);
1892 adjusted_range.replace(self.range_to_utf16(&range));
1893 Some(self.text.slice(range).to_string())
1894 }
1895
1896 fn selected_text_range(
1897 &mut self,
1898 _ignore_disabled_input: bool,
1899 _window: &mut Window,
1900 _cx: &mut Context<Self>,
1901 ) -> Option<UTF16Selection> {
1902 Some(UTF16Selection {
1903 range: self.range_to_utf16(&self.selected_range.into()),
1904 reversed: false,
1905 })
1906 }
1907
1908 fn marked_text_range(
1909 &self,
1910 _window: &mut Window,
1911 _cx: &mut Context<Self>,
1912 ) -> Option<Range<usize>> {
1913 self.ime_marked_range
1914 .map(|range| self.range_to_utf16(&range.into()))
1915 }
1916
1917 fn unmark_text(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
1918 self.ime_marked_range = None;
1919 }
1920
1921 fn replace_text_in_range(
1926 &mut self,
1927 range_utf16: Option<Range<usize>>,
1928 new_text: &str,
1929 window: &mut Window,
1930 cx: &mut Context<Self>,
1931 ) {
1932 if self.disabled {
1933 return;
1934 }
1935
1936 self.pause_blink_cursor(cx);
1937
1938 let range = range_utf16
1939 .as_ref()
1940 .map(|range_utf16| self.range_from_utf16(range_utf16))
1941 .or(self.ime_marked_range.map(|range| {
1942 let range = self.range_to_utf16(&(range.start..range.end));
1943 self.range_from_utf16(&range)
1944 }))
1945 .unwrap_or(self.selected_range.into());
1946
1947 let old_text = self.text.clone();
1948 self.text.replace(range.clone(), new_text);
1949
1950 let mut new_offset = (range.start + new_text.len()).min(self.text.len());
1951
1952 if self.mode.is_single_line() {
1953 let pending_text = self.text.to_string();
1954 if !self.is_valid_input(&pending_text, cx) {
1956 self.text = old_text;
1957 return;
1958 }
1959
1960 if !self.mask_pattern.is_none() {
1961 let mask_text = self.mask_pattern.mask(&pending_text);
1962 self.text = Rope::from(mask_text.as_str());
1963 let new_text_len =
1964 (new_text.len() + mask_text.len()).saturating_sub(pending_text.len());
1965 new_offset = (range.start + new_text_len).min(mask_text.len());
1966 }
1967 }
1968
1969 self.push_history(&old_text, &range, &new_text);
1970 self.history.end_grouping();
1971 if let Some(diagnostics) = self.mode.diagnostics_mut() {
1972 diagnostics.reset(&self.text)
1973 }
1974 self.text_wrapper
1975 .update(&self.text, &range, &Rope::from(new_text), cx);
1976 self.mode
1977 .update_highlighter(&range, &self.text, &new_text, true, cx);
1978 self.lsp.update(&self.text, window, cx);
1979 self.selected_range = (new_offset..new_offset).into();
1980 self.ime_marked_range.take();
1981 self.update_preferred_column();
1982 self.update_search(cx);
1983 self.mode.update_auto_grow(&self.text_wrapper);
1984 if !self.silent_replace_text {
1985 self.handle_completion_trigger(&range, &new_text, window, cx);
1986 }
1987 cx.emit(InputEvent::Change);
1988 cx.notify();
1989 }
1990
1991 fn replace_and_mark_text_in_range(
1993 &mut self,
1994 range_utf16: Option<Range<usize>>,
1995 new_text: &str,
1996 new_selected_range_utf16: Option<Range<usize>>,
1997 window: &mut Window,
1998 cx: &mut Context<Self>,
1999 ) {
2000 if self.disabled {
2001 return;
2002 }
2003
2004 self.lsp.reset();
2005
2006 let range = range_utf16
2007 .as_ref()
2008 .map(|range_utf16| self.range_from_utf16(range_utf16))
2009 .or(self.ime_marked_range.map(|range| {
2010 let range = self.range_to_utf16(&(range.start..range.end));
2011 self.range_from_utf16(&range)
2012 }))
2013 .unwrap_or(self.selected_range.into());
2014
2015 let old_text = self.text.clone();
2016 self.text.replace(range.clone(), new_text);
2017
2018 if self.mode.is_single_line() {
2019 let pending_text = self.text.to_string();
2020 if !self.is_valid_input(&pending_text, cx) {
2021 self.text = old_text;
2022 return;
2023 }
2024 }
2025
2026 if let Some(diagnostics) = self.mode.diagnostics_mut() {
2027 diagnostics.reset(&self.text)
2028 }
2029 self.text_wrapper
2030 .update(&self.text, &range, &Rope::from(new_text), cx);
2031 self.mode
2032 .update_highlighter(&range, &self.text, &new_text, true, cx);
2033 self.lsp.update(&self.text, window, cx);
2034 if new_text.is_empty() {
2035 self.selected_range = (range.start..range.start).into();
2037 self.ime_marked_range = None;
2038 } else {
2039 self.ime_marked_range = Some((range.start..range.start + new_text.len()).into());
2040 self.selected_range = new_selected_range_utf16
2041 .as_ref()
2042 .map(|range_utf16| self.range_from_utf16(range_utf16))
2043 .map(|new_range| new_range.start + range.start..new_range.end + range.end)
2044 .unwrap_or_else(|| range.start + new_text.len()..range.start + new_text.len())
2045 .into();
2046 }
2047 self.mode.update_auto_grow(&self.text_wrapper);
2048 self.history.start_grouping();
2049 self.push_history(&old_text, &range, new_text);
2050 cx.notify();
2051 }
2052
2053 fn bounds_for_range(
2055 &mut self,
2056 range_utf16: Range<usize>,
2057 bounds: Bounds<Pixels>,
2058 _window: &mut Window,
2059 _cx: &mut Context<Self>,
2060 ) -> Option<Bounds<Pixels>> {
2061 let last_layout = self.last_layout.as_ref()?;
2062 let line_height = last_layout.line_height;
2063 let line_number_width = last_layout.line_number_width;
2064 let range = self.range_from_utf16(&range_utf16);
2065
2066 let mut start_origin = None;
2067 let mut end_origin = None;
2068 let line_number_origin = point(line_number_width, px(0.));
2069 let mut y_offset = last_layout.visible_top;
2070 let mut index_offset = last_layout.visible_range_offset.start;
2071
2072 for line in last_layout.lines.iter() {
2073 if start_origin.is_some() && end_origin.is_some() {
2074 break;
2075 }
2076
2077 if start_origin.is_none() {
2078 if let Some(p) =
2079 line.position_for_index(range.start.saturating_sub(index_offset), line_height)
2080 {
2081 start_origin = Some(p + point(px(0.), y_offset));
2082 }
2083 }
2084
2085 if end_origin.is_none() {
2086 if let Some(p) =
2087 line.position_for_index(range.end.saturating_sub(index_offset), line_height)
2088 {
2089 end_origin = Some(p + point(px(0.), y_offset));
2090 }
2091 }
2092
2093 index_offset += line.len() + 1;
2094 y_offset += line.size(line_height).height;
2095 }
2096
2097 let start_origin = start_origin.unwrap_or_default();
2098 let mut end_origin = end_origin.unwrap_or_default();
2099 end_origin.y = start_origin.y;
2101
2102 Some(Bounds::from_corners(
2103 bounds.origin + line_number_origin + start_origin,
2104 bounds.origin + line_number_origin + point(end_origin.x, end_origin.y + line_height),
2106 ))
2107 }
2108
2109 fn character_index_for_point(
2110 &mut self,
2111 point: gpui::Point<Pixels>,
2112 _window: &mut Window,
2113 _cx: &mut Context<Self>,
2114 ) -> Option<usize> {
2115 let last_layout = self.last_layout.as_ref()?;
2116 let line_height = last_layout.line_height;
2117 let line_point = self.last_bounds?.localize(&point)?;
2118 let offset = last_layout.visible_range_offset.start;
2119
2120 for line in last_layout.lines.iter() {
2121 if let Some(utf8_index) = line.index_for_position(line_point, line_height) {
2122 return Some(self.offset_to_utf16(offset + utf8_index));
2123 }
2124 }
2125
2126 None
2127 }
2128}
2129
2130impl Focusable for InputState {
2131 fn focus_handle(&self, _cx: &App) -> FocusHandle {
2132 self.focus_handle.clone()
2133 }
2134}
2135
2136impl Render for InputState {
2137 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
2138 if self._pending_update {
2139 self.mode
2140 .update_highlighter(&(0..0), &self.text, "", false, cx);
2141 self.lsp.update(&self.text, window, cx);
2142 self._pending_update = false;
2143 }
2144
2145 div()
2146 .id("input-state")
2147 .flex_1()
2148 .when(self.mode.is_multi_line(), |this| this.h_full())
2149 .flex_grow()
2150 .overflow_x_hidden()
2151 .child(TextElement::new(cx.entity().clone()).placeholder(self.placeholder.clone()))
2152 .children(self.diagnostic_popover.clone())
2153 .children(self.context_menu.as_ref().map(|menu| menu.render()))
2154 .children(self.hover_popover.clone())
2155 }
2156}