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