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