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