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