1mod blink_manager;
2pub mod display_map;
3mod editor_settings;
4mod element;
5mod inlay_hint_cache;
6
7mod git;
8mod highlight_matching_bracket;
9mod hover_popover;
10pub mod items;
11mod link_go_to_definition;
12mod mouse_context_menu;
13pub mod movement;
14pub mod multi_buffer;
15mod persistence;
16pub mod scroll;
17pub mod selections_collection;
18
19#[cfg(test)]
20mod editor_tests;
21#[cfg(any(test, feature = "test-support"))]
22pub mod test;
23
24use ::git::diff::DiffHunk;
25use aho_corasick::AhoCorasick;
26use anyhow::{anyhow, Context, Result};
27use blink_manager::BlinkManager;
28use client::{ClickhouseEvent, Collaborator, ParticipantIndex, TelemetrySettings};
29use clock::{Global, ReplicaId};
30use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
31use convert_case::{Case, Casing};
32use copilot::Copilot;
33pub use display_map::DisplayPoint;
34use display_map::*;
35pub use editor_settings::EditorSettings;
36pub use element::{
37 Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
38};
39use futures::FutureExt;
40use fuzzy::{StringMatch, StringMatchCandidate};
41use gpui::{
42 actions,
43 color::Color,
44 elements::*,
45 executor,
46 fonts::{self, HighlightStyle, TextStyle},
47 geometry::vector::{vec2f, Vector2F},
48 impl_actions,
49 keymap_matcher::KeymapContext,
50 platform::{CursorStyle, MouseButton},
51 serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element,
52 Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
53 WindowContext,
54};
55use highlight_matching_bracket::refresh_matching_bracket_highlights;
56use hover_popover::{hide_hover, HoverState};
57use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
58pub use items::MAX_TAB_TITLE_LEN;
59use itertools::Itertools;
60pub use language::{char_kind, CharKind};
61use language::{
62 language_settings::{self, all_language_settings, InlayHintSettings},
63 point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion,
64 CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language,
65 LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal,
66 TransactionId,
67};
68use link_go_to_definition::{
69 hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight,
70 LinkGoToDefinitionState,
71};
72use log::error;
73use lsp::LanguageServerId;
74use multi_buffer::ToOffsetUtf16;
75pub use multi_buffer::{
76 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
77 ToPoint,
78};
79use ordered_float::OrderedFloat;
80use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
81use rand::{seq::SliceRandom, thread_rng};
82use rpc::proto::PeerId;
83use scroll::{
84 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
85};
86use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
87use serde::{Deserialize, Serialize};
88use settings::SettingsStore;
89use smallvec::SmallVec;
90use snippet::Snippet;
91use std::{
92 any::TypeId,
93 borrow::Cow,
94 cmp::{self, Ordering, Reverse},
95 mem,
96 num::NonZeroU32,
97 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
98 path::Path,
99 sync::Arc,
100 time::{Duration, Instant},
101};
102pub use sum_tree::Bias;
103use sum_tree::TreeMap;
104use text::Rope;
105use theme::{DiagnosticStyle, Theme, ThemeSettings};
106use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
107use workspace::{ItemNavHistory, SplitDirection, ViewId, Workspace};
108
109use crate::git::diff_hunk_to_display;
110
111const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
112const MAX_LINE_LEN: usize = 1024;
113const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
114const MAX_SELECTION_HISTORY_LEN: usize = 1024;
115const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
116pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
117pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
118
119pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
120
121#[derive(Clone, Deserialize, PartialEq, Default)]
122pub struct SelectNext {
123 #[serde(default)]
124 pub replace_newest: bool,
125}
126
127#[derive(Clone, Deserialize, PartialEq, Default)]
128pub struct SelectPrevious {
129 #[serde(default)]
130 pub replace_newest: bool,
131}
132
133#[derive(Clone, Deserialize, PartialEq, Default)]
134pub struct SelectAllMatches {
135 #[serde(default)]
136 pub replace_newest: bool,
137}
138
139#[derive(Clone, Deserialize, PartialEq)]
140pub struct SelectToBeginningOfLine {
141 #[serde(default)]
142 stop_at_soft_wraps: bool,
143}
144
145#[derive(Clone, Default, Deserialize, PartialEq)]
146pub struct MovePageUp {
147 #[serde(default)]
148 center_cursor: bool,
149}
150
151#[derive(Clone, Default, Deserialize, PartialEq)]
152pub struct MovePageDown {
153 #[serde(default)]
154 center_cursor: bool,
155}
156
157#[derive(Clone, Deserialize, PartialEq)]
158pub struct SelectToEndOfLine {
159 #[serde(default)]
160 stop_at_soft_wraps: bool,
161}
162
163#[derive(Clone, Deserialize, PartialEq)]
164pub struct ToggleCodeActions {
165 #[serde(default)]
166 pub deployed_from_indicator: bool,
167}
168
169#[derive(Clone, Default, Deserialize, PartialEq)]
170pub struct ConfirmCompletion {
171 #[serde(default)]
172 pub item_ix: Option<usize>,
173}
174
175#[derive(Clone, Default, Deserialize, PartialEq)]
176pub struct ConfirmCodeAction {
177 #[serde(default)]
178 pub item_ix: Option<usize>,
179}
180
181#[derive(Clone, Default, Deserialize, PartialEq)]
182pub struct ToggleComments {
183 #[serde(default)]
184 pub advance_downwards: bool,
185}
186
187#[derive(Clone, Default, Deserialize, PartialEq)]
188pub struct FoldAt {
189 pub buffer_row: u32,
190}
191
192#[derive(Clone, Default, Deserialize, PartialEq)]
193pub struct UnfoldAt {
194 pub buffer_row: u32,
195}
196
197#[derive(Clone, Default, Deserialize, PartialEq)]
198pub struct GutterHover {
199 pub hovered: bool,
200}
201
202#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
203pub enum InlayId {
204 Suggestion(usize),
205 Hint(usize),
206}
207
208impl InlayId {
209 fn id(&self) -> usize {
210 match self {
211 Self::Suggestion(id) => *id,
212 Self::Hint(id) => *id,
213 }
214 }
215}
216
217actions!(
218 editor,
219 [
220 Cancel,
221 Backspace,
222 Delete,
223 Newline,
224 NewlineAbove,
225 NewlineBelow,
226 GoToDiagnostic,
227 GoToPrevDiagnostic,
228 GoToHunk,
229 GoToPrevHunk,
230 Indent,
231 Outdent,
232 DeleteLine,
233 DeleteToPreviousWordStart,
234 DeleteToPreviousSubwordStart,
235 DeleteToNextWordEnd,
236 DeleteToNextSubwordEnd,
237 DeleteToBeginningOfLine,
238 DeleteToEndOfLine,
239 CutToEndOfLine,
240 DuplicateLine,
241 MoveLineUp,
242 MoveLineDown,
243 JoinLines,
244 SortLinesCaseSensitive,
245 SortLinesCaseInsensitive,
246 ReverseLines,
247 ShuffleLines,
248 ConvertToUpperCase,
249 ConvertToLowerCase,
250 ConvertToTitleCase,
251 ConvertToSnakeCase,
252 ConvertToKebabCase,
253 ConvertToUpperCamelCase,
254 ConvertToLowerCamelCase,
255 Transpose,
256 Cut,
257 Copy,
258 Paste,
259 Undo,
260 Redo,
261 MoveUp,
262 PageUp,
263 MoveDown,
264 PageDown,
265 MoveLeft,
266 MoveRight,
267 MoveToPreviousWordStart,
268 MoveToPreviousSubwordStart,
269 MoveToNextWordEnd,
270 MoveToNextSubwordEnd,
271 MoveToBeginningOfLine,
272 MoveToEndOfLine,
273 MoveToStartOfParagraph,
274 MoveToEndOfParagraph,
275 MoveToBeginning,
276 MoveToEnd,
277 SelectUp,
278 SelectDown,
279 SelectLeft,
280 SelectRight,
281 SelectToPreviousWordStart,
282 SelectToPreviousSubwordStart,
283 SelectToNextWordEnd,
284 SelectToNextSubwordEnd,
285 SelectToStartOfParagraph,
286 SelectToEndOfParagraph,
287 SelectToBeginning,
288 SelectToEnd,
289 SelectAll,
290 SelectLine,
291 SplitSelectionIntoLines,
292 AddSelectionAbove,
293 AddSelectionBelow,
294 Tab,
295 TabPrev,
296 ShowCharacterPalette,
297 SelectLargerSyntaxNode,
298 SelectSmallerSyntaxNode,
299 GoToDefinition,
300 GoToDefinitionSplit,
301 GoToTypeDefinition,
302 GoToTypeDefinitionSplit,
303 MoveToEnclosingBracket,
304 UndoSelection,
305 RedoSelection,
306 FindAllReferences,
307 Rename,
308 ConfirmRename,
309 Fold,
310 UnfoldLines,
311 FoldSelectedRanges,
312 ShowCompletions,
313 OpenExcerpts,
314 RestartLanguageServer,
315 Hover,
316 Format,
317 ToggleSoftWrap,
318 ToggleInlayHints,
319 RevealInFinder,
320 CopyPath,
321 CopyRelativePath,
322 CopyHighlightJson,
323 ContextMenuFirst,
324 ContextMenuPrev,
325 ContextMenuNext,
326 ContextMenuLast,
327 ]
328);
329
330impl_actions!(
331 editor,
332 [
333 SelectNext,
334 SelectPrevious,
335 SelectAllMatches,
336 SelectToBeginningOfLine,
337 SelectToEndOfLine,
338 ToggleCodeActions,
339 MovePageUp,
340 MovePageDown,
341 ConfirmCompletion,
342 ConfirmCodeAction,
343 ToggleComments,
344 FoldAt,
345 UnfoldAt,
346 GutterHover
347 ]
348);
349
350enum DocumentHighlightRead {}
351enum DocumentHighlightWrite {}
352enum InputComposition {}
353
354#[derive(Copy, Clone, PartialEq, Eq)]
355pub enum Direction {
356 Prev,
357 Next,
358}
359
360pub fn init_settings(cx: &mut AppContext) {
361 settings::register::<EditorSettings>(cx);
362}
363
364pub fn init(cx: &mut AppContext) {
365 init_settings(cx);
366 cx.add_action(Editor::new_file);
367 cx.add_action(Editor::new_file_in_direction);
368 cx.add_action(Editor::cancel);
369 cx.add_action(Editor::newline);
370 cx.add_action(Editor::newline_above);
371 cx.add_action(Editor::newline_below);
372 cx.add_action(Editor::backspace);
373 cx.add_action(Editor::delete);
374 cx.add_action(Editor::tab);
375 cx.add_action(Editor::tab_prev);
376 cx.add_action(Editor::indent);
377 cx.add_action(Editor::outdent);
378 cx.add_action(Editor::delete_line);
379 cx.add_action(Editor::join_lines);
380 cx.add_action(Editor::sort_lines_case_sensitive);
381 cx.add_action(Editor::sort_lines_case_insensitive);
382 cx.add_action(Editor::reverse_lines);
383 cx.add_action(Editor::shuffle_lines);
384 cx.add_action(Editor::convert_to_upper_case);
385 cx.add_action(Editor::convert_to_lower_case);
386 cx.add_action(Editor::convert_to_title_case);
387 cx.add_action(Editor::convert_to_snake_case);
388 cx.add_action(Editor::convert_to_kebab_case);
389 cx.add_action(Editor::convert_to_upper_camel_case);
390 cx.add_action(Editor::convert_to_lower_camel_case);
391 cx.add_action(Editor::delete_to_previous_word_start);
392 cx.add_action(Editor::delete_to_previous_subword_start);
393 cx.add_action(Editor::delete_to_next_word_end);
394 cx.add_action(Editor::delete_to_next_subword_end);
395 cx.add_action(Editor::delete_to_beginning_of_line);
396 cx.add_action(Editor::delete_to_end_of_line);
397 cx.add_action(Editor::cut_to_end_of_line);
398 cx.add_action(Editor::duplicate_line);
399 cx.add_action(Editor::move_line_up);
400 cx.add_action(Editor::move_line_down);
401 cx.add_action(Editor::transpose);
402 cx.add_action(Editor::cut);
403 cx.add_action(Editor::copy);
404 cx.add_action(Editor::paste);
405 cx.add_action(Editor::undo);
406 cx.add_action(Editor::redo);
407 cx.add_action(Editor::move_up);
408 cx.add_action(Editor::move_page_up);
409 cx.add_action(Editor::move_down);
410 cx.add_action(Editor::move_page_down);
411 cx.add_action(Editor::next_screen);
412 cx.add_action(Editor::move_left);
413 cx.add_action(Editor::move_right);
414 cx.add_action(Editor::move_to_previous_word_start);
415 cx.add_action(Editor::move_to_previous_subword_start);
416 cx.add_action(Editor::move_to_next_word_end);
417 cx.add_action(Editor::move_to_next_subword_end);
418 cx.add_action(Editor::move_to_beginning_of_line);
419 cx.add_action(Editor::move_to_end_of_line);
420 cx.add_action(Editor::move_to_start_of_paragraph);
421 cx.add_action(Editor::move_to_end_of_paragraph);
422 cx.add_action(Editor::move_to_beginning);
423 cx.add_action(Editor::move_to_end);
424 cx.add_action(Editor::select_up);
425 cx.add_action(Editor::select_down);
426 cx.add_action(Editor::select_left);
427 cx.add_action(Editor::select_right);
428 cx.add_action(Editor::select_to_previous_word_start);
429 cx.add_action(Editor::select_to_previous_subword_start);
430 cx.add_action(Editor::select_to_next_word_end);
431 cx.add_action(Editor::select_to_next_subword_end);
432 cx.add_action(Editor::select_to_beginning_of_line);
433 cx.add_action(Editor::select_to_end_of_line);
434 cx.add_action(Editor::select_to_start_of_paragraph);
435 cx.add_action(Editor::select_to_end_of_paragraph);
436 cx.add_action(Editor::select_to_beginning);
437 cx.add_action(Editor::select_to_end);
438 cx.add_action(Editor::select_all);
439 cx.add_action(Editor::select_all_matches);
440 cx.add_action(Editor::select_line);
441 cx.add_action(Editor::split_selection_into_lines);
442 cx.add_action(Editor::add_selection_above);
443 cx.add_action(Editor::add_selection_below);
444 cx.add_action(Editor::select_next);
445 cx.add_action(Editor::select_previous);
446 cx.add_action(Editor::toggle_comments);
447 cx.add_action(Editor::select_larger_syntax_node);
448 cx.add_action(Editor::select_smaller_syntax_node);
449 cx.add_action(Editor::move_to_enclosing_bracket);
450 cx.add_action(Editor::undo_selection);
451 cx.add_action(Editor::redo_selection);
452 cx.add_action(Editor::go_to_diagnostic);
453 cx.add_action(Editor::go_to_prev_diagnostic);
454 cx.add_action(Editor::go_to_hunk);
455 cx.add_action(Editor::go_to_prev_hunk);
456 cx.add_action(Editor::go_to_definition);
457 cx.add_action(Editor::go_to_definition_split);
458 cx.add_action(Editor::go_to_type_definition);
459 cx.add_action(Editor::go_to_type_definition_split);
460 cx.add_action(Editor::fold);
461 cx.add_action(Editor::fold_at);
462 cx.add_action(Editor::unfold_lines);
463 cx.add_action(Editor::unfold_at);
464 cx.add_action(Editor::gutter_hover);
465 cx.add_action(Editor::fold_selected_ranges);
466 cx.add_action(Editor::show_completions);
467 cx.add_action(Editor::toggle_code_actions);
468 cx.add_action(Editor::open_excerpts);
469 cx.add_action(Editor::toggle_soft_wrap);
470 cx.add_action(Editor::toggle_inlay_hints);
471 cx.add_action(Editor::reveal_in_finder);
472 cx.add_action(Editor::copy_path);
473 cx.add_action(Editor::copy_relative_path);
474 cx.add_action(Editor::copy_highlight_json);
475 cx.add_async_action(Editor::format);
476 cx.add_action(Editor::restart_language_server);
477 cx.add_action(Editor::show_character_palette);
478 cx.add_async_action(Editor::confirm_completion);
479 cx.add_async_action(Editor::confirm_code_action);
480 cx.add_async_action(Editor::rename);
481 cx.add_async_action(Editor::confirm_rename);
482 cx.add_async_action(Editor::find_all_references);
483 cx.add_action(Editor::next_copilot_suggestion);
484 cx.add_action(Editor::previous_copilot_suggestion);
485 cx.add_action(Editor::copilot_suggest);
486 cx.add_action(Editor::context_menu_first);
487 cx.add_action(Editor::context_menu_prev);
488 cx.add_action(Editor::context_menu_next);
489 cx.add_action(Editor::context_menu_last);
490
491 hover_popover::init(cx);
492 scroll::actions::init(cx);
493
494 workspace::register_project_item::<Editor>(cx);
495 workspace::register_followable_item::<Editor>(cx);
496 workspace::register_deserializable_item::<Editor>(cx);
497}
498
499trait InvalidationRegion {
500 fn ranges(&self) -> &[Range<Anchor>];
501}
502
503#[derive(Clone, Debug, PartialEq)]
504pub enum SelectPhase {
505 Begin {
506 position: DisplayPoint,
507 add: bool,
508 click_count: usize,
509 },
510 BeginColumnar {
511 position: DisplayPoint,
512 goal_column: u32,
513 },
514 Extend {
515 position: DisplayPoint,
516 click_count: usize,
517 },
518 Update {
519 position: DisplayPoint,
520 goal_column: u32,
521 scroll_position: Vector2F,
522 },
523 End,
524}
525
526#[derive(Clone, Debug)]
527pub enum SelectMode {
528 Character,
529 Word(Range<Anchor>),
530 Line(Range<Anchor>),
531 All,
532}
533
534#[derive(Copy, Clone, PartialEq, Eq, Debug)]
535pub enum EditorMode {
536 SingleLine,
537 AutoHeight { max_lines: usize },
538 Full,
539}
540
541#[derive(Clone, Debug)]
542pub enum SoftWrap {
543 None,
544 EditorWidth,
545 Column(u32),
546}
547
548#[derive(Clone)]
549pub struct EditorStyle {
550 pub text: TextStyle,
551 pub line_height_scalar: f32,
552 pub placeholder_text: Option<TextStyle>,
553 pub theme: theme::Editor,
554 pub theme_id: usize,
555}
556
557type CompletionId = usize;
558
559type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
560type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
561
562type BackgroundHighlight = (fn(&Theme) -> Color, Vec<Range<Anchor>>);
563type InlayBackgroundHighlight = (fn(&Theme) -> Color, Vec<InlayHighlight>);
564
565pub struct Editor {
566 handle: WeakViewHandle<Self>,
567 buffer: ModelHandle<MultiBuffer>,
568 display_map: ModelHandle<DisplayMap>,
569 pub selections: SelectionsCollection,
570 pub scroll_manager: ScrollManager,
571 columnar_selection_tail: Option<Anchor>,
572 add_selections_state: Option<AddSelectionsState>,
573 select_next_state: Option<SelectNextState>,
574 select_prev_state: Option<SelectNextState>,
575 selection_history: SelectionHistory,
576 autoclose_regions: Vec<AutocloseRegion>,
577 snippet_stack: InvalidationStack<SnippetState>,
578 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
579 ime_transaction: Option<TransactionId>,
580 active_diagnostics: Option<ActiveDiagnosticGroup>,
581 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
582 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
583 override_text_style: Option<Box<OverrideTextStyle>>,
584 project: Option<ModelHandle<Project>>,
585 collaboration_hub: Option<Box<dyn CollaborationHub>>,
586 focused: bool,
587 blink_manager: ModelHandle<BlinkManager>,
588 pub show_local_selections: bool,
589 mode: EditorMode,
590 show_gutter: bool,
591 show_wrap_guides: Option<bool>,
592 placeholder_text: Option<Arc<str>>,
593 highlighted_rows: Option<Range<u32>>,
594 background_highlights: BTreeMap<TypeId, BackgroundHighlight>,
595 inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
596 nav_history: Option<ItemNavHistory>,
597 context_menu: Option<ContextMenu>,
598 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
599 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
600 next_completion_id: CompletionId,
601 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
602 code_actions_task: Option<Task<()>>,
603 document_highlights_task: Option<Task<()>>,
604 pending_rename: Option<RenameState>,
605 searchable: bool,
606 cursor_shape: CursorShape,
607 collapse_matches: bool,
608 autoindent_mode: Option<AutoindentMode>,
609 workspace: Option<(WeakViewHandle<Workspace>, i64)>,
610 keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
611 input_enabled: bool,
612 read_only: bool,
613 leader_peer_id: Option<PeerId>,
614 remote_id: Option<ViewId>,
615 hover_state: HoverState,
616 gutter_hovered: bool,
617 link_go_to_definition_state: LinkGoToDefinitionState,
618 copilot_state: CopilotState,
619 inlay_hint_cache: InlayHintCache,
620 next_inlay_id: usize,
621 _subscriptions: Vec<Subscription>,
622 pixel_position_of_newest_cursor: Option<Vector2F>,
623}
624
625pub struct EditorSnapshot {
626 pub mode: EditorMode,
627 pub show_gutter: bool,
628 pub display_snapshot: DisplaySnapshot,
629 pub placeholder_text: Option<Arc<str>>,
630 is_focused: bool,
631 scroll_anchor: ScrollAnchor,
632 ongoing_scroll: OngoingScroll,
633}
634
635pub struct RemoteSelection {
636 pub replica_id: ReplicaId,
637 pub selection: Selection<Anchor>,
638 pub cursor_shape: CursorShape,
639 pub peer_id: PeerId,
640 pub line_mode: bool,
641 pub participant_index: Option<ParticipantIndex>,
642}
643
644#[derive(Clone, Debug)]
645struct SelectionHistoryEntry {
646 selections: Arc<[Selection<Anchor>]>,
647 select_next_state: Option<SelectNextState>,
648 select_prev_state: Option<SelectNextState>,
649 add_selections_state: Option<AddSelectionsState>,
650}
651
652enum SelectionHistoryMode {
653 Normal,
654 Undoing,
655 Redoing,
656}
657
658impl Default for SelectionHistoryMode {
659 fn default() -> Self {
660 Self::Normal
661 }
662}
663
664#[derive(Default)]
665struct SelectionHistory {
666 #[allow(clippy::type_complexity)]
667 selections_by_transaction:
668 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
669 mode: SelectionHistoryMode,
670 undo_stack: VecDeque<SelectionHistoryEntry>,
671 redo_stack: VecDeque<SelectionHistoryEntry>,
672}
673
674impl SelectionHistory {
675 fn insert_transaction(
676 &mut self,
677 transaction_id: TransactionId,
678 selections: Arc<[Selection<Anchor>]>,
679 ) {
680 self.selections_by_transaction
681 .insert(transaction_id, (selections, None));
682 }
683
684 #[allow(clippy::type_complexity)]
685 fn transaction(
686 &self,
687 transaction_id: TransactionId,
688 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
689 self.selections_by_transaction.get(&transaction_id)
690 }
691
692 #[allow(clippy::type_complexity)]
693 fn transaction_mut(
694 &mut self,
695 transaction_id: TransactionId,
696 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
697 self.selections_by_transaction.get_mut(&transaction_id)
698 }
699
700 fn push(&mut self, entry: SelectionHistoryEntry) {
701 if !entry.selections.is_empty() {
702 match self.mode {
703 SelectionHistoryMode::Normal => {
704 self.push_undo(entry);
705 self.redo_stack.clear();
706 }
707 SelectionHistoryMode::Undoing => self.push_redo(entry),
708 SelectionHistoryMode::Redoing => self.push_undo(entry),
709 }
710 }
711 }
712
713 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
714 if self
715 .undo_stack
716 .back()
717 .map_or(true, |e| e.selections != entry.selections)
718 {
719 self.undo_stack.push_back(entry);
720 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
721 self.undo_stack.pop_front();
722 }
723 }
724 }
725
726 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
727 if self
728 .redo_stack
729 .back()
730 .map_or(true, |e| e.selections != entry.selections)
731 {
732 self.redo_stack.push_back(entry);
733 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
734 self.redo_stack.pop_front();
735 }
736 }
737 }
738}
739
740#[derive(Clone, Debug)]
741struct AddSelectionsState {
742 above: bool,
743 stack: Vec<usize>,
744}
745
746#[derive(Clone)]
747struct SelectNextState {
748 query: AhoCorasick,
749 wordwise: bool,
750 done: bool,
751}
752
753impl std::fmt::Debug for SelectNextState {
754 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
755 f.debug_struct(std::any::type_name::<Self>())
756 .field("wordwise", &self.wordwise)
757 .field("done", &self.done)
758 .finish()
759 }
760}
761
762#[derive(Debug)]
763struct AutocloseRegion {
764 selection_id: usize,
765 range: Range<Anchor>,
766 pair: BracketPair,
767}
768
769#[derive(Debug)]
770struct SnippetState {
771 ranges: Vec<Vec<Range<Anchor>>>,
772 active_index: usize,
773}
774
775pub struct RenameState {
776 pub range: Range<Anchor>,
777 pub old_name: Arc<str>,
778 pub editor: ViewHandle<Editor>,
779 block_id: BlockId,
780}
781
782struct InvalidationStack<T>(Vec<T>);
783
784enum ContextMenu {
785 Completions(CompletionsMenu),
786 CodeActions(CodeActionsMenu),
787}
788
789impl ContextMenu {
790 fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
791 if self.visible() {
792 match self {
793 ContextMenu::Completions(menu) => menu.select_first(cx),
794 ContextMenu::CodeActions(menu) => menu.select_first(cx),
795 }
796 true
797 } else {
798 false
799 }
800 }
801
802 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
803 if self.visible() {
804 match self {
805 ContextMenu::Completions(menu) => menu.select_prev(cx),
806 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
807 }
808 true
809 } else {
810 false
811 }
812 }
813
814 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
815 if self.visible() {
816 match self {
817 ContextMenu::Completions(menu) => menu.select_next(cx),
818 ContextMenu::CodeActions(menu) => menu.select_next(cx),
819 }
820 true
821 } else {
822 false
823 }
824 }
825
826 fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
827 if self.visible() {
828 match self {
829 ContextMenu::Completions(menu) => menu.select_last(cx),
830 ContextMenu::CodeActions(menu) => menu.select_last(cx),
831 }
832 true
833 } else {
834 false
835 }
836 }
837
838 fn visible(&self) -> bool {
839 match self {
840 ContextMenu::Completions(menu) => menu.visible(),
841 ContextMenu::CodeActions(menu) => menu.visible(),
842 }
843 }
844
845 fn render(
846 &self,
847 cursor_position: DisplayPoint,
848 style: EditorStyle,
849 cx: &mut ViewContext<Editor>,
850 ) -> (DisplayPoint, AnyElement<Editor>) {
851 match self {
852 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
853 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
854 }
855 }
856}
857
858struct CompletionsMenu {
859 id: CompletionId,
860 initial_position: Anchor,
861 buffer: ModelHandle<Buffer>,
862 completions: Arc<[Completion]>,
863 match_candidates: Vec<StringMatchCandidate>,
864 matches: Arc<[StringMatch]>,
865 selected_item: usize,
866 list: UniformListState,
867}
868
869impl CompletionsMenu {
870 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
871 self.selected_item = 0;
872 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
873 cx.notify();
874 }
875
876 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
877 if self.selected_item > 0 {
878 self.selected_item -= 1;
879 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
880 }
881 cx.notify();
882 }
883
884 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
885 if self.selected_item + 1 < self.matches.len() {
886 self.selected_item += 1;
887 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
888 }
889 cx.notify();
890 }
891
892 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
893 self.selected_item = self.matches.len() - 1;
894 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
895 cx.notify();
896 }
897
898 fn visible(&self) -> bool {
899 !self.matches.is_empty()
900 }
901
902 fn render(&self, style: EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
903 enum CompletionTag {}
904
905 let widest_completion_ix = self
906 .matches
907 .iter()
908 .enumerate()
909 .max_by_key(|(_, mat)| {
910 let completion = &self.completions[mat.candidate_id];
911 let documentation = &completion.lsp_completion.documentation;
912
913 let mut len = completion.label.text.chars().count();
914 if let Some(lsp::Documentation::String(text)) = documentation {
915 len += text.chars().count();
916 }
917
918 len
919 })
920 .map(|(ix, _)| ix);
921
922 let completions = self.completions.clone();
923 let matches = self.matches.clone();
924 let selected_item = self.selected_item;
925
926 let alongside_docs_text_style = TextStyle {
927 soft_wrap: true,
928 ..style.text.clone()
929 };
930 let alongside_docs_width = style.autocomplete.alongside_docs_width;
931 let alongside_docs_container_style = style.autocomplete.alongside_docs_container;
932 let outer_container_style = style.autocomplete.container;
933
934 let list = UniformList::new(
935 self.list.clone(),
936 matches.len(),
937 cx,
938 move |_, range, items, cx| {
939 let start_ix = range.start;
940 for (ix, mat) in matches[range].iter().enumerate() {
941 let completion = &completions[mat.candidate_id];
942 let documentation = &completion.lsp_completion.documentation;
943 let item_ix = start_ix + ix;
944
945 items.push(
946 MouseEventHandler::new::<CompletionTag, _>(
947 mat.candidate_id,
948 cx,
949 |state, _| {
950 let item_style = if item_ix == selected_item {
951 style.autocomplete.selected_item
952 } else if state.hovered() {
953 style.autocomplete.hovered_item
954 } else {
955 style.autocomplete.item
956 };
957
958 let completion_label =
959 Text::new(completion.label.text.clone(), style.text.clone())
960 .with_soft_wrap(false)
961 .with_highlights(
962 combine_syntax_and_fuzzy_match_highlights(
963 &completion.label.text,
964 style.text.color.into(),
965 styled_runs_for_code_label(
966 &completion.label,
967 &style.syntax,
968 ),
969 &mat.positions,
970 ),
971 );
972
973 if let Some(lsp::Documentation::String(text)) = documentation {
974 Flex::row()
975 .with_child(completion_label)
976 .with_children((|| {
977 let text_style = TextStyle {
978 color: style.autocomplete.inline_docs_color,
979 font_size: style.text.font_size
980 * style.autocomplete.inline_docs_size_percent,
981 ..style.text.clone()
982 };
983
984 let label = Text::new(text.clone(), text_style)
985 .aligned()
986 .constrained()
987 .dynamically(move |constraint, _, _| {
988 gpui::SizeConstraint {
989 min: constraint.min,
990 max: vec2f(
991 constraint.max.x(),
992 constraint.min.y(),
993 ),
994 }
995 });
996
997 if Some(item_ix) == widest_completion_ix {
998 Some(
999 label
1000 .contained()
1001 .with_style(
1002 style
1003 .autocomplete
1004 .inline_docs_container,
1005 )
1006 .into_any(),
1007 )
1008 } else {
1009 Some(label.flex_float().into_any())
1010 }
1011 })())
1012 .into_any()
1013 } else {
1014 completion_label.into_any()
1015 }
1016 .contained()
1017 .with_style(item_style)
1018 .constrained()
1019 .dynamically(
1020 move |constraint, _, _| {
1021 if Some(item_ix) == widest_completion_ix {
1022 constraint
1023 } else {
1024 gpui::SizeConstraint {
1025 min: constraint.min,
1026 max: constraint.min,
1027 }
1028 }
1029 },
1030 )
1031 },
1032 )
1033 .with_cursor_style(CursorStyle::PointingHand)
1034 .on_down(MouseButton::Left, move |_, this, cx| {
1035 this.confirm_completion(
1036 &ConfirmCompletion {
1037 item_ix: Some(item_ix),
1038 },
1039 cx,
1040 )
1041 .map(|task| task.detach());
1042 })
1043 .into_any(),
1044 );
1045 }
1046 },
1047 )
1048 .with_width_from_item(widest_completion_ix);
1049
1050 Flex::row()
1051 .with_child(list)
1052 .with_children({
1053 let mat = &self.matches[selected_item];
1054 let completion = &self.completions[mat.candidate_id];
1055 let documentation = &completion.lsp_completion.documentation;
1056
1057 if let Some(lsp::Documentation::MarkupContent(content)) = documentation {
1058 Some(
1059 Text::new(content.value.clone(), alongside_docs_text_style)
1060 .constrained()
1061 .with_width(alongside_docs_width)
1062 .contained()
1063 .with_style(alongside_docs_container_style),
1064 )
1065 } else {
1066 None
1067 }
1068 })
1069 .contained()
1070 .with_style(outer_container_style)
1071 .into_any()
1072 }
1073
1074 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
1075 let mut matches = if let Some(query) = query {
1076 fuzzy::match_strings(
1077 &self.match_candidates,
1078 query,
1079 query.chars().any(|c| c.is_uppercase()),
1080 100,
1081 &Default::default(),
1082 executor,
1083 )
1084 .await
1085 } else {
1086 self.match_candidates
1087 .iter()
1088 .enumerate()
1089 .map(|(candidate_id, candidate)| StringMatch {
1090 candidate_id,
1091 score: Default::default(),
1092 positions: Default::default(),
1093 string: candidate.string.clone(),
1094 })
1095 .collect()
1096 };
1097
1098 //Remove all candidates where the query's start does not match the start of any word in the candidate
1099 if let Some(query) = query {
1100 if let Some(query_start) = query.chars().next() {
1101 matches.retain(|string_match| {
1102 split_words(&string_match.string).any(|word| {
1103 //Check that the first codepoint of the word as lowercase matches the first
1104 //codepoint of the query as lowercase
1105 word.chars()
1106 .flat_map(|codepoint| codepoint.to_lowercase())
1107 .zip(query_start.to_lowercase())
1108 .all(|(word_cp, query_cp)| word_cp == query_cp)
1109 })
1110 });
1111 }
1112 }
1113
1114 matches.sort_unstable_by_key(|mat| {
1115 let completion = &self.completions[mat.candidate_id];
1116 (
1117 completion.lsp_completion.sort_text.as_ref(),
1118 Reverse(OrderedFloat(mat.score)),
1119 completion.sort_key(),
1120 )
1121 });
1122
1123 for mat in &mut matches {
1124 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
1125 for position in &mut mat.positions {
1126 *position += filter_start;
1127 }
1128 }
1129
1130 self.matches = matches.into();
1131 }
1132}
1133
1134#[derive(Clone)]
1135struct CodeActionsMenu {
1136 actions: Arc<[CodeAction]>,
1137 buffer: ModelHandle<Buffer>,
1138 selected_item: usize,
1139 list: UniformListState,
1140 deployed_from_indicator: bool,
1141}
1142
1143impl CodeActionsMenu {
1144 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1145 self.selected_item = 0;
1146 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
1147 cx.notify()
1148 }
1149
1150 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1151 if self.selected_item > 0 {
1152 self.selected_item -= 1;
1153 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
1154 cx.notify()
1155 }
1156 }
1157
1158 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1159 if self.selected_item + 1 < self.actions.len() {
1160 self.selected_item += 1;
1161 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
1162 cx.notify()
1163 }
1164 }
1165
1166 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1167 self.selected_item = self.actions.len() - 1;
1168 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
1169 cx.notify()
1170 }
1171
1172 fn visible(&self) -> bool {
1173 !self.actions.is_empty()
1174 }
1175
1176 fn render(
1177 &self,
1178 mut cursor_position: DisplayPoint,
1179 style: EditorStyle,
1180 cx: &mut ViewContext<Editor>,
1181 ) -> (DisplayPoint, AnyElement<Editor>) {
1182 enum ActionTag {}
1183
1184 let container_style = style.autocomplete.container;
1185 let actions = self.actions.clone();
1186 let selected_item = self.selected_item;
1187 let element = UniformList::new(
1188 self.list.clone(),
1189 actions.len(),
1190 cx,
1191 move |_, range, items, cx| {
1192 let start_ix = range.start;
1193 for (ix, action) in actions[range].iter().enumerate() {
1194 let item_ix = start_ix + ix;
1195 items.push(
1196 MouseEventHandler::new::<ActionTag, _>(item_ix, cx, |state, _| {
1197 let item_style = if item_ix == selected_item {
1198 style.autocomplete.selected_item
1199 } else if state.hovered() {
1200 style.autocomplete.hovered_item
1201 } else {
1202 style.autocomplete.item
1203 };
1204
1205 Text::new(action.lsp_action.title.clone(), style.text.clone())
1206 .with_soft_wrap(false)
1207 .contained()
1208 .with_style(item_style)
1209 })
1210 .with_cursor_style(CursorStyle::PointingHand)
1211 .on_down(MouseButton::Left, move |_, this, cx| {
1212 let workspace = this
1213 .workspace
1214 .as_ref()
1215 .and_then(|(workspace, _)| workspace.upgrade(cx));
1216 cx.window_context().defer(move |cx| {
1217 if let Some(workspace) = workspace {
1218 workspace.update(cx, |workspace, cx| {
1219 if let Some(task) = Editor::confirm_code_action(
1220 workspace,
1221 &ConfirmCodeAction {
1222 item_ix: Some(item_ix),
1223 },
1224 cx,
1225 ) {
1226 task.detach_and_log_err(cx);
1227 }
1228 });
1229 }
1230 });
1231 })
1232 .into_any(),
1233 );
1234 }
1235 },
1236 )
1237 .with_width_from_item(
1238 self.actions
1239 .iter()
1240 .enumerate()
1241 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1242 .map(|(ix, _)| ix),
1243 )
1244 .contained()
1245 .with_style(container_style)
1246 .into_any();
1247
1248 if self.deployed_from_indicator {
1249 *cursor_position.column_mut() = 0;
1250 }
1251
1252 (cursor_position, element)
1253 }
1254}
1255
1256pub struct CopilotState {
1257 excerpt_id: Option<ExcerptId>,
1258 pending_refresh: Task<Option<()>>,
1259 pending_cycling_refresh: Task<Option<()>>,
1260 cycled: bool,
1261 completions: Vec<copilot::Completion>,
1262 active_completion_index: usize,
1263 suggestion: Option<Inlay>,
1264}
1265
1266impl Default for CopilotState {
1267 fn default() -> Self {
1268 Self {
1269 excerpt_id: None,
1270 pending_cycling_refresh: Task::ready(Some(())),
1271 pending_refresh: Task::ready(Some(())),
1272 completions: Default::default(),
1273 active_completion_index: 0,
1274 cycled: false,
1275 suggestion: None,
1276 }
1277 }
1278}
1279
1280impl CopilotState {
1281 fn active_completion(&self) -> Option<&copilot::Completion> {
1282 self.completions.get(self.active_completion_index)
1283 }
1284
1285 fn text_for_active_completion(
1286 &self,
1287 cursor: Anchor,
1288 buffer: &MultiBufferSnapshot,
1289 ) -> Option<&str> {
1290 use language::ToOffset as _;
1291
1292 let completion = self.active_completion()?;
1293 let excerpt_id = self.excerpt_id?;
1294 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1295 if excerpt_id != cursor.excerpt_id
1296 || !completion.range.start.is_valid(completion_buffer)
1297 || !completion.range.end.is_valid(completion_buffer)
1298 {
1299 return None;
1300 }
1301
1302 let mut completion_range = completion.range.to_offset(&completion_buffer);
1303 let prefix_len = Self::common_prefix(
1304 completion_buffer.chars_for_range(completion_range.clone()),
1305 completion.text.chars(),
1306 );
1307 completion_range.start += prefix_len;
1308 let suffix_len = Self::common_prefix(
1309 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1310 completion.text[prefix_len..].chars().rev(),
1311 );
1312 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1313
1314 if completion_range.is_empty()
1315 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1316 {
1317 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1318 } else {
1319 None
1320 }
1321 }
1322
1323 fn cycle_completions(&mut self, direction: Direction) {
1324 match direction {
1325 Direction::Prev => {
1326 self.active_completion_index = if self.active_completion_index == 0 {
1327 self.completions.len().saturating_sub(1)
1328 } else {
1329 self.active_completion_index - 1
1330 };
1331 }
1332 Direction::Next => {
1333 if self.completions.len() == 0 {
1334 self.active_completion_index = 0
1335 } else {
1336 self.active_completion_index =
1337 (self.active_completion_index + 1) % self.completions.len();
1338 }
1339 }
1340 }
1341 }
1342
1343 fn push_completion(&mut self, new_completion: copilot::Completion) {
1344 for completion in &self.completions {
1345 if completion.text == new_completion.text && completion.range == new_completion.range {
1346 return;
1347 }
1348 }
1349 self.completions.push(new_completion);
1350 }
1351
1352 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1353 a.zip(b)
1354 .take_while(|(a, b)| a == b)
1355 .map(|(a, _)| a.len_utf8())
1356 .sum()
1357 }
1358}
1359
1360#[derive(Debug)]
1361struct ActiveDiagnosticGroup {
1362 primary_range: Range<Anchor>,
1363 primary_message: String,
1364 blocks: HashMap<BlockId, Diagnostic>,
1365 is_valid: bool,
1366}
1367
1368#[derive(Serialize, Deserialize)]
1369pub struct ClipboardSelection {
1370 pub len: usize,
1371 pub is_entire_line: bool,
1372 pub first_line_indent: u32,
1373}
1374
1375#[derive(Debug)]
1376pub struct NavigationData {
1377 cursor_anchor: Anchor,
1378 cursor_position: Point,
1379 scroll_anchor: ScrollAnchor,
1380 scroll_top_row: u32,
1381}
1382
1383pub struct EditorCreated(pub ViewHandle<Editor>);
1384
1385enum GotoDefinitionKind {
1386 Symbol,
1387 Type,
1388}
1389
1390#[derive(Debug, Clone)]
1391enum InlayHintRefreshReason {
1392 Toggle(bool),
1393 SettingsChange(InlayHintSettings),
1394 NewLinesShown,
1395 BufferEdited(HashSet<Arc<Language>>),
1396 RefreshRequested,
1397 ExcerptsRemoved(Vec<ExcerptId>),
1398}
1399impl InlayHintRefreshReason {
1400 fn description(&self) -> &'static str {
1401 match self {
1402 Self::Toggle(_) => "toggle",
1403 Self::SettingsChange(_) => "settings change",
1404 Self::NewLinesShown => "new lines shown",
1405 Self::BufferEdited(_) => "buffer edited",
1406 Self::RefreshRequested => "refresh requested",
1407 Self::ExcerptsRemoved(_) => "excerpts removed",
1408 }
1409 }
1410}
1411
1412impl Editor {
1413 pub fn single_line(
1414 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1415 cx: &mut ViewContext<Self>,
1416 ) -> Self {
1417 let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new()));
1418 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1419 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
1420 }
1421
1422 pub fn multi_line(
1423 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1424 cx: &mut ViewContext<Self>,
1425 ) -> Self {
1426 let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new()));
1427 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1428 Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
1429 }
1430
1431 pub fn auto_height(
1432 max_lines: usize,
1433 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1434 cx: &mut ViewContext<Self>,
1435 ) -> Self {
1436 let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new()));
1437 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1438 Self::new(
1439 EditorMode::AutoHeight { max_lines },
1440 buffer,
1441 None,
1442 field_editor_style,
1443 cx,
1444 )
1445 }
1446
1447 pub fn for_buffer(
1448 buffer: ModelHandle<Buffer>,
1449 project: Option<ModelHandle<Project>>,
1450 cx: &mut ViewContext<Self>,
1451 ) -> Self {
1452 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1453 Self::new(EditorMode::Full, buffer, project, None, cx)
1454 }
1455
1456 pub fn for_multibuffer(
1457 buffer: ModelHandle<MultiBuffer>,
1458 project: Option<ModelHandle<Project>>,
1459 cx: &mut ViewContext<Self>,
1460 ) -> Self {
1461 Self::new(EditorMode::Full, buffer, project, None, cx)
1462 }
1463
1464 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1465 let mut clone = Self::new(
1466 self.mode,
1467 self.buffer.clone(),
1468 self.project.clone(),
1469 self.get_field_editor_theme.clone(),
1470 cx,
1471 );
1472 self.display_map.update(cx, |display_map, cx| {
1473 let snapshot = display_map.snapshot(cx);
1474 clone.display_map.update(cx, |display_map, cx| {
1475 display_map.set_state(&snapshot, cx);
1476 });
1477 });
1478 clone.selections.clone_state(&self.selections);
1479 clone.scroll_manager.clone_state(&self.scroll_manager);
1480 clone.searchable = self.searchable;
1481 clone
1482 }
1483
1484 fn new(
1485 mode: EditorMode,
1486 buffer: ModelHandle<MultiBuffer>,
1487 project: Option<ModelHandle<Project>>,
1488 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
1489 cx: &mut ViewContext<Self>,
1490 ) -> Self {
1491 let editor_view_id = cx.view_id();
1492 let display_map = cx.add_model(|cx| {
1493 let settings = settings::get::<ThemeSettings>(cx);
1494 let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx);
1495 DisplayMap::new(
1496 buffer.clone(),
1497 style.text.font_id,
1498 style.text.font_size,
1499 None,
1500 2,
1501 1,
1502 cx,
1503 )
1504 });
1505
1506 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1507
1508 let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1509
1510 let soft_wrap_mode_override =
1511 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1512
1513 let mut project_subscriptions = Vec::new();
1514 if mode == EditorMode::Full {
1515 if let Some(project) = project.as_ref() {
1516 if buffer.read(cx).is_singleton() {
1517 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1518 cx.emit(Event::TitleChanged);
1519 }));
1520 }
1521 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1522 if let project::Event::RefreshInlayHints = event {
1523 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1524 };
1525 }));
1526 }
1527 }
1528
1529 let inlay_hint_settings = inlay_hint_settings(
1530 selections.newest_anchor().head(),
1531 &buffer.read(cx).snapshot(cx),
1532 cx,
1533 );
1534
1535 let mut this = Self {
1536 handle: cx.weak_handle(),
1537 buffer: buffer.clone(),
1538 display_map: display_map.clone(),
1539 selections,
1540 scroll_manager: ScrollManager::new(),
1541 columnar_selection_tail: None,
1542 add_selections_state: None,
1543 select_next_state: None,
1544 select_prev_state: None,
1545 selection_history: Default::default(),
1546 autoclose_regions: Default::default(),
1547 snippet_stack: Default::default(),
1548 select_larger_syntax_node_stack: Vec::new(),
1549 ime_transaction: Default::default(),
1550 active_diagnostics: None,
1551 soft_wrap_mode_override,
1552 get_field_editor_theme,
1553 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1554 project,
1555 focused: false,
1556 blink_manager: blink_manager.clone(),
1557 show_local_selections: true,
1558 mode,
1559 show_gutter: mode == EditorMode::Full,
1560 show_wrap_guides: None,
1561 placeholder_text: None,
1562 highlighted_rows: None,
1563 background_highlights: Default::default(),
1564 inlay_background_highlights: Default::default(),
1565 nav_history: None,
1566 context_menu: None,
1567 mouse_context_menu: cx
1568 .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)),
1569 completion_tasks: Default::default(),
1570 next_completion_id: 0,
1571 next_inlay_id: 0,
1572 available_code_actions: Default::default(),
1573 code_actions_task: Default::default(),
1574 document_highlights_task: Default::default(),
1575 pending_rename: Default::default(),
1576 searchable: true,
1577 override_text_style: None,
1578 cursor_shape: Default::default(),
1579 autoindent_mode: Some(AutoindentMode::EachLine),
1580 collapse_matches: false,
1581 workspace: None,
1582 keymap_context_layers: Default::default(),
1583 input_enabled: true,
1584 read_only: false,
1585 leader_peer_id: None,
1586 remote_id: None,
1587 hover_state: Default::default(),
1588 link_go_to_definition_state: Default::default(),
1589 copilot_state: Default::default(),
1590 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1591 gutter_hovered: false,
1592 pixel_position_of_newest_cursor: None,
1593 _subscriptions: vec![
1594 cx.observe(&buffer, Self::on_buffer_changed),
1595 cx.subscribe(&buffer, Self::on_buffer_event),
1596 cx.observe(&display_map, Self::on_display_map_changed),
1597 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1598 cx.observe_global::<SettingsStore, _>(Self::settings_changed),
1599 cx.observe_window_activation(|editor, active, cx| {
1600 editor.blink_manager.update(cx, |blink_manager, cx| {
1601 if active {
1602 blink_manager.enable(cx);
1603 } else {
1604 blink_manager.show_cursor(cx);
1605 blink_manager.disable(cx);
1606 }
1607 });
1608 }),
1609 ],
1610 };
1611
1612 this._subscriptions.extend(project_subscriptions);
1613
1614 this.end_selection(cx);
1615 this.scroll_manager.show_scrollbar(cx);
1616
1617 let editor_created_event = EditorCreated(cx.handle());
1618 cx.emit_global(editor_created_event);
1619
1620 if mode == EditorMode::Full {
1621 let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
1622 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1623 }
1624
1625 this.report_editor_event("open", None, cx);
1626 this
1627 }
1628
1629 pub fn new_file(
1630 workspace: &mut Workspace,
1631 _: &workspace::NewFile,
1632 cx: &mut ViewContext<Workspace>,
1633 ) {
1634 let project = workspace.project().clone();
1635 if project.read(cx).is_remote() {
1636 cx.propagate_action();
1637 } else if let Some(buffer) = project
1638 .update(cx, |project, cx| project.create_buffer("", None, cx))
1639 .log_err()
1640 {
1641 workspace.add_item(
1642 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1643 cx,
1644 );
1645 }
1646 }
1647
1648 pub fn new_file_in_direction(
1649 workspace: &mut Workspace,
1650 action: &workspace::NewFileInDirection,
1651 cx: &mut ViewContext<Workspace>,
1652 ) {
1653 let project = workspace.project().clone();
1654 if project.read(cx).is_remote() {
1655 cx.propagate_action();
1656 } else if let Some(buffer) = project
1657 .update(cx, |project, cx| project.create_buffer("", None, cx))
1658 .log_err()
1659 {
1660 workspace.split_item(
1661 action.0,
1662 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1663 cx,
1664 );
1665 }
1666 }
1667
1668 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1669 self.buffer.read(cx).replica_id()
1670 }
1671
1672 pub fn leader_peer_id(&self) -> Option<PeerId> {
1673 self.leader_peer_id
1674 }
1675
1676 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1677 &self.buffer
1678 }
1679
1680 fn workspace(&self, cx: &AppContext) -> Option<ViewHandle<Workspace>> {
1681 self.workspace.as_ref()?.0.upgrade(cx)
1682 }
1683
1684 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1685 self.buffer().read(cx).title(cx)
1686 }
1687
1688 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1689 EditorSnapshot {
1690 mode: self.mode,
1691 show_gutter: self.show_gutter,
1692 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1693 scroll_anchor: self.scroll_manager.anchor(),
1694 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1695 placeholder_text: self.placeholder_text.clone(),
1696 is_focused: self
1697 .handle
1698 .upgrade(cx)
1699 .map_or(false, |handle| handle.is_focused(cx)),
1700 }
1701 }
1702
1703 pub fn language_at<'a, T: ToOffset>(
1704 &self,
1705 point: T,
1706 cx: &'a AppContext,
1707 ) -> Option<Arc<Language>> {
1708 self.buffer.read(cx).language_at(point, cx)
1709 }
1710
1711 pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option<Arc<dyn File>> {
1712 self.buffer.read(cx).read(cx).file_at(point).cloned()
1713 }
1714
1715 pub fn active_excerpt(
1716 &self,
1717 cx: &AppContext,
1718 ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
1719 self.buffer
1720 .read(cx)
1721 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1722 }
1723
1724 pub fn style(&self, cx: &AppContext) -> EditorStyle {
1725 build_style(
1726 settings::get::<ThemeSettings>(cx),
1727 self.get_field_editor_theme.as_deref(),
1728 self.override_text_style.as_deref(),
1729 cx,
1730 )
1731 }
1732
1733 pub fn mode(&self) -> EditorMode {
1734 self.mode
1735 }
1736
1737 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1738 self.collaboration_hub.as_deref()
1739 }
1740
1741 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1742 self.collaboration_hub = Some(hub);
1743 }
1744
1745 pub fn set_placeholder_text(
1746 &mut self,
1747 placeholder_text: impl Into<Arc<str>>,
1748 cx: &mut ViewContext<Self>,
1749 ) {
1750 self.placeholder_text = Some(placeholder_text.into());
1751 cx.notify();
1752 }
1753
1754 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1755 self.cursor_shape = cursor_shape;
1756 cx.notify();
1757 }
1758
1759 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1760 self.collapse_matches = collapse_matches;
1761 }
1762
1763 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1764 if self.collapse_matches {
1765 return range.start..range.start;
1766 }
1767 range.clone()
1768 }
1769
1770 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1771 if self.display_map.read(cx).clip_at_line_ends != clip {
1772 self.display_map
1773 .update(cx, |map, _| map.clip_at_line_ends = clip);
1774 }
1775 }
1776
1777 pub fn set_keymap_context_layer<Tag: 'static>(
1778 &mut self,
1779 context: KeymapContext,
1780 cx: &mut ViewContext<Self>,
1781 ) {
1782 self.keymap_context_layers
1783 .insert(TypeId::of::<Tag>(), context);
1784 cx.notify();
1785 }
1786
1787 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1788 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1789 cx.notify();
1790 }
1791
1792 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1793 self.input_enabled = input_enabled;
1794 }
1795
1796 pub fn set_autoindent(&mut self, autoindent: bool) {
1797 if autoindent {
1798 self.autoindent_mode = Some(AutoindentMode::EachLine);
1799 } else {
1800 self.autoindent_mode = None;
1801 }
1802 }
1803
1804 pub fn read_only(&self) -> bool {
1805 self.read_only
1806 }
1807
1808 pub fn set_read_only(&mut self, read_only: bool) {
1809 self.read_only = read_only;
1810 }
1811
1812 pub fn set_field_editor_style(
1813 &mut self,
1814 style: Option<Arc<GetFieldEditorTheme>>,
1815 cx: &mut ViewContext<Self>,
1816 ) {
1817 self.get_field_editor_theme = style;
1818 cx.notify();
1819 }
1820
1821 fn selections_did_change(
1822 &mut self,
1823 local: bool,
1824 old_cursor_position: &Anchor,
1825 cx: &mut ViewContext<Self>,
1826 ) {
1827 if self.focused && self.leader_peer_id.is_none() {
1828 self.buffer.update(cx, |buffer, cx| {
1829 buffer.set_active_selections(
1830 &self.selections.disjoint_anchors(),
1831 self.selections.line_mode,
1832 self.cursor_shape,
1833 cx,
1834 )
1835 });
1836 }
1837
1838 let display_map = self
1839 .display_map
1840 .update(cx, |display_map, cx| display_map.snapshot(cx));
1841 let buffer = &display_map.buffer_snapshot;
1842 self.add_selections_state = None;
1843 self.select_next_state = None;
1844 self.select_prev_state = None;
1845 self.select_larger_syntax_node_stack.clear();
1846 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1847 self.snippet_stack
1848 .invalidate(&self.selections.disjoint_anchors(), buffer);
1849 self.take_rename(false, cx);
1850
1851 let new_cursor_position = self.selections.newest_anchor().head();
1852
1853 self.push_to_nav_history(
1854 old_cursor_position.clone(),
1855 Some(new_cursor_position.to_point(buffer)),
1856 cx,
1857 );
1858
1859 if local {
1860 let new_cursor_position = self.selections.newest_anchor().head();
1861 let completion_menu = match self.context_menu.as_mut() {
1862 Some(ContextMenu::Completions(menu)) => Some(menu),
1863 _ => {
1864 self.context_menu.take();
1865 None
1866 }
1867 };
1868
1869 if let Some(completion_menu) = completion_menu {
1870 let cursor_position = new_cursor_position.to_offset(buffer);
1871 let (word_range, kind) =
1872 buffer.surrounding_word(completion_menu.initial_position.clone());
1873 if kind == Some(CharKind::Word)
1874 && word_range.to_inclusive().contains(&cursor_position)
1875 {
1876 let query = Self::completion_query(buffer, cursor_position);
1877 cx.background()
1878 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1879 self.show_completions(&ShowCompletions, cx);
1880 } else {
1881 self.hide_context_menu(cx);
1882 }
1883 }
1884
1885 hide_hover(self, cx);
1886
1887 if old_cursor_position.to_display_point(&display_map).row()
1888 != new_cursor_position.to_display_point(&display_map).row()
1889 {
1890 self.available_code_actions.take();
1891 }
1892 self.refresh_code_actions(cx);
1893 self.refresh_document_highlights(cx);
1894 refresh_matching_bracket_highlights(self, cx);
1895 self.discard_copilot_suggestion(cx);
1896 }
1897
1898 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1899 cx.emit(Event::SelectionsChanged { local });
1900 cx.notify();
1901 }
1902
1903 pub fn change_selections<R>(
1904 &mut self,
1905 autoscroll: Option<Autoscroll>,
1906 cx: &mut ViewContext<Self>,
1907 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1908 ) -> R {
1909 let old_cursor_position = self.selections.newest_anchor().head();
1910 self.push_to_selection_history();
1911
1912 let (changed, result) = self.selections.change_with(cx, change);
1913
1914 if changed {
1915 if let Some(autoscroll) = autoscroll {
1916 self.request_autoscroll(autoscroll, cx);
1917 }
1918 self.selections_did_change(true, &old_cursor_position, cx);
1919 }
1920
1921 result
1922 }
1923
1924 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1925 where
1926 I: IntoIterator<Item = (Range<S>, T)>,
1927 S: ToOffset,
1928 T: Into<Arc<str>>,
1929 {
1930 if self.read_only {
1931 return;
1932 }
1933
1934 self.buffer
1935 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1936 }
1937
1938 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1939 where
1940 I: IntoIterator<Item = (Range<S>, T)>,
1941 S: ToOffset,
1942 T: Into<Arc<str>>,
1943 {
1944 if self.read_only {
1945 return;
1946 }
1947
1948 self.buffer.update(cx, |buffer, cx| {
1949 buffer.edit(edits, self.autoindent_mode.clone(), cx)
1950 });
1951 }
1952
1953 pub fn edit_with_block_indent<I, S, T>(
1954 &mut self,
1955 edits: I,
1956 original_indent_columns: Vec<u32>,
1957 cx: &mut ViewContext<Self>,
1958 ) where
1959 I: IntoIterator<Item = (Range<S>, T)>,
1960 S: ToOffset,
1961 T: Into<Arc<str>>,
1962 {
1963 if self.read_only {
1964 return;
1965 }
1966
1967 self.buffer.update(cx, |buffer, cx| {
1968 buffer.edit(
1969 edits,
1970 Some(AutoindentMode::Block {
1971 original_indent_columns,
1972 }),
1973 cx,
1974 )
1975 });
1976 }
1977
1978 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
1979 self.hide_context_menu(cx);
1980
1981 match phase {
1982 SelectPhase::Begin {
1983 position,
1984 add,
1985 click_count,
1986 } => self.begin_selection(position, add, click_count, cx),
1987 SelectPhase::BeginColumnar {
1988 position,
1989 goal_column,
1990 } => self.begin_columnar_selection(position, goal_column, cx),
1991 SelectPhase::Extend {
1992 position,
1993 click_count,
1994 } => self.extend_selection(position, click_count, cx),
1995 SelectPhase::Update {
1996 position,
1997 goal_column,
1998 scroll_position,
1999 } => self.update_selection(position, goal_column, scroll_position, cx),
2000 SelectPhase::End => self.end_selection(cx),
2001 }
2002 }
2003
2004 fn extend_selection(
2005 &mut self,
2006 position: DisplayPoint,
2007 click_count: usize,
2008 cx: &mut ViewContext<Self>,
2009 ) {
2010 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2011 let tail = self.selections.newest::<usize>(cx).tail();
2012 self.begin_selection(position, false, click_count, cx);
2013
2014 let position = position.to_offset(&display_map, Bias::Left);
2015 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2016
2017 let mut pending_selection = self
2018 .selections
2019 .pending_anchor()
2020 .expect("extend_selection not called with pending selection");
2021 if position >= tail {
2022 pending_selection.start = tail_anchor;
2023 } else {
2024 pending_selection.end = tail_anchor;
2025 pending_selection.reversed = true;
2026 }
2027
2028 let mut pending_mode = self.selections.pending_mode().unwrap();
2029 match &mut pending_mode {
2030 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2031 _ => {}
2032 }
2033
2034 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2035 s.set_pending(pending_selection, pending_mode)
2036 });
2037 }
2038
2039 fn begin_selection(
2040 &mut self,
2041 position: DisplayPoint,
2042 add: bool,
2043 click_count: usize,
2044 cx: &mut ViewContext<Self>,
2045 ) {
2046 if !self.focused {
2047 cx.focus_self();
2048 }
2049
2050 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2051 let buffer = &display_map.buffer_snapshot;
2052 let newest_selection = self.selections.newest_anchor().clone();
2053 let position = display_map.clip_point(position, Bias::Left);
2054
2055 let start;
2056 let end;
2057 let mode;
2058 let auto_scroll;
2059 match click_count {
2060 1 => {
2061 start = buffer.anchor_before(position.to_point(&display_map));
2062 end = start.clone();
2063 mode = SelectMode::Character;
2064 auto_scroll = true;
2065 }
2066 2 => {
2067 let range = movement::surrounding_word(&display_map, position);
2068 start = buffer.anchor_before(range.start.to_point(&display_map));
2069 end = buffer.anchor_before(range.end.to_point(&display_map));
2070 mode = SelectMode::Word(start.clone()..end.clone());
2071 auto_scroll = true;
2072 }
2073 3 => {
2074 let position = display_map
2075 .clip_point(position, Bias::Left)
2076 .to_point(&display_map);
2077 let line_start = display_map.prev_line_boundary(position).0;
2078 let next_line_start = buffer.clip_point(
2079 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2080 Bias::Left,
2081 );
2082 start = buffer.anchor_before(line_start);
2083 end = buffer.anchor_before(next_line_start);
2084 mode = SelectMode::Line(start.clone()..end.clone());
2085 auto_scroll = true;
2086 }
2087 _ => {
2088 start = buffer.anchor_before(0);
2089 end = buffer.anchor_before(buffer.len());
2090 mode = SelectMode::All;
2091 auto_scroll = false;
2092 }
2093 }
2094
2095 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2096 if !add {
2097 s.clear_disjoint();
2098 } else if click_count > 1 {
2099 s.delete(newest_selection.id)
2100 }
2101
2102 s.set_pending_anchor_range(start..end, mode);
2103 });
2104 }
2105
2106 fn begin_columnar_selection(
2107 &mut self,
2108 position: DisplayPoint,
2109 goal_column: u32,
2110 cx: &mut ViewContext<Self>,
2111 ) {
2112 if !self.focused {
2113 cx.focus_self();
2114 }
2115
2116 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2117 let tail = self.selections.newest::<Point>(cx).tail();
2118 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2119
2120 self.select_columns(
2121 tail.to_display_point(&display_map),
2122 position,
2123 goal_column,
2124 &display_map,
2125 cx,
2126 );
2127 }
2128
2129 fn update_selection(
2130 &mut self,
2131 position: DisplayPoint,
2132 goal_column: u32,
2133 scroll_position: Vector2F,
2134 cx: &mut ViewContext<Self>,
2135 ) {
2136 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2137
2138 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2139 let tail = tail.to_display_point(&display_map);
2140 self.select_columns(tail, position, goal_column, &display_map, cx);
2141 } else if let Some(mut pending) = self.selections.pending_anchor() {
2142 let buffer = self.buffer.read(cx).snapshot(cx);
2143 let head;
2144 let tail;
2145 let mode = self.selections.pending_mode().unwrap();
2146 match &mode {
2147 SelectMode::Character => {
2148 head = position.to_point(&display_map);
2149 tail = pending.tail().to_point(&buffer);
2150 }
2151 SelectMode::Word(original_range) => {
2152 let original_display_range = original_range.start.to_display_point(&display_map)
2153 ..original_range.end.to_display_point(&display_map);
2154 let original_buffer_range = original_display_range.start.to_point(&display_map)
2155 ..original_display_range.end.to_point(&display_map);
2156 if movement::is_inside_word(&display_map, position)
2157 || original_display_range.contains(&position)
2158 {
2159 let word_range = movement::surrounding_word(&display_map, position);
2160 if word_range.start < original_display_range.start {
2161 head = word_range.start.to_point(&display_map);
2162 } else {
2163 head = word_range.end.to_point(&display_map);
2164 }
2165 } else {
2166 head = position.to_point(&display_map);
2167 }
2168
2169 if head <= original_buffer_range.start {
2170 tail = original_buffer_range.end;
2171 } else {
2172 tail = original_buffer_range.start;
2173 }
2174 }
2175 SelectMode::Line(original_range) => {
2176 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2177
2178 let position = display_map
2179 .clip_point(position, Bias::Left)
2180 .to_point(&display_map);
2181 let line_start = display_map.prev_line_boundary(position).0;
2182 let next_line_start = buffer.clip_point(
2183 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2184 Bias::Left,
2185 );
2186
2187 if line_start < original_range.start {
2188 head = line_start
2189 } else {
2190 head = next_line_start
2191 }
2192
2193 if head <= original_range.start {
2194 tail = original_range.end;
2195 } else {
2196 tail = original_range.start;
2197 }
2198 }
2199 SelectMode::All => {
2200 return;
2201 }
2202 };
2203
2204 if head < tail {
2205 pending.start = buffer.anchor_before(head);
2206 pending.end = buffer.anchor_before(tail);
2207 pending.reversed = true;
2208 } else {
2209 pending.start = buffer.anchor_before(tail);
2210 pending.end = buffer.anchor_before(head);
2211 pending.reversed = false;
2212 }
2213
2214 self.change_selections(None, cx, |s| {
2215 s.set_pending(pending, mode);
2216 });
2217 } else {
2218 error!("update_selection dispatched with no pending selection");
2219 return;
2220 }
2221
2222 self.set_scroll_position(scroll_position, cx);
2223 cx.notify();
2224 }
2225
2226 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2227 self.columnar_selection_tail.take();
2228 if self.selections.pending_anchor().is_some() {
2229 let selections = self.selections.all::<usize>(cx);
2230 self.change_selections(None, cx, |s| {
2231 s.select(selections);
2232 s.clear_pending();
2233 });
2234 }
2235 }
2236
2237 fn select_columns(
2238 &mut self,
2239 tail: DisplayPoint,
2240 head: DisplayPoint,
2241 goal_column: u32,
2242 display_map: &DisplaySnapshot,
2243 cx: &mut ViewContext<Self>,
2244 ) {
2245 let start_row = cmp::min(tail.row(), head.row());
2246 let end_row = cmp::max(tail.row(), head.row());
2247 let start_column = cmp::min(tail.column(), goal_column);
2248 let end_column = cmp::max(tail.column(), goal_column);
2249 let reversed = start_column < tail.column();
2250
2251 let selection_ranges = (start_row..=end_row)
2252 .filter_map(|row| {
2253 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2254 let start = display_map
2255 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2256 .to_point(display_map);
2257 let end = display_map
2258 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2259 .to_point(display_map);
2260 if reversed {
2261 Some(end..start)
2262 } else {
2263 Some(start..end)
2264 }
2265 } else {
2266 None
2267 }
2268 })
2269 .collect::<Vec<_>>();
2270
2271 self.change_selections(None, cx, |s| {
2272 s.select_ranges(selection_ranges);
2273 });
2274 cx.notify();
2275 }
2276
2277 pub fn has_pending_nonempty_selection(&self) -> bool {
2278 let pending_nonempty_selection = match self.selections.pending_anchor() {
2279 Some(Selection { start, end, .. }) => start != end,
2280 None => false,
2281 };
2282 pending_nonempty_selection || self.columnar_selection_tail.is_some()
2283 }
2284
2285 pub fn has_pending_selection(&self) -> bool {
2286 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2287 }
2288
2289 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2290 if self.take_rename(false, cx).is_some() {
2291 return;
2292 }
2293
2294 if hide_hover(self, cx) {
2295 return;
2296 }
2297
2298 if self.hide_context_menu(cx).is_some() {
2299 return;
2300 }
2301
2302 if self.discard_copilot_suggestion(cx) {
2303 return;
2304 }
2305
2306 if self.snippet_stack.pop().is_some() {
2307 return;
2308 }
2309
2310 if self.mode == EditorMode::Full {
2311 if self.active_diagnostics.is_some() {
2312 self.dismiss_diagnostics(cx);
2313 return;
2314 }
2315
2316 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2317 return;
2318 }
2319 }
2320
2321 cx.propagate_action();
2322 }
2323
2324 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2325 let text: Arc<str> = text.into();
2326
2327 if self.read_only {
2328 return;
2329 }
2330
2331 let selections = self.selections.all_adjusted(cx);
2332 let mut brace_inserted = false;
2333 let mut edits = Vec::new();
2334 let mut new_selections = Vec::with_capacity(selections.len());
2335 let mut new_autoclose_regions = Vec::new();
2336 let snapshot = self.buffer.read(cx).read(cx);
2337
2338 for (selection, autoclose_region) in
2339 self.selections_with_autoclose_regions(selections, &snapshot)
2340 {
2341 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2342 // Determine if the inserted text matches the opening or closing
2343 // bracket of any of this language's bracket pairs.
2344 let mut bracket_pair = None;
2345 let mut is_bracket_pair_start = false;
2346 if !text.is_empty() {
2347 // `text` can be empty when an user is using IME (e.g. Chinese Wubi Simplified)
2348 // and they are removing the character that triggered IME popup.
2349 for (pair, enabled) in scope.brackets() {
2350 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
2351 bracket_pair = Some(pair.clone());
2352 is_bracket_pair_start = true;
2353 break;
2354 } else if pair.end.as_str() == text.as_ref() {
2355 bracket_pair = Some(pair.clone());
2356 break;
2357 }
2358 }
2359 }
2360
2361 if let Some(bracket_pair) = bracket_pair {
2362 if selection.is_empty() {
2363 if is_bracket_pair_start {
2364 let prefix_len = bracket_pair.start.len() - text.len();
2365
2366 // If the inserted text is a suffix of an opening bracket and the
2367 // selection is preceded by the rest of the opening bracket, then
2368 // insert the closing bracket.
2369 let following_text_allows_autoclose = snapshot
2370 .chars_at(selection.start)
2371 .next()
2372 .map_or(true, |c| scope.should_autoclose_before(c));
2373 let preceding_text_matches_prefix = prefix_len == 0
2374 || (selection.start.column >= (prefix_len as u32)
2375 && snapshot.contains_str_at(
2376 Point::new(
2377 selection.start.row,
2378 selection.start.column - (prefix_len as u32),
2379 ),
2380 &bracket_pair.start[..prefix_len],
2381 ));
2382 if following_text_allows_autoclose && preceding_text_matches_prefix {
2383 let anchor = snapshot.anchor_before(selection.end);
2384 new_selections.push((selection.map(|_| anchor), text.len()));
2385 new_autoclose_regions.push((
2386 anchor,
2387 text.len(),
2388 selection.id,
2389 bracket_pair.clone(),
2390 ));
2391 edits.push((
2392 selection.range(),
2393 format!("{}{}", text, bracket_pair.end).into(),
2394 ));
2395 brace_inserted = true;
2396 continue;
2397 }
2398 }
2399
2400 if let Some(region) = autoclose_region {
2401 // If the selection is followed by an auto-inserted closing bracket,
2402 // then don't insert that closing bracket again; just move the selection
2403 // past the closing bracket.
2404 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2405 && text.as_ref() == region.pair.end.as_str();
2406 if should_skip {
2407 let anchor = snapshot.anchor_after(selection.end);
2408 new_selections
2409 .push((selection.map(|_| anchor), region.pair.end.len()));
2410 continue;
2411 }
2412 }
2413 }
2414 // If an opening bracket is 1 character long and is typed while
2415 // text is selected, then surround that text with the bracket pair.
2416 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2417 edits.push((selection.start..selection.start, text.clone()));
2418 edits.push((
2419 selection.end..selection.end,
2420 bracket_pair.end.as_str().into(),
2421 ));
2422 brace_inserted = true;
2423 new_selections.push((
2424 Selection {
2425 id: selection.id,
2426 start: snapshot.anchor_after(selection.start),
2427 end: snapshot.anchor_before(selection.end),
2428 reversed: selection.reversed,
2429 goal: selection.goal,
2430 },
2431 0,
2432 ));
2433 continue;
2434 }
2435 }
2436 }
2437
2438 // If not handling any auto-close operation, then just replace the selected
2439 // text with the given input and move the selection to the end of the
2440 // newly inserted text.
2441 let anchor = snapshot.anchor_after(selection.end);
2442 new_selections.push((selection.map(|_| anchor), 0));
2443 edits.push((selection.start..selection.end, text.clone()));
2444 }
2445
2446 drop(snapshot);
2447 self.transact(cx, |this, cx| {
2448 this.buffer.update(cx, |buffer, cx| {
2449 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2450 });
2451
2452 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2453 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2454 let snapshot = this.buffer.read(cx).read(cx);
2455 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2456 .zip(new_selection_deltas)
2457 .map(|(selection, delta)| Selection {
2458 id: selection.id,
2459 start: selection.start + delta,
2460 end: selection.end + delta,
2461 reversed: selection.reversed,
2462 goal: SelectionGoal::None,
2463 })
2464 .collect::<Vec<_>>();
2465
2466 let mut i = 0;
2467 for (position, delta, selection_id, pair) in new_autoclose_regions {
2468 let position = position.to_offset(&snapshot) + delta;
2469 let start = snapshot.anchor_before(position);
2470 let end = snapshot.anchor_after(position);
2471 while let Some(existing_state) = this.autoclose_regions.get(i) {
2472 match existing_state.range.start.cmp(&start, &snapshot) {
2473 Ordering::Less => i += 1,
2474 Ordering::Greater => break,
2475 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2476 Ordering::Less => i += 1,
2477 Ordering::Equal => break,
2478 Ordering::Greater => break,
2479 },
2480 }
2481 }
2482 this.autoclose_regions.insert(
2483 i,
2484 AutocloseRegion {
2485 selection_id,
2486 range: start..end,
2487 pair,
2488 },
2489 );
2490 }
2491
2492 drop(snapshot);
2493 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2494 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2495
2496 if !brace_inserted && settings::get::<EditorSettings>(cx).use_on_type_format {
2497 if let Some(on_type_format_task) =
2498 this.trigger_on_type_formatting(text.to_string(), cx)
2499 {
2500 on_type_format_task.detach_and_log_err(cx);
2501 }
2502 }
2503
2504 if had_active_copilot_suggestion {
2505 this.refresh_copilot_suggestions(true, cx);
2506 if !this.has_active_copilot_suggestion(cx) {
2507 this.trigger_completion_on_input(&text, cx);
2508 }
2509 } else {
2510 this.trigger_completion_on_input(&text, cx);
2511 this.refresh_copilot_suggestions(true, cx);
2512 }
2513 });
2514 }
2515
2516 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2517 self.transact(cx, |this, cx| {
2518 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2519 let selections = this.selections.all::<usize>(cx);
2520 let multi_buffer = this.buffer.read(cx);
2521 let buffer = multi_buffer.snapshot(cx);
2522 selections
2523 .iter()
2524 .map(|selection| {
2525 let start_point = selection.start.to_point(&buffer);
2526 let mut indent = buffer.indent_size_for_line(start_point.row);
2527 indent.len = cmp::min(indent.len, start_point.column);
2528 let start = selection.start;
2529 let end = selection.end;
2530 let is_cursor = start == end;
2531 let language_scope = buffer.language_scope_at(start);
2532 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2533 &language_scope
2534 {
2535 let leading_whitespace_len = buffer
2536 .reversed_chars_at(start)
2537 .take_while(|c| c.is_whitespace() && *c != '\n')
2538 .map(|c| c.len_utf8())
2539 .sum::<usize>();
2540
2541 let trailing_whitespace_len = buffer
2542 .chars_at(end)
2543 .take_while(|c| c.is_whitespace() && *c != '\n')
2544 .map(|c| c.len_utf8())
2545 .sum::<usize>();
2546
2547 let insert_extra_newline =
2548 language.brackets().any(|(pair, enabled)| {
2549 let pair_start = pair.start.trim_end();
2550 let pair_end = pair.end.trim_start();
2551
2552 enabled
2553 && pair.newline
2554 && buffer.contains_str_at(
2555 end + trailing_whitespace_len,
2556 pair_end,
2557 )
2558 && buffer.contains_str_at(
2559 (start - leading_whitespace_len)
2560 .saturating_sub(pair_start.len()),
2561 pair_start,
2562 )
2563 });
2564 // Comment extension on newline is allowed only for cursor selections
2565 let comment_delimiter = language.line_comment_prefix().filter(|_| {
2566 let is_comment_extension_enabled =
2567 multi_buffer.settings_at(0, cx).extend_comment_on_newline;
2568 is_cursor && is_comment_extension_enabled
2569 });
2570 let comment_delimiter = if let Some(delimiter) = comment_delimiter {
2571 buffer
2572 .buffer_line_for_row(start_point.row)
2573 .is_some_and(|(snapshot, range)| {
2574 let mut index_of_first_non_whitespace = 0;
2575 let line_starts_with_comment = snapshot
2576 .chars_for_range(range)
2577 .skip_while(|c| {
2578 let should_skip = c.is_whitespace();
2579 if should_skip {
2580 index_of_first_non_whitespace += 1;
2581 }
2582 should_skip
2583 })
2584 .take(delimiter.len())
2585 .eq(delimiter.chars());
2586 let cursor_is_placed_after_comment_marker =
2587 index_of_first_non_whitespace + delimiter.len()
2588 <= start_point.column as usize;
2589 line_starts_with_comment
2590 && cursor_is_placed_after_comment_marker
2591 })
2592 .then(|| delimiter.clone())
2593 } else {
2594 None
2595 };
2596 (comment_delimiter, insert_extra_newline)
2597 } else {
2598 (None, false)
2599 };
2600
2601 let capacity_for_delimiter = comment_delimiter
2602 .as_deref()
2603 .map(str::len)
2604 .unwrap_or_default();
2605 let mut new_text =
2606 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2607 new_text.push_str("\n");
2608 new_text.extend(indent.chars());
2609 if let Some(delimiter) = &comment_delimiter {
2610 new_text.push_str(&delimiter);
2611 }
2612 if insert_extra_newline {
2613 new_text = new_text.repeat(2);
2614 }
2615
2616 let anchor = buffer.anchor_after(end);
2617 let new_selection = selection.map(|_| anchor);
2618 (
2619 (start..end, new_text),
2620 (insert_extra_newline, new_selection),
2621 )
2622 })
2623 .unzip()
2624 };
2625
2626 this.edit_with_autoindent(edits, cx);
2627 let buffer = this.buffer.read(cx).snapshot(cx);
2628 let new_selections = selection_fixup_info
2629 .into_iter()
2630 .map(|(extra_newline_inserted, new_selection)| {
2631 let mut cursor = new_selection.end.to_point(&buffer);
2632 if extra_newline_inserted {
2633 cursor.row -= 1;
2634 cursor.column = buffer.line_len(cursor.row);
2635 }
2636 new_selection.map(|_| cursor)
2637 })
2638 .collect();
2639
2640 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2641 this.refresh_copilot_suggestions(true, cx);
2642 });
2643 }
2644
2645 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2646 let buffer = self.buffer.read(cx);
2647 let snapshot = buffer.snapshot(cx);
2648
2649 let mut edits = Vec::new();
2650 let mut rows = Vec::new();
2651 let mut rows_inserted = 0;
2652
2653 for selection in self.selections.all_adjusted(cx) {
2654 let cursor = selection.head();
2655 let row = cursor.row;
2656
2657 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2658
2659 let newline = "\n".to_string();
2660 edits.push((start_of_line..start_of_line, newline));
2661
2662 rows.push(row + rows_inserted);
2663 rows_inserted += 1;
2664 }
2665
2666 self.transact(cx, |editor, cx| {
2667 editor.edit(edits, cx);
2668
2669 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2670 let mut index = 0;
2671 s.move_cursors_with(|map, _, _| {
2672 let row = rows[index];
2673 index += 1;
2674
2675 let point = Point::new(row, 0);
2676 let boundary = map.next_line_boundary(point).1;
2677 let clipped = map.clip_point(boundary, Bias::Left);
2678
2679 (clipped, SelectionGoal::None)
2680 });
2681 });
2682
2683 let mut indent_edits = Vec::new();
2684 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2685 for row in rows {
2686 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2687 for (row, indent) in indents {
2688 if indent.len == 0 {
2689 continue;
2690 }
2691
2692 let text = match indent.kind {
2693 IndentKind::Space => " ".repeat(indent.len as usize),
2694 IndentKind::Tab => "\t".repeat(indent.len as usize),
2695 };
2696 let point = Point::new(row, 0);
2697 indent_edits.push((point..point, text));
2698 }
2699 }
2700 editor.edit(indent_edits, cx);
2701 });
2702 }
2703
2704 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2705 let buffer = self.buffer.read(cx);
2706 let snapshot = buffer.snapshot(cx);
2707
2708 let mut edits = Vec::new();
2709 let mut rows = Vec::new();
2710 let mut rows_inserted = 0;
2711
2712 for selection in self.selections.all_adjusted(cx) {
2713 let cursor = selection.head();
2714 let row = cursor.row;
2715
2716 let point = Point::new(row + 1, 0);
2717 let start_of_line = snapshot.clip_point(point, Bias::Left);
2718
2719 let newline = "\n".to_string();
2720 edits.push((start_of_line..start_of_line, newline));
2721
2722 rows_inserted += 1;
2723 rows.push(row + rows_inserted);
2724 }
2725
2726 self.transact(cx, |editor, cx| {
2727 editor.edit(edits, cx);
2728
2729 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2730 let mut index = 0;
2731 s.move_cursors_with(|map, _, _| {
2732 let row = rows[index];
2733 index += 1;
2734
2735 let point = Point::new(row, 0);
2736 let boundary = map.next_line_boundary(point).1;
2737 let clipped = map.clip_point(boundary, Bias::Left);
2738
2739 (clipped, SelectionGoal::None)
2740 });
2741 });
2742
2743 let mut indent_edits = Vec::new();
2744 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2745 for row in rows {
2746 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2747 for (row, indent) in indents {
2748 if indent.len == 0 {
2749 continue;
2750 }
2751
2752 let text = match indent.kind {
2753 IndentKind::Space => " ".repeat(indent.len as usize),
2754 IndentKind::Tab => "\t".repeat(indent.len as usize),
2755 };
2756 let point = Point::new(row, 0);
2757 indent_edits.push((point..point, text));
2758 }
2759 }
2760 editor.edit(indent_edits, cx);
2761 });
2762 }
2763
2764 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2765 self.insert_with_autoindent_mode(
2766 text,
2767 Some(AutoindentMode::Block {
2768 original_indent_columns: Vec::new(),
2769 }),
2770 cx,
2771 );
2772 }
2773
2774 fn insert_with_autoindent_mode(
2775 &mut self,
2776 text: &str,
2777 autoindent_mode: Option<AutoindentMode>,
2778 cx: &mut ViewContext<Self>,
2779 ) {
2780 if self.read_only {
2781 return;
2782 }
2783
2784 let text: Arc<str> = text.into();
2785 self.transact(cx, |this, cx| {
2786 let old_selections = this.selections.all_adjusted(cx);
2787 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2788 let anchors = {
2789 let snapshot = buffer.read(cx);
2790 old_selections
2791 .iter()
2792 .map(|s| {
2793 let anchor = snapshot.anchor_after(s.head());
2794 s.map(|_| anchor)
2795 })
2796 .collect::<Vec<_>>()
2797 };
2798 buffer.edit(
2799 old_selections
2800 .iter()
2801 .map(|s| (s.start..s.end, text.clone())),
2802 autoindent_mode,
2803 cx,
2804 );
2805 anchors
2806 });
2807
2808 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2809 s.select_anchors(selection_anchors);
2810 })
2811 });
2812 }
2813
2814 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2815 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2816 return;
2817 }
2818
2819 let selection = self.selections.newest_anchor();
2820 if self
2821 .buffer
2822 .read(cx)
2823 .is_completion_trigger(selection.head(), text, cx)
2824 {
2825 self.show_completions(&ShowCompletions, cx);
2826 } else {
2827 self.hide_context_menu(cx);
2828 }
2829 }
2830
2831 /// If any empty selections is touching the start of its innermost containing autoclose
2832 /// region, expand it to select the brackets.
2833 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2834 let selections = self.selections.all::<usize>(cx);
2835 let buffer = self.buffer.read(cx).read(cx);
2836 let mut new_selections = Vec::new();
2837 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2838 if let (Some(region), true) = (region, selection.is_empty()) {
2839 let mut range = region.range.to_offset(&buffer);
2840 if selection.start == range.start {
2841 if range.start >= region.pair.start.len() {
2842 range.start -= region.pair.start.len();
2843 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2844 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2845 range.end += region.pair.end.len();
2846 selection.start = range.start;
2847 selection.end = range.end;
2848 }
2849 }
2850 }
2851 }
2852 }
2853 new_selections.push(selection);
2854 }
2855
2856 drop(buffer);
2857 self.change_selections(None, cx, |selections| selections.select(new_selections));
2858 }
2859
2860 /// Iterate the given selections, and for each one, find the smallest surrounding
2861 /// autoclose region. This uses the ordering of the selections and the autoclose
2862 /// regions to avoid repeated comparisons.
2863 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2864 &'a self,
2865 selections: impl IntoIterator<Item = Selection<D>>,
2866 buffer: &'a MultiBufferSnapshot,
2867 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2868 let mut i = 0;
2869 let mut regions = self.autoclose_regions.as_slice();
2870 selections.into_iter().map(move |selection| {
2871 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2872
2873 let mut enclosing = None;
2874 while let Some(pair_state) = regions.get(i) {
2875 if pair_state.range.end.to_offset(buffer) < range.start {
2876 regions = ®ions[i + 1..];
2877 i = 0;
2878 } else if pair_state.range.start.to_offset(buffer) > range.end {
2879 break;
2880 } else if pair_state.selection_id == selection.id {
2881 enclosing = Some(pair_state);
2882 i += 1;
2883 }
2884 }
2885
2886 (selection.clone(), enclosing)
2887 })
2888 }
2889
2890 /// Remove any autoclose regions that no longer contain their selection.
2891 fn invalidate_autoclose_regions(
2892 &mut self,
2893 mut selections: &[Selection<Anchor>],
2894 buffer: &MultiBufferSnapshot,
2895 ) {
2896 self.autoclose_regions.retain(|state| {
2897 let mut i = 0;
2898 while let Some(selection) = selections.get(i) {
2899 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2900 selections = &selections[1..];
2901 continue;
2902 }
2903 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2904 break;
2905 }
2906 if selection.id == state.selection_id {
2907 return true;
2908 } else {
2909 i += 1;
2910 }
2911 }
2912 false
2913 });
2914 }
2915 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2916 let offset = position.to_offset(buffer);
2917 let (word_range, kind) = buffer.surrounding_word(offset);
2918 if offset > word_range.start && kind == Some(CharKind::Word) {
2919 Some(
2920 buffer
2921 .text_for_range(word_range.start..offset)
2922 .collect::<String>(),
2923 )
2924 } else {
2925 None
2926 }
2927 }
2928
2929 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
2930 self.refresh_inlay_hints(
2931 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
2932 cx,
2933 );
2934 }
2935
2936 pub fn inlay_hints_enabled(&self) -> bool {
2937 self.inlay_hint_cache.enabled
2938 }
2939
2940 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
2941 if self.project.is_none() || self.mode != EditorMode::Full {
2942 return;
2943 }
2944
2945 let reason_description = reason.description();
2946 let (invalidate_cache, required_languages) = match reason {
2947 InlayHintRefreshReason::Toggle(enabled) => {
2948 self.inlay_hint_cache.enabled = enabled;
2949 if enabled {
2950 (InvalidationStrategy::RefreshRequested, None)
2951 } else {
2952 self.inlay_hint_cache.clear();
2953 self.splice_inlay_hints(
2954 self.visible_inlay_hints(cx)
2955 .iter()
2956 .map(|inlay| inlay.id)
2957 .collect(),
2958 Vec::new(),
2959 cx,
2960 );
2961 return;
2962 }
2963 }
2964 InlayHintRefreshReason::SettingsChange(new_settings) => {
2965 match self.inlay_hint_cache.update_settings(
2966 &self.buffer,
2967 new_settings,
2968 self.visible_inlay_hints(cx),
2969 cx,
2970 ) {
2971 ControlFlow::Break(Some(InlaySplice {
2972 to_remove,
2973 to_insert,
2974 })) => {
2975 self.splice_inlay_hints(to_remove, to_insert, cx);
2976 return;
2977 }
2978 ControlFlow::Break(None) => return,
2979 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
2980 }
2981 }
2982 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
2983 if let Some(InlaySplice {
2984 to_remove,
2985 to_insert,
2986 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
2987 {
2988 self.splice_inlay_hints(to_remove, to_insert, cx);
2989 }
2990 return;
2991 }
2992 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
2993 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
2994 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
2995 }
2996 InlayHintRefreshReason::RefreshRequested => {
2997 (InvalidationStrategy::RefreshRequested, None)
2998 }
2999 };
3000
3001 if let Some(InlaySplice {
3002 to_remove,
3003 to_insert,
3004 }) = self.inlay_hint_cache.spawn_hint_refresh(
3005 reason_description,
3006 self.excerpt_visible_offsets(required_languages.as_ref(), cx),
3007 invalidate_cache,
3008 cx,
3009 ) {
3010 self.splice_inlay_hints(to_remove, to_insert, cx);
3011 }
3012 }
3013
3014 fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
3015 self.display_map
3016 .read(cx)
3017 .current_inlays()
3018 .filter(move |inlay| {
3019 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
3020 })
3021 .cloned()
3022 .collect()
3023 }
3024
3025 pub fn excerpt_visible_offsets(
3026 &self,
3027 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3028 cx: &mut ViewContext<'_, '_, Editor>,
3029 ) -> HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)> {
3030 let multi_buffer = self.buffer().read(cx);
3031 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3032 let multi_buffer_visible_start = self
3033 .scroll_manager
3034 .anchor()
3035 .anchor
3036 .to_point(&multi_buffer_snapshot);
3037 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3038 multi_buffer_visible_start
3039 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3040 Bias::Left,
3041 );
3042 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3043 multi_buffer
3044 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3045 .into_iter()
3046 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3047 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3048 let buffer = buffer_handle.read(cx);
3049 let language = buffer.language()?;
3050 if let Some(restrict_to_languages) = restrict_to_languages {
3051 if !restrict_to_languages.contains(language) {
3052 return None;
3053 }
3054 }
3055 Some((
3056 excerpt_id,
3057 (
3058 buffer_handle,
3059 buffer.version().clone(),
3060 excerpt_visible_range,
3061 ),
3062 ))
3063 })
3064 .collect()
3065 }
3066
3067 fn splice_inlay_hints(
3068 &self,
3069 to_remove: Vec<InlayId>,
3070 to_insert: Vec<Inlay>,
3071 cx: &mut ViewContext<Self>,
3072 ) {
3073 self.display_map.update(cx, |display_map, cx| {
3074 display_map.splice_inlays(to_remove, to_insert, cx);
3075 });
3076 cx.notify();
3077 }
3078
3079 fn trigger_on_type_formatting(
3080 &self,
3081 input: String,
3082 cx: &mut ViewContext<Self>,
3083 ) -> Option<Task<Result<()>>> {
3084 if input.len() != 1 {
3085 return None;
3086 }
3087
3088 let project = self.project.as_ref()?;
3089 let position = self.selections.newest_anchor().head();
3090 let (buffer, buffer_position) = self
3091 .buffer
3092 .read(cx)
3093 .text_anchor_for_position(position.clone(), cx)?;
3094
3095 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3096 // hence we do LSP request & edit on host side only — add formats to host's history.
3097 let push_to_lsp_host_history = true;
3098 // If this is not the host, append its history with new edits.
3099 let push_to_client_history = project.read(cx).is_remote();
3100
3101 let on_type_formatting = project.update(cx, |project, cx| {
3102 project.on_type_format(
3103 buffer.clone(),
3104 buffer_position,
3105 input,
3106 push_to_lsp_host_history,
3107 cx,
3108 )
3109 });
3110 Some(cx.spawn(|editor, mut cx| async move {
3111 if let Some(transaction) = on_type_formatting.await? {
3112 if push_to_client_history {
3113 buffer.update(&mut cx, |buffer, _| {
3114 buffer.push_transaction(transaction, Instant::now());
3115 });
3116 }
3117 editor.update(&mut cx, |editor, cx| {
3118 editor.refresh_document_highlights(cx);
3119 })?;
3120 }
3121 Ok(())
3122 }))
3123 }
3124
3125 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3126 if self.pending_rename.is_some() {
3127 return;
3128 }
3129
3130 let project = if let Some(project) = self.project.clone() {
3131 project
3132 } else {
3133 return;
3134 };
3135
3136 let position = self.selections.newest_anchor().head();
3137 let (buffer, buffer_position) = if let Some(output) = self
3138 .buffer
3139 .read(cx)
3140 .text_anchor_for_position(position.clone(), cx)
3141 {
3142 output
3143 } else {
3144 return;
3145 };
3146
3147 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
3148 let completions = project.update(cx, |project, cx| {
3149 project.completions(&buffer, buffer_position, cx)
3150 });
3151
3152 let id = post_inc(&mut self.next_completion_id);
3153 let task = cx.spawn(|this, mut cx| {
3154 async move {
3155 let menu = if let Some(completions) = completions.await.log_err() {
3156 let mut menu = CompletionsMenu {
3157 id,
3158 initial_position: position,
3159 match_candidates: completions
3160 .iter()
3161 .enumerate()
3162 .map(|(id, completion)| {
3163 StringMatchCandidate::new(
3164 id,
3165 completion.label.text[completion.label.filter_range.clone()]
3166 .into(),
3167 )
3168 })
3169 .collect(),
3170 buffer,
3171 completions: completions.into(),
3172 matches: Vec::new().into(),
3173 selected_item: 0,
3174 list: Default::default(),
3175 };
3176 menu.filter(query.as_deref(), cx.background()).await;
3177 if menu.matches.is_empty() {
3178 None
3179 } else {
3180 Some(menu)
3181 }
3182 } else {
3183 None
3184 };
3185
3186 this.update(&mut cx, |this, cx| {
3187 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
3188
3189 match this.context_menu.as_ref() {
3190 None => {}
3191 Some(ContextMenu::Completions(prev_menu)) => {
3192 if prev_menu.id > id {
3193 return;
3194 }
3195 }
3196 _ => return,
3197 }
3198
3199 if this.focused && menu.is_some() {
3200 let menu = menu.unwrap();
3201 this.show_context_menu(ContextMenu::Completions(menu), cx);
3202 } else if this.completion_tasks.is_empty() {
3203 // If there are no more completion tasks and the last menu was
3204 // empty, we should hide it. If it was already hidden, we should
3205 // also show the copilot suggestion when available.
3206 if this.hide_context_menu(cx).is_none() {
3207 this.update_visible_copilot_suggestion(cx);
3208 }
3209 }
3210 })?;
3211
3212 Ok::<_, anyhow::Error>(())
3213 }
3214 .log_err()
3215 });
3216 self.completion_tasks.push((id, task));
3217 }
3218
3219 pub fn confirm_completion(
3220 &mut self,
3221 action: &ConfirmCompletion,
3222 cx: &mut ViewContext<Self>,
3223 ) -> Option<Task<Result<()>>> {
3224 use language::ToOffset as _;
3225
3226 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3227 menu
3228 } else {
3229 return None;
3230 };
3231
3232 let mat = completions_menu
3233 .matches
3234 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3235 let buffer_handle = completions_menu.buffer;
3236 let completion = completions_menu.completions.get(mat.candidate_id)?;
3237
3238 let snippet;
3239 let text;
3240 if completion.is_snippet() {
3241 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3242 text = snippet.as_ref().unwrap().text.clone();
3243 } else {
3244 snippet = None;
3245 text = completion.new_text.clone();
3246 };
3247 let selections = self.selections.all::<usize>(cx);
3248 let buffer = buffer_handle.read(cx);
3249 let old_range = completion.old_range.to_offset(buffer);
3250 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3251
3252 let newest_selection = self.selections.newest_anchor();
3253 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3254 return None;
3255 }
3256
3257 let lookbehind = newest_selection
3258 .start
3259 .text_anchor
3260 .to_offset(buffer)
3261 .saturating_sub(old_range.start);
3262 let lookahead = old_range
3263 .end
3264 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3265 let mut common_prefix_len = old_text
3266 .bytes()
3267 .zip(text.bytes())
3268 .take_while(|(a, b)| a == b)
3269 .count();
3270
3271 let snapshot = self.buffer.read(cx).snapshot(cx);
3272 let mut range_to_replace: Option<Range<isize>> = None;
3273 let mut ranges = Vec::new();
3274 for selection in &selections {
3275 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3276 let start = selection.start.saturating_sub(lookbehind);
3277 let end = selection.end + lookahead;
3278 if selection.id == newest_selection.id {
3279 range_to_replace = Some(
3280 ((start + common_prefix_len) as isize - selection.start as isize)
3281 ..(end as isize - selection.start as isize),
3282 );
3283 }
3284 ranges.push(start + common_prefix_len..end);
3285 } else {
3286 common_prefix_len = 0;
3287 ranges.clear();
3288 ranges.extend(selections.iter().map(|s| {
3289 if s.id == newest_selection.id {
3290 range_to_replace = Some(
3291 old_range.start.to_offset_utf16(&snapshot).0 as isize
3292 - selection.start as isize
3293 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3294 - selection.start as isize,
3295 );
3296 old_range.clone()
3297 } else {
3298 s.start..s.end
3299 }
3300 }));
3301 break;
3302 }
3303 }
3304 let text = &text[common_prefix_len..];
3305
3306 cx.emit(Event::InputHandled {
3307 utf16_range_to_replace: range_to_replace,
3308 text: text.into(),
3309 });
3310
3311 self.transact(cx, |this, cx| {
3312 if let Some(mut snippet) = snippet {
3313 snippet.text = text.to_string();
3314 for tabstop in snippet.tabstops.iter_mut().flatten() {
3315 tabstop.start -= common_prefix_len as isize;
3316 tabstop.end -= common_prefix_len as isize;
3317 }
3318
3319 this.insert_snippet(&ranges, snippet, cx).log_err();
3320 } else {
3321 this.buffer.update(cx, |buffer, cx| {
3322 buffer.edit(
3323 ranges.iter().map(|range| (range.clone(), text)),
3324 this.autoindent_mode.clone(),
3325 cx,
3326 );
3327 });
3328 }
3329
3330 this.refresh_copilot_suggestions(true, cx);
3331 });
3332
3333 let project = self.project.clone()?;
3334 let apply_edits = project.update(cx, |project, cx| {
3335 project.apply_additional_edits_for_completion(
3336 buffer_handle,
3337 completion.clone(),
3338 true,
3339 cx,
3340 )
3341 });
3342 Some(cx.foreground().spawn(async move {
3343 apply_edits.await?;
3344 Ok(())
3345 }))
3346 }
3347
3348 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3349 if matches!(
3350 self.context_menu.as_ref(),
3351 Some(ContextMenu::CodeActions(_))
3352 ) {
3353 self.context_menu.take();
3354 cx.notify();
3355 return;
3356 }
3357
3358 let deployed_from_indicator = action.deployed_from_indicator;
3359 let mut task = self.code_actions_task.take();
3360 cx.spawn(|this, mut cx| async move {
3361 while let Some(prev_task) = task {
3362 prev_task.await;
3363 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3364 }
3365
3366 this.update(&mut cx, |this, cx| {
3367 if this.focused {
3368 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3369 this.show_context_menu(
3370 ContextMenu::CodeActions(CodeActionsMenu {
3371 buffer,
3372 actions,
3373 selected_item: Default::default(),
3374 list: Default::default(),
3375 deployed_from_indicator,
3376 }),
3377 cx,
3378 );
3379 }
3380 }
3381 })?;
3382
3383 Ok::<_, anyhow::Error>(())
3384 })
3385 .detach_and_log_err(cx);
3386 }
3387
3388 pub fn confirm_code_action(
3389 workspace: &mut Workspace,
3390 action: &ConfirmCodeAction,
3391 cx: &mut ViewContext<Workspace>,
3392 ) -> Option<Task<Result<()>>> {
3393 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3394 let actions_menu = if let ContextMenu::CodeActions(menu) =
3395 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3396 {
3397 menu
3398 } else {
3399 return None;
3400 };
3401 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3402 let action = actions_menu.actions.get(action_ix)?.clone();
3403 let title = action.lsp_action.title.clone();
3404 let buffer = actions_menu.buffer;
3405
3406 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3407 project.apply_code_action(buffer, action, true, cx)
3408 });
3409 let editor = editor.downgrade();
3410 Some(cx.spawn(|workspace, cx| async move {
3411 let project_transaction = apply_code_actions.await?;
3412 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3413 }))
3414 }
3415
3416 async fn open_project_transaction(
3417 this: &WeakViewHandle<Editor>,
3418 workspace: WeakViewHandle<Workspace>,
3419 transaction: ProjectTransaction,
3420 title: String,
3421 mut cx: AsyncAppContext,
3422 ) -> Result<()> {
3423 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3424
3425 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3426 entries.sort_unstable_by_key(|(buffer, _)| {
3427 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3428 });
3429
3430 // If the project transaction's edits are all contained within this editor, then
3431 // avoid opening a new editor to display them.
3432
3433 if let Some((buffer, transaction)) = entries.first() {
3434 if entries.len() == 1 {
3435 let excerpt = this.read_with(&cx, |editor, cx| {
3436 editor
3437 .buffer()
3438 .read(cx)
3439 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3440 })?;
3441 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3442 if excerpted_buffer == *buffer {
3443 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3444 let excerpt_range = excerpt_range.to_offset(buffer);
3445 buffer
3446 .edited_ranges_for_transaction::<usize>(transaction)
3447 .all(|range| {
3448 excerpt_range.start <= range.start
3449 && excerpt_range.end >= range.end
3450 })
3451 });
3452
3453 if all_edits_within_excerpt {
3454 return Ok(());
3455 }
3456 }
3457 }
3458 }
3459 } else {
3460 return Ok(());
3461 }
3462
3463 let mut ranges_to_highlight = Vec::new();
3464 let excerpt_buffer = cx.add_model(|cx| {
3465 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3466 for (buffer_handle, transaction) in &entries {
3467 let buffer = buffer_handle.read(cx);
3468 ranges_to_highlight.extend(
3469 multibuffer.push_excerpts_with_context_lines(
3470 buffer_handle.clone(),
3471 buffer
3472 .edited_ranges_for_transaction::<usize>(transaction)
3473 .collect(),
3474 1,
3475 cx,
3476 ),
3477 );
3478 }
3479 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3480 multibuffer
3481 });
3482
3483 workspace.update(&mut cx, |workspace, cx| {
3484 let project = workspace.project().clone();
3485 let editor =
3486 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3487 workspace.add_item(Box::new(editor.clone()), cx);
3488 editor.update(cx, |editor, cx| {
3489 editor.highlight_background::<Self>(
3490 ranges_to_highlight,
3491 |theme| theme.editor.highlighted_line_background,
3492 cx,
3493 );
3494 });
3495 })?;
3496
3497 Ok(())
3498 }
3499
3500 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3501 let project = self.project.clone()?;
3502 let buffer = self.buffer.read(cx);
3503 let newest_selection = self.selections.newest_anchor().clone();
3504 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3505 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3506 if start_buffer != end_buffer {
3507 return None;
3508 }
3509
3510 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3511 cx.background().timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT).await;
3512
3513 let actions = project
3514 .update(&mut cx, |project, cx| {
3515 project.code_actions(&start_buffer, start..end, cx)
3516 })
3517 .await;
3518
3519 this.update(&mut cx, |this, cx| {
3520 this.available_code_actions = actions.log_err().and_then(|actions| {
3521 if actions.is_empty() {
3522 None
3523 } else {
3524 Some((start_buffer, actions.into()))
3525 }
3526 });
3527 cx.notify();
3528 })
3529 .log_err();
3530 }));
3531 None
3532 }
3533
3534 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3535 if self.pending_rename.is_some() {
3536 return None;
3537 }
3538
3539 let project = self.project.clone()?;
3540 let buffer = self.buffer.read(cx);
3541 let newest_selection = self.selections.newest_anchor().clone();
3542 let cursor_position = newest_selection.head();
3543 let (cursor_buffer, cursor_buffer_position) =
3544 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3545 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3546 if cursor_buffer != tail_buffer {
3547 return None;
3548 }
3549
3550 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3551 cx.background()
3552 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
3553 .await;
3554
3555 let highlights = project
3556 .update(&mut cx, |project, cx| {
3557 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3558 })
3559 .await
3560 .log_err();
3561
3562 if let Some(highlights) = highlights {
3563 this.update(&mut cx, |this, cx| {
3564 if this.pending_rename.is_some() {
3565 return;
3566 }
3567
3568 let buffer_id = cursor_position.buffer_id;
3569 let buffer = this.buffer.read(cx);
3570 if !buffer
3571 .text_anchor_for_position(cursor_position, cx)
3572 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3573 {
3574 return;
3575 }
3576
3577 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3578 let mut write_ranges = Vec::new();
3579 let mut read_ranges = Vec::new();
3580 for highlight in highlights {
3581 for (excerpt_id, excerpt_range) in
3582 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3583 {
3584 let start = highlight
3585 .range
3586 .start
3587 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3588 let end = highlight
3589 .range
3590 .end
3591 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3592 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3593 continue;
3594 }
3595
3596 let range = Anchor {
3597 buffer_id,
3598 excerpt_id: excerpt_id.clone(),
3599 text_anchor: start,
3600 }..Anchor {
3601 buffer_id,
3602 excerpt_id,
3603 text_anchor: end,
3604 };
3605 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3606 write_ranges.push(range);
3607 } else {
3608 read_ranges.push(range);
3609 }
3610 }
3611 }
3612
3613 this.highlight_background::<DocumentHighlightRead>(
3614 read_ranges,
3615 |theme| theme.editor.document_highlight_read_background,
3616 cx,
3617 );
3618 this.highlight_background::<DocumentHighlightWrite>(
3619 write_ranges,
3620 |theme| theme.editor.document_highlight_write_background,
3621 cx,
3622 );
3623 cx.notify();
3624 })
3625 .log_err();
3626 }
3627 }));
3628 None
3629 }
3630
3631 fn refresh_copilot_suggestions(
3632 &mut self,
3633 debounce: bool,
3634 cx: &mut ViewContext<Self>,
3635 ) -> Option<()> {
3636 let copilot = Copilot::global(cx)?;
3637 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3638 self.clear_copilot_suggestions(cx);
3639 return None;
3640 }
3641 self.update_visible_copilot_suggestion(cx);
3642
3643 let snapshot = self.buffer.read(cx).snapshot(cx);
3644 let cursor = self.selections.newest_anchor().head();
3645 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3646 self.clear_copilot_suggestions(cx);
3647 return None;
3648 }
3649
3650 let (buffer, buffer_position) =
3651 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3652 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3653 if debounce {
3654 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3655 }
3656
3657 let completions = copilot
3658 .update(&mut cx, |copilot, cx| {
3659 copilot.completions(&buffer, buffer_position, cx)
3660 })
3661 .await
3662 .log_err()
3663 .into_iter()
3664 .flatten()
3665 .collect_vec();
3666
3667 this.update(&mut cx, |this, cx| {
3668 if !completions.is_empty() {
3669 this.copilot_state.cycled = false;
3670 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3671 this.copilot_state.completions.clear();
3672 this.copilot_state.active_completion_index = 0;
3673 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3674 for completion in completions {
3675 this.copilot_state.push_completion(completion);
3676 }
3677 this.update_visible_copilot_suggestion(cx);
3678 }
3679 })
3680 .log_err()?;
3681 Some(())
3682 });
3683
3684 Some(())
3685 }
3686
3687 fn cycle_copilot_suggestions(
3688 &mut self,
3689 direction: Direction,
3690 cx: &mut ViewContext<Self>,
3691 ) -> Option<()> {
3692 let copilot = Copilot::global(cx)?;
3693 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3694 return None;
3695 }
3696
3697 if self.copilot_state.cycled {
3698 self.copilot_state.cycle_completions(direction);
3699 self.update_visible_copilot_suggestion(cx);
3700 } else {
3701 let cursor = self.selections.newest_anchor().head();
3702 let (buffer, buffer_position) =
3703 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3704 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3705 let completions = copilot
3706 .update(&mut cx, |copilot, cx| {
3707 copilot.completions_cycling(&buffer, buffer_position, cx)
3708 })
3709 .await;
3710
3711 this.update(&mut cx, |this, cx| {
3712 this.copilot_state.cycled = true;
3713 for completion in completions.log_err().into_iter().flatten() {
3714 this.copilot_state.push_completion(completion);
3715 }
3716 this.copilot_state.cycle_completions(direction);
3717 this.update_visible_copilot_suggestion(cx);
3718 })
3719 .log_err()?;
3720
3721 Some(())
3722 });
3723 }
3724
3725 Some(())
3726 }
3727
3728 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3729 if !self.has_active_copilot_suggestion(cx) {
3730 self.refresh_copilot_suggestions(false, cx);
3731 return;
3732 }
3733
3734 self.update_visible_copilot_suggestion(cx);
3735 }
3736
3737 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3738 if self.has_active_copilot_suggestion(cx) {
3739 self.cycle_copilot_suggestions(Direction::Next, cx);
3740 } else {
3741 self.refresh_copilot_suggestions(false, cx);
3742 }
3743 }
3744
3745 fn previous_copilot_suggestion(
3746 &mut self,
3747 _: &copilot::PreviousSuggestion,
3748 cx: &mut ViewContext<Self>,
3749 ) {
3750 if self.has_active_copilot_suggestion(cx) {
3751 self.cycle_copilot_suggestions(Direction::Prev, cx);
3752 } else {
3753 self.refresh_copilot_suggestions(false, cx);
3754 }
3755 }
3756
3757 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3758 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3759 if let Some((copilot, completion)) =
3760 Copilot::global(cx).zip(self.copilot_state.active_completion())
3761 {
3762 copilot
3763 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3764 .detach_and_log_err(cx);
3765
3766 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3767 }
3768 cx.emit(Event::InputHandled {
3769 utf16_range_to_replace: None,
3770 text: suggestion.text.to_string().into(),
3771 });
3772 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3773 cx.notify();
3774 true
3775 } else {
3776 false
3777 }
3778 }
3779
3780 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3781 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3782 if let Some(copilot) = Copilot::global(cx) {
3783 copilot
3784 .update(cx, |copilot, cx| {
3785 copilot.discard_completions(&self.copilot_state.completions, cx)
3786 })
3787 .detach_and_log_err(cx);
3788
3789 self.report_copilot_event(None, false, cx)
3790 }
3791
3792 self.display_map.update(cx, |map, cx| {
3793 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3794 });
3795 cx.notify();
3796 true
3797 } else {
3798 false
3799 }
3800 }
3801
3802 fn is_copilot_enabled_at(
3803 &self,
3804 location: Anchor,
3805 snapshot: &MultiBufferSnapshot,
3806 cx: &mut ViewContext<Self>,
3807 ) -> bool {
3808 let file = snapshot.file_at(location);
3809 let language = snapshot.language_at(location);
3810 let settings = all_language_settings(file, cx);
3811 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3812 }
3813
3814 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3815 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3816 let buffer = self.buffer.read(cx).read(cx);
3817 suggestion.position.is_valid(&buffer)
3818 } else {
3819 false
3820 }
3821 }
3822
3823 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3824 let suggestion = self.copilot_state.suggestion.take()?;
3825 self.display_map.update(cx, |map, cx| {
3826 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3827 });
3828 let buffer = self.buffer.read(cx).read(cx);
3829
3830 if suggestion.position.is_valid(&buffer) {
3831 Some(suggestion)
3832 } else {
3833 None
3834 }
3835 }
3836
3837 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3838 let snapshot = self.buffer.read(cx).snapshot(cx);
3839 let selection = self.selections.newest_anchor();
3840 let cursor = selection.head();
3841
3842 if self.context_menu.is_some()
3843 || !self.completion_tasks.is_empty()
3844 || selection.start != selection.end
3845 {
3846 self.discard_copilot_suggestion(cx);
3847 } else if let Some(text) = self
3848 .copilot_state
3849 .text_for_active_completion(cursor, &snapshot)
3850 {
3851 let text = Rope::from(text);
3852 let mut to_remove = Vec::new();
3853 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3854 to_remove.push(suggestion.id);
3855 }
3856
3857 let suggestion_inlay =
3858 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3859 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3860 self.display_map.update(cx, move |map, cx| {
3861 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3862 });
3863 cx.notify();
3864 } else {
3865 self.discard_copilot_suggestion(cx);
3866 }
3867 }
3868
3869 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3870 self.copilot_state = Default::default();
3871 self.discard_copilot_suggestion(cx);
3872 }
3873
3874 pub fn render_code_actions_indicator(
3875 &self,
3876 style: &EditorStyle,
3877 is_active: bool,
3878 cx: &mut ViewContext<Self>,
3879 ) -> Option<AnyElement<Self>> {
3880 if self.available_code_actions.is_some() {
3881 enum CodeActions {}
3882 Some(
3883 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
3884 Svg::new("icons/bolt.svg").with_color(
3885 style
3886 .code_actions
3887 .indicator
3888 .in_state(is_active)
3889 .style_for(state)
3890 .color,
3891 )
3892 })
3893 .with_cursor_style(CursorStyle::PointingHand)
3894 .with_padding(Padding::uniform(3.))
3895 .on_down(MouseButton::Left, |_, this, cx| {
3896 this.toggle_code_actions(
3897 &ToggleCodeActions {
3898 deployed_from_indicator: true,
3899 },
3900 cx,
3901 );
3902 })
3903 .into_any(),
3904 )
3905 } else {
3906 None
3907 }
3908 }
3909
3910 pub fn render_fold_indicators(
3911 &self,
3912 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3913 style: &EditorStyle,
3914 gutter_hovered: bool,
3915 line_height: f32,
3916 gutter_margin: f32,
3917 cx: &mut ViewContext<Self>,
3918 ) -> Vec<Option<AnyElement<Self>>> {
3919 enum FoldIndicators {}
3920
3921 let style = style.folds.clone();
3922
3923 fold_data
3924 .iter()
3925 .enumerate()
3926 .map(|(ix, fold_data)| {
3927 fold_data
3928 .map(|(fold_status, buffer_row, active)| {
3929 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3930 MouseEventHandler::new::<FoldIndicators, _>(
3931 ix as usize,
3932 cx,
3933 |mouse_state, _| {
3934 Svg::new(match fold_status {
3935 FoldStatus::Folded => style.folded_icon.clone(),
3936 FoldStatus::Foldable => style.foldable_icon.clone(),
3937 })
3938 .with_color(
3939 style
3940 .indicator
3941 .in_state(fold_status == FoldStatus::Folded)
3942 .style_for(mouse_state)
3943 .color,
3944 )
3945 .constrained()
3946 .with_width(gutter_margin * style.icon_margin_scale)
3947 .aligned()
3948 .constrained()
3949 .with_height(line_height)
3950 .with_width(gutter_margin)
3951 .aligned()
3952 },
3953 )
3954 .with_cursor_style(CursorStyle::PointingHand)
3955 .with_padding(Padding::uniform(3.))
3956 .on_click(MouseButton::Left, {
3957 move |_, editor, cx| match fold_status {
3958 FoldStatus::Folded => {
3959 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3960 }
3961 FoldStatus::Foldable => {
3962 editor.fold_at(&FoldAt { buffer_row }, cx);
3963 }
3964 }
3965 })
3966 .into_any()
3967 })
3968 })
3969 .flatten()
3970 })
3971 .collect()
3972 }
3973
3974 pub fn context_menu_visible(&self) -> bool {
3975 self.context_menu
3976 .as_ref()
3977 .map_or(false, |menu| menu.visible())
3978 }
3979
3980 pub fn render_context_menu(
3981 &self,
3982 cursor_position: DisplayPoint,
3983 style: EditorStyle,
3984 cx: &mut ViewContext<Editor>,
3985 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3986 self.context_menu
3987 .as_ref()
3988 .map(|menu| menu.render(cursor_position, style, cx))
3989 }
3990
3991 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3992 if !matches!(menu, ContextMenu::Completions(_)) {
3993 self.completion_tasks.clear();
3994 }
3995 self.context_menu = Some(menu);
3996 self.discard_copilot_suggestion(cx);
3997 cx.notify();
3998 }
3999
4000 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4001 cx.notify();
4002 self.completion_tasks.clear();
4003 let context_menu = self.context_menu.take();
4004 if context_menu.is_some() {
4005 self.update_visible_copilot_suggestion(cx);
4006 }
4007 context_menu
4008 }
4009
4010 pub fn insert_snippet(
4011 &mut self,
4012 insertion_ranges: &[Range<usize>],
4013 snippet: Snippet,
4014 cx: &mut ViewContext<Self>,
4015 ) -> Result<()> {
4016 let tabstops = self.buffer.update(cx, |buffer, cx| {
4017 let snippet_text: Arc<str> = snippet.text.clone().into();
4018 buffer.edit(
4019 insertion_ranges
4020 .iter()
4021 .cloned()
4022 .map(|range| (range, snippet_text.clone())),
4023 Some(AutoindentMode::EachLine),
4024 cx,
4025 );
4026
4027 let snapshot = &*buffer.read(cx);
4028 let snippet = &snippet;
4029 snippet
4030 .tabstops
4031 .iter()
4032 .map(|tabstop| {
4033 let mut tabstop_ranges = tabstop
4034 .iter()
4035 .flat_map(|tabstop_range| {
4036 let mut delta = 0_isize;
4037 insertion_ranges.iter().map(move |insertion_range| {
4038 let insertion_start = insertion_range.start as isize + delta;
4039 delta +=
4040 snippet.text.len() as isize - insertion_range.len() as isize;
4041
4042 let start = snapshot.anchor_before(
4043 (insertion_start + tabstop_range.start) as usize,
4044 );
4045 let end = snapshot
4046 .anchor_after((insertion_start + tabstop_range.end) as usize);
4047 start..end
4048 })
4049 })
4050 .collect::<Vec<_>>();
4051 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4052 tabstop_ranges
4053 })
4054 .collect::<Vec<_>>()
4055 });
4056
4057 if let Some(tabstop) = tabstops.first() {
4058 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4059 s.select_ranges(tabstop.iter().cloned());
4060 });
4061 self.snippet_stack.push(SnippetState {
4062 active_index: 0,
4063 ranges: tabstops,
4064 });
4065 }
4066
4067 Ok(())
4068 }
4069
4070 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4071 self.move_to_snippet_tabstop(Bias::Right, cx)
4072 }
4073
4074 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4075 self.move_to_snippet_tabstop(Bias::Left, cx)
4076 }
4077
4078 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4079 if let Some(mut snippet) = self.snippet_stack.pop() {
4080 match bias {
4081 Bias::Left => {
4082 if snippet.active_index > 0 {
4083 snippet.active_index -= 1;
4084 } else {
4085 self.snippet_stack.push(snippet);
4086 return false;
4087 }
4088 }
4089 Bias::Right => {
4090 if snippet.active_index + 1 < snippet.ranges.len() {
4091 snippet.active_index += 1;
4092 } else {
4093 self.snippet_stack.push(snippet);
4094 return false;
4095 }
4096 }
4097 }
4098 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4099 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4100 s.select_anchor_ranges(current_ranges.iter().cloned())
4101 });
4102 // If snippet state is not at the last tabstop, push it back on the stack
4103 if snippet.active_index + 1 < snippet.ranges.len() {
4104 self.snippet_stack.push(snippet);
4105 }
4106 return true;
4107 }
4108 }
4109
4110 false
4111 }
4112
4113 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4114 self.transact(cx, |this, cx| {
4115 this.select_all(&SelectAll, cx);
4116 this.insert("", cx);
4117 });
4118 }
4119
4120 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4121 self.transact(cx, |this, cx| {
4122 this.select_autoclose_pair(cx);
4123 let mut selections = this.selections.all::<Point>(cx);
4124 if !this.selections.line_mode {
4125 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4126 for selection in &mut selections {
4127 if selection.is_empty() {
4128 let old_head = selection.head();
4129 let mut new_head =
4130 movement::left(&display_map, old_head.to_display_point(&display_map))
4131 .to_point(&display_map);
4132 if let Some((buffer, line_buffer_range)) = display_map
4133 .buffer_snapshot
4134 .buffer_line_for_row(old_head.row)
4135 {
4136 let indent_size =
4137 buffer.indent_size_for_line(line_buffer_range.start.row);
4138 let indent_len = match indent_size.kind {
4139 IndentKind::Space => {
4140 buffer.settings_at(line_buffer_range.start, cx).tab_size
4141 }
4142 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4143 };
4144 if old_head.column <= indent_size.len && old_head.column > 0 {
4145 let indent_len = indent_len.get();
4146 new_head = cmp::min(
4147 new_head,
4148 Point::new(
4149 old_head.row,
4150 ((old_head.column - 1) / indent_len) * indent_len,
4151 ),
4152 );
4153 }
4154 }
4155
4156 selection.set_head(new_head, SelectionGoal::None);
4157 }
4158 }
4159 }
4160
4161 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4162 this.insert("", cx);
4163 this.refresh_copilot_suggestions(true, cx);
4164 });
4165 }
4166
4167 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4168 self.transact(cx, |this, cx| {
4169 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4170 let line_mode = s.line_mode;
4171 s.move_with(|map, selection| {
4172 if selection.is_empty() && !line_mode {
4173 let cursor = movement::right(map, selection.head());
4174 selection.end = cursor;
4175 selection.reversed = true;
4176 selection.goal = SelectionGoal::None;
4177 }
4178 })
4179 });
4180 this.insert("", cx);
4181 this.refresh_copilot_suggestions(true, cx);
4182 });
4183 }
4184
4185 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4186 if self.move_to_prev_snippet_tabstop(cx) {
4187 return;
4188 }
4189
4190 self.outdent(&Outdent, cx);
4191 }
4192
4193 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4194 if self.move_to_next_snippet_tabstop(cx) {
4195 return;
4196 }
4197
4198 let mut selections = self.selections.all_adjusted(cx);
4199 let buffer = self.buffer.read(cx);
4200 let snapshot = buffer.snapshot(cx);
4201 let rows_iter = selections.iter().map(|s| s.head().row);
4202 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4203
4204 let mut edits = Vec::new();
4205 let mut prev_edited_row = 0;
4206 let mut row_delta = 0;
4207 for selection in &mut selections {
4208 if selection.start.row != prev_edited_row {
4209 row_delta = 0;
4210 }
4211 prev_edited_row = selection.end.row;
4212
4213 // If the selection is non-empty, then increase the indentation of the selected lines.
4214 if !selection.is_empty() {
4215 row_delta =
4216 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4217 continue;
4218 }
4219
4220 // If the selection is empty and the cursor is in the leading whitespace before the
4221 // suggested indentation, then auto-indent the line.
4222 let cursor = selection.head();
4223 let current_indent = snapshot.indent_size_for_line(cursor.row);
4224 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4225 if cursor.column < suggested_indent.len
4226 && cursor.column <= current_indent.len
4227 && current_indent.len <= suggested_indent.len
4228 {
4229 selection.start = Point::new(cursor.row, suggested_indent.len);
4230 selection.end = selection.start;
4231 if row_delta == 0 {
4232 edits.extend(Buffer::edit_for_indent_size_adjustment(
4233 cursor.row,
4234 current_indent,
4235 suggested_indent,
4236 ));
4237 row_delta = suggested_indent.len - current_indent.len;
4238 }
4239 continue;
4240 }
4241 }
4242
4243 // Accept copilot suggestion if there is only one selection and the cursor is not
4244 // in the leading whitespace.
4245 if self.selections.count() == 1
4246 && cursor.column >= current_indent.len
4247 && self.has_active_copilot_suggestion(cx)
4248 {
4249 self.accept_copilot_suggestion(cx);
4250 return;
4251 }
4252
4253 // Otherwise, insert a hard or soft tab.
4254 let settings = buffer.settings_at(cursor, cx);
4255 let tab_size = if settings.hard_tabs {
4256 IndentSize::tab()
4257 } else {
4258 let tab_size = settings.tab_size.get();
4259 let char_column = snapshot
4260 .text_for_range(Point::new(cursor.row, 0)..cursor)
4261 .flat_map(str::chars)
4262 .count()
4263 + row_delta as usize;
4264 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4265 IndentSize::spaces(chars_to_next_tab_stop)
4266 };
4267 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4268 selection.end = selection.start;
4269 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4270 row_delta += tab_size.len;
4271 }
4272
4273 self.transact(cx, |this, cx| {
4274 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4275 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4276 this.refresh_copilot_suggestions(true, cx);
4277 });
4278 }
4279
4280 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4281 let mut selections = self.selections.all::<Point>(cx);
4282 let mut prev_edited_row = 0;
4283 let mut row_delta = 0;
4284 let mut edits = Vec::new();
4285 let buffer = self.buffer.read(cx);
4286 let snapshot = buffer.snapshot(cx);
4287 for selection in &mut selections {
4288 if selection.start.row != prev_edited_row {
4289 row_delta = 0;
4290 }
4291 prev_edited_row = selection.end.row;
4292
4293 row_delta =
4294 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4295 }
4296
4297 self.transact(cx, |this, cx| {
4298 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4299 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4300 });
4301 }
4302
4303 fn indent_selection(
4304 buffer: &MultiBuffer,
4305 snapshot: &MultiBufferSnapshot,
4306 selection: &mut Selection<Point>,
4307 edits: &mut Vec<(Range<Point>, String)>,
4308 delta_for_start_row: u32,
4309 cx: &AppContext,
4310 ) -> u32 {
4311 let settings = buffer.settings_at(selection.start, cx);
4312 let tab_size = settings.tab_size.get();
4313 let indent_kind = if settings.hard_tabs {
4314 IndentKind::Tab
4315 } else {
4316 IndentKind::Space
4317 };
4318 let mut start_row = selection.start.row;
4319 let mut end_row = selection.end.row + 1;
4320
4321 // If a selection ends at the beginning of a line, don't indent
4322 // that last line.
4323 if selection.end.column == 0 {
4324 end_row -= 1;
4325 }
4326
4327 // Avoid re-indenting a row that has already been indented by a
4328 // previous selection, but still update this selection's column
4329 // to reflect that indentation.
4330 if delta_for_start_row > 0 {
4331 start_row += 1;
4332 selection.start.column += delta_for_start_row;
4333 if selection.end.row == selection.start.row {
4334 selection.end.column += delta_for_start_row;
4335 }
4336 }
4337
4338 let mut delta_for_end_row = 0;
4339 for row in start_row..end_row {
4340 let current_indent = snapshot.indent_size_for_line(row);
4341 let indent_delta = match (current_indent.kind, indent_kind) {
4342 (IndentKind::Space, IndentKind::Space) => {
4343 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4344 IndentSize::spaces(columns_to_next_tab_stop)
4345 }
4346 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4347 (_, IndentKind::Tab) => IndentSize::tab(),
4348 };
4349
4350 let row_start = Point::new(row, 0);
4351 edits.push((
4352 row_start..row_start,
4353 indent_delta.chars().collect::<String>(),
4354 ));
4355
4356 // Update this selection's endpoints to reflect the indentation.
4357 if row == selection.start.row {
4358 selection.start.column += indent_delta.len;
4359 }
4360 if row == selection.end.row {
4361 selection.end.column += indent_delta.len;
4362 delta_for_end_row = indent_delta.len;
4363 }
4364 }
4365
4366 if selection.start.row == selection.end.row {
4367 delta_for_start_row + delta_for_end_row
4368 } else {
4369 delta_for_end_row
4370 }
4371 }
4372
4373 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4374 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4375 let selections = self.selections.all::<Point>(cx);
4376 let mut deletion_ranges = Vec::new();
4377 let mut last_outdent = None;
4378 {
4379 let buffer = self.buffer.read(cx);
4380 let snapshot = buffer.snapshot(cx);
4381 for selection in &selections {
4382 let settings = buffer.settings_at(selection.start, cx);
4383 let tab_size = settings.tab_size.get();
4384 let mut rows = selection.spanned_rows(false, &display_map);
4385
4386 // Avoid re-outdenting a row that has already been outdented by a
4387 // previous selection.
4388 if let Some(last_row) = last_outdent {
4389 if last_row == rows.start {
4390 rows.start += 1;
4391 }
4392 }
4393
4394 for row in rows {
4395 let indent_size = snapshot.indent_size_for_line(row);
4396 if indent_size.len > 0 {
4397 let deletion_len = match indent_size.kind {
4398 IndentKind::Space => {
4399 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4400 if columns_to_prev_tab_stop == 0 {
4401 tab_size
4402 } else {
4403 columns_to_prev_tab_stop
4404 }
4405 }
4406 IndentKind::Tab => 1,
4407 };
4408 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4409 last_outdent = Some(row);
4410 }
4411 }
4412 }
4413 }
4414
4415 self.transact(cx, |this, cx| {
4416 this.buffer.update(cx, |buffer, cx| {
4417 let empty_str: Arc<str> = "".into();
4418 buffer.edit(
4419 deletion_ranges
4420 .into_iter()
4421 .map(|range| (range, empty_str.clone())),
4422 None,
4423 cx,
4424 );
4425 });
4426 let selections = this.selections.all::<usize>(cx);
4427 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4428 });
4429 }
4430
4431 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4432 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4433 let selections = self.selections.all::<Point>(cx);
4434
4435 let mut new_cursors = Vec::new();
4436 let mut edit_ranges = Vec::new();
4437 let mut selections = selections.iter().peekable();
4438 while let Some(selection) = selections.next() {
4439 let mut rows = selection.spanned_rows(false, &display_map);
4440 let goal_display_column = selection.head().to_display_point(&display_map).column();
4441
4442 // Accumulate contiguous regions of rows that we want to delete.
4443 while let Some(next_selection) = selections.peek() {
4444 let next_rows = next_selection.spanned_rows(false, &display_map);
4445 if next_rows.start <= rows.end {
4446 rows.end = next_rows.end;
4447 selections.next().unwrap();
4448 } else {
4449 break;
4450 }
4451 }
4452
4453 let buffer = &display_map.buffer_snapshot;
4454 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4455 let edit_end;
4456 let cursor_buffer_row;
4457 if buffer.max_point().row >= rows.end {
4458 // If there's a line after the range, delete the \n from the end of the row range
4459 // and position the cursor on the next line.
4460 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4461 cursor_buffer_row = rows.end;
4462 } else {
4463 // If there isn't a line after the range, delete the \n from the line before the
4464 // start of the row range and position the cursor there.
4465 edit_start = edit_start.saturating_sub(1);
4466 edit_end = buffer.len();
4467 cursor_buffer_row = rows.start.saturating_sub(1);
4468 }
4469
4470 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4471 *cursor.column_mut() =
4472 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4473
4474 new_cursors.push((
4475 selection.id,
4476 buffer.anchor_after(cursor.to_point(&display_map)),
4477 ));
4478 edit_ranges.push(edit_start..edit_end);
4479 }
4480
4481 self.transact(cx, |this, cx| {
4482 let buffer = this.buffer.update(cx, |buffer, cx| {
4483 let empty_str: Arc<str> = "".into();
4484 buffer.edit(
4485 edit_ranges
4486 .into_iter()
4487 .map(|range| (range, empty_str.clone())),
4488 None,
4489 cx,
4490 );
4491 buffer.snapshot(cx)
4492 });
4493 let new_selections = new_cursors
4494 .into_iter()
4495 .map(|(id, cursor)| {
4496 let cursor = cursor.to_point(&buffer);
4497 Selection {
4498 id,
4499 start: cursor,
4500 end: cursor,
4501 reversed: false,
4502 goal: SelectionGoal::None,
4503 }
4504 })
4505 .collect();
4506
4507 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4508 s.select(new_selections);
4509 });
4510 });
4511 }
4512
4513 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4514 let mut row_ranges = Vec::<Range<u32>>::new();
4515 for selection in self.selections.all::<Point>(cx) {
4516 let start = selection.start.row;
4517 let end = if selection.start.row == selection.end.row {
4518 selection.start.row + 1
4519 } else {
4520 selection.end.row
4521 };
4522
4523 if let Some(last_row_range) = row_ranges.last_mut() {
4524 if start <= last_row_range.end {
4525 last_row_range.end = end;
4526 continue;
4527 }
4528 }
4529 row_ranges.push(start..end);
4530 }
4531
4532 let snapshot = self.buffer.read(cx).snapshot(cx);
4533 let mut cursor_positions = Vec::new();
4534 for row_range in &row_ranges {
4535 let anchor = snapshot.anchor_before(Point::new(
4536 row_range.end - 1,
4537 snapshot.line_len(row_range.end - 1),
4538 ));
4539 cursor_positions.push(anchor.clone()..anchor);
4540 }
4541
4542 self.transact(cx, |this, cx| {
4543 for row_range in row_ranges.into_iter().rev() {
4544 for row in row_range.rev() {
4545 let end_of_line = Point::new(row, snapshot.line_len(row));
4546 let indent = snapshot.indent_size_for_line(row + 1);
4547 let start_of_next_line = Point::new(row + 1, indent.len);
4548
4549 let replace = if snapshot.line_len(row + 1) > indent.len {
4550 " "
4551 } else {
4552 ""
4553 };
4554
4555 this.buffer.update(cx, |buffer, cx| {
4556 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4557 });
4558 }
4559 }
4560
4561 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4562 s.select_anchor_ranges(cursor_positions)
4563 });
4564 });
4565 }
4566
4567 pub fn sort_lines_case_sensitive(
4568 &mut self,
4569 _: &SortLinesCaseSensitive,
4570 cx: &mut ViewContext<Self>,
4571 ) {
4572 self.manipulate_lines(cx, |lines| lines.sort())
4573 }
4574
4575 pub fn sort_lines_case_insensitive(
4576 &mut self,
4577 _: &SortLinesCaseInsensitive,
4578 cx: &mut ViewContext<Self>,
4579 ) {
4580 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4581 }
4582
4583 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4584 self.manipulate_lines(cx, |lines| lines.reverse())
4585 }
4586
4587 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4588 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4589 }
4590
4591 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4592 where
4593 Fn: FnMut(&mut [&str]),
4594 {
4595 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4596 let buffer = self.buffer.read(cx).snapshot(cx);
4597
4598 let mut edits = Vec::new();
4599
4600 let selections = self.selections.all::<Point>(cx);
4601 let mut selections = selections.iter().peekable();
4602 let mut contiguous_row_selections = Vec::new();
4603 let mut new_selections = Vec::new();
4604
4605 while let Some(selection) = selections.next() {
4606 let (start_row, end_row) = consume_contiguous_rows(
4607 &mut contiguous_row_selections,
4608 selection,
4609 &display_map,
4610 &mut selections,
4611 );
4612
4613 let start_point = Point::new(start_row, 0);
4614 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4615 let text = buffer
4616 .text_for_range(start_point..end_point)
4617 .collect::<String>();
4618 let mut lines = text.split("\n").collect_vec();
4619
4620 let lines_len = lines.len();
4621 callback(&mut lines);
4622
4623 // This is a current limitation with selections.
4624 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4625 debug_assert!(
4626 lines.len() == lines_len,
4627 "callback should not change the number of lines"
4628 );
4629
4630 edits.push((start_point..end_point, lines.join("\n")));
4631 let start_anchor = buffer.anchor_after(start_point);
4632 let end_anchor = buffer.anchor_before(end_point);
4633
4634 // Make selection and push
4635 new_selections.push(Selection {
4636 id: selection.id,
4637 start: start_anchor.to_offset(&buffer),
4638 end: end_anchor.to_offset(&buffer),
4639 goal: SelectionGoal::None,
4640 reversed: selection.reversed,
4641 });
4642 }
4643
4644 self.transact(cx, |this, cx| {
4645 this.buffer.update(cx, |buffer, cx| {
4646 buffer.edit(edits, None, cx);
4647 });
4648
4649 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4650 s.select(new_selections);
4651 });
4652
4653 this.request_autoscroll(Autoscroll::fit(), cx);
4654 });
4655 }
4656
4657 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4658 self.manipulate_text(cx, |text| text.to_uppercase())
4659 }
4660
4661 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4662 self.manipulate_text(cx, |text| text.to_lowercase())
4663 }
4664
4665 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4666 self.manipulate_text(cx, |text| {
4667 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4668 // https://github.com/rutrum/convert-case/issues/16
4669 text.split("\n")
4670 .map(|line| line.to_case(Case::Title))
4671 .join("\n")
4672 })
4673 }
4674
4675 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4676 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4677 }
4678
4679 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4680 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4681 }
4682
4683 pub fn convert_to_upper_camel_case(
4684 &mut self,
4685 _: &ConvertToUpperCamelCase,
4686 cx: &mut ViewContext<Self>,
4687 ) {
4688 self.manipulate_text(cx, |text| {
4689 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4690 // https://github.com/rutrum/convert-case/issues/16
4691 text.split("\n")
4692 .map(|line| line.to_case(Case::UpperCamel))
4693 .join("\n")
4694 })
4695 }
4696
4697 pub fn convert_to_lower_camel_case(
4698 &mut self,
4699 _: &ConvertToLowerCamelCase,
4700 cx: &mut ViewContext<Self>,
4701 ) {
4702 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4703 }
4704
4705 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4706 where
4707 Fn: FnMut(&str) -> String,
4708 {
4709 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4710 let buffer = self.buffer.read(cx).snapshot(cx);
4711
4712 let mut new_selections = Vec::new();
4713 let mut edits = Vec::new();
4714 let mut selection_adjustment = 0i32;
4715
4716 for selection in self.selections.all::<usize>(cx) {
4717 let selection_is_empty = selection.is_empty();
4718
4719 let (start, end) = if selection_is_empty {
4720 let word_range = movement::surrounding_word(
4721 &display_map,
4722 selection.start.to_display_point(&display_map),
4723 );
4724 let start = word_range.start.to_offset(&display_map, Bias::Left);
4725 let end = word_range.end.to_offset(&display_map, Bias::Left);
4726 (start, end)
4727 } else {
4728 (selection.start, selection.end)
4729 };
4730
4731 let text = buffer.text_for_range(start..end).collect::<String>();
4732 let old_length = text.len() as i32;
4733 let text = callback(&text);
4734
4735 new_selections.push(Selection {
4736 start: (start as i32 - selection_adjustment) as usize,
4737 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4738 goal: SelectionGoal::None,
4739 ..selection
4740 });
4741
4742 selection_adjustment += old_length - text.len() as i32;
4743
4744 edits.push((start..end, text));
4745 }
4746
4747 self.transact(cx, |this, cx| {
4748 this.buffer.update(cx, |buffer, cx| {
4749 buffer.edit(edits, None, cx);
4750 });
4751
4752 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4753 s.select(new_selections);
4754 });
4755
4756 this.request_autoscroll(Autoscroll::fit(), cx);
4757 });
4758 }
4759
4760 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4761 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4762 let buffer = &display_map.buffer_snapshot;
4763 let selections = self.selections.all::<Point>(cx);
4764
4765 let mut edits = Vec::new();
4766 let mut selections_iter = selections.iter().peekable();
4767 while let Some(selection) = selections_iter.next() {
4768 // Avoid duplicating the same lines twice.
4769 let mut rows = selection.spanned_rows(false, &display_map);
4770
4771 while let Some(next_selection) = selections_iter.peek() {
4772 let next_rows = next_selection.spanned_rows(false, &display_map);
4773 if next_rows.start < rows.end {
4774 rows.end = next_rows.end;
4775 selections_iter.next().unwrap();
4776 } else {
4777 break;
4778 }
4779 }
4780
4781 // Copy the text from the selected row region and splice it at the start of the region.
4782 let start = Point::new(rows.start, 0);
4783 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4784 let text = buffer
4785 .text_for_range(start..end)
4786 .chain(Some("\n"))
4787 .collect::<String>();
4788 edits.push((start..start, text));
4789 }
4790
4791 self.transact(cx, |this, cx| {
4792 this.buffer.update(cx, |buffer, cx| {
4793 buffer.edit(edits, None, cx);
4794 });
4795
4796 this.request_autoscroll(Autoscroll::fit(), cx);
4797 });
4798 }
4799
4800 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4801 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4802 let buffer = self.buffer.read(cx).snapshot(cx);
4803
4804 let mut edits = Vec::new();
4805 let mut unfold_ranges = Vec::new();
4806 let mut refold_ranges = Vec::new();
4807
4808 let selections = self.selections.all::<Point>(cx);
4809 let mut selections = selections.iter().peekable();
4810 let mut contiguous_row_selections = Vec::new();
4811 let mut new_selections = Vec::new();
4812
4813 while let Some(selection) = selections.next() {
4814 // Find all the selections that span a contiguous row range
4815 let (start_row, end_row) = consume_contiguous_rows(
4816 &mut contiguous_row_selections,
4817 selection,
4818 &display_map,
4819 &mut selections,
4820 );
4821
4822 // Move the text spanned by the row range to be before the line preceding the row range
4823 if start_row > 0 {
4824 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4825 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4826 let insertion_point = display_map
4827 .prev_line_boundary(Point::new(start_row - 1, 0))
4828 .0;
4829
4830 // Don't move lines across excerpts
4831 if buffer
4832 .excerpt_boundaries_in_range((
4833 Bound::Excluded(insertion_point),
4834 Bound::Included(range_to_move.end),
4835 ))
4836 .next()
4837 .is_none()
4838 {
4839 let text = buffer
4840 .text_for_range(range_to_move.clone())
4841 .flat_map(|s| s.chars())
4842 .skip(1)
4843 .chain(['\n'])
4844 .collect::<String>();
4845
4846 edits.push((
4847 buffer.anchor_after(range_to_move.start)
4848 ..buffer.anchor_before(range_to_move.end),
4849 String::new(),
4850 ));
4851 let insertion_anchor = buffer.anchor_after(insertion_point);
4852 edits.push((insertion_anchor..insertion_anchor, text));
4853
4854 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4855
4856 // Move selections up
4857 new_selections.extend(contiguous_row_selections.drain(..).map(
4858 |mut selection| {
4859 selection.start.row -= row_delta;
4860 selection.end.row -= row_delta;
4861 selection
4862 },
4863 ));
4864
4865 // Move folds up
4866 unfold_ranges.push(range_to_move.clone());
4867 for fold in display_map.folds_in_range(
4868 buffer.anchor_before(range_to_move.start)
4869 ..buffer.anchor_after(range_to_move.end),
4870 ) {
4871 let mut start = fold.start.to_point(&buffer);
4872 let mut end = fold.end.to_point(&buffer);
4873 start.row -= row_delta;
4874 end.row -= row_delta;
4875 refold_ranges.push(start..end);
4876 }
4877 }
4878 }
4879
4880 // If we didn't move line(s), preserve the existing selections
4881 new_selections.append(&mut contiguous_row_selections);
4882 }
4883
4884 self.transact(cx, |this, cx| {
4885 this.unfold_ranges(unfold_ranges, true, true, cx);
4886 this.buffer.update(cx, |buffer, cx| {
4887 for (range, text) in edits {
4888 buffer.edit([(range, text)], None, cx);
4889 }
4890 });
4891 this.fold_ranges(refold_ranges, true, cx);
4892 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4893 s.select(new_selections);
4894 })
4895 });
4896 }
4897
4898 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4899 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4900 let buffer = self.buffer.read(cx).snapshot(cx);
4901
4902 let mut edits = Vec::new();
4903 let mut unfold_ranges = Vec::new();
4904 let mut refold_ranges = Vec::new();
4905
4906 let selections = self.selections.all::<Point>(cx);
4907 let mut selections = selections.iter().peekable();
4908 let mut contiguous_row_selections = Vec::new();
4909 let mut new_selections = Vec::new();
4910
4911 while let Some(selection) = selections.next() {
4912 // Find all the selections that span a contiguous row range
4913 let (start_row, end_row) = consume_contiguous_rows(
4914 &mut contiguous_row_selections,
4915 selection,
4916 &display_map,
4917 &mut selections,
4918 );
4919
4920 // Move the text spanned by the row range to be after the last line of the row range
4921 if end_row <= buffer.max_point().row {
4922 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4923 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4924
4925 // Don't move lines across excerpt boundaries
4926 if buffer
4927 .excerpt_boundaries_in_range((
4928 Bound::Excluded(range_to_move.start),
4929 Bound::Included(insertion_point),
4930 ))
4931 .next()
4932 .is_none()
4933 {
4934 let mut text = String::from("\n");
4935 text.extend(buffer.text_for_range(range_to_move.clone()));
4936 text.pop(); // Drop trailing newline
4937 edits.push((
4938 buffer.anchor_after(range_to_move.start)
4939 ..buffer.anchor_before(range_to_move.end),
4940 String::new(),
4941 ));
4942 let insertion_anchor = buffer.anchor_after(insertion_point);
4943 edits.push((insertion_anchor..insertion_anchor, text));
4944
4945 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4946
4947 // Move selections down
4948 new_selections.extend(contiguous_row_selections.drain(..).map(
4949 |mut selection| {
4950 selection.start.row += row_delta;
4951 selection.end.row += row_delta;
4952 selection
4953 },
4954 ));
4955
4956 // Move folds down
4957 unfold_ranges.push(range_to_move.clone());
4958 for fold in display_map.folds_in_range(
4959 buffer.anchor_before(range_to_move.start)
4960 ..buffer.anchor_after(range_to_move.end),
4961 ) {
4962 let mut start = fold.start.to_point(&buffer);
4963 let mut end = fold.end.to_point(&buffer);
4964 start.row += row_delta;
4965 end.row += row_delta;
4966 refold_ranges.push(start..end);
4967 }
4968 }
4969 }
4970
4971 // If we didn't move line(s), preserve the existing selections
4972 new_selections.append(&mut contiguous_row_selections);
4973 }
4974
4975 self.transact(cx, |this, cx| {
4976 this.unfold_ranges(unfold_ranges, true, true, cx);
4977 this.buffer.update(cx, |buffer, cx| {
4978 for (range, text) in edits {
4979 buffer.edit([(range, text)], None, cx);
4980 }
4981 });
4982 this.fold_ranges(refold_ranges, true, cx);
4983 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4984 });
4985 }
4986
4987 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4988 self.transact(cx, |this, cx| {
4989 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4990 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4991 let line_mode = s.line_mode;
4992 s.move_with(|display_map, selection| {
4993 if !selection.is_empty() || line_mode {
4994 return;
4995 }
4996
4997 let mut head = selection.head();
4998 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4999 if head.column() == display_map.line_len(head.row()) {
5000 transpose_offset = display_map
5001 .buffer_snapshot
5002 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5003 }
5004
5005 if transpose_offset == 0 {
5006 return;
5007 }
5008
5009 *head.column_mut() += 1;
5010 head = display_map.clip_point(head, Bias::Right);
5011 selection.collapse_to(head, SelectionGoal::Column(head.column()));
5012
5013 let transpose_start = display_map
5014 .buffer_snapshot
5015 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5016 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5017 let transpose_end = display_map
5018 .buffer_snapshot
5019 .clip_offset(transpose_offset + 1, Bias::Right);
5020 if let Some(ch) =
5021 display_map.buffer_snapshot.chars_at(transpose_start).next()
5022 {
5023 edits.push((transpose_start..transpose_offset, String::new()));
5024 edits.push((transpose_end..transpose_end, ch.to_string()));
5025 }
5026 }
5027 });
5028 edits
5029 });
5030 this.buffer
5031 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5032 let selections = this.selections.all::<usize>(cx);
5033 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5034 s.select(selections);
5035 });
5036 });
5037 }
5038
5039 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5040 let mut text = String::new();
5041 let buffer = self.buffer.read(cx).snapshot(cx);
5042 let mut selections = self.selections.all::<Point>(cx);
5043 let mut clipboard_selections = Vec::with_capacity(selections.len());
5044 {
5045 let max_point = buffer.max_point();
5046 let mut is_first = true;
5047 for selection in &mut selections {
5048 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5049 if is_entire_line {
5050 selection.start = Point::new(selection.start.row, 0);
5051 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5052 selection.goal = SelectionGoal::None;
5053 }
5054 if is_first {
5055 is_first = false;
5056 } else {
5057 text += "\n";
5058 }
5059 let mut len = 0;
5060 for chunk in buffer.text_for_range(selection.start..selection.end) {
5061 text.push_str(chunk);
5062 len += chunk.len();
5063 }
5064 clipboard_selections.push(ClipboardSelection {
5065 len,
5066 is_entire_line,
5067 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5068 });
5069 }
5070 }
5071
5072 self.transact(cx, |this, cx| {
5073 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5074 s.select(selections);
5075 });
5076 this.insert("", cx);
5077 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5078 });
5079 }
5080
5081 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5082 let selections = self.selections.all::<Point>(cx);
5083 let buffer = self.buffer.read(cx).read(cx);
5084 let mut text = String::new();
5085
5086 let mut clipboard_selections = Vec::with_capacity(selections.len());
5087 {
5088 let max_point = buffer.max_point();
5089 let mut is_first = true;
5090 for selection in selections.iter() {
5091 let mut start = selection.start;
5092 let mut end = selection.end;
5093 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5094 if is_entire_line {
5095 start = Point::new(start.row, 0);
5096 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5097 }
5098 if is_first {
5099 is_first = false;
5100 } else {
5101 text += "\n";
5102 }
5103 let mut len = 0;
5104 for chunk in buffer.text_for_range(start..end) {
5105 text.push_str(chunk);
5106 len += chunk.len();
5107 }
5108 clipboard_selections.push(ClipboardSelection {
5109 len,
5110 is_entire_line,
5111 first_line_indent: buffer.indent_size_for_line(start.row).len,
5112 });
5113 }
5114 }
5115
5116 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5117 }
5118
5119 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5120 self.transact(cx, |this, cx| {
5121 if let Some(item) = cx.read_from_clipboard() {
5122 let clipboard_text = Cow::Borrowed(item.text());
5123 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5124 let old_selections = this.selections.all::<usize>(cx);
5125 let all_selections_were_entire_line =
5126 clipboard_selections.iter().all(|s| s.is_entire_line);
5127 let first_selection_indent_column =
5128 clipboard_selections.first().map(|s| s.first_line_indent);
5129 if clipboard_selections.len() != old_selections.len() {
5130 clipboard_selections.drain(..);
5131 }
5132
5133 this.buffer.update(cx, |buffer, cx| {
5134 let snapshot = buffer.read(cx);
5135 let mut start_offset = 0;
5136 let mut edits = Vec::new();
5137 let mut original_indent_columns = Vec::new();
5138 let line_mode = this.selections.line_mode;
5139 for (ix, selection) in old_selections.iter().enumerate() {
5140 let to_insert;
5141 let entire_line;
5142 let original_indent_column;
5143 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5144 let end_offset = start_offset + clipboard_selection.len;
5145 to_insert = &clipboard_text[start_offset..end_offset];
5146 entire_line = clipboard_selection.is_entire_line;
5147 start_offset = end_offset + 1;
5148 original_indent_column =
5149 Some(clipboard_selection.first_line_indent);
5150 } else {
5151 to_insert = clipboard_text.as_str();
5152 entire_line = all_selections_were_entire_line;
5153 original_indent_column = first_selection_indent_column
5154 }
5155
5156 // If the corresponding selection was empty when this slice of the
5157 // clipboard text was written, then the entire line containing the
5158 // selection was copied. If this selection is also currently empty,
5159 // then paste the line before the current line of the buffer.
5160 let range = if selection.is_empty() && !line_mode && entire_line {
5161 let column = selection.start.to_point(&snapshot).column as usize;
5162 let line_start = selection.start - column;
5163 line_start..line_start
5164 } else {
5165 selection.range()
5166 };
5167
5168 edits.push((range, to_insert));
5169 original_indent_columns.extend(original_indent_column);
5170 }
5171 drop(snapshot);
5172
5173 buffer.edit(
5174 edits,
5175 Some(AutoindentMode::Block {
5176 original_indent_columns,
5177 }),
5178 cx,
5179 );
5180 });
5181
5182 let selections = this.selections.all::<usize>(cx);
5183 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5184 } else {
5185 this.insert(&clipboard_text, cx);
5186 }
5187 }
5188 });
5189 }
5190
5191 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5192 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5193 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5194 self.change_selections(None, cx, |s| {
5195 s.select_anchors(selections.to_vec());
5196 });
5197 }
5198 self.request_autoscroll(Autoscroll::fit(), cx);
5199 self.unmark_text(cx);
5200 self.refresh_copilot_suggestions(true, cx);
5201 cx.emit(Event::Edited);
5202 }
5203 }
5204
5205 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5206 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5207 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5208 {
5209 self.change_selections(None, cx, |s| {
5210 s.select_anchors(selections.to_vec());
5211 });
5212 }
5213 self.request_autoscroll(Autoscroll::fit(), cx);
5214 self.unmark_text(cx);
5215 self.refresh_copilot_suggestions(true, cx);
5216 cx.emit(Event::Edited);
5217 }
5218 }
5219
5220 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5221 self.buffer
5222 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5223 }
5224
5225 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5226 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5227 let line_mode = s.line_mode;
5228 s.move_with(|map, selection| {
5229 let cursor = if selection.is_empty() && !line_mode {
5230 movement::left(map, selection.start)
5231 } else {
5232 selection.start
5233 };
5234 selection.collapse_to(cursor, SelectionGoal::None);
5235 });
5236 })
5237 }
5238
5239 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5240 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5241 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5242 })
5243 }
5244
5245 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5246 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5247 let line_mode = s.line_mode;
5248 s.move_with(|map, selection| {
5249 let cursor = if selection.is_empty() && !line_mode {
5250 movement::right(map, selection.end)
5251 } else {
5252 selection.end
5253 };
5254 selection.collapse_to(cursor, SelectionGoal::None)
5255 });
5256 })
5257 }
5258
5259 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5260 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5261 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5262 })
5263 }
5264
5265 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5266 if self.take_rename(true, cx).is_some() {
5267 return;
5268 }
5269
5270 if matches!(self.mode, EditorMode::SingleLine) {
5271 cx.propagate_action();
5272 return;
5273 }
5274
5275 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5276 let line_mode = s.line_mode;
5277 s.move_with(|map, selection| {
5278 if !selection.is_empty() && !line_mode {
5279 selection.goal = SelectionGoal::None;
5280 }
5281 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
5282 selection.collapse_to(cursor, goal);
5283 });
5284 })
5285 }
5286
5287 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5288 if self.take_rename(true, cx).is_some() {
5289 return;
5290 }
5291
5292 if matches!(self.mode, EditorMode::SingleLine) {
5293 cx.propagate_action();
5294 return;
5295 }
5296
5297 let row_count = if let Some(row_count) = self.visible_line_count() {
5298 row_count as u32 - 1
5299 } else {
5300 return;
5301 };
5302
5303 let autoscroll = if action.center_cursor {
5304 Autoscroll::center()
5305 } else {
5306 Autoscroll::fit()
5307 };
5308
5309 self.change_selections(Some(autoscroll), cx, |s| {
5310 let line_mode = s.line_mode;
5311 s.move_with(|map, selection| {
5312 if !selection.is_empty() && !line_mode {
5313 selection.goal = SelectionGoal::None;
5314 }
5315 let (cursor, goal) =
5316 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
5317 selection.collapse_to(cursor, goal);
5318 });
5319 });
5320 }
5321
5322 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5323 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5324 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5325 })
5326 }
5327
5328 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5329 self.take_rename(true, cx);
5330
5331 if self.mode == EditorMode::SingleLine {
5332 cx.propagate_action();
5333 return;
5334 }
5335
5336 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5337 let line_mode = s.line_mode;
5338 s.move_with(|map, selection| {
5339 if !selection.is_empty() && !line_mode {
5340 selection.goal = SelectionGoal::None;
5341 }
5342 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5343 selection.collapse_to(cursor, goal);
5344 });
5345 });
5346 }
5347
5348 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5349 if self.take_rename(true, cx).is_some() {
5350 return;
5351 }
5352
5353 if self
5354 .context_menu
5355 .as_mut()
5356 .map(|menu| menu.select_last(cx))
5357 .unwrap_or(false)
5358 {
5359 return;
5360 }
5361
5362 if matches!(self.mode, EditorMode::SingleLine) {
5363 cx.propagate_action();
5364 return;
5365 }
5366
5367 let row_count = if let Some(row_count) = self.visible_line_count() {
5368 row_count as u32 - 1
5369 } else {
5370 return;
5371 };
5372
5373 let autoscroll = if action.center_cursor {
5374 Autoscroll::center()
5375 } else {
5376 Autoscroll::fit()
5377 };
5378
5379 self.change_selections(Some(autoscroll), cx, |s| {
5380 let line_mode = s.line_mode;
5381 s.move_with(|map, selection| {
5382 if !selection.is_empty() && !line_mode {
5383 selection.goal = SelectionGoal::None;
5384 }
5385 let (cursor, goal) =
5386 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5387 selection.collapse_to(cursor, goal);
5388 });
5389 });
5390 }
5391
5392 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5393 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5394 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5395 });
5396 }
5397
5398 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5399 if let Some(context_menu) = self.context_menu.as_mut() {
5400 context_menu.select_first(cx);
5401 }
5402 }
5403
5404 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5405 if let Some(context_menu) = self.context_menu.as_mut() {
5406 context_menu.select_prev(cx);
5407 }
5408 }
5409
5410 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5411 if let Some(context_menu) = self.context_menu.as_mut() {
5412 context_menu.select_next(cx);
5413 }
5414 }
5415
5416 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5417 if let Some(context_menu) = self.context_menu.as_mut() {
5418 context_menu.select_last(cx);
5419 }
5420 }
5421
5422 pub fn move_to_previous_word_start(
5423 &mut self,
5424 _: &MoveToPreviousWordStart,
5425 cx: &mut ViewContext<Self>,
5426 ) {
5427 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5428 s.move_cursors_with(|map, head, _| {
5429 (
5430 movement::previous_word_start(map, head),
5431 SelectionGoal::None,
5432 )
5433 });
5434 })
5435 }
5436
5437 pub fn move_to_previous_subword_start(
5438 &mut self,
5439 _: &MoveToPreviousSubwordStart,
5440 cx: &mut ViewContext<Self>,
5441 ) {
5442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5443 s.move_cursors_with(|map, head, _| {
5444 (
5445 movement::previous_subword_start(map, head),
5446 SelectionGoal::None,
5447 )
5448 });
5449 })
5450 }
5451
5452 pub fn select_to_previous_word_start(
5453 &mut self,
5454 _: &SelectToPreviousWordStart,
5455 cx: &mut ViewContext<Self>,
5456 ) {
5457 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5458 s.move_heads_with(|map, head, _| {
5459 (
5460 movement::previous_word_start(map, head),
5461 SelectionGoal::None,
5462 )
5463 });
5464 })
5465 }
5466
5467 pub fn select_to_previous_subword_start(
5468 &mut self,
5469 _: &SelectToPreviousSubwordStart,
5470 cx: &mut ViewContext<Self>,
5471 ) {
5472 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5473 s.move_heads_with(|map, head, _| {
5474 (
5475 movement::previous_subword_start(map, head),
5476 SelectionGoal::None,
5477 )
5478 });
5479 })
5480 }
5481
5482 pub fn delete_to_previous_word_start(
5483 &mut self,
5484 _: &DeleteToPreviousWordStart,
5485 cx: &mut ViewContext<Self>,
5486 ) {
5487 self.transact(cx, |this, cx| {
5488 this.select_autoclose_pair(cx);
5489 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5490 let line_mode = s.line_mode;
5491 s.move_with(|map, selection| {
5492 if selection.is_empty() && !line_mode {
5493 let cursor = movement::previous_word_start(map, selection.head());
5494 selection.set_head(cursor, SelectionGoal::None);
5495 }
5496 });
5497 });
5498 this.insert("", cx);
5499 });
5500 }
5501
5502 pub fn delete_to_previous_subword_start(
5503 &mut self,
5504 _: &DeleteToPreviousSubwordStart,
5505 cx: &mut ViewContext<Self>,
5506 ) {
5507 self.transact(cx, |this, cx| {
5508 this.select_autoclose_pair(cx);
5509 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5510 let line_mode = s.line_mode;
5511 s.move_with(|map, selection| {
5512 if selection.is_empty() && !line_mode {
5513 let cursor = movement::previous_subword_start(map, selection.head());
5514 selection.set_head(cursor, SelectionGoal::None);
5515 }
5516 });
5517 });
5518 this.insert("", cx);
5519 });
5520 }
5521
5522 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5523 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5524 s.move_cursors_with(|map, head, _| {
5525 (movement::next_word_end(map, head), SelectionGoal::None)
5526 });
5527 })
5528 }
5529
5530 pub fn move_to_next_subword_end(
5531 &mut self,
5532 _: &MoveToNextSubwordEnd,
5533 cx: &mut ViewContext<Self>,
5534 ) {
5535 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5536 s.move_cursors_with(|map, head, _| {
5537 (movement::next_subword_end(map, head), SelectionGoal::None)
5538 });
5539 })
5540 }
5541
5542 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5543 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5544 s.move_heads_with(|map, head, _| {
5545 (movement::next_word_end(map, head), SelectionGoal::None)
5546 });
5547 })
5548 }
5549
5550 pub fn select_to_next_subword_end(
5551 &mut self,
5552 _: &SelectToNextSubwordEnd,
5553 cx: &mut ViewContext<Self>,
5554 ) {
5555 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5556 s.move_heads_with(|map, head, _| {
5557 (movement::next_subword_end(map, head), SelectionGoal::None)
5558 });
5559 })
5560 }
5561
5562 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5563 self.transact(cx, |this, cx| {
5564 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5565 let line_mode = s.line_mode;
5566 s.move_with(|map, selection| {
5567 if selection.is_empty() && !line_mode {
5568 let cursor = movement::next_word_end(map, selection.head());
5569 selection.set_head(cursor, SelectionGoal::None);
5570 }
5571 });
5572 });
5573 this.insert("", cx);
5574 });
5575 }
5576
5577 pub fn delete_to_next_subword_end(
5578 &mut self,
5579 _: &DeleteToNextSubwordEnd,
5580 cx: &mut ViewContext<Self>,
5581 ) {
5582 self.transact(cx, |this, cx| {
5583 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5584 s.move_with(|map, selection| {
5585 if selection.is_empty() {
5586 let cursor = movement::next_subword_end(map, selection.head());
5587 selection.set_head(cursor, SelectionGoal::None);
5588 }
5589 });
5590 });
5591 this.insert("", cx);
5592 });
5593 }
5594
5595 pub fn move_to_beginning_of_line(
5596 &mut self,
5597 _: &MoveToBeginningOfLine,
5598 cx: &mut ViewContext<Self>,
5599 ) {
5600 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5601 s.move_cursors_with(|map, head, _| {
5602 (
5603 movement::indented_line_beginning(map, head, true),
5604 SelectionGoal::None,
5605 )
5606 });
5607 })
5608 }
5609
5610 pub fn select_to_beginning_of_line(
5611 &mut self,
5612 action: &SelectToBeginningOfLine,
5613 cx: &mut ViewContext<Self>,
5614 ) {
5615 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5616 s.move_heads_with(|map, head, _| {
5617 (
5618 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5619 SelectionGoal::None,
5620 )
5621 });
5622 });
5623 }
5624
5625 pub fn delete_to_beginning_of_line(
5626 &mut self,
5627 _: &DeleteToBeginningOfLine,
5628 cx: &mut ViewContext<Self>,
5629 ) {
5630 self.transact(cx, |this, cx| {
5631 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5632 s.move_with(|_, selection| {
5633 selection.reversed = true;
5634 });
5635 });
5636
5637 this.select_to_beginning_of_line(
5638 &SelectToBeginningOfLine {
5639 stop_at_soft_wraps: false,
5640 },
5641 cx,
5642 );
5643 this.backspace(&Backspace, cx);
5644 });
5645 }
5646
5647 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5648 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5649 s.move_cursors_with(|map, head, _| {
5650 (movement::line_end(map, head, true), SelectionGoal::None)
5651 });
5652 })
5653 }
5654
5655 pub fn select_to_end_of_line(
5656 &mut self,
5657 action: &SelectToEndOfLine,
5658 cx: &mut ViewContext<Self>,
5659 ) {
5660 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5661 s.move_heads_with(|map, head, _| {
5662 (
5663 movement::line_end(map, head, action.stop_at_soft_wraps),
5664 SelectionGoal::None,
5665 )
5666 });
5667 })
5668 }
5669
5670 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5671 self.transact(cx, |this, cx| {
5672 this.select_to_end_of_line(
5673 &SelectToEndOfLine {
5674 stop_at_soft_wraps: false,
5675 },
5676 cx,
5677 );
5678 this.delete(&Delete, cx);
5679 });
5680 }
5681
5682 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5683 self.transact(cx, |this, cx| {
5684 this.select_to_end_of_line(
5685 &SelectToEndOfLine {
5686 stop_at_soft_wraps: false,
5687 },
5688 cx,
5689 );
5690 this.cut(&Cut, cx);
5691 });
5692 }
5693
5694 pub fn move_to_start_of_paragraph(
5695 &mut self,
5696 _: &MoveToStartOfParagraph,
5697 cx: &mut ViewContext<Self>,
5698 ) {
5699 if matches!(self.mode, EditorMode::SingleLine) {
5700 cx.propagate_action();
5701 return;
5702 }
5703
5704 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5705 s.move_with(|map, selection| {
5706 selection.collapse_to(
5707 movement::start_of_paragraph(map, selection.head(), 1),
5708 SelectionGoal::None,
5709 )
5710 });
5711 })
5712 }
5713
5714 pub fn move_to_end_of_paragraph(
5715 &mut self,
5716 _: &MoveToEndOfParagraph,
5717 cx: &mut ViewContext<Self>,
5718 ) {
5719 if matches!(self.mode, EditorMode::SingleLine) {
5720 cx.propagate_action();
5721 return;
5722 }
5723
5724 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5725 s.move_with(|map, selection| {
5726 selection.collapse_to(
5727 movement::end_of_paragraph(map, selection.head(), 1),
5728 SelectionGoal::None,
5729 )
5730 });
5731 })
5732 }
5733
5734 pub fn select_to_start_of_paragraph(
5735 &mut self,
5736 _: &SelectToStartOfParagraph,
5737 cx: &mut ViewContext<Self>,
5738 ) {
5739 if matches!(self.mode, EditorMode::SingleLine) {
5740 cx.propagate_action();
5741 return;
5742 }
5743
5744 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5745 s.move_heads_with(|map, head, _| {
5746 (
5747 movement::start_of_paragraph(map, head, 1),
5748 SelectionGoal::None,
5749 )
5750 });
5751 })
5752 }
5753
5754 pub fn select_to_end_of_paragraph(
5755 &mut self,
5756 _: &SelectToEndOfParagraph,
5757 cx: &mut ViewContext<Self>,
5758 ) {
5759 if matches!(self.mode, EditorMode::SingleLine) {
5760 cx.propagate_action();
5761 return;
5762 }
5763
5764 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5765 s.move_heads_with(|map, head, _| {
5766 (
5767 movement::end_of_paragraph(map, head, 1),
5768 SelectionGoal::None,
5769 )
5770 });
5771 })
5772 }
5773
5774 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5775 if matches!(self.mode, EditorMode::SingleLine) {
5776 cx.propagate_action();
5777 return;
5778 }
5779
5780 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5781 s.select_ranges(vec![0..0]);
5782 });
5783 }
5784
5785 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5786 let mut selection = self.selections.last::<Point>(cx);
5787 selection.set_head(Point::zero(), SelectionGoal::None);
5788
5789 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5790 s.select(vec![selection]);
5791 });
5792 }
5793
5794 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5795 if matches!(self.mode, EditorMode::SingleLine) {
5796 cx.propagate_action();
5797 return;
5798 }
5799
5800 let cursor = self.buffer.read(cx).read(cx).len();
5801 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5802 s.select_ranges(vec![cursor..cursor])
5803 });
5804 }
5805
5806 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5807 self.nav_history = nav_history;
5808 }
5809
5810 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5811 self.nav_history.as_ref()
5812 }
5813
5814 fn push_to_nav_history(
5815 &mut self,
5816 cursor_anchor: Anchor,
5817 new_position: Option<Point>,
5818 cx: &mut ViewContext<Self>,
5819 ) {
5820 if let Some(nav_history) = self.nav_history.as_mut() {
5821 let buffer = self.buffer.read(cx).read(cx);
5822 let cursor_position = cursor_anchor.to_point(&buffer);
5823 let scroll_state = self.scroll_manager.anchor();
5824 let scroll_top_row = scroll_state.top_row(&buffer);
5825 drop(buffer);
5826
5827 if let Some(new_position) = new_position {
5828 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5829 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5830 return;
5831 }
5832 }
5833
5834 nav_history.push(
5835 Some(NavigationData {
5836 cursor_anchor,
5837 cursor_position,
5838 scroll_anchor: scroll_state,
5839 scroll_top_row,
5840 }),
5841 cx,
5842 );
5843 }
5844 }
5845
5846 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5847 let buffer = self.buffer.read(cx).snapshot(cx);
5848 let mut selection = self.selections.first::<usize>(cx);
5849 selection.set_head(buffer.len(), SelectionGoal::None);
5850 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5851 s.select(vec![selection]);
5852 });
5853 }
5854
5855 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5856 let end = self.buffer.read(cx).read(cx).len();
5857 self.change_selections(None, cx, |s| {
5858 s.select_ranges(vec![0..end]);
5859 });
5860 }
5861
5862 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5863 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5864 let mut selections = self.selections.all::<Point>(cx);
5865 let max_point = display_map.buffer_snapshot.max_point();
5866 for selection in &mut selections {
5867 let rows = selection.spanned_rows(true, &display_map);
5868 selection.start = Point::new(rows.start, 0);
5869 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5870 selection.reversed = false;
5871 }
5872 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5873 s.select(selections);
5874 });
5875 }
5876
5877 pub fn split_selection_into_lines(
5878 &mut self,
5879 _: &SplitSelectionIntoLines,
5880 cx: &mut ViewContext<Self>,
5881 ) {
5882 let mut to_unfold = Vec::new();
5883 let mut new_selection_ranges = Vec::new();
5884 {
5885 let selections = self.selections.all::<Point>(cx);
5886 let buffer = self.buffer.read(cx).read(cx);
5887 for selection in selections {
5888 for row in selection.start.row..selection.end.row {
5889 let cursor = Point::new(row, buffer.line_len(row));
5890 new_selection_ranges.push(cursor..cursor);
5891 }
5892 new_selection_ranges.push(selection.end..selection.end);
5893 to_unfold.push(selection.start..selection.end);
5894 }
5895 }
5896 self.unfold_ranges(to_unfold, true, true, cx);
5897 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5898 s.select_ranges(new_selection_ranges);
5899 });
5900 }
5901
5902 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5903 self.add_selection(true, cx);
5904 }
5905
5906 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5907 self.add_selection(false, cx);
5908 }
5909
5910 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5911 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5912 let mut selections = self.selections.all::<Point>(cx);
5913 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5914 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5915 let range = oldest_selection.display_range(&display_map).sorted();
5916 let columns = cmp::min(range.start.column(), range.end.column())
5917 ..cmp::max(range.start.column(), range.end.column());
5918
5919 selections.clear();
5920 let mut stack = Vec::new();
5921 for row in range.start.row()..=range.end.row() {
5922 if let Some(selection) = self.selections.build_columnar_selection(
5923 &display_map,
5924 row,
5925 &columns,
5926 oldest_selection.reversed,
5927 ) {
5928 stack.push(selection.id);
5929 selections.push(selection);
5930 }
5931 }
5932
5933 if above {
5934 stack.reverse();
5935 }
5936
5937 AddSelectionsState { above, stack }
5938 });
5939
5940 let last_added_selection = *state.stack.last().unwrap();
5941 let mut new_selections = Vec::new();
5942 if above == state.above {
5943 let end_row = if above {
5944 0
5945 } else {
5946 display_map.max_point().row()
5947 };
5948
5949 'outer: for selection in selections {
5950 if selection.id == last_added_selection {
5951 let range = selection.display_range(&display_map).sorted();
5952 debug_assert_eq!(range.start.row(), range.end.row());
5953 let mut row = range.start.row();
5954 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5955 {
5956 start..end
5957 } else {
5958 cmp::min(range.start.column(), range.end.column())
5959 ..cmp::max(range.start.column(), range.end.column())
5960 };
5961
5962 while row != end_row {
5963 if above {
5964 row -= 1;
5965 } else {
5966 row += 1;
5967 }
5968
5969 if let Some(new_selection) = self.selections.build_columnar_selection(
5970 &display_map,
5971 row,
5972 &columns,
5973 selection.reversed,
5974 ) {
5975 state.stack.push(new_selection.id);
5976 if above {
5977 new_selections.push(new_selection);
5978 new_selections.push(selection);
5979 } else {
5980 new_selections.push(selection);
5981 new_selections.push(new_selection);
5982 }
5983
5984 continue 'outer;
5985 }
5986 }
5987 }
5988
5989 new_selections.push(selection);
5990 }
5991 } else {
5992 new_selections = selections;
5993 new_selections.retain(|s| s.id != last_added_selection);
5994 state.stack.pop();
5995 }
5996
5997 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5998 s.select(new_selections);
5999 });
6000 if state.stack.len() > 1 {
6001 self.add_selections_state = Some(state);
6002 }
6003 }
6004
6005 pub fn select_next_match_internal(
6006 &mut self,
6007 display_map: &DisplaySnapshot,
6008 replace_newest: bool,
6009 autoscroll: Option<Autoscroll>,
6010 cx: &mut ViewContext<Self>,
6011 ) -> Result<()> {
6012 fn select_next_match_ranges(
6013 this: &mut Editor,
6014 range: Range<usize>,
6015 replace_newest: bool,
6016 auto_scroll: Option<Autoscroll>,
6017 cx: &mut ViewContext<Editor>,
6018 ) {
6019 this.unfold_ranges([range.clone()], false, true, cx);
6020 this.change_selections(auto_scroll, cx, |s| {
6021 if replace_newest {
6022 s.delete(s.newest_anchor().id);
6023 }
6024 s.insert_range(range.clone());
6025 });
6026 }
6027
6028 let buffer = &display_map.buffer_snapshot;
6029 let mut selections = self.selections.all::<usize>(cx);
6030 if let Some(mut select_next_state) = self.select_next_state.take() {
6031 let query = &select_next_state.query;
6032 if !select_next_state.done {
6033 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6034 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6035 let mut next_selected_range = None;
6036
6037 let bytes_after_last_selection =
6038 buffer.bytes_in_range(last_selection.end..buffer.len());
6039 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6040 let query_matches = query
6041 .stream_find_iter(bytes_after_last_selection)
6042 .map(|result| (last_selection.end, result))
6043 .chain(
6044 query
6045 .stream_find_iter(bytes_before_first_selection)
6046 .map(|result| (0, result)),
6047 );
6048
6049 for (start_offset, query_match) in query_matches {
6050 let query_match = query_match.unwrap(); // can only fail due to I/O
6051 let offset_range =
6052 start_offset + query_match.start()..start_offset + query_match.end();
6053 let display_range = offset_range.start.to_display_point(&display_map)
6054 ..offset_range.end.to_display_point(&display_map);
6055
6056 if !select_next_state.wordwise
6057 || (!movement::is_inside_word(&display_map, display_range.start)
6058 && !movement::is_inside_word(&display_map, display_range.end))
6059 {
6060 if selections
6061 .iter()
6062 .find(|selection| selection.equals(&offset_range))
6063 .is_none()
6064 {
6065 next_selected_range = Some(offset_range);
6066 break;
6067 }
6068 }
6069 }
6070
6071 if let Some(next_selected_range) = next_selected_range {
6072 select_next_match_ranges(
6073 self,
6074 next_selected_range,
6075 replace_newest,
6076 autoscroll,
6077 cx,
6078 );
6079 } else {
6080 select_next_state.done = true;
6081 }
6082 }
6083
6084 self.select_next_state = Some(select_next_state);
6085 } else if selections.len() == 1 {
6086 let selection = selections.last_mut().unwrap();
6087 if selection.start == selection.end {
6088 let word_range = movement::surrounding_word(
6089 &display_map,
6090 selection.start.to_display_point(&display_map),
6091 );
6092 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6093 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6094 selection.goal = SelectionGoal::None;
6095 selection.reversed = false;
6096
6097 let query = buffer
6098 .text_for_range(selection.start..selection.end)
6099 .collect::<String>();
6100
6101 let is_empty = query.is_empty();
6102 let select_state = SelectNextState {
6103 query: AhoCorasick::new(&[query])?,
6104 wordwise: true,
6105 done: is_empty,
6106 };
6107 select_next_match_ranges(
6108 self,
6109 selection.start..selection.end,
6110 replace_newest,
6111 autoscroll,
6112 cx,
6113 );
6114 self.select_next_state = Some(select_state);
6115 } else {
6116 let query = buffer
6117 .text_for_range(selection.start..selection.end)
6118 .collect::<String>();
6119 self.select_next_state = Some(SelectNextState {
6120 query: AhoCorasick::new(&[query])?,
6121 wordwise: false,
6122 done: false,
6123 });
6124 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6125 }
6126 }
6127 Ok(())
6128 }
6129
6130 pub fn select_all_matches(
6131 &mut self,
6132 action: &SelectAllMatches,
6133 cx: &mut ViewContext<Self>,
6134 ) -> Result<()> {
6135 self.push_to_selection_history();
6136 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6137
6138 loop {
6139 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6140
6141 if self
6142 .select_next_state
6143 .as_ref()
6144 .map(|selection_state| selection_state.done)
6145 .unwrap_or(true)
6146 {
6147 break;
6148 }
6149 }
6150
6151 Ok(())
6152 }
6153
6154 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6155 self.push_to_selection_history();
6156 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6157 self.select_next_match_internal(
6158 &display_map,
6159 action.replace_newest,
6160 Some(Autoscroll::newest()),
6161 cx,
6162 )?;
6163 Ok(())
6164 }
6165
6166 pub fn select_previous(
6167 &mut self,
6168 action: &SelectPrevious,
6169 cx: &mut ViewContext<Self>,
6170 ) -> Result<()> {
6171 self.push_to_selection_history();
6172 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6173 let buffer = &display_map.buffer_snapshot;
6174 let mut selections = self.selections.all::<usize>(cx);
6175 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6176 let query = &select_prev_state.query;
6177 if !select_prev_state.done {
6178 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6179 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6180 let mut next_selected_range = None;
6181 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6182 let bytes_before_last_selection =
6183 buffer.reversed_bytes_in_range(0..last_selection.start);
6184 let bytes_after_first_selection =
6185 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6186 let query_matches = query
6187 .stream_find_iter(bytes_before_last_selection)
6188 .map(|result| (last_selection.start, result))
6189 .chain(
6190 query
6191 .stream_find_iter(bytes_after_first_selection)
6192 .map(|result| (buffer.len(), result)),
6193 );
6194 for (end_offset, query_match) in query_matches {
6195 let query_match = query_match.unwrap(); // can only fail due to I/O
6196 let offset_range =
6197 end_offset - query_match.end()..end_offset - query_match.start();
6198 let display_range = offset_range.start.to_display_point(&display_map)
6199 ..offset_range.end.to_display_point(&display_map);
6200
6201 if !select_prev_state.wordwise
6202 || (!movement::is_inside_word(&display_map, display_range.start)
6203 && !movement::is_inside_word(&display_map, display_range.end))
6204 {
6205 next_selected_range = Some(offset_range);
6206 break;
6207 }
6208 }
6209
6210 if let Some(next_selected_range) = next_selected_range {
6211 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6212 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6213 if action.replace_newest {
6214 s.delete(s.newest_anchor().id);
6215 }
6216 s.insert_range(next_selected_range);
6217 });
6218 } else {
6219 select_prev_state.done = true;
6220 }
6221 }
6222
6223 self.select_prev_state = Some(select_prev_state);
6224 } else if selections.len() == 1 {
6225 let selection = selections.last_mut().unwrap();
6226 if selection.start == selection.end {
6227 let word_range = movement::surrounding_word(
6228 &display_map,
6229 selection.start.to_display_point(&display_map),
6230 );
6231 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6232 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6233 selection.goal = SelectionGoal::None;
6234 selection.reversed = false;
6235
6236 let query = buffer
6237 .text_for_range(selection.start..selection.end)
6238 .collect::<String>();
6239 let query = query.chars().rev().collect::<String>();
6240 let select_state = SelectNextState {
6241 query: AhoCorasick::new(&[query])?,
6242 wordwise: true,
6243 done: false,
6244 };
6245 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6246 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6247 s.select(selections);
6248 });
6249 self.select_prev_state = Some(select_state);
6250 } else {
6251 let query = buffer
6252 .text_for_range(selection.start..selection.end)
6253 .collect::<String>();
6254 let query = query.chars().rev().collect::<String>();
6255 self.select_prev_state = Some(SelectNextState {
6256 query: AhoCorasick::new(&[query])?,
6257 wordwise: false,
6258 done: false,
6259 });
6260 self.select_previous(action, cx)?;
6261 }
6262 }
6263 Ok(())
6264 }
6265
6266 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6267 self.transact(cx, |this, cx| {
6268 let mut selections = this.selections.all::<Point>(cx);
6269 let mut edits = Vec::new();
6270 let mut selection_edit_ranges = Vec::new();
6271 let mut last_toggled_row = None;
6272 let snapshot = this.buffer.read(cx).read(cx);
6273 let empty_str: Arc<str> = "".into();
6274 let mut suffixes_inserted = Vec::new();
6275
6276 fn comment_prefix_range(
6277 snapshot: &MultiBufferSnapshot,
6278 row: u32,
6279 comment_prefix: &str,
6280 comment_prefix_whitespace: &str,
6281 ) -> Range<Point> {
6282 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6283
6284 let mut line_bytes = snapshot
6285 .bytes_in_range(start..snapshot.max_point())
6286 .flatten()
6287 .copied();
6288
6289 // If this line currently begins with the line comment prefix, then record
6290 // the range containing the prefix.
6291 if line_bytes
6292 .by_ref()
6293 .take(comment_prefix.len())
6294 .eq(comment_prefix.bytes())
6295 {
6296 // Include any whitespace that matches the comment prefix.
6297 let matching_whitespace_len = line_bytes
6298 .zip(comment_prefix_whitespace.bytes())
6299 .take_while(|(a, b)| a == b)
6300 .count() as u32;
6301 let end = Point::new(
6302 start.row,
6303 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6304 );
6305 start..end
6306 } else {
6307 start..start
6308 }
6309 }
6310
6311 fn comment_suffix_range(
6312 snapshot: &MultiBufferSnapshot,
6313 row: u32,
6314 comment_suffix: &str,
6315 comment_suffix_has_leading_space: bool,
6316 ) -> Range<Point> {
6317 let end = Point::new(row, snapshot.line_len(row));
6318 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6319
6320 let mut line_end_bytes = snapshot
6321 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6322 .flatten()
6323 .copied();
6324
6325 let leading_space_len = if suffix_start_column > 0
6326 && line_end_bytes.next() == Some(b' ')
6327 && comment_suffix_has_leading_space
6328 {
6329 1
6330 } else {
6331 0
6332 };
6333
6334 // If this line currently begins with the line comment prefix, then record
6335 // the range containing the prefix.
6336 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6337 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6338 start..end
6339 } else {
6340 end..end
6341 }
6342 }
6343
6344 // TODO: Handle selections that cross excerpts
6345 for selection in &mut selections {
6346 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6347 let language = if let Some(language) =
6348 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6349 {
6350 language
6351 } else {
6352 continue;
6353 };
6354
6355 selection_edit_ranges.clear();
6356
6357 // If multiple selections contain a given row, avoid processing that
6358 // row more than once.
6359 let mut start_row = selection.start.row;
6360 if last_toggled_row == Some(start_row) {
6361 start_row += 1;
6362 }
6363 let end_row =
6364 if selection.end.row > selection.start.row && selection.end.column == 0 {
6365 selection.end.row - 1
6366 } else {
6367 selection.end.row
6368 };
6369 last_toggled_row = Some(end_row);
6370
6371 if start_row > end_row {
6372 continue;
6373 }
6374
6375 // If the language has line comments, toggle those.
6376 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6377 // Split the comment prefix's trailing whitespace into a separate string,
6378 // as that portion won't be used for detecting if a line is a comment.
6379 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6380 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6381 let mut all_selection_lines_are_comments = true;
6382
6383 for row in start_row..=end_row {
6384 if snapshot.is_line_blank(row) && start_row < end_row {
6385 continue;
6386 }
6387
6388 let prefix_range = comment_prefix_range(
6389 snapshot.deref(),
6390 row,
6391 comment_prefix,
6392 comment_prefix_whitespace,
6393 );
6394 if prefix_range.is_empty() {
6395 all_selection_lines_are_comments = false;
6396 }
6397 selection_edit_ranges.push(prefix_range);
6398 }
6399
6400 if all_selection_lines_are_comments {
6401 edits.extend(
6402 selection_edit_ranges
6403 .iter()
6404 .cloned()
6405 .map(|range| (range, empty_str.clone())),
6406 );
6407 } else {
6408 let min_column = selection_edit_ranges
6409 .iter()
6410 .map(|r| r.start.column)
6411 .min()
6412 .unwrap_or(0);
6413 edits.extend(selection_edit_ranges.iter().map(|range| {
6414 let position = Point::new(range.start.row, min_column);
6415 (position..position, full_comment_prefix.clone())
6416 }));
6417 }
6418 } else if let Some((full_comment_prefix, comment_suffix)) =
6419 language.block_comment_delimiters()
6420 {
6421 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6422 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6423 let prefix_range = comment_prefix_range(
6424 snapshot.deref(),
6425 start_row,
6426 comment_prefix,
6427 comment_prefix_whitespace,
6428 );
6429 let suffix_range = comment_suffix_range(
6430 snapshot.deref(),
6431 end_row,
6432 comment_suffix.trim_start_matches(' '),
6433 comment_suffix.starts_with(' '),
6434 );
6435
6436 if prefix_range.is_empty() || suffix_range.is_empty() {
6437 edits.push((
6438 prefix_range.start..prefix_range.start,
6439 full_comment_prefix.clone(),
6440 ));
6441 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6442 suffixes_inserted.push((end_row, comment_suffix.len()));
6443 } else {
6444 edits.push((prefix_range, empty_str.clone()));
6445 edits.push((suffix_range, empty_str.clone()));
6446 }
6447 } else {
6448 continue;
6449 }
6450 }
6451
6452 drop(snapshot);
6453 this.buffer.update(cx, |buffer, cx| {
6454 buffer.edit(edits, None, cx);
6455 });
6456
6457 // Adjust selections so that they end before any comment suffixes that
6458 // were inserted.
6459 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6460 let mut selections = this.selections.all::<Point>(cx);
6461 let snapshot = this.buffer.read(cx).read(cx);
6462 for selection in &mut selections {
6463 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6464 match row.cmp(&selection.end.row) {
6465 Ordering::Less => {
6466 suffixes_inserted.next();
6467 continue;
6468 }
6469 Ordering::Greater => break,
6470 Ordering::Equal => {
6471 if selection.end.column == snapshot.line_len(row) {
6472 if selection.is_empty() {
6473 selection.start.column -= suffix_len as u32;
6474 }
6475 selection.end.column -= suffix_len as u32;
6476 }
6477 break;
6478 }
6479 }
6480 }
6481 }
6482
6483 drop(snapshot);
6484 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6485
6486 let selections = this.selections.all::<Point>(cx);
6487 let selections_on_single_row = selections.windows(2).all(|selections| {
6488 selections[0].start.row == selections[1].start.row
6489 && selections[0].end.row == selections[1].end.row
6490 && selections[0].start.row == selections[0].end.row
6491 });
6492 let selections_selecting = selections
6493 .iter()
6494 .any(|selection| selection.start != selection.end);
6495 let advance_downwards = action.advance_downwards
6496 && selections_on_single_row
6497 && !selections_selecting
6498 && this.mode != EditorMode::SingleLine;
6499
6500 if advance_downwards {
6501 let snapshot = this.buffer.read(cx).snapshot(cx);
6502
6503 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6504 s.move_cursors_with(|display_snapshot, display_point, _| {
6505 let mut point = display_point.to_point(display_snapshot);
6506 point.row += 1;
6507 point = snapshot.clip_point(point, Bias::Left);
6508 let display_point = point.to_display_point(display_snapshot);
6509 (display_point, SelectionGoal::Column(display_point.column()))
6510 })
6511 });
6512 }
6513 });
6514 }
6515
6516 pub fn select_larger_syntax_node(
6517 &mut self,
6518 _: &SelectLargerSyntaxNode,
6519 cx: &mut ViewContext<Self>,
6520 ) {
6521 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6522 let buffer = self.buffer.read(cx).snapshot(cx);
6523 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6524
6525 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6526 let mut selected_larger_node = false;
6527 let new_selections = old_selections
6528 .iter()
6529 .map(|selection| {
6530 let old_range = selection.start..selection.end;
6531 let mut new_range = old_range.clone();
6532 while let Some(containing_range) =
6533 buffer.range_for_syntax_ancestor(new_range.clone())
6534 {
6535 new_range = containing_range;
6536 if !display_map.intersects_fold(new_range.start)
6537 && !display_map.intersects_fold(new_range.end)
6538 {
6539 break;
6540 }
6541 }
6542
6543 selected_larger_node |= new_range != old_range;
6544 Selection {
6545 id: selection.id,
6546 start: new_range.start,
6547 end: new_range.end,
6548 goal: SelectionGoal::None,
6549 reversed: selection.reversed,
6550 }
6551 })
6552 .collect::<Vec<_>>();
6553
6554 if selected_larger_node {
6555 stack.push(old_selections);
6556 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6557 s.select(new_selections);
6558 });
6559 }
6560 self.select_larger_syntax_node_stack = stack;
6561 }
6562
6563 pub fn select_smaller_syntax_node(
6564 &mut self,
6565 _: &SelectSmallerSyntaxNode,
6566 cx: &mut ViewContext<Self>,
6567 ) {
6568 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6569 if let Some(selections) = stack.pop() {
6570 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6571 s.select(selections.to_vec());
6572 });
6573 }
6574 self.select_larger_syntax_node_stack = stack;
6575 }
6576
6577 pub fn move_to_enclosing_bracket(
6578 &mut self,
6579 _: &MoveToEnclosingBracket,
6580 cx: &mut ViewContext<Self>,
6581 ) {
6582 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6583 s.move_offsets_with(|snapshot, selection| {
6584 let Some(enclosing_bracket_ranges) =
6585 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
6586 else {
6587 return;
6588 };
6589
6590 let mut best_length = usize::MAX;
6591 let mut best_inside = false;
6592 let mut best_in_bracket_range = false;
6593 let mut best_destination = None;
6594 for (open, close) in enclosing_bracket_ranges {
6595 let close = close.to_inclusive();
6596 let length = close.end() - open.start;
6597 let inside = selection.start >= open.end && selection.end <= *close.start();
6598 let in_bracket_range = open.to_inclusive().contains(&selection.head())
6599 || close.contains(&selection.head());
6600
6601 // If best is next to a bracket and current isn't, skip
6602 if !in_bracket_range && best_in_bracket_range {
6603 continue;
6604 }
6605
6606 // Prefer smaller lengths unless best is inside and current isn't
6607 if length > best_length && (best_inside || !inside) {
6608 continue;
6609 }
6610
6611 best_length = length;
6612 best_inside = inside;
6613 best_in_bracket_range = in_bracket_range;
6614 best_destination = Some(
6615 if close.contains(&selection.start) && close.contains(&selection.end) {
6616 if inside {
6617 open.end
6618 } else {
6619 open.start
6620 }
6621 } else {
6622 if inside {
6623 *close.start()
6624 } else {
6625 *close.end()
6626 }
6627 },
6628 );
6629 }
6630
6631 if let Some(destination) = best_destination {
6632 selection.collapse_to(destination, SelectionGoal::None);
6633 }
6634 })
6635 });
6636 }
6637
6638 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6639 self.end_selection(cx);
6640 self.selection_history.mode = SelectionHistoryMode::Undoing;
6641 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6642 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6643 self.select_next_state = entry.select_next_state;
6644 self.select_prev_state = entry.select_prev_state;
6645 self.add_selections_state = entry.add_selections_state;
6646 self.request_autoscroll(Autoscroll::newest(), cx);
6647 }
6648 self.selection_history.mode = SelectionHistoryMode::Normal;
6649 }
6650
6651 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6652 self.end_selection(cx);
6653 self.selection_history.mode = SelectionHistoryMode::Redoing;
6654 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6655 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6656 self.select_next_state = entry.select_next_state;
6657 self.select_prev_state = entry.select_prev_state;
6658 self.add_selections_state = entry.add_selections_state;
6659 self.request_autoscroll(Autoscroll::newest(), cx);
6660 }
6661 self.selection_history.mode = SelectionHistoryMode::Normal;
6662 }
6663
6664 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6665 self.go_to_diagnostic_impl(Direction::Next, cx)
6666 }
6667
6668 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6669 self.go_to_diagnostic_impl(Direction::Prev, cx)
6670 }
6671
6672 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6673 let buffer = self.buffer.read(cx).snapshot(cx);
6674 let selection = self.selections.newest::<usize>(cx);
6675
6676 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6677 if direction == Direction::Next {
6678 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6679 let (group_id, jump_to) = popover.activation_info();
6680 if self.activate_diagnostics(group_id, cx) {
6681 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6682 let mut new_selection = s.newest_anchor().clone();
6683 new_selection.collapse_to(jump_to, SelectionGoal::None);
6684 s.select_anchors(vec![new_selection.clone()]);
6685 });
6686 }
6687 return;
6688 }
6689 }
6690
6691 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6692 active_diagnostics
6693 .primary_range
6694 .to_offset(&buffer)
6695 .to_inclusive()
6696 });
6697 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6698 if active_primary_range.contains(&selection.head()) {
6699 *active_primary_range.end()
6700 } else {
6701 selection.head()
6702 }
6703 } else {
6704 selection.head()
6705 };
6706
6707 loop {
6708 let mut diagnostics = if direction == Direction::Prev {
6709 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6710 } else {
6711 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6712 };
6713 let group = diagnostics.find_map(|entry| {
6714 if entry.diagnostic.is_primary
6715 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6716 && !entry.range.is_empty()
6717 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6718 {
6719 Some((entry.range, entry.diagnostic.group_id))
6720 } else {
6721 None
6722 }
6723 });
6724
6725 if let Some((primary_range, group_id)) = group {
6726 if self.activate_diagnostics(group_id, cx) {
6727 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6728 s.select(vec![Selection {
6729 id: selection.id,
6730 start: primary_range.start,
6731 end: primary_range.start,
6732 reversed: false,
6733 goal: SelectionGoal::None,
6734 }]);
6735 });
6736 }
6737 break;
6738 } else {
6739 // Cycle around to the start of the buffer, potentially moving back to the start of
6740 // the currently active diagnostic.
6741 active_primary_range.take();
6742 if direction == Direction::Prev {
6743 if search_start == buffer.len() {
6744 break;
6745 } else {
6746 search_start = buffer.len();
6747 }
6748 } else if search_start == 0 {
6749 break;
6750 } else {
6751 search_start = 0;
6752 }
6753 }
6754 }
6755 }
6756
6757 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6758 let snapshot = self
6759 .display_map
6760 .update(cx, |display_map, cx| display_map.snapshot(cx));
6761 let selection = self.selections.newest::<Point>(cx);
6762
6763 if !self.seek_in_direction(
6764 &snapshot,
6765 selection.head(),
6766 false,
6767 snapshot
6768 .buffer_snapshot
6769 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6770 cx,
6771 ) {
6772 let wrapped_point = Point::zero();
6773 self.seek_in_direction(
6774 &snapshot,
6775 wrapped_point,
6776 true,
6777 snapshot
6778 .buffer_snapshot
6779 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6780 cx,
6781 );
6782 }
6783 }
6784
6785 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6786 let snapshot = self
6787 .display_map
6788 .update(cx, |display_map, cx| display_map.snapshot(cx));
6789 let selection = self.selections.newest::<Point>(cx);
6790
6791 if !self.seek_in_direction(
6792 &snapshot,
6793 selection.head(),
6794 false,
6795 snapshot
6796 .buffer_snapshot
6797 .git_diff_hunks_in_range_rev(0..selection.head().row),
6798 cx,
6799 ) {
6800 let wrapped_point = snapshot.buffer_snapshot.max_point();
6801 self.seek_in_direction(
6802 &snapshot,
6803 wrapped_point,
6804 true,
6805 snapshot
6806 .buffer_snapshot
6807 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6808 cx,
6809 );
6810 }
6811 }
6812
6813 fn seek_in_direction(
6814 &mut self,
6815 snapshot: &DisplaySnapshot,
6816 initial_point: Point,
6817 is_wrapped: bool,
6818 hunks: impl Iterator<Item = DiffHunk<u32>>,
6819 cx: &mut ViewContext<Editor>,
6820 ) -> bool {
6821 let display_point = initial_point.to_display_point(snapshot);
6822 let mut hunks = hunks
6823 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6824 .skip_while(|hunk| {
6825 if is_wrapped {
6826 false
6827 } else {
6828 hunk.contains_display_row(display_point.row())
6829 }
6830 })
6831 .dedup();
6832
6833 if let Some(hunk) = hunks.next() {
6834 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6835 let row = hunk.start_display_row();
6836 let point = DisplayPoint::new(row, 0);
6837 s.select_display_ranges([point..point]);
6838 });
6839
6840 true
6841 } else {
6842 false
6843 }
6844 }
6845
6846 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6847 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6848 }
6849
6850 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6851 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6852 }
6853
6854 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6855 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6856 }
6857
6858 pub fn go_to_type_definition_split(
6859 &mut self,
6860 _: &GoToTypeDefinitionSplit,
6861 cx: &mut ViewContext<Self>,
6862 ) {
6863 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6864 }
6865
6866 fn go_to_definition_of_kind(
6867 &mut self,
6868 kind: GotoDefinitionKind,
6869 split: bool,
6870 cx: &mut ViewContext<Self>,
6871 ) {
6872 let Some(workspace) = self.workspace(cx) else {
6873 return;
6874 };
6875 let buffer = self.buffer.read(cx);
6876 let head = self.selections.newest::<usize>(cx).head();
6877 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6878 text_anchor
6879 } else {
6880 return;
6881 };
6882
6883 let project = workspace.read(cx).project().clone();
6884 let definitions = project.update(cx, |project, cx| match kind {
6885 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6886 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6887 });
6888
6889 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6890 let definitions = definitions.await?;
6891 editor.update(&mut cx, |editor, cx| {
6892 editor.navigate_to_definitions(
6893 definitions
6894 .into_iter()
6895 .map(GoToDefinitionLink::Text)
6896 .collect(),
6897 split,
6898 cx,
6899 );
6900 })?;
6901 Ok::<(), anyhow::Error>(())
6902 })
6903 .detach_and_log_err(cx);
6904 }
6905
6906 pub fn navigate_to_definitions(
6907 &mut self,
6908 mut definitions: Vec<GoToDefinitionLink>,
6909 split: bool,
6910 cx: &mut ViewContext<Editor>,
6911 ) {
6912 let Some(workspace) = self.workspace(cx) else {
6913 return;
6914 };
6915 let pane = workspace.read(cx).active_pane().clone();
6916 // If there is one definition, just open it directly
6917 if definitions.len() == 1 {
6918 let definition = definitions.pop().unwrap();
6919 let target_task = match definition {
6920 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
6921 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6922 self.compute_target_location(lsp_location, server_id, cx)
6923 }
6924 };
6925 cx.spawn(|editor, mut cx| async move {
6926 let target = target_task.await.context("target resolution task")?;
6927 if let Some(target) = target {
6928 editor.update(&mut cx, |editor, cx| {
6929 let range = target.range.to_offset(target.buffer.read(cx));
6930 let range = editor.range_for_match(&range);
6931 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
6932 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6933 s.select_ranges([range]);
6934 });
6935 } else {
6936 cx.window_context().defer(move |cx| {
6937 let target_editor: ViewHandle<Self> =
6938 workspace.update(cx, |workspace, cx| {
6939 if split {
6940 workspace.split_project_item(target.buffer.clone(), cx)
6941 } else {
6942 workspace.open_project_item(target.buffer.clone(), cx)
6943 }
6944 });
6945 target_editor.update(cx, |target_editor, cx| {
6946 // When selecting a definition in a different buffer, disable the nav history
6947 // to avoid creating a history entry at the previous cursor location.
6948 pane.update(cx, |pane, _| pane.disable_history());
6949 target_editor.change_selections(
6950 Some(Autoscroll::fit()),
6951 cx,
6952 |s| {
6953 s.select_ranges([range]);
6954 },
6955 );
6956 pane.update(cx, |pane, _| pane.enable_history());
6957 });
6958 });
6959 }
6960 })
6961 } else {
6962 Ok(())
6963 }
6964 })
6965 .detach_and_log_err(cx);
6966 } else if !definitions.is_empty() {
6967 let replica_id = self.replica_id(cx);
6968 cx.spawn(|editor, mut cx| async move {
6969 let (title, location_tasks) = editor
6970 .update(&mut cx, |editor, cx| {
6971 let title = definitions
6972 .iter()
6973 .find_map(|definition| match definition {
6974 GoToDefinitionLink::Text(link) => {
6975 link.origin.as_ref().map(|origin| {
6976 let buffer = origin.buffer.read(cx);
6977 format!(
6978 "Definitions for {}",
6979 buffer
6980 .text_for_range(origin.range.clone())
6981 .collect::<String>()
6982 )
6983 })
6984 }
6985 GoToDefinitionLink::InlayHint(_, _) => None,
6986 })
6987 .unwrap_or("Definitions".to_string());
6988 let location_tasks = definitions
6989 .into_iter()
6990 .map(|definition| match definition {
6991 GoToDefinitionLink::Text(link) => {
6992 Task::Ready(Some(Ok(Some(link.target))))
6993 }
6994 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6995 editor.compute_target_location(lsp_location, server_id, cx)
6996 }
6997 })
6998 .collect::<Vec<_>>();
6999 (title, location_tasks)
7000 })
7001 .context("location tasks preparation")?;
7002
7003 let locations = futures::future::join_all(location_tasks)
7004 .await
7005 .into_iter()
7006 .filter_map(|location| location.transpose())
7007 .collect::<Result<_>>()
7008 .context("location tasks")?;
7009 workspace.update(&mut cx, |workspace, cx| {
7010 Self::open_locations_in_multibuffer(
7011 workspace, locations, replica_id, title, split, cx,
7012 )
7013 });
7014
7015 anyhow::Ok(())
7016 })
7017 .detach_and_log_err(cx);
7018 }
7019 }
7020
7021 fn compute_target_location(
7022 &self,
7023 lsp_location: lsp::Location,
7024 server_id: LanguageServerId,
7025 cx: &mut ViewContext<Editor>,
7026 ) -> Task<anyhow::Result<Option<Location>>> {
7027 let Some(project) = self.project.clone() else {
7028 return Task::Ready(Some(Ok(None)));
7029 };
7030
7031 cx.spawn(move |editor, mut cx| async move {
7032 let location_task = editor.update(&mut cx, |editor, cx| {
7033 project.update(cx, |project, cx| {
7034 let language_server_name =
7035 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7036 project
7037 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7038 .map(|(_, lsp_adapter)| {
7039 LanguageServerName(Arc::from(lsp_adapter.name()))
7040 })
7041 });
7042 language_server_name.map(|language_server_name| {
7043 project.open_local_buffer_via_lsp(
7044 lsp_location.uri.clone(),
7045 server_id,
7046 language_server_name,
7047 cx,
7048 )
7049 })
7050 })
7051 })?;
7052 let location = match location_task {
7053 Some(task) => Some({
7054 let target_buffer_handle = task.await.context("open local buffer")?;
7055 let range = {
7056 target_buffer_handle.update(&mut cx, |target_buffer, _| {
7057 let target_start = target_buffer.clip_point_utf16(
7058 point_from_lsp(lsp_location.range.start),
7059 Bias::Left,
7060 );
7061 let target_end = target_buffer.clip_point_utf16(
7062 point_from_lsp(lsp_location.range.end),
7063 Bias::Left,
7064 );
7065 target_buffer.anchor_after(target_start)
7066 ..target_buffer.anchor_before(target_end)
7067 })
7068 };
7069 Location {
7070 buffer: target_buffer_handle,
7071 range,
7072 }
7073 }),
7074 None => None,
7075 };
7076 Ok(location)
7077 })
7078 }
7079
7080 pub fn find_all_references(
7081 workspace: &mut Workspace,
7082 _: &FindAllReferences,
7083 cx: &mut ViewContext<Workspace>,
7084 ) -> Option<Task<Result<()>>> {
7085 let active_item = workspace.active_item(cx)?;
7086 let editor_handle = active_item.act_as::<Self>(cx)?;
7087
7088 let editor = editor_handle.read(cx);
7089 let buffer = editor.buffer.read(cx);
7090 let head = editor.selections.newest::<usize>(cx).head();
7091 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7092 let replica_id = editor.replica_id(cx);
7093
7094 let project = workspace.project().clone();
7095 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7096 Some(cx.spawn_labeled(
7097 "Finding All References...",
7098 |workspace, mut cx| async move {
7099 let locations = references.await?;
7100 if locations.is_empty() {
7101 return Ok(());
7102 }
7103
7104 workspace.update(&mut cx, |workspace, cx| {
7105 let title = locations
7106 .first()
7107 .as_ref()
7108 .map(|location| {
7109 let buffer = location.buffer.read(cx);
7110 format!(
7111 "References to `{}`",
7112 buffer
7113 .text_for_range(location.range.clone())
7114 .collect::<String>()
7115 )
7116 })
7117 .unwrap();
7118 Self::open_locations_in_multibuffer(
7119 workspace, locations, replica_id, title, false, cx,
7120 );
7121 })?;
7122
7123 Ok(())
7124 },
7125 ))
7126 }
7127
7128 /// Opens a multibuffer with the given project locations in it
7129 pub fn open_locations_in_multibuffer(
7130 workspace: &mut Workspace,
7131 mut locations: Vec<Location>,
7132 replica_id: ReplicaId,
7133 title: String,
7134 split: bool,
7135 cx: &mut ViewContext<Workspace>,
7136 ) {
7137 // If there are multiple definitions, open them in a multibuffer
7138 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7139 let mut locations = locations.into_iter().peekable();
7140 let mut ranges_to_highlight = Vec::new();
7141
7142 let excerpt_buffer = cx.add_model(|cx| {
7143 let mut multibuffer = MultiBuffer::new(replica_id);
7144 while let Some(location) = locations.next() {
7145 let buffer = location.buffer.read(cx);
7146 let mut ranges_for_buffer = Vec::new();
7147 let range = location.range.to_offset(buffer);
7148 ranges_for_buffer.push(range.clone());
7149
7150 while let Some(next_location) = locations.peek() {
7151 if next_location.buffer == location.buffer {
7152 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7153 locations.next();
7154 } else {
7155 break;
7156 }
7157 }
7158
7159 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7160 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7161 location.buffer.clone(),
7162 ranges_for_buffer,
7163 1,
7164 cx,
7165 ))
7166 }
7167
7168 multibuffer.with_title(title)
7169 });
7170
7171 let editor = cx.add_view(|cx| {
7172 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7173 });
7174 editor.update(cx, |editor, cx| {
7175 editor.highlight_background::<Self>(
7176 ranges_to_highlight,
7177 |theme| theme.editor.highlighted_line_background,
7178 cx,
7179 );
7180 });
7181 if split {
7182 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7183 } else {
7184 workspace.add_item(Box::new(editor), cx);
7185 }
7186 }
7187
7188 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7189 use language::ToOffset as _;
7190
7191 let project = self.project.clone()?;
7192 let selection = self.selections.newest_anchor().clone();
7193 let (cursor_buffer, cursor_buffer_position) = self
7194 .buffer
7195 .read(cx)
7196 .text_anchor_for_position(selection.head(), cx)?;
7197 let (tail_buffer, _) = self
7198 .buffer
7199 .read(cx)
7200 .text_anchor_for_position(selection.tail(), cx)?;
7201 if tail_buffer != cursor_buffer {
7202 return None;
7203 }
7204
7205 let snapshot = cursor_buffer.read(cx).snapshot();
7206 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7207 let prepare_rename = project.update(cx, |project, cx| {
7208 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7209 });
7210
7211 Some(cx.spawn(|this, mut cx| async move {
7212 let rename_range = if let Some(range) = prepare_rename.await? {
7213 Some(range)
7214 } else {
7215 this.update(&mut cx, |this, cx| {
7216 let buffer = this.buffer.read(cx).snapshot(cx);
7217 let mut buffer_highlights = this
7218 .document_highlights_for_position(selection.head(), &buffer)
7219 .filter(|highlight| {
7220 highlight.start.excerpt_id() == selection.head().excerpt_id()
7221 && highlight.end.excerpt_id() == selection.head().excerpt_id()
7222 });
7223 buffer_highlights
7224 .next()
7225 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7226 })?
7227 };
7228 if let Some(rename_range) = rename_range {
7229 let rename_buffer_range = rename_range.to_offset(&snapshot);
7230 let cursor_offset_in_rename_range =
7231 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7232
7233 this.update(&mut cx, |this, cx| {
7234 this.take_rename(false, cx);
7235 let style = this.style(cx);
7236 let buffer = this.buffer.read(cx).read(cx);
7237 let cursor_offset = selection.head().to_offset(&buffer);
7238 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7239 let rename_end = rename_start + rename_buffer_range.len();
7240 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7241 let mut old_highlight_id = None;
7242 let old_name: Arc<str> = buffer
7243 .chunks(rename_start..rename_end, true)
7244 .map(|chunk| {
7245 if old_highlight_id.is_none() {
7246 old_highlight_id = chunk.syntax_highlight_id;
7247 }
7248 chunk.text
7249 })
7250 .collect::<String>()
7251 .into();
7252
7253 drop(buffer);
7254
7255 // Position the selection in the rename editor so that it matches the current selection.
7256 this.show_local_selections = false;
7257 let rename_editor = cx.add_view(|cx| {
7258 let mut editor = Editor::single_line(None, cx);
7259 if let Some(old_highlight_id) = old_highlight_id {
7260 editor.override_text_style =
7261 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7262 }
7263 editor.buffer.update(cx, |buffer, cx| {
7264 buffer.edit([(0..0, old_name.clone())], None, cx)
7265 });
7266 editor.select_all(&SelectAll, cx);
7267 editor
7268 });
7269
7270 let ranges = this
7271 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7272 .into_iter()
7273 .flat_map(|(_, ranges)| ranges.into_iter())
7274 .chain(
7275 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7276 .into_iter()
7277 .flat_map(|(_, ranges)| ranges.into_iter()),
7278 )
7279 .collect();
7280
7281 this.highlight_text::<Rename>(
7282 ranges,
7283 HighlightStyle {
7284 fade_out: Some(style.rename_fade),
7285 ..Default::default()
7286 },
7287 cx,
7288 );
7289 cx.focus(&rename_editor);
7290 let block_id = this.insert_blocks(
7291 [BlockProperties {
7292 style: BlockStyle::Flex,
7293 position: range.start.clone(),
7294 height: 1,
7295 render: Arc::new({
7296 let editor = rename_editor.clone();
7297 move |cx: &mut BlockContext| {
7298 ChildView::new(&editor, cx)
7299 .contained()
7300 .with_padding_left(cx.anchor_x)
7301 .into_any()
7302 }
7303 }),
7304 disposition: BlockDisposition::Below,
7305 }],
7306 Some(Autoscroll::fit()),
7307 cx,
7308 )[0];
7309 this.pending_rename = Some(RenameState {
7310 range,
7311 old_name,
7312 editor: rename_editor,
7313 block_id,
7314 });
7315 })?;
7316 }
7317
7318 Ok(())
7319 }))
7320 }
7321
7322 pub fn confirm_rename(
7323 workspace: &mut Workspace,
7324 _: &ConfirmRename,
7325 cx: &mut ViewContext<Workspace>,
7326 ) -> Option<Task<Result<()>>> {
7327 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7328
7329 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7330 let rename = editor.take_rename(false, cx)?;
7331 let buffer = editor.buffer.read(cx);
7332 let (start_buffer, start) =
7333 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7334 let (end_buffer, end) =
7335 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7336 if start_buffer == end_buffer {
7337 let new_name = rename.editor.read(cx).text(cx);
7338 Some((start_buffer, start..end, rename.old_name, new_name))
7339 } else {
7340 None
7341 }
7342 })?;
7343
7344 let rename = workspace.project().clone().update(cx, |project, cx| {
7345 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7346 });
7347
7348 let editor = editor.downgrade();
7349 Some(cx.spawn(|workspace, mut cx| async move {
7350 let project_transaction = rename.await?;
7351 Self::open_project_transaction(
7352 &editor,
7353 workspace,
7354 project_transaction,
7355 format!("Rename: {} → {}", old_name, new_name),
7356 cx.clone(),
7357 )
7358 .await?;
7359
7360 editor.update(&mut cx, |editor, cx| {
7361 editor.refresh_document_highlights(cx);
7362 })?;
7363 Ok(())
7364 }))
7365 }
7366
7367 fn take_rename(
7368 &mut self,
7369 moving_cursor: bool,
7370 cx: &mut ViewContext<Self>,
7371 ) -> Option<RenameState> {
7372 let rename = self.pending_rename.take()?;
7373 self.remove_blocks(
7374 [rename.block_id].into_iter().collect(),
7375 Some(Autoscroll::fit()),
7376 cx,
7377 );
7378 self.clear_highlights::<Rename>(cx);
7379 self.show_local_selections = true;
7380
7381 if moving_cursor {
7382 let rename_editor = rename.editor.read(cx);
7383 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7384
7385 // Update the selection to match the position of the selection inside
7386 // the rename editor.
7387 let snapshot = self.buffer.read(cx).read(cx);
7388 let rename_range = rename.range.to_offset(&snapshot);
7389 let cursor_in_editor = snapshot
7390 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7391 .min(rename_range.end);
7392 drop(snapshot);
7393
7394 self.change_selections(None, cx, |s| {
7395 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7396 });
7397 } else {
7398 self.refresh_document_highlights(cx);
7399 }
7400
7401 Some(rename)
7402 }
7403
7404 #[cfg(any(test, feature = "test-support"))]
7405 pub fn pending_rename(&self) -> Option<&RenameState> {
7406 self.pending_rename.as_ref()
7407 }
7408
7409 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7410 let project = match &self.project {
7411 Some(project) => project.clone(),
7412 None => return None,
7413 };
7414
7415 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7416 }
7417
7418 fn perform_format(
7419 &mut self,
7420 project: ModelHandle<Project>,
7421 trigger: FormatTrigger,
7422 cx: &mut ViewContext<Self>,
7423 ) -> Task<Result<()>> {
7424 let buffer = self.buffer().clone();
7425 let buffers = buffer.read(cx).all_buffers();
7426
7427 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7428 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7429
7430 cx.spawn(|_, mut cx| async move {
7431 let transaction = futures::select_biased! {
7432 _ = timeout => {
7433 log::warn!("timed out waiting for formatting");
7434 None
7435 }
7436 transaction = format.log_err().fuse() => transaction,
7437 };
7438
7439 buffer.update(&mut cx, |buffer, cx| {
7440 if let Some(transaction) = transaction {
7441 if !buffer.is_singleton() {
7442 buffer.push_transaction(&transaction.0, cx);
7443 }
7444 }
7445
7446 cx.notify();
7447 });
7448
7449 Ok(())
7450 })
7451 }
7452
7453 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7454 if let Some(project) = self.project.clone() {
7455 self.buffer.update(cx, |multi_buffer, cx| {
7456 project.update(cx, |project, cx| {
7457 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7458 });
7459 })
7460 }
7461 }
7462
7463 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7464 cx.show_character_palette();
7465 }
7466
7467 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7468 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7469 let buffer = self.buffer.read(cx).snapshot(cx);
7470 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7471 let is_valid = buffer
7472 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7473 .any(|entry| {
7474 entry.diagnostic.is_primary
7475 && !entry.range.is_empty()
7476 && entry.range.start == primary_range_start
7477 && entry.diagnostic.message == active_diagnostics.primary_message
7478 });
7479
7480 if is_valid != active_diagnostics.is_valid {
7481 active_diagnostics.is_valid = is_valid;
7482 let mut new_styles = HashMap::default();
7483 for (block_id, diagnostic) in &active_diagnostics.blocks {
7484 new_styles.insert(
7485 *block_id,
7486 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7487 );
7488 }
7489 self.display_map
7490 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7491 }
7492 }
7493 }
7494
7495 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7496 self.dismiss_diagnostics(cx);
7497 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7498 let buffer = self.buffer.read(cx).snapshot(cx);
7499
7500 let mut primary_range = None;
7501 let mut primary_message = None;
7502 let mut group_end = Point::zero();
7503 let diagnostic_group = buffer
7504 .diagnostic_group::<Point>(group_id)
7505 .map(|entry| {
7506 if entry.range.end > group_end {
7507 group_end = entry.range.end;
7508 }
7509 if entry.diagnostic.is_primary {
7510 primary_range = Some(entry.range.clone());
7511 primary_message = Some(entry.diagnostic.message.clone());
7512 }
7513 entry
7514 })
7515 .collect::<Vec<_>>();
7516 let primary_range = primary_range?;
7517 let primary_message = primary_message?;
7518 let primary_range =
7519 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7520
7521 let blocks = display_map
7522 .insert_blocks(
7523 diagnostic_group.iter().map(|entry| {
7524 let diagnostic = entry.diagnostic.clone();
7525 let message_height = diagnostic.message.lines().count() as u8;
7526 BlockProperties {
7527 style: BlockStyle::Fixed,
7528 position: buffer.anchor_after(entry.range.start),
7529 height: message_height,
7530 render: diagnostic_block_renderer(diagnostic, true),
7531 disposition: BlockDisposition::Below,
7532 }
7533 }),
7534 cx,
7535 )
7536 .into_iter()
7537 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7538 .collect();
7539
7540 Some(ActiveDiagnosticGroup {
7541 primary_range,
7542 primary_message,
7543 blocks,
7544 is_valid: true,
7545 })
7546 });
7547 self.active_diagnostics.is_some()
7548 }
7549
7550 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7551 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7552 self.display_map.update(cx, |display_map, cx| {
7553 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7554 });
7555 cx.notify();
7556 }
7557 }
7558
7559 pub fn set_selections_from_remote(
7560 &mut self,
7561 selections: Vec<Selection<Anchor>>,
7562 pending_selection: Option<Selection<Anchor>>,
7563 cx: &mut ViewContext<Self>,
7564 ) {
7565 let old_cursor_position = self.selections.newest_anchor().head();
7566 self.selections.change_with(cx, |s| {
7567 s.select_anchors(selections);
7568 if let Some(pending_selection) = pending_selection {
7569 s.set_pending(pending_selection, SelectMode::Character);
7570 } else {
7571 s.clear_pending();
7572 }
7573 });
7574 self.selections_did_change(false, &old_cursor_position, cx);
7575 }
7576
7577 fn push_to_selection_history(&mut self) {
7578 self.selection_history.push(SelectionHistoryEntry {
7579 selections: self.selections.disjoint_anchors(),
7580 select_next_state: self.select_next_state.clone(),
7581 select_prev_state: self.select_prev_state.clone(),
7582 add_selections_state: self.add_selections_state.clone(),
7583 });
7584 }
7585
7586 pub fn transact(
7587 &mut self,
7588 cx: &mut ViewContext<Self>,
7589 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7590 ) -> Option<TransactionId> {
7591 self.start_transaction_at(Instant::now(), cx);
7592 update(self, cx);
7593 self.end_transaction_at(Instant::now(), cx)
7594 }
7595
7596 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7597 self.end_selection(cx);
7598 if let Some(tx_id) = self
7599 .buffer
7600 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7601 {
7602 self.selection_history
7603 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7604 }
7605 }
7606
7607 fn end_transaction_at(
7608 &mut self,
7609 now: Instant,
7610 cx: &mut ViewContext<Self>,
7611 ) -> Option<TransactionId> {
7612 if let Some(tx_id) = self
7613 .buffer
7614 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7615 {
7616 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7617 *end_selections = Some(self.selections.disjoint_anchors());
7618 } else {
7619 error!("unexpectedly ended a transaction that wasn't started by this editor");
7620 }
7621
7622 cx.emit(Event::Edited);
7623 Some(tx_id)
7624 } else {
7625 None
7626 }
7627 }
7628
7629 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7630 let mut fold_ranges = Vec::new();
7631
7632 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7633
7634 let selections = self.selections.all_adjusted(cx);
7635 for selection in selections {
7636 let range = selection.range().sorted();
7637 let buffer_start_row = range.start.row;
7638
7639 for row in (0..=range.end.row).rev() {
7640 let fold_range = display_map.foldable_range(row);
7641
7642 if let Some(fold_range) = fold_range {
7643 if fold_range.end.row >= buffer_start_row {
7644 fold_ranges.push(fold_range);
7645 if row <= range.start.row {
7646 break;
7647 }
7648 }
7649 }
7650 }
7651 }
7652
7653 self.fold_ranges(fold_ranges, true, cx);
7654 }
7655
7656 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7657 let buffer_row = fold_at.buffer_row;
7658 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7659
7660 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7661 let autoscroll = self
7662 .selections
7663 .all::<Point>(cx)
7664 .iter()
7665 .any(|selection| fold_range.overlaps(&selection.range()));
7666
7667 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7668 }
7669 }
7670
7671 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7672 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7673 let buffer = &display_map.buffer_snapshot;
7674 let selections = self.selections.all::<Point>(cx);
7675 let ranges = selections
7676 .iter()
7677 .map(|s| {
7678 let range = s.display_range(&display_map).sorted();
7679 let mut start = range.start.to_point(&display_map);
7680 let mut end = range.end.to_point(&display_map);
7681 start.column = 0;
7682 end.column = buffer.line_len(end.row);
7683 start..end
7684 })
7685 .collect::<Vec<_>>();
7686
7687 self.unfold_ranges(ranges, true, true, cx);
7688 }
7689
7690 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7691 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7692
7693 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7694 ..Point::new(
7695 unfold_at.buffer_row,
7696 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7697 );
7698
7699 let autoscroll = self
7700 .selections
7701 .all::<Point>(cx)
7702 .iter()
7703 .any(|selection| selection.range().overlaps(&intersection_range));
7704
7705 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7706 }
7707
7708 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7709 let selections = self.selections.all::<Point>(cx);
7710 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7711 let line_mode = self.selections.line_mode;
7712 let ranges = selections.into_iter().map(|s| {
7713 if line_mode {
7714 let start = Point::new(s.start.row, 0);
7715 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
7716 start..end
7717 } else {
7718 s.start..s.end
7719 }
7720 });
7721 self.fold_ranges(ranges, true, cx);
7722 }
7723
7724 pub fn fold_ranges<T: ToOffset + Clone>(
7725 &mut self,
7726 ranges: impl IntoIterator<Item = Range<T>>,
7727 auto_scroll: bool,
7728 cx: &mut ViewContext<Self>,
7729 ) {
7730 let mut ranges = ranges.into_iter().peekable();
7731 if ranges.peek().is_some() {
7732 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7733
7734 if auto_scroll {
7735 self.request_autoscroll(Autoscroll::fit(), cx);
7736 }
7737
7738 cx.notify();
7739 }
7740 }
7741
7742 pub fn unfold_ranges<T: ToOffset + Clone>(
7743 &mut self,
7744 ranges: impl IntoIterator<Item = Range<T>>,
7745 inclusive: bool,
7746 auto_scroll: bool,
7747 cx: &mut ViewContext<Self>,
7748 ) {
7749 let mut ranges = ranges.into_iter().peekable();
7750 if ranges.peek().is_some() {
7751 self.display_map
7752 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7753 if auto_scroll {
7754 self.request_autoscroll(Autoscroll::fit(), cx);
7755 }
7756
7757 cx.notify();
7758 }
7759 }
7760
7761 pub fn gutter_hover(
7762 &mut self,
7763 GutterHover { hovered }: &GutterHover,
7764 cx: &mut ViewContext<Self>,
7765 ) {
7766 self.gutter_hovered = *hovered;
7767 cx.notify();
7768 }
7769
7770 pub fn insert_blocks(
7771 &mut self,
7772 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7773 autoscroll: Option<Autoscroll>,
7774 cx: &mut ViewContext<Self>,
7775 ) -> Vec<BlockId> {
7776 let blocks = self
7777 .display_map
7778 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7779 if let Some(autoscroll) = autoscroll {
7780 self.request_autoscroll(autoscroll, cx);
7781 }
7782 blocks
7783 }
7784
7785 pub fn replace_blocks(
7786 &mut self,
7787 blocks: HashMap<BlockId, RenderBlock>,
7788 autoscroll: Option<Autoscroll>,
7789 cx: &mut ViewContext<Self>,
7790 ) {
7791 self.display_map
7792 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7793 if let Some(autoscroll) = autoscroll {
7794 self.request_autoscroll(autoscroll, cx);
7795 }
7796 }
7797
7798 pub fn remove_blocks(
7799 &mut self,
7800 block_ids: HashSet<BlockId>,
7801 autoscroll: Option<Autoscroll>,
7802 cx: &mut ViewContext<Self>,
7803 ) {
7804 self.display_map.update(cx, |display_map, cx| {
7805 display_map.remove_blocks(block_ids, cx)
7806 });
7807 if let Some(autoscroll) = autoscroll {
7808 self.request_autoscroll(autoscroll, cx);
7809 }
7810 }
7811
7812 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7813 self.display_map
7814 .update(cx, |map, cx| map.snapshot(cx))
7815 .longest_row()
7816 }
7817
7818 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7819 self.display_map
7820 .update(cx, |map, cx| map.snapshot(cx))
7821 .max_point()
7822 }
7823
7824 pub fn text(&self, cx: &AppContext) -> String {
7825 self.buffer.read(cx).read(cx).text()
7826 }
7827
7828 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7829 self.transact(cx, |this, cx| {
7830 this.buffer
7831 .read(cx)
7832 .as_singleton()
7833 .expect("you can only call set_text on editors for singleton buffers")
7834 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7835 });
7836 }
7837
7838 pub fn display_text(&self, cx: &mut AppContext) -> String {
7839 self.display_map
7840 .update(cx, |map, cx| map.snapshot(cx))
7841 .text()
7842 }
7843
7844 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7845 let mut wrap_guides = smallvec::smallvec![];
7846
7847 if self.show_wrap_guides == Some(false) {
7848 return wrap_guides;
7849 }
7850
7851 let settings = self.buffer.read(cx).settings_at(0, cx);
7852 if settings.show_wrap_guides {
7853 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7854 wrap_guides.push((soft_wrap as usize, true));
7855 }
7856 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7857 }
7858
7859 wrap_guides
7860 }
7861
7862 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7863 let settings = self.buffer.read(cx).settings_at(0, cx);
7864 let mode = self
7865 .soft_wrap_mode_override
7866 .unwrap_or_else(|| settings.soft_wrap);
7867 match mode {
7868 language_settings::SoftWrap::None => SoftWrap::None,
7869 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7870 language_settings::SoftWrap::PreferredLineLength => {
7871 SoftWrap::Column(settings.preferred_line_length)
7872 }
7873 }
7874 }
7875
7876 pub fn set_soft_wrap_mode(
7877 &mut self,
7878 mode: language_settings::SoftWrap,
7879 cx: &mut ViewContext<Self>,
7880 ) {
7881 self.soft_wrap_mode_override = Some(mode);
7882 cx.notify();
7883 }
7884
7885 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7886 self.display_map
7887 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7888 }
7889
7890 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7891 if self.soft_wrap_mode_override.is_some() {
7892 self.soft_wrap_mode_override.take();
7893 } else {
7894 let soft_wrap = match self.soft_wrap_mode(cx) {
7895 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7896 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7897 };
7898 self.soft_wrap_mode_override = Some(soft_wrap);
7899 }
7900 cx.notify();
7901 }
7902
7903 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7904 self.show_gutter = show_gutter;
7905 cx.notify();
7906 }
7907
7908 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7909 self.show_wrap_guides = Some(show_gutter);
7910 cx.notify();
7911 }
7912
7913 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7914 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7915 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7916 cx.reveal_path(&file.abs_path(cx));
7917 }
7918 }
7919 }
7920
7921 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7922 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7923 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7924 if let Some(path) = file.abs_path(cx).to_str() {
7925 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7926 }
7927 }
7928 }
7929 }
7930
7931 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7932 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7933 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7934 if let Some(path) = file.path().to_str() {
7935 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7936 }
7937 }
7938 }
7939 }
7940
7941 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7942 self.highlighted_rows = rows;
7943 }
7944
7945 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7946 self.highlighted_rows.clone()
7947 }
7948
7949 pub fn highlight_background<T: 'static>(
7950 &mut self,
7951 ranges: Vec<Range<Anchor>>,
7952 color_fetcher: fn(&Theme) -> Color,
7953 cx: &mut ViewContext<Self>,
7954 ) {
7955 self.background_highlights
7956 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7957 cx.notify();
7958 }
7959
7960 pub fn highlight_inlay_background<T: 'static>(
7961 &mut self,
7962 ranges: Vec<InlayHighlight>,
7963 color_fetcher: fn(&Theme) -> Color,
7964 cx: &mut ViewContext<Self>,
7965 ) {
7966 // TODO: no actual highlights happen for inlays currently, find a way to do that
7967 self.inlay_background_highlights
7968 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
7969 cx.notify();
7970 }
7971
7972 pub fn clear_background_highlights<T: 'static>(
7973 &mut self,
7974 cx: &mut ViewContext<Self>,
7975 ) -> Option<BackgroundHighlight> {
7976 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
7977 let inlay_highlights = self
7978 .inlay_background_highlights
7979 .remove(&Some(TypeId::of::<T>()));
7980 if text_highlights.is_some() || inlay_highlights.is_some() {
7981 cx.notify();
7982 }
7983 text_highlights
7984 }
7985
7986 #[cfg(feature = "test-support")]
7987 pub fn all_text_background_highlights(
7988 &mut self,
7989 cx: &mut ViewContext<Self>,
7990 ) -> Vec<(Range<DisplayPoint>, Color)> {
7991 let snapshot = self.snapshot(cx);
7992 let buffer = &snapshot.buffer_snapshot;
7993 let start = buffer.anchor_before(0);
7994 let end = buffer.anchor_after(buffer.len());
7995 let theme = theme::current(cx);
7996 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7997 }
7998
7999 fn document_highlights_for_position<'a>(
8000 &'a self,
8001 position: Anchor,
8002 buffer: &'a MultiBufferSnapshot,
8003 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8004 let read_highlights = self
8005 .background_highlights
8006 .get(&TypeId::of::<DocumentHighlightRead>())
8007 .map(|h| &h.1);
8008 let write_highlights = self
8009 .background_highlights
8010 .get(&TypeId::of::<DocumentHighlightWrite>())
8011 .map(|h| &h.1);
8012 let left_position = position.bias_left(buffer);
8013 let right_position = position.bias_right(buffer);
8014 read_highlights
8015 .into_iter()
8016 .chain(write_highlights)
8017 .flat_map(move |ranges| {
8018 let start_ix = match ranges.binary_search_by(|probe| {
8019 let cmp = probe.end.cmp(&left_position, buffer);
8020 if cmp.is_ge() {
8021 Ordering::Greater
8022 } else {
8023 Ordering::Less
8024 }
8025 }) {
8026 Ok(i) | Err(i) => i,
8027 };
8028
8029 let right_position = right_position.clone();
8030 ranges[start_ix..]
8031 .iter()
8032 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8033 })
8034 }
8035
8036 pub fn background_highlights_in_range(
8037 &self,
8038 search_range: Range<Anchor>,
8039 display_snapshot: &DisplaySnapshot,
8040 theme: &Theme,
8041 ) -> Vec<(Range<DisplayPoint>, Color)> {
8042 let mut results = Vec::new();
8043 for (color_fetcher, ranges) in self.background_highlights.values() {
8044 let color = color_fetcher(theme);
8045 let start_ix = match ranges.binary_search_by(|probe| {
8046 let cmp = probe
8047 .end
8048 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8049 if cmp.is_gt() {
8050 Ordering::Greater
8051 } else {
8052 Ordering::Less
8053 }
8054 }) {
8055 Ok(i) | Err(i) => i,
8056 };
8057 for range in &ranges[start_ix..] {
8058 if range
8059 .start
8060 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8061 .is_ge()
8062 {
8063 break;
8064 }
8065
8066 let start = range.start.to_display_point(&display_snapshot);
8067 let end = range.end.to_display_point(&display_snapshot);
8068 results.push((start..end, color))
8069 }
8070 }
8071 results
8072 }
8073
8074 pub fn background_highlight_row_ranges<T: 'static>(
8075 &self,
8076 search_range: Range<Anchor>,
8077 display_snapshot: &DisplaySnapshot,
8078 count: usize,
8079 ) -> Vec<RangeInclusive<DisplayPoint>> {
8080 let mut results = Vec::new();
8081 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8082 return vec![];
8083 };
8084
8085 let start_ix = match ranges.binary_search_by(|probe| {
8086 let cmp = probe
8087 .end
8088 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8089 if cmp.is_gt() {
8090 Ordering::Greater
8091 } else {
8092 Ordering::Less
8093 }
8094 }) {
8095 Ok(i) | Err(i) => i,
8096 };
8097 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8098 if let (Some(start_display), Some(end_display)) = (start, end) {
8099 results.push(
8100 start_display.to_display_point(display_snapshot)
8101 ..=end_display.to_display_point(display_snapshot),
8102 );
8103 }
8104 };
8105 let mut start_row: Option<Point> = None;
8106 let mut end_row: Option<Point> = None;
8107 if ranges.len() > count {
8108 return Vec::new();
8109 }
8110 for range in &ranges[start_ix..] {
8111 if range
8112 .start
8113 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8114 .is_ge()
8115 {
8116 break;
8117 }
8118 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8119 if let Some(current_row) = &end_row {
8120 if end.row == current_row.row {
8121 continue;
8122 }
8123 }
8124 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8125 if start_row.is_none() {
8126 assert_eq!(end_row, None);
8127 start_row = Some(start);
8128 end_row = Some(end);
8129 continue;
8130 }
8131 if let Some(current_end) = end_row.as_mut() {
8132 if start.row > current_end.row + 1 {
8133 push_region(start_row, end_row);
8134 start_row = Some(start);
8135 end_row = Some(end);
8136 } else {
8137 // Merge two hunks.
8138 *current_end = end;
8139 }
8140 } else {
8141 unreachable!();
8142 }
8143 }
8144 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8145 push_region(start_row, end_row);
8146 results
8147 }
8148
8149 pub fn highlight_text<T: 'static>(
8150 &mut self,
8151 ranges: Vec<Range<Anchor>>,
8152 style: HighlightStyle,
8153 cx: &mut ViewContext<Self>,
8154 ) {
8155 self.display_map.update(cx, |map, _| {
8156 map.highlight_text(TypeId::of::<T>(), ranges, style)
8157 });
8158 cx.notify();
8159 }
8160
8161 pub fn highlight_inlays<T: 'static>(
8162 &mut self,
8163 highlights: Vec<InlayHighlight>,
8164 style: HighlightStyle,
8165 cx: &mut ViewContext<Self>,
8166 ) {
8167 self.display_map.update(cx, |map, _| {
8168 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8169 });
8170 cx.notify();
8171 }
8172
8173 pub fn text_highlights<'a, T: 'static>(
8174 &'a self,
8175 cx: &'a AppContext,
8176 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8177 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8178 }
8179
8180 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8181 let cleared = self
8182 .display_map
8183 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8184 if cleared {
8185 cx.notify();
8186 }
8187 }
8188
8189 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8190 self.blink_manager.read(cx).visible() && self.focused
8191 }
8192
8193 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8194 cx.notify();
8195 }
8196
8197 fn on_buffer_event(
8198 &mut self,
8199 multibuffer: ModelHandle<MultiBuffer>,
8200 event: &multi_buffer::Event,
8201 cx: &mut ViewContext<Self>,
8202 ) {
8203 match event {
8204 multi_buffer::Event::Edited {
8205 sigleton_buffer_edited,
8206 } => {
8207 self.refresh_active_diagnostics(cx);
8208 self.refresh_code_actions(cx);
8209 if self.has_active_copilot_suggestion(cx) {
8210 self.update_visible_copilot_suggestion(cx);
8211 }
8212 cx.emit(Event::BufferEdited);
8213
8214 if *sigleton_buffer_edited {
8215 if let Some(project) = &self.project {
8216 let project = project.read(cx);
8217 let languages_affected = multibuffer
8218 .read(cx)
8219 .all_buffers()
8220 .into_iter()
8221 .filter_map(|buffer| {
8222 let buffer = buffer.read(cx);
8223 let language = buffer.language()?;
8224 if project.is_local()
8225 && project.language_servers_for_buffer(buffer, cx).count() == 0
8226 {
8227 None
8228 } else {
8229 Some(language)
8230 }
8231 })
8232 .cloned()
8233 .collect::<HashSet<_>>();
8234 if !languages_affected.is_empty() {
8235 self.refresh_inlay_hints(
8236 InlayHintRefreshReason::BufferEdited(languages_affected),
8237 cx,
8238 );
8239 }
8240 }
8241 }
8242 }
8243 multi_buffer::Event::ExcerptsAdded {
8244 buffer,
8245 predecessor,
8246 excerpts,
8247 } => {
8248 cx.emit(Event::ExcerptsAdded {
8249 buffer: buffer.clone(),
8250 predecessor: *predecessor,
8251 excerpts: excerpts.clone(),
8252 });
8253 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8254 }
8255 multi_buffer::Event::ExcerptsRemoved { ids } => {
8256 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8257 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8258 }
8259 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8260 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8261 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8262 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8263 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8264 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8265 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8266 multi_buffer::Event::DiagnosticsUpdated => {
8267 self.refresh_active_diagnostics(cx);
8268 }
8269 _ => {}
8270 };
8271 }
8272
8273 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8274 cx.notify();
8275 }
8276
8277 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8278 self.refresh_copilot_suggestions(true, cx);
8279 self.refresh_inlay_hints(
8280 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8281 self.selections.newest_anchor().head(),
8282 &self.buffer.read(cx).snapshot(cx),
8283 cx,
8284 )),
8285 cx,
8286 );
8287 }
8288
8289 pub fn set_searchable(&mut self, searchable: bool) {
8290 self.searchable = searchable;
8291 }
8292
8293 pub fn searchable(&self) -> bool {
8294 self.searchable
8295 }
8296
8297 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8298 let active_item = workspace.active_item(cx);
8299 let editor_handle = if let Some(editor) = active_item
8300 .as_ref()
8301 .and_then(|item| item.act_as::<Self>(cx))
8302 {
8303 editor
8304 } else {
8305 cx.propagate_action();
8306 return;
8307 };
8308
8309 let editor = editor_handle.read(cx);
8310 let buffer = editor.buffer.read(cx);
8311 if buffer.is_singleton() {
8312 cx.propagate_action();
8313 return;
8314 }
8315
8316 let mut new_selections_by_buffer = HashMap::default();
8317 for selection in editor.selections.all::<usize>(cx) {
8318 for (buffer, mut range, _) in
8319 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8320 {
8321 if selection.reversed {
8322 mem::swap(&mut range.start, &mut range.end);
8323 }
8324 new_selections_by_buffer
8325 .entry(buffer)
8326 .or_insert(Vec::new())
8327 .push(range)
8328 }
8329 }
8330
8331 editor_handle.update(cx, |editor, cx| {
8332 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8333 });
8334 let pane = workspace.active_pane().clone();
8335 pane.update(cx, |pane, _| pane.disable_history());
8336
8337 // We defer the pane interaction because we ourselves are a workspace item
8338 // and activating a new item causes the pane to call a method on us reentrantly,
8339 // which panics if we're on the stack.
8340 cx.defer(move |workspace, cx| {
8341 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8342 let editor = workspace.open_project_item::<Self>(buffer, cx);
8343 editor.update(cx, |editor, cx| {
8344 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8345 s.select_ranges(ranges);
8346 });
8347 });
8348 }
8349
8350 pane.update(cx, |pane, _| pane.enable_history());
8351 });
8352 }
8353
8354 fn jump(
8355 workspace: &mut Workspace,
8356 path: ProjectPath,
8357 position: Point,
8358 anchor: language::Anchor,
8359 cx: &mut ViewContext<Workspace>,
8360 ) {
8361 let editor = workspace.open_path(path, None, true, cx);
8362 cx.spawn(|_, mut cx| async move {
8363 let editor = editor
8364 .await?
8365 .downcast::<Editor>()
8366 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8367 .downgrade();
8368 editor.update(&mut cx, |editor, cx| {
8369 let buffer = editor
8370 .buffer()
8371 .read(cx)
8372 .as_singleton()
8373 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8374 let buffer = buffer.read(cx);
8375 let cursor = if buffer.can_resolve(&anchor) {
8376 language::ToPoint::to_point(&anchor, buffer)
8377 } else {
8378 buffer.clip_point(position, Bias::Left)
8379 };
8380
8381 let nav_history = editor.nav_history.take();
8382 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8383 s.select_ranges([cursor..cursor]);
8384 });
8385 editor.nav_history = nav_history;
8386
8387 anyhow::Ok(())
8388 })??;
8389
8390 anyhow::Ok(())
8391 })
8392 .detach_and_log_err(cx);
8393 }
8394
8395 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8396 let snapshot = self.buffer.read(cx).read(cx);
8397 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8398 Some(
8399 ranges
8400 .iter()
8401 .map(move |range| {
8402 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8403 })
8404 .collect(),
8405 )
8406 }
8407
8408 fn selection_replacement_ranges(
8409 &self,
8410 range: Range<OffsetUtf16>,
8411 cx: &AppContext,
8412 ) -> Vec<Range<OffsetUtf16>> {
8413 let selections = self.selections.all::<OffsetUtf16>(cx);
8414 let newest_selection = selections
8415 .iter()
8416 .max_by_key(|selection| selection.id)
8417 .unwrap();
8418 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8419 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8420 let snapshot = self.buffer.read(cx).read(cx);
8421 selections
8422 .into_iter()
8423 .map(|mut selection| {
8424 selection.start.0 =
8425 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8426 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8427 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8428 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8429 })
8430 .collect()
8431 }
8432
8433 fn report_copilot_event(
8434 &self,
8435 suggestion_id: Option<String>,
8436 suggestion_accepted: bool,
8437 cx: &AppContext,
8438 ) {
8439 let Some(project) = &self.project else { return };
8440
8441 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8442 let file_extension = self
8443 .buffer
8444 .read(cx)
8445 .as_singleton()
8446 .and_then(|b| b.read(cx).file())
8447 .and_then(|file| Path::new(file.file_name(cx)).extension())
8448 .and_then(|e| e.to_str())
8449 .map(|a| a.to_string());
8450
8451 let telemetry = project.read(cx).client().telemetry().clone();
8452 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8453
8454 let event = ClickhouseEvent::Copilot {
8455 suggestion_id,
8456 suggestion_accepted,
8457 file_extension,
8458 };
8459 telemetry.report_clickhouse_event(event, telemetry_settings);
8460 }
8461
8462 fn report_editor_event(
8463 &self,
8464 operation: &'static str,
8465 file_extension: Option<String>,
8466 cx: &AppContext,
8467 ) {
8468 let Some(project) = &self.project else { return };
8469
8470 // If None, we are in a file without an extension
8471 let file = self
8472 .buffer
8473 .read(cx)
8474 .as_singleton()
8475 .and_then(|b| b.read(cx).file());
8476 let file_extension = file_extension.or(file
8477 .as_ref()
8478 .and_then(|file| Path::new(file.file_name(cx)).extension())
8479 .and_then(|e| e.to_str())
8480 .map(|a| a.to_string()));
8481
8482 let vim_mode = cx
8483 .global::<SettingsStore>()
8484 .raw_user_settings()
8485 .get("vim_mode")
8486 == Some(&serde_json::Value::Bool(true));
8487 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8488 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8489 let copilot_enabled_for_language = self
8490 .buffer
8491 .read(cx)
8492 .settings_at(0, cx)
8493 .show_copilot_suggestions;
8494
8495 let telemetry = project.read(cx).client().telemetry().clone();
8496 let event = ClickhouseEvent::Editor {
8497 file_extension,
8498 vim_mode,
8499 operation,
8500 copilot_enabled,
8501 copilot_enabled_for_language,
8502 };
8503 telemetry.report_clickhouse_event(event, telemetry_settings)
8504 }
8505
8506 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8507 /// with each line being an array of {text, highlight} objects.
8508 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8509 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8510 return;
8511 };
8512
8513 #[derive(Serialize)]
8514 struct Chunk<'a> {
8515 text: String,
8516 highlight: Option<&'a str>,
8517 }
8518
8519 let snapshot = buffer.read(cx).snapshot();
8520 let range = self
8521 .selected_text_range(cx)
8522 .and_then(|selected_range| {
8523 if selected_range.is_empty() {
8524 None
8525 } else {
8526 Some(selected_range)
8527 }
8528 })
8529 .unwrap_or_else(|| 0..snapshot.len());
8530
8531 let chunks = snapshot.chunks(range, true);
8532 let mut lines = Vec::new();
8533 let mut line: VecDeque<Chunk> = VecDeque::new();
8534
8535 let theme = &theme::current(cx).editor.syntax;
8536
8537 for chunk in chunks {
8538 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8539 let mut chunk_lines = chunk.text.split("\n").peekable();
8540 while let Some(text) = chunk_lines.next() {
8541 let mut merged_with_last_token = false;
8542 if let Some(last_token) = line.back_mut() {
8543 if last_token.highlight == highlight {
8544 last_token.text.push_str(text);
8545 merged_with_last_token = true;
8546 }
8547 }
8548
8549 if !merged_with_last_token {
8550 line.push_back(Chunk {
8551 text: text.into(),
8552 highlight,
8553 });
8554 }
8555
8556 if chunk_lines.peek().is_some() {
8557 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8558 line.pop_front();
8559 }
8560 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8561 line.pop_back();
8562 }
8563
8564 lines.push(mem::take(&mut line));
8565 }
8566 }
8567 }
8568
8569 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
8570 return;
8571 };
8572 cx.write_to_clipboard(ClipboardItem::new(lines));
8573 }
8574
8575 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8576 &self.inlay_hint_cache
8577 }
8578
8579 pub fn replay_insert_event(
8580 &mut self,
8581 text: &str,
8582 relative_utf16_range: Option<Range<isize>>,
8583 cx: &mut ViewContext<Self>,
8584 ) {
8585 if !self.input_enabled {
8586 cx.emit(Event::InputIgnored { text: text.into() });
8587 return;
8588 }
8589 if let Some(relative_utf16_range) = relative_utf16_range {
8590 let selections = self.selections.all::<OffsetUtf16>(cx);
8591 self.change_selections(None, cx, |s| {
8592 let new_ranges = selections.into_iter().map(|range| {
8593 let start = OffsetUtf16(
8594 range
8595 .head()
8596 .0
8597 .saturating_add_signed(relative_utf16_range.start),
8598 );
8599 let end = OffsetUtf16(
8600 range
8601 .head()
8602 .0
8603 .saturating_add_signed(relative_utf16_range.end),
8604 );
8605 start..end
8606 });
8607 s.select_ranges(new_ranges);
8608 });
8609 }
8610
8611 self.handle_input(text, cx);
8612 }
8613
8614 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
8615 let Some(project) = self.project.as_ref() else {
8616 return false;
8617 };
8618 let project = project.read(cx);
8619
8620 let mut supports = false;
8621 self.buffer().read(cx).for_each_buffer(|buffer| {
8622 if !supports {
8623 supports = project
8624 .language_servers_for_buffer(buffer.read(cx), cx)
8625 .any(
8626 |(_, server)| match server.capabilities().inlay_hint_provider {
8627 Some(lsp::OneOf::Left(enabled)) => enabled,
8628 Some(lsp::OneOf::Right(_)) => true,
8629 None => false,
8630 },
8631 )
8632 }
8633 });
8634 supports
8635 }
8636}
8637
8638pub trait CollaborationHub {
8639 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
8640 fn user_participant_indices<'a>(
8641 &self,
8642 cx: &'a AppContext,
8643 ) -> &'a HashMap<u64, ParticipantIndex>;
8644}
8645
8646impl CollaborationHub for ModelHandle<Project> {
8647 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
8648 self.read(cx).collaborators()
8649 }
8650
8651 fn user_participant_indices<'a>(
8652 &self,
8653 cx: &'a AppContext,
8654 ) -> &'a HashMap<u64, ParticipantIndex> {
8655 self.read(cx).user_store().read(cx).participant_indices()
8656 }
8657}
8658
8659fn inlay_hint_settings(
8660 location: Anchor,
8661 snapshot: &MultiBufferSnapshot,
8662 cx: &mut ViewContext<'_, '_, Editor>,
8663) -> InlayHintSettings {
8664 let file = snapshot.file_at(location);
8665 let language = snapshot.language_at(location);
8666 let settings = all_language_settings(file, cx);
8667 settings
8668 .language(language.map(|l| l.name()).as_deref())
8669 .inlay_hints
8670}
8671
8672fn consume_contiguous_rows(
8673 contiguous_row_selections: &mut Vec<Selection<Point>>,
8674 selection: &Selection<Point>,
8675 display_map: &DisplaySnapshot,
8676 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8677) -> (u32, u32) {
8678 contiguous_row_selections.push(selection.clone());
8679 let start_row = selection.start.row;
8680 let mut end_row = ending_row(selection, display_map);
8681
8682 while let Some(next_selection) = selections.peek() {
8683 if next_selection.start.row <= end_row {
8684 end_row = ending_row(next_selection, display_map);
8685 contiguous_row_selections.push(selections.next().unwrap().clone());
8686 } else {
8687 break;
8688 }
8689 }
8690 (start_row, end_row)
8691}
8692
8693fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8694 if next_selection.end.column > 0 || next_selection.is_empty() {
8695 display_map.next_line_boundary(next_selection.end).0.row + 1
8696 } else {
8697 next_selection.end.row
8698 }
8699}
8700
8701impl EditorSnapshot {
8702 pub fn remote_selections_in_range<'a>(
8703 &'a self,
8704 range: &'a Range<Anchor>,
8705 collaboration_hub: &dyn CollaborationHub,
8706 cx: &'a AppContext,
8707 ) -> impl 'a + Iterator<Item = RemoteSelection> {
8708 let participant_indices = collaboration_hub.user_participant_indices(cx);
8709 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
8710 let collaborators_by_replica_id = collaborators_by_peer_id
8711 .iter()
8712 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
8713 .collect::<HashMap<_, _>>();
8714 self.buffer_snapshot
8715 .remote_selections_in_range(range)
8716 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
8717 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
8718 let participant_index = participant_indices.get(&collaborator.user_id).copied();
8719 Some(RemoteSelection {
8720 replica_id,
8721 selection,
8722 cursor_shape,
8723 line_mode,
8724 participant_index,
8725 peer_id: collaborator.peer_id,
8726 })
8727 })
8728 }
8729
8730 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8731 self.display_snapshot.buffer_snapshot.language_at(position)
8732 }
8733
8734 pub fn is_focused(&self) -> bool {
8735 self.is_focused
8736 }
8737
8738 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8739 self.placeholder_text.as_ref()
8740 }
8741
8742 pub fn scroll_position(&self) -> Vector2F {
8743 self.scroll_anchor.scroll_position(&self.display_snapshot)
8744 }
8745}
8746
8747impl Deref for EditorSnapshot {
8748 type Target = DisplaySnapshot;
8749
8750 fn deref(&self) -> &Self::Target {
8751 &self.display_snapshot
8752 }
8753}
8754
8755#[derive(Clone, Debug, PartialEq, Eq)]
8756pub enum Event {
8757 InputIgnored {
8758 text: Arc<str>,
8759 },
8760 InputHandled {
8761 utf16_range_to_replace: Option<Range<isize>>,
8762 text: Arc<str>,
8763 },
8764 ExcerptsAdded {
8765 buffer: ModelHandle<Buffer>,
8766 predecessor: ExcerptId,
8767 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8768 },
8769 ExcerptsRemoved {
8770 ids: Vec<ExcerptId>,
8771 },
8772 BufferEdited,
8773 Edited,
8774 Reparsed,
8775 Focused,
8776 Blurred,
8777 DirtyChanged,
8778 Saved,
8779 TitleChanged,
8780 DiffBaseChanged,
8781 SelectionsChanged {
8782 local: bool,
8783 },
8784 ScrollPositionChanged {
8785 local: bool,
8786 autoscroll: bool,
8787 },
8788 Closed,
8789}
8790
8791pub struct EditorFocused(pub ViewHandle<Editor>);
8792pub struct EditorBlurred(pub ViewHandle<Editor>);
8793pub struct EditorReleased(pub WeakViewHandle<Editor>);
8794
8795impl Entity for Editor {
8796 type Event = Event;
8797
8798 fn release(&mut self, cx: &mut AppContext) {
8799 cx.emit_global(EditorReleased(self.handle.clone()));
8800 }
8801}
8802
8803impl View for Editor {
8804 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8805 let style = self.style(cx);
8806 let font_changed = self.display_map.update(cx, |map, cx| {
8807 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8808 map.set_font(style.text.font_id, style.text.font_size, cx)
8809 });
8810
8811 if font_changed {
8812 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8813 hide_hover(editor, cx);
8814 hide_link_definition(editor, cx);
8815 });
8816 }
8817
8818 Stack::new()
8819 .with_child(EditorElement::new(style.clone()))
8820 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8821 .into_any()
8822 }
8823
8824 fn ui_name() -> &'static str {
8825 "Editor"
8826 }
8827
8828 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
8829 if cx.is_self_focused() {
8830 let focused_event = EditorFocused(cx.handle());
8831 cx.emit(Event::Focused);
8832 cx.emit_global(focused_event);
8833 }
8834 if let Some(rename) = self.pending_rename.as_ref() {
8835 cx.focus(&rename.editor);
8836 } else if cx.is_self_focused() || !focused.is::<Editor>() {
8837 if !self.focused {
8838 self.blink_manager.update(cx, BlinkManager::enable);
8839 }
8840 self.focused = true;
8841 self.buffer.update(cx, |buffer, cx| {
8842 buffer.finalize_last_transaction(cx);
8843 if self.leader_peer_id.is_none() {
8844 buffer.set_active_selections(
8845 &self.selections.disjoint_anchors(),
8846 self.selections.line_mode,
8847 self.cursor_shape,
8848 cx,
8849 );
8850 }
8851 });
8852 }
8853 }
8854
8855 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8856 let blurred_event = EditorBlurred(cx.handle());
8857 cx.emit_global(blurred_event);
8858 self.focused = false;
8859 self.blink_manager.update(cx, BlinkManager::disable);
8860 self.buffer
8861 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8862 self.hide_context_menu(cx);
8863 hide_hover(self, cx);
8864 cx.emit(Event::Blurred);
8865 cx.notify();
8866 }
8867
8868 fn modifiers_changed(
8869 &mut self,
8870 event: &gpui::platform::ModifiersChangedEvent,
8871 cx: &mut ViewContext<Self>,
8872 ) -> bool {
8873 let pending_selection = self.has_pending_selection();
8874
8875 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
8876 if event.cmd && !pending_selection {
8877 let point = point.clone();
8878 let snapshot = self.snapshot(cx);
8879 let kind = point.definition_kind(event.shift);
8880
8881 show_link_definition(kind, self, point, snapshot, cx);
8882 return false;
8883 }
8884 }
8885
8886 {
8887 if self.link_go_to_definition_state.symbol_range.is_some()
8888 || !self.link_go_to_definition_state.definitions.is_empty()
8889 {
8890 self.link_go_to_definition_state.symbol_range.take();
8891 self.link_go_to_definition_state.definitions.clear();
8892 cx.notify();
8893 }
8894
8895 self.link_go_to_definition_state.task = None;
8896
8897 self.clear_highlights::<LinkGoToDefinitionState>(cx);
8898 }
8899
8900 false
8901 }
8902
8903 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8904 Self::reset_to_default_keymap_context(keymap);
8905 let mode = match self.mode {
8906 EditorMode::SingleLine => "single_line",
8907 EditorMode::AutoHeight { .. } => "auto_height",
8908 EditorMode::Full => "full",
8909 };
8910 keymap.add_key("mode", mode);
8911 if self.pending_rename.is_some() {
8912 keymap.add_identifier("renaming");
8913 }
8914 if self.context_menu_visible() {
8915 match self.context_menu.as_ref() {
8916 Some(ContextMenu::Completions(_)) => {
8917 keymap.add_identifier("menu");
8918 keymap.add_identifier("showing_completions")
8919 }
8920 Some(ContextMenu::CodeActions(_)) => {
8921 keymap.add_identifier("menu");
8922 keymap.add_identifier("showing_code_actions")
8923 }
8924 None => {}
8925 }
8926 }
8927
8928 for layer in self.keymap_context_layers.values() {
8929 keymap.extend(layer);
8930 }
8931
8932 if let Some(extension) = self
8933 .buffer
8934 .read(cx)
8935 .as_singleton()
8936 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8937 {
8938 keymap.add_key("extension", extension.to_string());
8939 }
8940 }
8941
8942 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8943 Some(
8944 self.buffer
8945 .read(cx)
8946 .read(cx)
8947 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8948 .collect(),
8949 )
8950 }
8951
8952 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8953 // Prevent the IME menu from appearing when holding down an alphabetic key
8954 // while input is disabled.
8955 if !self.input_enabled {
8956 return None;
8957 }
8958
8959 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8960 Some(range.start.0..range.end.0)
8961 }
8962
8963 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8964 let snapshot = self.buffer.read(cx).read(cx);
8965 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8966 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8967 }
8968
8969 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8970 self.clear_highlights::<InputComposition>(cx);
8971 self.ime_transaction.take();
8972 }
8973
8974 fn replace_text_in_range(
8975 &mut self,
8976 range_utf16: Option<Range<usize>>,
8977 text: &str,
8978 cx: &mut ViewContext<Self>,
8979 ) {
8980 if !self.input_enabled {
8981 cx.emit(Event::InputIgnored { text: text.into() });
8982 return;
8983 }
8984
8985 self.transact(cx, |this, cx| {
8986 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8987 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8988 Some(this.selection_replacement_ranges(range_utf16, cx))
8989 } else {
8990 this.marked_text_ranges(cx)
8991 };
8992
8993 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
8994 let newest_selection_id = this.selections.newest_anchor().id;
8995 this.selections
8996 .all::<OffsetUtf16>(cx)
8997 .iter()
8998 .zip(ranges_to_replace.iter())
8999 .find_map(|(selection, range)| {
9000 if selection.id == newest_selection_id {
9001 Some(
9002 (range.start.0 as isize - selection.head().0 as isize)
9003 ..(range.end.0 as isize - selection.head().0 as isize),
9004 )
9005 } else {
9006 None
9007 }
9008 })
9009 });
9010
9011 cx.emit(Event::InputHandled {
9012 utf16_range_to_replace: range_to_replace,
9013 text: text.into(),
9014 });
9015
9016 if let Some(new_selected_ranges) = new_selected_ranges {
9017 this.change_selections(None, cx, |selections| {
9018 selections.select_ranges(new_selected_ranges)
9019 });
9020 }
9021
9022 this.handle_input(text, cx);
9023 });
9024
9025 if let Some(transaction) = self.ime_transaction {
9026 self.buffer.update(cx, |buffer, cx| {
9027 buffer.group_until_transaction(transaction, cx);
9028 });
9029 }
9030
9031 self.unmark_text(cx);
9032 }
9033
9034 fn replace_and_mark_text_in_range(
9035 &mut self,
9036 range_utf16: Option<Range<usize>>,
9037 text: &str,
9038 new_selected_range_utf16: Option<Range<usize>>,
9039 cx: &mut ViewContext<Self>,
9040 ) {
9041 if !self.input_enabled {
9042 cx.emit(Event::InputIgnored { text: text.into() });
9043 return;
9044 }
9045
9046 let transaction = self.transact(cx, |this, cx| {
9047 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9048 let snapshot = this.buffer.read(cx).read(cx);
9049 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9050 for marked_range in &mut marked_ranges {
9051 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9052 marked_range.start.0 += relative_range_utf16.start;
9053 marked_range.start =
9054 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9055 marked_range.end =
9056 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9057 }
9058 }
9059 Some(marked_ranges)
9060 } else if let Some(range_utf16) = range_utf16 {
9061 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9062 Some(this.selection_replacement_ranges(range_utf16, cx))
9063 } else {
9064 None
9065 };
9066
9067 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9068 let newest_selection_id = this.selections.newest_anchor().id;
9069 this.selections
9070 .all::<OffsetUtf16>(cx)
9071 .iter()
9072 .zip(ranges_to_replace.iter())
9073 .find_map(|(selection, range)| {
9074 if selection.id == newest_selection_id {
9075 Some(
9076 (range.start.0 as isize - selection.head().0 as isize)
9077 ..(range.end.0 as isize - selection.head().0 as isize),
9078 )
9079 } else {
9080 None
9081 }
9082 })
9083 });
9084
9085 cx.emit(Event::InputHandled {
9086 utf16_range_to_replace: range_to_replace,
9087 text: text.into(),
9088 });
9089
9090 if let Some(ranges) = ranges_to_replace {
9091 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9092 }
9093
9094 let marked_ranges = {
9095 let snapshot = this.buffer.read(cx).read(cx);
9096 this.selections
9097 .disjoint_anchors()
9098 .iter()
9099 .map(|selection| {
9100 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9101 })
9102 .collect::<Vec<_>>()
9103 };
9104
9105 if text.is_empty() {
9106 this.unmark_text(cx);
9107 } else {
9108 this.highlight_text::<InputComposition>(
9109 marked_ranges.clone(),
9110 this.style(cx).composition_mark,
9111 cx,
9112 );
9113 }
9114
9115 this.handle_input(text, cx);
9116
9117 if let Some(new_selected_range) = new_selected_range_utf16 {
9118 let snapshot = this.buffer.read(cx).read(cx);
9119 let new_selected_ranges = marked_ranges
9120 .into_iter()
9121 .map(|marked_range| {
9122 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9123 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9124 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9125 snapshot.clip_offset_utf16(new_start, Bias::Left)
9126 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9127 })
9128 .collect::<Vec<_>>();
9129
9130 drop(snapshot);
9131 this.change_selections(None, cx, |selections| {
9132 selections.select_ranges(new_selected_ranges)
9133 });
9134 }
9135 });
9136
9137 self.ime_transaction = self.ime_transaction.or(transaction);
9138 if let Some(transaction) = self.ime_transaction {
9139 self.buffer.update(cx, |buffer, cx| {
9140 buffer.group_until_transaction(transaction, cx);
9141 });
9142 }
9143
9144 if self.text_highlights::<InputComposition>(cx).is_none() {
9145 self.ime_transaction.take();
9146 }
9147 }
9148}
9149
9150fn build_style(
9151 settings: &ThemeSettings,
9152 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9153 override_text_style: Option<&OverrideTextStyle>,
9154 cx: &AppContext,
9155) -> EditorStyle {
9156 let font_cache = cx.font_cache();
9157 let line_height_scalar = settings.line_height();
9158 let theme_id = settings.theme.meta.id;
9159 let mut theme = settings.theme.editor.clone();
9160 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9161 let field_editor_theme = get_field_editor_theme(&settings.theme);
9162 theme.text_color = field_editor_theme.text.color;
9163 theme.selection = field_editor_theme.selection;
9164 theme.background = field_editor_theme
9165 .container
9166 .background_color
9167 .unwrap_or_default();
9168 EditorStyle {
9169 text: field_editor_theme.text,
9170 placeholder_text: field_editor_theme.placeholder_text,
9171 line_height_scalar,
9172 theme,
9173 theme_id,
9174 }
9175 } else {
9176 let font_family_id = settings.buffer_font_family;
9177 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9178 let font_properties = Default::default();
9179 let font_id = font_cache
9180 .select_font(font_family_id, &font_properties)
9181 .unwrap();
9182 let font_size = settings.buffer_font_size(cx);
9183 EditorStyle {
9184 text: TextStyle {
9185 color: settings.theme.editor.text_color,
9186 font_family_name,
9187 font_family_id,
9188 font_id,
9189 font_size,
9190 font_properties,
9191 underline: Default::default(),
9192 soft_wrap: false,
9193 },
9194 placeholder_text: None,
9195 line_height_scalar,
9196 theme,
9197 theme_id,
9198 }
9199 };
9200
9201 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9202 if let Some(highlighted) = style
9203 .text
9204 .clone()
9205 .highlight(highlight_style, font_cache)
9206 .log_err()
9207 {
9208 style.text = highlighted;
9209 }
9210 }
9211
9212 style
9213}
9214
9215trait SelectionExt {
9216 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9217 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9218 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9219 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9220 -> Range<u32>;
9221}
9222
9223impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9224 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9225 let start = self.start.to_point(buffer);
9226 let end = self.end.to_point(buffer);
9227 if self.reversed {
9228 end..start
9229 } else {
9230 start..end
9231 }
9232 }
9233
9234 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9235 let start = self.start.to_offset(buffer);
9236 let end = self.end.to_offset(buffer);
9237 if self.reversed {
9238 end..start
9239 } else {
9240 start..end
9241 }
9242 }
9243
9244 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9245 let start = self
9246 .start
9247 .to_point(&map.buffer_snapshot)
9248 .to_display_point(map);
9249 let end = self
9250 .end
9251 .to_point(&map.buffer_snapshot)
9252 .to_display_point(map);
9253 if self.reversed {
9254 end..start
9255 } else {
9256 start..end
9257 }
9258 }
9259
9260 fn spanned_rows(
9261 &self,
9262 include_end_if_at_line_start: bool,
9263 map: &DisplaySnapshot,
9264 ) -> Range<u32> {
9265 let start = self.start.to_point(&map.buffer_snapshot);
9266 let mut end = self.end.to_point(&map.buffer_snapshot);
9267 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9268 end.row -= 1;
9269 }
9270
9271 let buffer_start = map.prev_line_boundary(start).0;
9272 let buffer_end = map.next_line_boundary(end).0;
9273 buffer_start.row..buffer_end.row + 1
9274 }
9275}
9276
9277impl<T: InvalidationRegion> InvalidationStack<T> {
9278 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9279 where
9280 S: Clone + ToOffset,
9281 {
9282 while let Some(region) = self.last() {
9283 let all_selections_inside_invalidation_ranges =
9284 if selections.len() == region.ranges().len() {
9285 selections
9286 .iter()
9287 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9288 .all(|(selection, invalidation_range)| {
9289 let head = selection.head().to_offset(buffer);
9290 invalidation_range.start <= head && invalidation_range.end >= head
9291 })
9292 } else {
9293 false
9294 };
9295
9296 if all_selections_inside_invalidation_ranges {
9297 break;
9298 } else {
9299 self.pop();
9300 }
9301 }
9302 }
9303}
9304
9305impl<T> Default for InvalidationStack<T> {
9306 fn default() -> Self {
9307 Self(Default::default())
9308 }
9309}
9310
9311impl<T> Deref for InvalidationStack<T> {
9312 type Target = Vec<T>;
9313
9314 fn deref(&self) -> &Self::Target {
9315 &self.0
9316 }
9317}
9318
9319impl<T> DerefMut for InvalidationStack<T> {
9320 fn deref_mut(&mut self) -> &mut Self::Target {
9321 &mut self.0
9322 }
9323}
9324
9325impl InvalidationRegion for SnippetState {
9326 fn ranges(&self) -> &[Range<Anchor>] {
9327 &self.ranges[self.active_index]
9328 }
9329}
9330
9331impl Deref for EditorStyle {
9332 type Target = theme::Editor;
9333
9334 fn deref(&self) -> &Self::Target {
9335 &self.theme
9336 }
9337}
9338
9339pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9340 let mut highlighted_lines = Vec::new();
9341
9342 for (index, line) in diagnostic.message.lines().enumerate() {
9343 let line = match &diagnostic.source {
9344 Some(source) if index == 0 => {
9345 let source_highlight = Vec::from_iter(0..source.len());
9346 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9347 }
9348
9349 _ => highlight_diagnostic_message(Vec::new(), line),
9350 };
9351 highlighted_lines.push(line);
9352 }
9353 let message = diagnostic.message;
9354 Arc::new(move |cx: &mut BlockContext| {
9355 let message = message.clone();
9356 let settings = settings::get::<ThemeSettings>(cx);
9357 let tooltip_style = settings.theme.tooltip.clone();
9358 let theme = &settings.theme.editor;
9359 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9360 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9361 let anchor_x = cx.anchor_x;
9362 enum BlockContextToolip {}
9363 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9364 Flex::column()
9365 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9366 Label::new(
9367 line.clone(),
9368 style.message.clone().with_font_size(font_size),
9369 )
9370 .with_highlights(highlights.clone())
9371 .contained()
9372 .with_margin_left(anchor_x)
9373 }))
9374 .aligned()
9375 .left()
9376 .into_any()
9377 })
9378 .with_cursor_style(CursorStyle::PointingHand)
9379 .on_click(MouseButton::Left, move |_, _, cx| {
9380 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9381 })
9382 // We really need to rethink this ID system...
9383 .with_tooltip::<BlockContextToolip>(
9384 cx.block_id,
9385 "Copy diagnostic message",
9386 None,
9387 tooltip_style,
9388 cx,
9389 )
9390 .into_any()
9391 })
9392}
9393
9394pub fn highlight_diagnostic_message(
9395 initial_highlights: Vec<usize>,
9396 message: &str,
9397) -> (String, Vec<usize>) {
9398 let mut message_without_backticks = String::new();
9399 let mut prev_offset = 0;
9400 let mut inside_block = false;
9401 let mut highlights = initial_highlights;
9402 for (match_ix, (offset, _)) in message
9403 .match_indices('`')
9404 .chain([(message.len(), "")])
9405 .enumerate()
9406 {
9407 message_without_backticks.push_str(&message[prev_offset..offset]);
9408 if inside_block {
9409 highlights.extend(prev_offset - match_ix..offset - match_ix);
9410 }
9411
9412 inside_block = !inside_block;
9413 prev_offset = offset + 1;
9414 }
9415
9416 (message_without_backticks, highlights)
9417}
9418
9419pub fn diagnostic_style(
9420 severity: DiagnosticSeverity,
9421 valid: bool,
9422 theme: &theme::Editor,
9423) -> DiagnosticStyle {
9424 match (severity, valid) {
9425 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9426 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9427 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9428 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9429 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9430 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9431 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9432 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9433 _ => theme.invalid_hint_diagnostic.clone(),
9434 }
9435}
9436
9437pub fn combine_syntax_and_fuzzy_match_highlights(
9438 text: &str,
9439 default_style: HighlightStyle,
9440 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9441 match_indices: &[usize],
9442) -> Vec<(Range<usize>, HighlightStyle)> {
9443 let mut result = Vec::new();
9444 let mut match_indices = match_indices.iter().copied().peekable();
9445
9446 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9447 {
9448 syntax_highlight.weight = None;
9449
9450 // Add highlights for any fuzzy match characters before the next
9451 // syntax highlight range.
9452 while let Some(&match_index) = match_indices.peek() {
9453 if match_index >= range.start {
9454 break;
9455 }
9456 match_indices.next();
9457 let end_index = char_ix_after(match_index, text);
9458 let mut match_style = default_style;
9459 match_style.weight = Some(fonts::Weight::BOLD);
9460 result.push((match_index..end_index, match_style));
9461 }
9462
9463 if range.start == usize::MAX {
9464 break;
9465 }
9466
9467 // Add highlights for any fuzzy match characters within the
9468 // syntax highlight range.
9469 let mut offset = range.start;
9470 while let Some(&match_index) = match_indices.peek() {
9471 if match_index >= range.end {
9472 break;
9473 }
9474
9475 match_indices.next();
9476 if match_index > offset {
9477 result.push((offset..match_index, syntax_highlight));
9478 }
9479
9480 let mut end_index = char_ix_after(match_index, text);
9481 while let Some(&next_match_index) = match_indices.peek() {
9482 if next_match_index == end_index && next_match_index < range.end {
9483 end_index = char_ix_after(next_match_index, text);
9484 match_indices.next();
9485 } else {
9486 break;
9487 }
9488 }
9489
9490 let mut match_style = syntax_highlight;
9491 match_style.weight = Some(fonts::Weight::BOLD);
9492 result.push((match_index..end_index, match_style));
9493 offset = end_index;
9494 }
9495
9496 if offset < range.end {
9497 result.push((offset..range.end, syntax_highlight));
9498 }
9499 }
9500
9501 fn char_ix_after(ix: usize, text: &str) -> usize {
9502 ix + text[ix..].chars().next().unwrap().len_utf8()
9503 }
9504
9505 result
9506}
9507
9508pub fn styled_runs_for_code_label<'a>(
9509 label: &'a CodeLabel,
9510 syntax_theme: &'a theme::SyntaxTheme,
9511) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9512 let fade_out = HighlightStyle {
9513 fade_out: Some(0.35),
9514 ..Default::default()
9515 };
9516
9517 let mut prev_end = label.filter_range.end;
9518 label
9519 .runs
9520 .iter()
9521 .enumerate()
9522 .flat_map(move |(ix, (range, highlight_id))| {
9523 let style = if let Some(style) = highlight_id.style(syntax_theme) {
9524 style
9525 } else {
9526 return Default::default();
9527 };
9528 let mut muted_style = style;
9529 muted_style.highlight(fade_out);
9530
9531 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
9532 if range.start >= label.filter_range.end {
9533 if range.start > prev_end {
9534 runs.push((prev_end..range.start, fade_out));
9535 }
9536 runs.push((range.clone(), muted_style));
9537 } else if range.end <= label.filter_range.end {
9538 runs.push((range.clone(), style));
9539 } else {
9540 runs.push((range.start..label.filter_range.end, style));
9541 runs.push((label.filter_range.end..range.end, muted_style));
9542 }
9543 prev_end = cmp::max(prev_end, range.end);
9544
9545 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
9546 runs.push((prev_end..label.text.len(), fade_out));
9547 }
9548
9549 runs
9550 })
9551}
9552
9553pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
9554 let mut index = 0;
9555 let mut codepoints = text.char_indices().peekable();
9556
9557 std::iter::from_fn(move || {
9558 let start_index = index;
9559 while let Some((new_index, codepoint)) = codepoints.next() {
9560 index = new_index + codepoint.len_utf8();
9561 let current_upper = codepoint.is_uppercase();
9562 let next_upper = codepoints
9563 .peek()
9564 .map(|(_, c)| c.is_uppercase())
9565 .unwrap_or(false);
9566
9567 if !current_upper && next_upper {
9568 return Some(&text[start_index..index]);
9569 }
9570 }
9571
9572 index = text.len();
9573 if start_index < text.len() {
9574 return Some(&text[start_index..]);
9575 }
9576 None
9577 })
9578 .flat_map(|word| word.split_inclusive('_'))
9579 .flat_map(|word| word.split_inclusive('-'))
9580}
9581
9582trait RangeToAnchorExt {
9583 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
9584}
9585
9586impl<T: ToOffset> RangeToAnchorExt for Range<T> {
9587 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9588 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9589 }
9590}