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