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