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