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