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", None, 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(Some(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 self.report_copilot_event(None, false, cx)
3121 }
3122
3123 self.display_map
3124 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
3125 cx.notify();
3126 true
3127 } else {
3128 false
3129 }
3130 }
3131
3132 fn is_copilot_enabled_at(
3133 &self,
3134 location: Anchor,
3135 snapshot: &MultiBufferSnapshot,
3136 cx: &mut ViewContext<Self>,
3137 ) -> bool {
3138 let settings = cx.global::<Settings>();
3139
3140 let path = snapshot.file_at(location).map(|file| file.path());
3141 let language_name = snapshot
3142 .language_at(location)
3143 .map(|language| language.name());
3144 if !settings.show_copilot_suggestions(language_name.as_deref(), path.map(|p| p.as_ref())) {
3145 return false;
3146 }
3147
3148 true
3149 }
3150
3151 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3152 self.display_map.read(cx).has_suggestion()
3153 }
3154
3155 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3156 let snapshot = self.buffer.read(cx).snapshot(cx);
3157 let selection = self.selections.newest_anchor();
3158 let cursor = selection.head();
3159
3160 if self.context_menu.is_some()
3161 || !self.completion_tasks.is_empty()
3162 || selection.start != selection.end
3163 {
3164 self.discard_copilot_suggestion(cx);
3165 } else if let Some(text) = self
3166 .copilot_state
3167 .text_for_active_completion(cursor, &snapshot)
3168 {
3169 self.display_map.update(cx, move |map, cx| {
3170 map.replace_suggestion(
3171 Some(Suggestion {
3172 position: cursor,
3173 text: text.trim_end().into(),
3174 }),
3175 cx,
3176 )
3177 });
3178 cx.notify();
3179 } else {
3180 self.discard_copilot_suggestion(cx);
3181 }
3182 }
3183
3184 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3185 self.copilot_state = Default::default();
3186 self.discard_copilot_suggestion(cx);
3187 }
3188
3189 pub fn render_code_actions_indicator(
3190 &self,
3191 style: &EditorStyle,
3192 active: bool,
3193 cx: &mut ViewContext<Self>,
3194 ) -> Option<AnyElement<Self>> {
3195 if self.available_code_actions.is_some() {
3196 enum CodeActions {}
3197 Some(
3198 MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
3199 Svg::new("icons/bolt_8.svg")
3200 .with_color(style.code_actions.indicator.style_for(state, active).color)
3201 })
3202 .with_cursor_style(CursorStyle::PointingHand)
3203 .with_padding(Padding::uniform(3.))
3204 .on_down(MouseButton::Left, |_, this, cx| {
3205 this.toggle_code_actions(
3206 &ToggleCodeActions {
3207 deployed_from_indicator: true,
3208 },
3209 cx,
3210 );
3211 })
3212 .into_any(),
3213 )
3214 } else {
3215 None
3216 }
3217 }
3218
3219 pub fn render_fold_indicators(
3220 &self,
3221 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3222 style: &EditorStyle,
3223 gutter_hovered: bool,
3224 line_height: f32,
3225 gutter_margin: f32,
3226 cx: &mut ViewContext<Self>,
3227 ) -> Vec<Option<AnyElement<Self>>> {
3228 enum FoldIndicators {}
3229
3230 let style = style.folds.clone();
3231
3232 fold_data
3233 .iter()
3234 .enumerate()
3235 .map(|(ix, fold_data)| {
3236 fold_data
3237 .map(|(fold_status, buffer_row, active)| {
3238 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3239 MouseEventHandler::<FoldIndicators, _>::new(
3240 ix as usize,
3241 cx,
3242 |mouse_state, _| {
3243 Svg::new(match fold_status {
3244 FoldStatus::Folded => style.folded_icon.clone(),
3245 FoldStatus::Foldable => style.foldable_icon.clone(),
3246 })
3247 .with_color(
3248 style
3249 .indicator
3250 .style_for(
3251 mouse_state,
3252 fold_status == FoldStatus::Folded,
3253 )
3254 .color,
3255 )
3256 .constrained()
3257 .with_width(gutter_margin * style.icon_margin_scale)
3258 .aligned()
3259 .constrained()
3260 .with_height(line_height)
3261 .with_width(gutter_margin)
3262 .aligned()
3263 },
3264 )
3265 .with_cursor_style(CursorStyle::PointingHand)
3266 .with_padding(Padding::uniform(3.))
3267 .on_click(MouseButton::Left, {
3268 move |_, editor, cx| match fold_status {
3269 FoldStatus::Folded => {
3270 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3271 }
3272 FoldStatus::Foldable => {
3273 editor.fold_at(&FoldAt { buffer_row }, cx);
3274 }
3275 }
3276 })
3277 .into_any()
3278 })
3279 })
3280 .flatten()
3281 })
3282 .collect()
3283 }
3284
3285 pub fn context_menu_visible(&self) -> bool {
3286 self.context_menu
3287 .as_ref()
3288 .map_or(false, |menu| menu.visible())
3289 }
3290
3291 pub fn render_context_menu(
3292 &self,
3293 cursor_position: DisplayPoint,
3294 style: EditorStyle,
3295 cx: &mut ViewContext<Editor>,
3296 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3297 self.context_menu
3298 .as_ref()
3299 .map(|menu| menu.render(cursor_position, style, cx))
3300 }
3301
3302 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3303 if !matches!(menu, ContextMenu::Completions(_)) {
3304 self.completion_tasks.clear();
3305 }
3306 self.context_menu = Some(menu);
3307 self.discard_copilot_suggestion(cx);
3308 cx.notify();
3309 }
3310
3311 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3312 cx.notify();
3313 self.completion_tasks.clear();
3314 let context_menu = self.context_menu.take();
3315 if context_menu.is_some() {
3316 self.update_visible_copilot_suggestion(cx);
3317 }
3318 context_menu
3319 }
3320
3321 pub fn insert_snippet(
3322 &mut self,
3323 insertion_ranges: &[Range<usize>],
3324 snippet: Snippet,
3325 cx: &mut ViewContext<Self>,
3326 ) -> Result<()> {
3327 let tabstops = self.buffer.update(cx, |buffer, cx| {
3328 let snippet_text: Arc<str> = snippet.text.clone().into();
3329 buffer.edit(
3330 insertion_ranges
3331 .iter()
3332 .cloned()
3333 .map(|range| (range, snippet_text.clone())),
3334 Some(AutoindentMode::EachLine),
3335 cx,
3336 );
3337
3338 let snapshot = &*buffer.read(cx);
3339 let snippet = &snippet;
3340 snippet
3341 .tabstops
3342 .iter()
3343 .map(|tabstop| {
3344 let mut tabstop_ranges = tabstop
3345 .iter()
3346 .flat_map(|tabstop_range| {
3347 let mut delta = 0_isize;
3348 insertion_ranges.iter().map(move |insertion_range| {
3349 let insertion_start = insertion_range.start as isize + delta;
3350 delta +=
3351 snippet.text.len() as isize - insertion_range.len() as isize;
3352
3353 let start = snapshot.anchor_before(
3354 (insertion_start + tabstop_range.start) as usize,
3355 );
3356 let end = snapshot
3357 .anchor_after((insertion_start + tabstop_range.end) as usize);
3358 start..end
3359 })
3360 })
3361 .collect::<Vec<_>>();
3362 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3363 tabstop_ranges
3364 })
3365 .collect::<Vec<_>>()
3366 });
3367
3368 if let Some(tabstop) = tabstops.first() {
3369 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3370 s.select_ranges(tabstop.iter().cloned());
3371 });
3372 self.snippet_stack.push(SnippetState {
3373 active_index: 0,
3374 ranges: tabstops,
3375 });
3376 }
3377
3378 Ok(())
3379 }
3380
3381 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3382 self.move_to_snippet_tabstop(Bias::Right, cx)
3383 }
3384
3385 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3386 self.move_to_snippet_tabstop(Bias::Left, cx)
3387 }
3388
3389 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3390 if let Some(mut snippet) = self.snippet_stack.pop() {
3391 match bias {
3392 Bias::Left => {
3393 if snippet.active_index > 0 {
3394 snippet.active_index -= 1;
3395 } else {
3396 self.snippet_stack.push(snippet);
3397 return false;
3398 }
3399 }
3400 Bias::Right => {
3401 if snippet.active_index + 1 < snippet.ranges.len() {
3402 snippet.active_index += 1;
3403 } else {
3404 self.snippet_stack.push(snippet);
3405 return false;
3406 }
3407 }
3408 }
3409 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3410 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3411 s.select_anchor_ranges(current_ranges.iter().cloned())
3412 });
3413 // If snippet state is not at the last tabstop, push it back on the stack
3414 if snippet.active_index + 1 < snippet.ranges.len() {
3415 self.snippet_stack.push(snippet);
3416 }
3417 return true;
3418 }
3419 }
3420
3421 false
3422 }
3423
3424 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3425 self.transact(cx, |this, cx| {
3426 this.select_all(&SelectAll, cx);
3427 this.insert("", cx);
3428 });
3429 }
3430
3431 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3432 self.transact(cx, |this, cx| {
3433 this.select_autoclose_pair(cx);
3434 let mut selections = this.selections.all::<Point>(cx);
3435 if !this.selections.line_mode {
3436 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3437 for selection in &mut selections {
3438 if selection.is_empty() {
3439 let old_head = selection.head();
3440 let mut new_head =
3441 movement::left(&display_map, old_head.to_display_point(&display_map))
3442 .to_point(&display_map);
3443 if let Some((buffer, line_buffer_range)) = display_map
3444 .buffer_snapshot
3445 .buffer_line_for_row(old_head.row)
3446 {
3447 let indent_size =
3448 buffer.indent_size_for_line(line_buffer_range.start.row);
3449 let language_name = buffer
3450 .language_at(line_buffer_range.start)
3451 .map(|language| language.name());
3452 let indent_len = match indent_size.kind {
3453 IndentKind::Space => {
3454 cx.global::<Settings>().tab_size(language_name.as_deref())
3455 }
3456 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3457 };
3458 if old_head.column <= indent_size.len && old_head.column > 0 {
3459 let indent_len = indent_len.get();
3460 new_head = cmp::min(
3461 new_head,
3462 Point::new(
3463 old_head.row,
3464 ((old_head.column - 1) / indent_len) * indent_len,
3465 ),
3466 );
3467 }
3468 }
3469
3470 selection.set_head(new_head, SelectionGoal::None);
3471 }
3472 }
3473 }
3474
3475 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3476 this.insert("", cx);
3477 this.refresh_copilot_suggestions(true, cx);
3478 });
3479 }
3480
3481 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3482 self.transact(cx, |this, cx| {
3483 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3484 let line_mode = s.line_mode;
3485 s.move_with(|map, selection| {
3486 if selection.is_empty() && !line_mode {
3487 let cursor = movement::right(map, selection.head());
3488 selection.set_head(cursor, SelectionGoal::None);
3489 }
3490 })
3491 });
3492 this.insert("", cx);
3493 this.refresh_copilot_suggestions(true, cx);
3494 });
3495 }
3496
3497 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3498 if self.move_to_prev_snippet_tabstop(cx) {
3499 return;
3500 }
3501
3502 self.outdent(&Outdent, cx);
3503 }
3504
3505 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3506 if self.move_to_next_snippet_tabstop(cx) {
3507 return;
3508 }
3509
3510 let mut selections = self.selections.all_adjusted(cx);
3511 let buffer = self.buffer.read(cx);
3512 let snapshot = buffer.snapshot(cx);
3513 let rows_iter = selections.iter().map(|s| s.head().row);
3514 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3515
3516 let mut edits = Vec::new();
3517 let mut prev_edited_row = 0;
3518 let mut row_delta = 0;
3519 for selection in &mut selections {
3520 if selection.start.row != prev_edited_row {
3521 row_delta = 0;
3522 }
3523 prev_edited_row = selection.end.row;
3524
3525 // If the selection is non-empty, then increase the indentation of the selected lines.
3526 if !selection.is_empty() {
3527 row_delta =
3528 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3529 continue;
3530 }
3531
3532 // If the selection is empty and the cursor is in the leading whitespace before the
3533 // suggested indentation, then auto-indent the line.
3534 let cursor = selection.head();
3535 let current_indent = snapshot.indent_size_for_line(cursor.row);
3536 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3537 if cursor.column < suggested_indent.len
3538 && cursor.column <= current_indent.len
3539 && current_indent.len <= suggested_indent.len
3540 {
3541 selection.start = Point::new(cursor.row, suggested_indent.len);
3542 selection.end = selection.start;
3543 if row_delta == 0 {
3544 edits.extend(Buffer::edit_for_indent_size_adjustment(
3545 cursor.row,
3546 current_indent,
3547 suggested_indent,
3548 ));
3549 row_delta = suggested_indent.len - current_indent.len;
3550 }
3551 continue;
3552 }
3553 }
3554
3555 // Accept copilot suggestion if there is only one selection and the cursor is not
3556 // in the leading whitespace.
3557 if self.selections.count() == 1
3558 && cursor.column >= current_indent.len
3559 && self.has_active_copilot_suggestion(cx)
3560 {
3561 self.accept_copilot_suggestion(cx);
3562 return;
3563 }
3564
3565 // Otherwise, insert a hard or soft tab.
3566 let settings = cx.global::<Settings>();
3567 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3568 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3569 IndentSize::tab()
3570 } else {
3571 let tab_size = settings.tab_size(language_name.as_deref()).get();
3572 let char_column = snapshot
3573 .text_for_range(Point::new(cursor.row, 0)..cursor)
3574 .flat_map(str::chars)
3575 .count()
3576 + row_delta as usize;
3577 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3578 IndentSize::spaces(chars_to_next_tab_stop)
3579 };
3580 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3581 selection.end = selection.start;
3582 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3583 row_delta += tab_size.len;
3584 }
3585
3586 self.transact(cx, |this, cx| {
3587 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3588 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3589 this.refresh_copilot_suggestions(true, cx);
3590 });
3591 }
3592
3593 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3594 let mut selections = self.selections.all::<Point>(cx);
3595 let mut prev_edited_row = 0;
3596 let mut row_delta = 0;
3597 let mut edits = Vec::new();
3598 let buffer = self.buffer.read(cx);
3599 let snapshot = buffer.snapshot(cx);
3600 for selection in &mut selections {
3601 if selection.start.row != prev_edited_row {
3602 row_delta = 0;
3603 }
3604 prev_edited_row = selection.end.row;
3605
3606 row_delta =
3607 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3608 }
3609
3610 self.transact(cx, |this, cx| {
3611 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3612 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3613 });
3614 }
3615
3616 fn indent_selection(
3617 buffer: &MultiBuffer,
3618 snapshot: &MultiBufferSnapshot,
3619 selection: &mut Selection<Point>,
3620 edits: &mut Vec<(Range<Point>, String)>,
3621 delta_for_start_row: u32,
3622 cx: &AppContext,
3623 ) -> u32 {
3624 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3625 let settings = cx.global::<Settings>();
3626 let tab_size = settings.tab_size(language_name.as_deref()).get();
3627 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3628 IndentKind::Tab
3629 } else {
3630 IndentKind::Space
3631 };
3632 let mut start_row = selection.start.row;
3633 let mut end_row = selection.end.row + 1;
3634
3635 // If a selection ends at the beginning of a line, don't indent
3636 // that last line.
3637 if selection.end.column == 0 {
3638 end_row -= 1;
3639 }
3640
3641 // Avoid re-indenting a row that has already been indented by a
3642 // previous selection, but still update this selection's column
3643 // to reflect that indentation.
3644 if delta_for_start_row > 0 {
3645 start_row += 1;
3646 selection.start.column += delta_for_start_row;
3647 if selection.end.row == selection.start.row {
3648 selection.end.column += delta_for_start_row;
3649 }
3650 }
3651
3652 let mut delta_for_end_row = 0;
3653 for row in start_row..end_row {
3654 let current_indent = snapshot.indent_size_for_line(row);
3655 let indent_delta = match (current_indent.kind, indent_kind) {
3656 (IndentKind::Space, IndentKind::Space) => {
3657 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3658 IndentSize::spaces(columns_to_next_tab_stop)
3659 }
3660 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3661 (_, IndentKind::Tab) => IndentSize::tab(),
3662 };
3663
3664 let row_start = Point::new(row, 0);
3665 edits.push((
3666 row_start..row_start,
3667 indent_delta.chars().collect::<String>(),
3668 ));
3669
3670 // Update this selection's endpoints to reflect the indentation.
3671 if row == selection.start.row {
3672 selection.start.column += indent_delta.len;
3673 }
3674 if row == selection.end.row {
3675 selection.end.column += indent_delta.len;
3676 delta_for_end_row = indent_delta.len;
3677 }
3678 }
3679
3680 if selection.start.row == selection.end.row {
3681 delta_for_start_row + delta_for_end_row
3682 } else {
3683 delta_for_end_row
3684 }
3685 }
3686
3687 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3688 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3689 let selections = self.selections.all::<Point>(cx);
3690 let mut deletion_ranges = Vec::new();
3691 let mut last_outdent = None;
3692 {
3693 let buffer = self.buffer.read(cx);
3694 let snapshot = buffer.snapshot(cx);
3695 for selection in &selections {
3696 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3697 let tab_size = cx
3698 .global::<Settings>()
3699 .tab_size(language_name.as_deref())
3700 .get();
3701 let mut rows = selection.spanned_rows(false, &display_map);
3702
3703 // Avoid re-outdenting a row that has already been outdented by a
3704 // previous selection.
3705 if let Some(last_row) = last_outdent {
3706 if last_row == rows.start {
3707 rows.start += 1;
3708 }
3709 }
3710
3711 for row in rows {
3712 let indent_size = snapshot.indent_size_for_line(row);
3713 if indent_size.len > 0 {
3714 let deletion_len = match indent_size.kind {
3715 IndentKind::Space => {
3716 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3717 if columns_to_prev_tab_stop == 0 {
3718 tab_size
3719 } else {
3720 columns_to_prev_tab_stop
3721 }
3722 }
3723 IndentKind::Tab => 1,
3724 };
3725 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3726 last_outdent = Some(row);
3727 }
3728 }
3729 }
3730 }
3731
3732 self.transact(cx, |this, cx| {
3733 this.buffer.update(cx, |buffer, cx| {
3734 let empty_str: Arc<str> = "".into();
3735 buffer.edit(
3736 deletion_ranges
3737 .into_iter()
3738 .map(|range| (range, empty_str.clone())),
3739 None,
3740 cx,
3741 );
3742 });
3743 let selections = this.selections.all::<usize>(cx);
3744 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3745 });
3746 }
3747
3748 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3749 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3750 let selections = self.selections.all::<Point>(cx);
3751
3752 let mut new_cursors = Vec::new();
3753 let mut edit_ranges = Vec::new();
3754 let mut selections = selections.iter().peekable();
3755 while let Some(selection) = selections.next() {
3756 let mut rows = selection.spanned_rows(false, &display_map);
3757 let goal_display_column = selection.head().to_display_point(&display_map).column();
3758
3759 // Accumulate contiguous regions of rows that we want to delete.
3760 while let Some(next_selection) = selections.peek() {
3761 let next_rows = next_selection.spanned_rows(false, &display_map);
3762 if next_rows.start <= rows.end {
3763 rows.end = next_rows.end;
3764 selections.next().unwrap();
3765 } else {
3766 break;
3767 }
3768 }
3769
3770 let buffer = &display_map.buffer_snapshot;
3771 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3772 let edit_end;
3773 let cursor_buffer_row;
3774 if buffer.max_point().row >= rows.end {
3775 // If there's a line after the range, delete the \n from the end of the row range
3776 // and position the cursor on the next line.
3777 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3778 cursor_buffer_row = rows.end;
3779 } else {
3780 // If there isn't a line after the range, delete the \n from the line before the
3781 // start of the row range and position the cursor there.
3782 edit_start = edit_start.saturating_sub(1);
3783 edit_end = buffer.len();
3784 cursor_buffer_row = rows.start.saturating_sub(1);
3785 }
3786
3787 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3788 *cursor.column_mut() =
3789 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3790
3791 new_cursors.push((
3792 selection.id,
3793 buffer.anchor_after(cursor.to_point(&display_map)),
3794 ));
3795 edit_ranges.push(edit_start..edit_end);
3796 }
3797
3798 self.transact(cx, |this, cx| {
3799 let buffer = this.buffer.update(cx, |buffer, cx| {
3800 let empty_str: Arc<str> = "".into();
3801 buffer.edit(
3802 edit_ranges
3803 .into_iter()
3804 .map(|range| (range, empty_str.clone())),
3805 None,
3806 cx,
3807 );
3808 buffer.snapshot(cx)
3809 });
3810 let new_selections = new_cursors
3811 .into_iter()
3812 .map(|(id, cursor)| {
3813 let cursor = cursor.to_point(&buffer);
3814 Selection {
3815 id,
3816 start: cursor,
3817 end: cursor,
3818 reversed: false,
3819 goal: SelectionGoal::None,
3820 }
3821 })
3822 .collect();
3823
3824 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3825 s.select(new_selections);
3826 });
3827 });
3828 }
3829
3830 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3831 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3832 let buffer = &display_map.buffer_snapshot;
3833 let selections = self.selections.all::<Point>(cx);
3834
3835 let mut edits = Vec::new();
3836 let mut selections_iter = selections.iter().peekable();
3837 while let Some(selection) = selections_iter.next() {
3838 // Avoid duplicating the same lines twice.
3839 let mut rows = selection.spanned_rows(false, &display_map);
3840
3841 while let Some(next_selection) = selections_iter.peek() {
3842 let next_rows = next_selection.spanned_rows(false, &display_map);
3843 if next_rows.start < rows.end {
3844 rows.end = next_rows.end;
3845 selections_iter.next().unwrap();
3846 } else {
3847 break;
3848 }
3849 }
3850
3851 // Copy the text from the selected row region and splice it at the start of the region.
3852 let start = Point::new(rows.start, 0);
3853 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3854 let text = buffer
3855 .text_for_range(start..end)
3856 .chain(Some("\n"))
3857 .collect::<String>();
3858 edits.push((start..start, text));
3859 }
3860
3861 self.transact(cx, |this, cx| {
3862 this.buffer.update(cx, |buffer, cx| {
3863 buffer.edit(edits, None, cx);
3864 });
3865
3866 this.request_autoscroll(Autoscroll::fit(), cx);
3867 });
3868 }
3869
3870 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3871 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3872 let buffer = self.buffer.read(cx).snapshot(cx);
3873
3874 let mut edits = Vec::new();
3875 let mut unfold_ranges = Vec::new();
3876 let mut refold_ranges = Vec::new();
3877
3878 let selections = self.selections.all::<Point>(cx);
3879 let mut selections = selections.iter().peekable();
3880 let mut contiguous_row_selections = Vec::new();
3881 let mut new_selections = Vec::new();
3882
3883 while let Some(selection) = selections.next() {
3884 // Find all the selections that span a contiguous row range
3885 let (start_row, end_row) = consume_contiguous_rows(
3886 &mut contiguous_row_selections,
3887 selection,
3888 &display_map,
3889 &mut selections,
3890 );
3891
3892 // Move the text spanned by the row range to be before the line preceding the row range
3893 if start_row > 0 {
3894 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3895 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3896 let insertion_point = display_map
3897 .prev_line_boundary(Point::new(start_row - 1, 0))
3898 .0;
3899
3900 // Don't move lines across excerpts
3901 if buffer
3902 .excerpt_boundaries_in_range((
3903 Bound::Excluded(insertion_point),
3904 Bound::Included(range_to_move.end),
3905 ))
3906 .next()
3907 .is_none()
3908 {
3909 let text = buffer
3910 .text_for_range(range_to_move.clone())
3911 .flat_map(|s| s.chars())
3912 .skip(1)
3913 .chain(['\n'])
3914 .collect::<String>();
3915
3916 edits.push((
3917 buffer.anchor_after(range_to_move.start)
3918 ..buffer.anchor_before(range_to_move.end),
3919 String::new(),
3920 ));
3921 let insertion_anchor = buffer.anchor_after(insertion_point);
3922 edits.push((insertion_anchor..insertion_anchor, text));
3923
3924 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3925
3926 // Move selections up
3927 new_selections.extend(contiguous_row_selections.drain(..).map(
3928 |mut selection| {
3929 selection.start.row -= row_delta;
3930 selection.end.row -= row_delta;
3931 selection
3932 },
3933 ));
3934
3935 // Move folds up
3936 unfold_ranges.push(range_to_move.clone());
3937 for fold in display_map.folds_in_range(
3938 buffer.anchor_before(range_to_move.start)
3939 ..buffer.anchor_after(range_to_move.end),
3940 ) {
3941 let mut start = fold.start.to_point(&buffer);
3942 let mut end = fold.end.to_point(&buffer);
3943 start.row -= row_delta;
3944 end.row -= row_delta;
3945 refold_ranges.push(start..end);
3946 }
3947 }
3948 }
3949
3950 // If we didn't move line(s), preserve the existing selections
3951 new_selections.append(&mut contiguous_row_selections);
3952 }
3953
3954 self.transact(cx, |this, cx| {
3955 this.unfold_ranges(unfold_ranges, true, true, cx);
3956 this.buffer.update(cx, |buffer, cx| {
3957 for (range, text) in edits {
3958 buffer.edit([(range, text)], None, cx);
3959 }
3960 });
3961 this.fold_ranges(refold_ranges, true, cx);
3962 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3963 s.select(new_selections);
3964 })
3965 });
3966 }
3967
3968 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3969 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3970 let buffer = self.buffer.read(cx).snapshot(cx);
3971
3972 let mut edits = Vec::new();
3973 let mut unfold_ranges = Vec::new();
3974 let mut refold_ranges = Vec::new();
3975
3976 let selections = self.selections.all::<Point>(cx);
3977 let mut selections = selections.iter().peekable();
3978 let mut contiguous_row_selections = Vec::new();
3979 let mut new_selections = Vec::new();
3980
3981 while let Some(selection) = selections.next() {
3982 // Find all the selections that span a contiguous row range
3983 let (start_row, end_row) = consume_contiguous_rows(
3984 &mut contiguous_row_selections,
3985 selection,
3986 &display_map,
3987 &mut selections,
3988 );
3989
3990 // Move the text spanned by the row range to be after the last line of the row range
3991 if end_row <= buffer.max_point().row {
3992 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3993 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3994
3995 // Don't move lines across excerpt boundaries
3996 if buffer
3997 .excerpt_boundaries_in_range((
3998 Bound::Excluded(range_to_move.start),
3999 Bound::Included(insertion_point),
4000 ))
4001 .next()
4002 .is_none()
4003 {
4004 let mut text = String::from("\n");
4005 text.extend(buffer.text_for_range(range_to_move.clone()));
4006 text.pop(); // Drop trailing newline
4007 edits.push((
4008 buffer.anchor_after(range_to_move.start)
4009 ..buffer.anchor_before(range_to_move.end),
4010 String::new(),
4011 ));
4012 let insertion_anchor = buffer.anchor_after(insertion_point);
4013 edits.push((insertion_anchor..insertion_anchor, text));
4014
4015 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4016
4017 // Move selections down
4018 new_selections.extend(contiguous_row_selections.drain(..).map(
4019 |mut selection| {
4020 selection.start.row += row_delta;
4021 selection.end.row += row_delta;
4022 selection
4023 },
4024 ));
4025
4026 // Move folds down
4027 unfold_ranges.push(range_to_move.clone());
4028 for fold in display_map.folds_in_range(
4029 buffer.anchor_before(range_to_move.start)
4030 ..buffer.anchor_after(range_to_move.end),
4031 ) {
4032 let mut start = fold.start.to_point(&buffer);
4033 let mut end = fold.end.to_point(&buffer);
4034 start.row += row_delta;
4035 end.row += row_delta;
4036 refold_ranges.push(start..end);
4037 }
4038 }
4039 }
4040
4041 // If we didn't move line(s), preserve the existing selections
4042 new_selections.append(&mut contiguous_row_selections);
4043 }
4044
4045 self.transact(cx, |this, cx| {
4046 this.unfold_ranges(unfold_ranges, true, true, cx);
4047 this.buffer.update(cx, |buffer, cx| {
4048 for (range, text) in edits {
4049 buffer.edit([(range, text)], None, cx);
4050 }
4051 });
4052 this.fold_ranges(refold_ranges, true, cx);
4053 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4054 });
4055 }
4056
4057 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4058 self.transact(cx, |this, cx| {
4059 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4060 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4061 let line_mode = s.line_mode;
4062 s.move_with(|display_map, selection| {
4063 if !selection.is_empty() || line_mode {
4064 return;
4065 }
4066
4067 let mut head = selection.head();
4068 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4069 if head.column() == display_map.line_len(head.row()) {
4070 transpose_offset = display_map
4071 .buffer_snapshot
4072 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4073 }
4074
4075 if transpose_offset == 0 {
4076 return;
4077 }
4078
4079 *head.column_mut() += 1;
4080 head = display_map.clip_point(head, Bias::Right);
4081 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4082
4083 let transpose_start = display_map
4084 .buffer_snapshot
4085 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4086 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4087 let transpose_end = display_map
4088 .buffer_snapshot
4089 .clip_offset(transpose_offset + 1, Bias::Right);
4090 if let Some(ch) =
4091 display_map.buffer_snapshot.chars_at(transpose_start).next()
4092 {
4093 edits.push((transpose_start..transpose_offset, String::new()));
4094 edits.push((transpose_end..transpose_end, ch.to_string()));
4095 }
4096 }
4097 });
4098 edits
4099 });
4100 this.buffer
4101 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4102 let selections = this.selections.all::<usize>(cx);
4103 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4104 s.select(selections);
4105 });
4106 });
4107 }
4108
4109 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4110 let mut text = String::new();
4111 let buffer = self.buffer.read(cx).snapshot(cx);
4112 let mut selections = self.selections.all::<Point>(cx);
4113 let mut clipboard_selections = Vec::with_capacity(selections.len());
4114 {
4115 let max_point = buffer.max_point();
4116 for selection in &mut selections {
4117 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4118 if is_entire_line {
4119 selection.start = Point::new(selection.start.row, 0);
4120 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4121 selection.goal = SelectionGoal::None;
4122 }
4123 let mut len = 0;
4124 for chunk in buffer.text_for_range(selection.start..selection.end) {
4125 text.push_str(chunk);
4126 len += chunk.len();
4127 }
4128 clipboard_selections.push(ClipboardSelection {
4129 len,
4130 is_entire_line,
4131 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4132 });
4133 }
4134 }
4135
4136 self.transact(cx, |this, cx| {
4137 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4138 s.select(selections);
4139 });
4140 this.insert("", cx);
4141 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4142 });
4143 }
4144
4145 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4146 let selections = self.selections.all::<Point>(cx);
4147 let buffer = self.buffer.read(cx).read(cx);
4148 let mut text = String::new();
4149
4150 let mut clipboard_selections = Vec::with_capacity(selections.len());
4151 {
4152 let max_point = buffer.max_point();
4153 for selection in selections.iter() {
4154 let mut start = selection.start;
4155 let mut end = selection.end;
4156 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4157 if is_entire_line {
4158 start = Point::new(start.row, 0);
4159 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4160 }
4161 let mut len = 0;
4162 for chunk in buffer.text_for_range(start..end) {
4163 text.push_str(chunk);
4164 len += chunk.len();
4165 }
4166 clipboard_selections.push(ClipboardSelection {
4167 len,
4168 is_entire_line,
4169 first_line_indent: buffer.indent_size_for_line(start.row).len,
4170 });
4171 }
4172 }
4173
4174 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4175 }
4176
4177 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4178 self.transact(cx, |this, cx| {
4179 if let Some(item) = cx.read_from_clipboard() {
4180 let mut clipboard_text = Cow::Borrowed(item.text());
4181 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4182 let old_selections = this.selections.all::<usize>(cx);
4183 let all_selections_were_entire_line =
4184 clipboard_selections.iter().all(|s| s.is_entire_line);
4185 let first_selection_indent_column =
4186 clipboard_selections.first().map(|s| s.first_line_indent);
4187 if clipboard_selections.len() != old_selections.len() {
4188 let mut newline_separated_text = String::new();
4189 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4190 let mut ix = 0;
4191 while let Some(clipboard_selection) = clipboard_selections.next() {
4192 newline_separated_text
4193 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4194 ix += clipboard_selection.len;
4195 if clipboard_selections.peek().is_some() {
4196 newline_separated_text.push('\n');
4197 }
4198 }
4199 clipboard_text = Cow::Owned(newline_separated_text);
4200 }
4201
4202 this.buffer.update(cx, |buffer, cx| {
4203 let snapshot = buffer.read(cx);
4204 let mut start_offset = 0;
4205 let mut edits = Vec::new();
4206 let mut original_indent_columns = Vec::new();
4207 let line_mode = this.selections.line_mode;
4208 for (ix, selection) in old_selections.iter().enumerate() {
4209 let to_insert;
4210 let entire_line;
4211 let original_indent_column;
4212 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4213 let end_offset = start_offset + clipboard_selection.len;
4214 to_insert = &clipboard_text[start_offset..end_offset];
4215 entire_line = clipboard_selection.is_entire_line;
4216 start_offset = end_offset;
4217 original_indent_column =
4218 Some(clipboard_selection.first_line_indent);
4219 } else {
4220 to_insert = clipboard_text.as_str();
4221 entire_line = all_selections_were_entire_line;
4222 original_indent_column = first_selection_indent_column
4223 }
4224
4225 // If the corresponding selection was empty when this slice of the
4226 // clipboard text was written, then the entire line containing the
4227 // selection was copied. If this selection is also currently empty,
4228 // then paste the line before the current line of the buffer.
4229 let range = if selection.is_empty() && !line_mode && entire_line {
4230 let column = selection.start.to_point(&snapshot).column as usize;
4231 let line_start = selection.start - column;
4232 line_start..line_start
4233 } else {
4234 selection.range()
4235 };
4236
4237 edits.push((range, to_insert));
4238 original_indent_columns.extend(original_indent_column);
4239 }
4240 drop(snapshot);
4241
4242 buffer.edit(
4243 edits,
4244 Some(AutoindentMode::Block {
4245 original_indent_columns,
4246 }),
4247 cx,
4248 );
4249 });
4250
4251 let selections = this.selections.all::<usize>(cx);
4252 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4253 } else {
4254 this.insert(&clipboard_text, cx);
4255 }
4256 }
4257 });
4258 }
4259
4260 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4261 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4262 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4263 self.change_selections(None, cx, |s| {
4264 s.select_anchors(selections.to_vec());
4265 });
4266 }
4267 self.request_autoscroll(Autoscroll::fit(), cx);
4268 self.unmark_text(cx);
4269 self.refresh_copilot_suggestions(true, cx);
4270 cx.emit(Event::Edited);
4271 }
4272 }
4273
4274 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4275 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4276 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4277 {
4278 self.change_selections(None, cx, |s| {
4279 s.select_anchors(selections.to_vec());
4280 });
4281 }
4282 self.request_autoscroll(Autoscroll::fit(), cx);
4283 self.unmark_text(cx);
4284 self.refresh_copilot_suggestions(true, cx);
4285 cx.emit(Event::Edited);
4286 }
4287 }
4288
4289 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4290 self.buffer
4291 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4292 }
4293
4294 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4295 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4296 let line_mode = s.line_mode;
4297 s.move_with(|map, selection| {
4298 let cursor = if selection.is_empty() && !line_mode {
4299 movement::left(map, selection.start)
4300 } else {
4301 selection.start
4302 };
4303 selection.collapse_to(cursor, SelectionGoal::None);
4304 });
4305 })
4306 }
4307
4308 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4309 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4310 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4311 })
4312 }
4313
4314 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4315 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4316 let line_mode = s.line_mode;
4317 s.move_with(|map, selection| {
4318 let cursor = if selection.is_empty() && !line_mode {
4319 movement::right(map, selection.end)
4320 } else {
4321 selection.end
4322 };
4323 selection.collapse_to(cursor, SelectionGoal::None)
4324 });
4325 })
4326 }
4327
4328 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4329 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4330 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4331 })
4332 }
4333
4334 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4335 if self.take_rename(true, cx).is_some() {
4336 return;
4337 }
4338
4339 if let Some(context_menu) = self.context_menu.as_mut() {
4340 if context_menu.select_prev(cx) {
4341 return;
4342 }
4343 }
4344
4345 if matches!(self.mode, EditorMode::SingleLine) {
4346 cx.propagate_action();
4347 return;
4348 }
4349
4350 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4351 let line_mode = s.line_mode;
4352 s.move_with(|map, selection| {
4353 if !selection.is_empty() && !line_mode {
4354 selection.goal = SelectionGoal::None;
4355 }
4356 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4357 selection.collapse_to(cursor, goal);
4358 });
4359 })
4360 }
4361
4362 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4363 if self.take_rename(true, cx).is_some() {
4364 return;
4365 }
4366
4367 if self
4368 .context_menu
4369 .as_mut()
4370 .map(|menu| menu.select_first(cx))
4371 .unwrap_or(false)
4372 {
4373 return;
4374 }
4375
4376 if matches!(self.mode, EditorMode::SingleLine) {
4377 cx.propagate_action();
4378 return;
4379 }
4380
4381 let row_count = if let Some(row_count) = self.visible_line_count() {
4382 row_count as u32 - 1
4383 } else {
4384 return;
4385 };
4386
4387 let autoscroll = if action.center_cursor {
4388 Autoscroll::center()
4389 } else {
4390 Autoscroll::fit()
4391 };
4392
4393 self.change_selections(Some(autoscroll), cx, |s| {
4394 let line_mode = s.line_mode;
4395 s.move_with(|map, selection| {
4396 if !selection.is_empty() && !line_mode {
4397 selection.goal = SelectionGoal::None;
4398 }
4399 let (cursor, goal) =
4400 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4401 selection.collapse_to(cursor, goal);
4402 });
4403 });
4404 }
4405
4406 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4407 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4408 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4409 })
4410 }
4411
4412 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4413 self.take_rename(true, cx);
4414
4415 if let Some(context_menu) = self.context_menu.as_mut() {
4416 if context_menu.select_next(cx) {
4417 return;
4418 }
4419 }
4420
4421 if self.mode == EditorMode::SingleLine {
4422 cx.propagate_action();
4423 return;
4424 }
4425
4426 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4427 let line_mode = s.line_mode;
4428 s.move_with(|map, selection| {
4429 if !selection.is_empty() && !line_mode {
4430 selection.goal = SelectionGoal::None;
4431 }
4432 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4433 selection.collapse_to(cursor, goal);
4434 });
4435 });
4436 }
4437
4438 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4439 if self.take_rename(true, cx).is_some() {
4440 return;
4441 }
4442
4443 if self
4444 .context_menu
4445 .as_mut()
4446 .map(|menu| menu.select_last(cx))
4447 .unwrap_or(false)
4448 {
4449 return;
4450 }
4451
4452 if matches!(self.mode, EditorMode::SingleLine) {
4453 cx.propagate_action();
4454 return;
4455 }
4456
4457 let row_count = if let Some(row_count) = self.visible_line_count() {
4458 row_count as u32 - 1
4459 } else {
4460 return;
4461 };
4462
4463 let autoscroll = if action.center_cursor {
4464 Autoscroll::center()
4465 } else {
4466 Autoscroll::fit()
4467 };
4468
4469 self.change_selections(Some(autoscroll), cx, |s| {
4470 let line_mode = s.line_mode;
4471 s.move_with(|map, selection| {
4472 if !selection.is_empty() && !line_mode {
4473 selection.goal = SelectionGoal::None;
4474 }
4475 let (cursor, goal) =
4476 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4477 selection.collapse_to(cursor, goal);
4478 });
4479 });
4480 }
4481
4482 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4483 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4484 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4485 });
4486 }
4487
4488 pub fn move_to_previous_word_start(
4489 &mut self,
4490 _: &MoveToPreviousWordStart,
4491 cx: &mut ViewContext<Self>,
4492 ) {
4493 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4494 s.move_cursors_with(|map, head, _| {
4495 (
4496 movement::previous_word_start(map, head),
4497 SelectionGoal::None,
4498 )
4499 });
4500 })
4501 }
4502
4503 pub fn move_to_previous_subword_start(
4504 &mut self,
4505 _: &MoveToPreviousSubwordStart,
4506 cx: &mut ViewContext<Self>,
4507 ) {
4508 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4509 s.move_cursors_with(|map, head, _| {
4510 (
4511 movement::previous_subword_start(map, head),
4512 SelectionGoal::None,
4513 )
4514 });
4515 })
4516 }
4517
4518 pub fn select_to_previous_word_start(
4519 &mut self,
4520 _: &SelectToPreviousWordStart,
4521 cx: &mut ViewContext<Self>,
4522 ) {
4523 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4524 s.move_heads_with(|map, head, _| {
4525 (
4526 movement::previous_word_start(map, head),
4527 SelectionGoal::None,
4528 )
4529 });
4530 })
4531 }
4532
4533 pub fn select_to_previous_subword_start(
4534 &mut self,
4535 _: &SelectToPreviousSubwordStart,
4536 cx: &mut ViewContext<Self>,
4537 ) {
4538 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4539 s.move_heads_with(|map, head, _| {
4540 (
4541 movement::previous_subword_start(map, head),
4542 SelectionGoal::None,
4543 )
4544 });
4545 })
4546 }
4547
4548 pub fn delete_to_previous_word_start(
4549 &mut self,
4550 _: &DeleteToPreviousWordStart,
4551 cx: &mut ViewContext<Self>,
4552 ) {
4553 self.transact(cx, |this, cx| {
4554 this.select_autoclose_pair(cx);
4555 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4556 let line_mode = s.line_mode;
4557 s.move_with(|map, selection| {
4558 if selection.is_empty() && !line_mode {
4559 let cursor = movement::previous_word_start(map, selection.head());
4560 selection.set_head(cursor, SelectionGoal::None);
4561 }
4562 });
4563 });
4564 this.insert("", cx);
4565 });
4566 }
4567
4568 pub fn delete_to_previous_subword_start(
4569 &mut self,
4570 _: &DeleteToPreviousSubwordStart,
4571 cx: &mut ViewContext<Self>,
4572 ) {
4573 self.transact(cx, |this, cx| {
4574 this.select_autoclose_pair(cx);
4575 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4576 let line_mode = s.line_mode;
4577 s.move_with(|map, selection| {
4578 if selection.is_empty() && !line_mode {
4579 let cursor = movement::previous_subword_start(map, selection.head());
4580 selection.set_head(cursor, SelectionGoal::None);
4581 }
4582 });
4583 });
4584 this.insert("", cx);
4585 });
4586 }
4587
4588 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4589 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4590 s.move_cursors_with(|map, head, _| {
4591 (movement::next_word_end(map, head), SelectionGoal::None)
4592 });
4593 })
4594 }
4595
4596 pub fn move_to_next_subword_end(
4597 &mut self,
4598 _: &MoveToNextSubwordEnd,
4599 cx: &mut ViewContext<Self>,
4600 ) {
4601 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4602 s.move_cursors_with(|map, head, _| {
4603 (movement::next_subword_end(map, head), SelectionGoal::None)
4604 });
4605 })
4606 }
4607
4608 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4609 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4610 s.move_heads_with(|map, head, _| {
4611 (movement::next_word_end(map, head), SelectionGoal::None)
4612 });
4613 })
4614 }
4615
4616 pub fn select_to_next_subword_end(
4617 &mut self,
4618 _: &SelectToNextSubwordEnd,
4619 cx: &mut ViewContext<Self>,
4620 ) {
4621 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4622 s.move_heads_with(|map, head, _| {
4623 (movement::next_subword_end(map, head), SelectionGoal::None)
4624 });
4625 })
4626 }
4627
4628 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4629 self.transact(cx, |this, cx| {
4630 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4631 let line_mode = s.line_mode;
4632 s.move_with(|map, selection| {
4633 if selection.is_empty() && !line_mode {
4634 let cursor = movement::next_word_end(map, selection.head());
4635 selection.set_head(cursor, SelectionGoal::None);
4636 }
4637 });
4638 });
4639 this.insert("", cx);
4640 });
4641 }
4642
4643 pub fn delete_to_next_subword_end(
4644 &mut self,
4645 _: &DeleteToNextSubwordEnd,
4646 cx: &mut ViewContext<Self>,
4647 ) {
4648 self.transact(cx, |this, cx| {
4649 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4650 s.move_with(|map, selection| {
4651 if selection.is_empty() {
4652 let cursor = movement::next_subword_end(map, selection.head());
4653 selection.set_head(cursor, SelectionGoal::None);
4654 }
4655 });
4656 });
4657 this.insert("", cx);
4658 });
4659 }
4660
4661 pub fn move_to_beginning_of_line(
4662 &mut self,
4663 _: &MoveToBeginningOfLine,
4664 cx: &mut ViewContext<Self>,
4665 ) {
4666 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4667 s.move_cursors_with(|map, head, _| {
4668 (
4669 movement::indented_line_beginning(map, head, true),
4670 SelectionGoal::None,
4671 )
4672 });
4673 })
4674 }
4675
4676 pub fn select_to_beginning_of_line(
4677 &mut self,
4678 action: &SelectToBeginningOfLine,
4679 cx: &mut ViewContext<Self>,
4680 ) {
4681 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4682 s.move_heads_with(|map, head, _| {
4683 (
4684 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4685 SelectionGoal::None,
4686 )
4687 });
4688 });
4689 }
4690
4691 pub fn delete_to_beginning_of_line(
4692 &mut self,
4693 _: &DeleteToBeginningOfLine,
4694 cx: &mut ViewContext<Self>,
4695 ) {
4696 self.transact(cx, |this, cx| {
4697 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4698 s.move_with(|_, selection| {
4699 selection.reversed = true;
4700 });
4701 });
4702
4703 this.select_to_beginning_of_line(
4704 &SelectToBeginningOfLine {
4705 stop_at_soft_wraps: false,
4706 },
4707 cx,
4708 );
4709 this.backspace(&Backspace, cx);
4710 });
4711 }
4712
4713 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4714 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4715 s.move_cursors_with(|map, head, _| {
4716 (movement::line_end(map, head, true), SelectionGoal::None)
4717 });
4718 })
4719 }
4720
4721 pub fn select_to_end_of_line(
4722 &mut self,
4723 action: &SelectToEndOfLine,
4724 cx: &mut ViewContext<Self>,
4725 ) {
4726 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4727 s.move_heads_with(|map, head, _| {
4728 (
4729 movement::line_end(map, head, action.stop_at_soft_wraps),
4730 SelectionGoal::None,
4731 )
4732 });
4733 })
4734 }
4735
4736 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4737 self.transact(cx, |this, cx| {
4738 this.select_to_end_of_line(
4739 &SelectToEndOfLine {
4740 stop_at_soft_wraps: false,
4741 },
4742 cx,
4743 );
4744 this.delete(&Delete, cx);
4745 });
4746 }
4747
4748 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4749 self.transact(cx, |this, cx| {
4750 this.select_to_end_of_line(
4751 &SelectToEndOfLine {
4752 stop_at_soft_wraps: false,
4753 },
4754 cx,
4755 );
4756 this.cut(&Cut, cx);
4757 });
4758 }
4759
4760 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4761 if matches!(self.mode, EditorMode::SingleLine) {
4762 cx.propagate_action();
4763 return;
4764 }
4765
4766 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4767 s.select_ranges(vec![0..0]);
4768 });
4769 }
4770
4771 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4772 let mut selection = self.selections.last::<Point>(cx);
4773 selection.set_head(Point::zero(), SelectionGoal::None);
4774
4775 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4776 s.select(vec![selection]);
4777 });
4778 }
4779
4780 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4781 if matches!(self.mode, EditorMode::SingleLine) {
4782 cx.propagate_action();
4783 return;
4784 }
4785
4786 let cursor = self.buffer.read(cx).read(cx).len();
4787 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4788 s.select_ranges(vec![cursor..cursor])
4789 });
4790 }
4791
4792 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4793 self.nav_history = nav_history;
4794 }
4795
4796 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4797 self.nav_history.as_ref()
4798 }
4799
4800 fn push_to_nav_history(
4801 &self,
4802 cursor_anchor: Anchor,
4803 new_position: Option<Point>,
4804 cx: &mut ViewContext<Self>,
4805 ) {
4806 if let Some(nav_history) = &self.nav_history {
4807 let buffer = self.buffer.read(cx).read(cx);
4808 let cursor_position = cursor_anchor.to_point(&buffer);
4809 let scroll_state = self.scroll_manager.anchor();
4810 let scroll_top_row = scroll_state.top_row(&buffer);
4811 drop(buffer);
4812
4813 if let Some(new_position) = new_position {
4814 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
4815 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4816 return;
4817 }
4818 }
4819
4820 nav_history.push(
4821 Some(NavigationData {
4822 cursor_anchor,
4823 cursor_position,
4824 scroll_anchor: scroll_state,
4825 scroll_top_row,
4826 }),
4827 cx,
4828 );
4829 }
4830 }
4831
4832 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4833 let buffer = self.buffer.read(cx).snapshot(cx);
4834 let mut selection = self.selections.first::<usize>(cx);
4835 selection.set_head(buffer.len(), SelectionGoal::None);
4836 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4837 s.select(vec![selection]);
4838 });
4839 }
4840
4841 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4842 let end = self.buffer.read(cx).read(cx).len();
4843 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4844 s.select_ranges(vec![0..end]);
4845 });
4846 }
4847
4848 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4849 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4850 let mut selections = self.selections.all::<Point>(cx);
4851 let max_point = display_map.buffer_snapshot.max_point();
4852 for selection in &mut selections {
4853 let rows = selection.spanned_rows(true, &display_map);
4854 selection.start = Point::new(rows.start, 0);
4855 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4856 selection.reversed = false;
4857 }
4858 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4859 s.select(selections);
4860 });
4861 }
4862
4863 pub fn split_selection_into_lines(
4864 &mut self,
4865 _: &SplitSelectionIntoLines,
4866 cx: &mut ViewContext<Self>,
4867 ) {
4868 let mut to_unfold = Vec::new();
4869 let mut new_selection_ranges = Vec::new();
4870 {
4871 let selections = self.selections.all::<Point>(cx);
4872 let buffer = self.buffer.read(cx).read(cx);
4873 for selection in selections {
4874 for row in selection.start.row..selection.end.row {
4875 let cursor = Point::new(row, buffer.line_len(row));
4876 new_selection_ranges.push(cursor..cursor);
4877 }
4878 new_selection_ranges.push(selection.end..selection.end);
4879 to_unfold.push(selection.start..selection.end);
4880 }
4881 }
4882 self.unfold_ranges(to_unfold, true, true, cx);
4883 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4884 s.select_ranges(new_selection_ranges);
4885 });
4886 }
4887
4888 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4889 self.add_selection(true, cx);
4890 }
4891
4892 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4893 self.add_selection(false, cx);
4894 }
4895
4896 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4897 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4898 let mut selections = self.selections.all::<Point>(cx);
4899 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4900 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4901 let range = oldest_selection.display_range(&display_map).sorted();
4902 let columns = cmp::min(range.start.column(), range.end.column())
4903 ..cmp::max(range.start.column(), range.end.column());
4904
4905 selections.clear();
4906 let mut stack = Vec::new();
4907 for row in range.start.row()..=range.end.row() {
4908 if let Some(selection) = self.selections.build_columnar_selection(
4909 &display_map,
4910 row,
4911 &columns,
4912 oldest_selection.reversed,
4913 ) {
4914 stack.push(selection.id);
4915 selections.push(selection);
4916 }
4917 }
4918
4919 if above {
4920 stack.reverse();
4921 }
4922
4923 AddSelectionsState { above, stack }
4924 });
4925
4926 let last_added_selection = *state.stack.last().unwrap();
4927 let mut new_selections = Vec::new();
4928 if above == state.above {
4929 let end_row = if above {
4930 0
4931 } else {
4932 display_map.max_point().row()
4933 };
4934
4935 'outer: for selection in selections {
4936 if selection.id == last_added_selection {
4937 let range = selection.display_range(&display_map).sorted();
4938 debug_assert_eq!(range.start.row(), range.end.row());
4939 let mut row = range.start.row();
4940 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4941 {
4942 start..end
4943 } else {
4944 cmp::min(range.start.column(), range.end.column())
4945 ..cmp::max(range.start.column(), range.end.column())
4946 };
4947
4948 while row != end_row {
4949 if above {
4950 row -= 1;
4951 } else {
4952 row += 1;
4953 }
4954
4955 if let Some(new_selection) = self.selections.build_columnar_selection(
4956 &display_map,
4957 row,
4958 &columns,
4959 selection.reversed,
4960 ) {
4961 state.stack.push(new_selection.id);
4962 if above {
4963 new_selections.push(new_selection);
4964 new_selections.push(selection);
4965 } else {
4966 new_selections.push(selection);
4967 new_selections.push(new_selection);
4968 }
4969
4970 continue 'outer;
4971 }
4972 }
4973 }
4974
4975 new_selections.push(selection);
4976 }
4977 } else {
4978 new_selections = selections;
4979 new_selections.retain(|s| s.id != last_added_selection);
4980 state.stack.pop();
4981 }
4982
4983 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4984 s.select(new_selections);
4985 });
4986 if state.stack.len() > 1 {
4987 self.add_selections_state = Some(state);
4988 }
4989 }
4990
4991 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4992 self.push_to_selection_history();
4993 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4994 let buffer = &display_map.buffer_snapshot;
4995 let mut selections = self.selections.all::<usize>(cx);
4996 if let Some(mut select_next_state) = self.select_next_state.take() {
4997 let query = &select_next_state.query;
4998 if !select_next_state.done {
4999 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5000 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5001 let mut next_selected_range = None;
5002
5003 let bytes_after_last_selection =
5004 buffer.bytes_in_range(last_selection.end..buffer.len());
5005 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5006 let query_matches = query
5007 .stream_find_iter(bytes_after_last_selection)
5008 .map(|result| (last_selection.end, result))
5009 .chain(
5010 query
5011 .stream_find_iter(bytes_before_first_selection)
5012 .map(|result| (0, result)),
5013 );
5014 for (start_offset, query_match) in query_matches {
5015 let query_match = query_match.unwrap(); // can only fail due to I/O
5016 let offset_range =
5017 start_offset + query_match.start()..start_offset + query_match.end();
5018 let display_range = offset_range.start.to_display_point(&display_map)
5019 ..offset_range.end.to_display_point(&display_map);
5020
5021 if !select_next_state.wordwise
5022 || (!movement::is_inside_word(&display_map, display_range.start)
5023 && !movement::is_inside_word(&display_map, display_range.end))
5024 {
5025 next_selected_range = Some(offset_range);
5026 break;
5027 }
5028 }
5029
5030 if let Some(next_selected_range) = next_selected_range {
5031 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5032 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5033 if action.replace_newest {
5034 s.delete(s.newest_anchor().id);
5035 }
5036 s.insert_range(next_selected_range);
5037 });
5038 } else {
5039 select_next_state.done = true;
5040 }
5041 }
5042
5043 self.select_next_state = Some(select_next_state);
5044 } else if selections.len() == 1 {
5045 let selection = selections.last_mut().unwrap();
5046 if selection.start == selection.end {
5047 let word_range = movement::surrounding_word(
5048 &display_map,
5049 selection.start.to_display_point(&display_map),
5050 );
5051 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5052 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5053 selection.goal = SelectionGoal::None;
5054 selection.reversed = false;
5055
5056 let query = buffer
5057 .text_for_range(selection.start..selection.end)
5058 .collect::<String>();
5059 let select_state = SelectNextState {
5060 query: AhoCorasick::new_auto_configured(&[query]),
5061 wordwise: true,
5062 done: false,
5063 };
5064 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5065 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5066 s.select(selections);
5067 });
5068 self.select_next_state = Some(select_state);
5069 } else {
5070 let query = buffer
5071 .text_for_range(selection.start..selection.end)
5072 .collect::<String>();
5073 self.select_next_state = Some(SelectNextState {
5074 query: AhoCorasick::new_auto_configured(&[query]),
5075 wordwise: false,
5076 done: false,
5077 });
5078 self.select_next(action, cx);
5079 }
5080 }
5081 }
5082
5083 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5084 self.transact(cx, |this, cx| {
5085 let mut selections = this.selections.all::<Point>(cx);
5086 let mut edits = Vec::new();
5087 let mut selection_edit_ranges = Vec::new();
5088 let mut last_toggled_row = None;
5089 let snapshot = this.buffer.read(cx).read(cx);
5090 let empty_str: Arc<str> = "".into();
5091 let mut suffixes_inserted = Vec::new();
5092
5093 fn comment_prefix_range(
5094 snapshot: &MultiBufferSnapshot,
5095 row: u32,
5096 comment_prefix: &str,
5097 comment_prefix_whitespace: &str,
5098 ) -> Range<Point> {
5099 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5100
5101 let mut line_bytes = snapshot
5102 .bytes_in_range(start..snapshot.max_point())
5103 .flatten()
5104 .copied();
5105
5106 // If this line currently begins with the line comment prefix, then record
5107 // the range containing the prefix.
5108 if line_bytes
5109 .by_ref()
5110 .take(comment_prefix.len())
5111 .eq(comment_prefix.bytes())
5112 {
5113 // Include any whitespace that matches the comment prefix.
5114 let matching_whitespace_len = line_bytes
5115 .zip(comment_prefix_whitespace.bytes())
5116 .take_while(|(a, b)| a == b)
5117 .count() as u32;
5118 let end = Point::new(
5119 start.row,
5120 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5121 );
5122 start..end
5123 } else {
5124 start..start
5125 }
5126 }
5127
5128 fn comment_suffix_range(
5129 snapshot: &MultiBufferSnapshot,
5130 row: u32,
5131 comment_suffix: &str,
5132 comment_suffix_has_leading_space: bool,
5133 ) -> Range<Point> {
5134 let end = Point::new(row, snapshot.line_len(row));
5135 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5136
5137 let mut line_end_bytes = snapshot
5138 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5139 .flatten()
5140 .copied();
5141
5142 let leading_space_len = if suffix_start_column > 0
5143 && line_end_bytes.next() == Some(b' ')
5144 && comment_suffix_has_leading_space
5145 {
5146 1
5147 } else {
5148 0
5149 };
5150
5151 // If this line currently begins with the line comment prefix, then record
5152 // the range containing the prefix.
5153 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5154 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5155 start..end
5156 } else {
5157 end..end
5158 }
5159 }
5160
5161 // TODO: Handle selections that cross excerpts
5162 for selection in &mut selections {
5163 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5164 let language = if let Some(language) =
5165 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5166 {
5167 language
5168 } else {
5169 continue;
5170 };
5171
5172 selection_edit_ranges.clear();
5173
5174 // If multiple selections contain a given row, avoid processing that
5175 // row more than once.
5176 let mut start_row = selection.start.row;
5177 if last_toggled_row == Some(start_row) {
5178 start_row += 1;
5179 }
5180 let end_row =
5181 if selection.end.row > selection.start.row && selection.end.column == 0 {
5182 selection.end.row - 1
5183 } else {
5184 selection.end.row
5185 };
5186 last_toggled_row = Some(end_row);
5187
5188 if start_row > end_row {
5189 continue;
5190 }
5191
5192 // If the language has line comments, toggle those.
5193 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5194 // Split the comment prefix's trailing whitespace into a separate string,
5195 // as that portion won't be used for detecting if a line is a comment.
5196 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5197 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5198 let mut all_selection_lines_are_comments = true;
5199
5200 for row in start_row..=end_row {
5201 if snapshot.is_line_blank(row) {
5202 continue;
5203 }
5204
5205 let prefix_range = comment_prefix_range(
5206 snapshot.deref(),
5207 row,
5208 comment_prefix,
5209 comment_prefix_whitespace,
5210 );
5211 if prefix_range.is_empty() {
5212 all_selection_lines_are_comments = false;
5213 }
5214 selection_edit_ranges.push(prefix_range);
5215 }
5216
5217 if all_selection_lines_are_comments {
5218 edits.extend(
5219 selection_edit_ranges
5220 .iter()
5221 .cloned()
5222 .map(|range| (range, empty_str.clone())),
5223 );
5224 } else {
5225 let min_column = selection_edit_ranges
5226 .iter()
5227 .map(|r| r.start.column)
5228 .min()
5229 .unwrap_or(0);
5230 edits.extend(selection_edit_ranges.iter().map(|range| {
5231 let position = Point::new(range.start.row, min_column);
5232 (position..position, full_comment_prefix.clone())
5233 }));
5234 }
5235 } else if let Some((full_comment_prefix, comment_suffix)) =
5236 language.block_comment_delimiters()
5237 {
5238 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5239 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5240 let prefix_range = comment_prefix_range(
5241 snapshot.deref(),
5242 start_row,
5243 comment_prefix,
5244 comment_prefix_whitespace,
5245 );
5246 let suffix_range = comment_suffix_range(
5247 snapshot.deref(),
5248 end_row,
5249 comment_suffix.trim_start_matches(' '),
5250 comment_suffix.starts_with(' '),
5251 );
5252
5253 if prefix_range.is_empty() || suffix_range.is_empty() {
5254 edits.push((
5255 prefix_range.start..prefix_range.start,
5256 full_comment_prefix.clone(),
5257 ));
5258 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5259 suffixes_inserted.push((end_row, comment_suffix.len()));
5260 } else {
5261 edits.push((prefix_range, empty_str.clone()));
5262 edits.push((suffix_range, empty_str.clone()));
5263 }
5264 } else {
5265 continue;
5266 }
5267 }
5268
5269 drop(snapshot);
5270 this.buffer.update(cx, |buffer, cx| {
5271 buffer.edit(edits, None, cx);
5272 });
5273
5274 // Adjust selections so that they end before any comment suffixes that
5275 // were inserted.
5276 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5277 let mut selections = this.selections.all::<Point>(cx);
5278 let snapshot = this.buffer.read(cx).read(cx);
5279 for selection in &mut selections {
5280 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5281 match row.cmp(&selection.end.row) {
5282 Ordering::Less => {
5283 suffixes_inserted.next();
5284 continue;
5285 }
5286 Ordering::Greater => break,
5287 Ordering::Equal => {
5288 if selection.end.column == snapshot.line_len(row) {
5289 if selection.is_empty() {
5290 selection.start.column -= suffix_len as u32;
5291 }
5292 selection.end.column -= suffix_len as u32;
5293 }
5294 break;
5295 }
5296 }
5297 }
5298 }
5299
5300 drop(snapshot);
5301 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5302
5303 let selections = this.selections.all::<Point>(cx);
5304 let selections_on_single_row = selections.windows(2).all(|selections| {
5305 selections[0].start.row == selections[1].start.row
5306 && selections[0].end.row == selections[1].end.row
5307 && selections[0].start.row == selections[0].end.row
5308 });
5309 let selections_selecting = selections
5310 .iter()
5311 .any(|selection| selection.start != selection.end);
5312 let advance_downwards = action.advance_downwards
5313 && selections_on_single_row
5314 && !selections_selecting
5315 && this.mode != EditorMode::SingleLine;
5316
5317 if advance_downwards {
5318 let snapshot = this.buffer.read(cx).snapshot(cx);
5319
5320 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5321 s.move_cursors_with(|display_snapshot, display_point, _| {
5322 let mut point = display_point.to_point(display_snapshot);
5323 point.row += 1;
5324 point = snapshot.clip_point(point, Bias::Left);
5325 let display_point = point.to_display_point(display_snapshot);
5326 (display_point, SelectionGoal::Column(display_point.column()))
5327 })
5328 });
5329 }
5330 });
5331 }
5332
5333 pub fn select_larger_syntax_node(
5334 &mut self,
5335 _: &SelectLargerSyntaxNode,
5336 cx: &mut ViewContext<Self>,
5337 ) {
5338 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5339 let buffer = self.buffer.read(cx).snapshot(cx);
5340 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5341
5342 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5343 let mut selected_larger_node = false;
5344 let new_selections = old_selections
5345 .iter()
5346 .map(|selection| {
5347 let old_range = selection.start..selection.end;
5348 let mut new_range = old_range.clone();
5349 while let Some(containing_range) =
5350 buffer.range_for_syntax_ancestor(new_range.clone())
5351 {
5352 new_range = containing_range;
5353 if !display_map.intersects_fold(new_range.start)
5354 && !display_map.intersects_fold(new_range.end)
5355 {
5356 break;
5357 }
5358 }
5359
5360 selected_larger_node |= new_range != old_range;
5361 Selection {
5362 id: selection.id,
5363 start: new_range.start,
5364 end: new_range.end,
5365 goal: SelectionGoal::None,
5366 reversed: selection.reversed,
5367 }
5368 })
5369 .collect::<Vec<_>>();
5370
5371 if selected_larger_node {
5372 stack.push(old_selections);
5373 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5374 s.select(new_selections);
5375 });
5376 }
5377 self.select_larger_syntax_node_stack = stack;
5378 }
5379
5380 pub fn select_smaller_syntax_node(
5381 &mut self,
5382 _: &SelectSmallerSyntaxNode,
5383 cx: &mut ViewContext<Self>,
5384 ) {
5385 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5386 if let Some(selections) = stack.pop() {
5387 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5388 s.select(selections.to_vec());
5389 });
5390 }
5391 self.select_larger_syntax_node_stack = stack;
5392 }
5393
5394 pub fn move_to_enclosing_bracket(
5395 &mut self,
5396 _: &MoveToEnclosingBracket,
5397 cx: &mut ViewContext<Self>,
5398 ) {
5399 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5400 s.move_offsets_with(|snapshot, selection| {
5401 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5402 return;
5403 };
5404
5405 let mut best_length = usize::MAX;
5406 let mut best_inside = false;
5407 let mut best_in_bracket_range = false;
5408 let mut best_destination = None;
5409 for (open, close) in enclosing_bracket_ranges {
5410 let close = close.to_inclusive();
5411 let length = close.end() - open.start;
5412 let inside = selection.start >= open.end && selection.end <= *close.start();
5413 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5414
5415 // If best is next to a bracket and current isn't, skip
5416 if !in_bracket_range && best_in_bracket_range {
5417 continue;
5418 }
5419
5420 // Prefer smaller lengths unless best is inside and current isn't
5421 if length > best_length && (best_inside || !inside) {
5422 continue;
5423 }
5424
5425 best_length = length;
5426 best_inside = inside;
5427 best_in_bracket_range = in_bracket_range;
5428 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5429 if inside {
5430 open.end
5431 } else {
5432 open.start
5433 }
5434 } else {
5435 if inside {
5436 *close.start()
5437 } else {
5438 *close.end()
5439 }
5440 });
5441 }
5442
5443 if let Some(destination) = best_destination {
5444 selection.collapse_to(destination, SelectionGoal::None);
5445 }
5446 })
5447 });
5448 }
5449
5450 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5451 self.end_selection(cx);
5452 self.selection_history.mode = SelectionHistoryMode::Undoing;
5453 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5454 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5455 self.select_next_state = entry.select_next_state;
5456 self.add_selections_state = entry.add_selections_state;
5457 self.request_autoscroll(Autoscroll::newest(), cx);
5458 }
5459 self.selection_history.mode = SelectionHistoryMode::Normal;
5460 }
5461
5462 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5463 self.end_selection(cx);
5464 self.selection_history.mode = SelectionHistoryMode::Redoing;
5465 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5466 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5467 self.select_next_state = entry.select_next_state;
5468 self.add_selections_state = entry.add_selections_state;
5469 self.request_autoscroll(Autoscroll::newest(), cx);
5470 }
5471 self.selection_history.mode = SelectionHistoryMode::Normal;
5472 }
5473
5474 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5475 self.go_to_diagnostic_impl(Direction::Next, cx)
5476 }
5477
5478 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5479 self.go_to_diagnostic_impl(Direction::Prev, cx)
5480 }
5481
5482 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5483 let buffer = self.buffer.read(cx).snapshot(cx);
5484 let selection = self.selections.newest::<usize>(cx);
5485
5486 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5487 if direction == Direction::Next {
5488 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5489 let (group_id, jump_to) = popover.activation_info();
5490 if self.activate_diagnostics(group_id, cx) {
5491 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5492 let mut new_selection = s.newest_anchor().clone();
5493 new_selection.collapse_to(jump_to, SelectionGoal::None);
5494 s.select_anchors(vec![new_selection.clone()]);
5495 });
5496 }
5497 return;
5498 }
5499 }
5500
5501 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5502 active_diagnostics
5503 .primary_range
5504 .to_offset(&buffer)
5505 .to_inclusive()
5506 });
5507 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5508 if active_primary_range.contains(&selection.head()) {
5509 *active_primary_range.end()
5510 } else {
5511 selection.head()
5512 }
5513 } else {
5514 selection.head()
5515 };
5516
5517 loop {
5518 let mut diagnostics = if direction == Direction::Prev {
5519 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5520 } else {
5521 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5522 };
5523 let group = diagnostics.find_map(|entry| {
5524 if entry.diagnostic.is_primary
5525 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5526 && !entry.range.is_empty()
5527 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5528 {
5529 Some((entry.range, entry.diagnostic.group_id))
5530 } else {
5531 None
5532 }
5533 });
5534
5535 if let Some((primary_range, group_id)) = group {
5536 if self.activate_diagnostics(group_id, cx) {
5537 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5538 s.select(vec![Selection {
5539 id: selection.id,
5540 start: primary_range.start,
5541 end: primary_range.start,
5542 reversed: false,
5543 goal: SelectionGoal::None,
5544 }]);
5545 });
5546 }
5547 break;
5548 } else {
5549 // Cycle around to the start of the buffer, potentially moving back to the start of
5550 // the currently active diagnostic.
5551 active_primary_range.take();
5552 if direction == Direction::Prev {
5553 if search_start == buffer.len() {
5554 break;
5555 } else {
5556 search_start = buffer.len();
5557 }
5558 } else if search_start == 0 {
5559 break;
5560 } else {
5561 search_start = 0;
5562 }
5563 }
5564 }
5565 }
5566
5567 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5568 self.go_to_hunk_impl(Direction::Next, cx)
5569 }
5570
5571 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5572 self.go_to_hunk_impl(Direction::Prev, cx)
5573 }
5574
5575 pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5576 let snapshot = self
5577 .display_map
5578 .update(cx, |display_map, cx| display_map.snapshot(cx));
5579 let selection = self.selections.newest::<Point>(cx);
5580
5581 fn seek_in_direction(
5582 this: &mut Editor,
5583 snapshot: &DisplaySnapshot,
5584 initial_point: Point,
5585 is_wrapped: bool,
5586 direction: Direction,
5587 cx: &mut ViewContext<Editor>,
5588 ) -> bool {
5589 let hunks = if direction == Direction::Next {
5590 snapshot
5591 .buffer_snapshot
5592 .git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
5593 } else {
5594 snapshot
5595 .buffer_snapshot
5596 .git_diff_hunks_in_range(0..initial_point.row, true)
5597 };
5598
5599 let display_point = initial_point.to_display_point(snapshot);
5600 let mut hunks = hunks
5601 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5602 .skip_while(|hunk| {
5603 if is_wrapped {
5604 false
5605 } else {
5606 hunk.contains_display_row(display_point.row())
5607 }
5608 })
5609 .dedup();
5610
5611 if let Some(hunk) = hunks.next() {
5612 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5613 let row = hunk.start_display_row();
5614 let point = DisplayPoint::new(row, 0);
5615 s.select_display_ranges([point..point]);
5616 });
5617
5618 true
5619 } else {
5620 false
5621 }
5622 }
5623
5624 if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
5625 let wrapped_point = match direction {
5626 Direction::Next => Point::zero(),
5627 Direction::Prev => snapshot.buffer_snapshot.max_point(),
5628 };
5629 seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
5630 }
5631 }
5632
5633 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
5634 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
5635 }
5636
5637 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
5638 self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
5639 }
5640
5641 fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
5642 let Some(workspace) = self.workspace(cx) else { return };
5643 let buffer = self.buffer.read(cx);
5644 let head = self.selections.newest::<usize>(cx).head();
5645 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5646 text_anchor
5647 } else {
5648 return;
5649 };
5650
5651 let project = workspace.read(cx).project().clone();
5652 let definitions = project.update(cx, |project, cx| match kind {
5653 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5654 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5655 });
5656
5657 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
5658 let definitions = definitions.await?;
5659 editor.update(&mut cx, |editor, cx| {
5660 editor.navigate_to_definitions(definitions, cx);
5661 })?;
5662 Ok::<(), anyhow::Error>(())
5663 })
5664 .detach_and_log_err(cx);
5665 }
5666
5667 pub fn navigate_to_definitions(
5668 &mut self,
5669 mut definitions: Vec<LocationLink>,
5670 cx: &mut ViewContext<Editor>,
5671 ) {
5672 let Some(workspace) = self.workspace(cx) else { return };
5673 let pane = workspace.read(cx).active_pane().clone();
5674 // If there is one definition, just open it directly
5675 if definitions.len() == 1 {
5676 let definition = definitions.pop().unwrap();
5677 let range = definition
5678 .target
5679 .range
5680 .to_offset(definition.target.buffer.read(cx));
5681
5682 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
5683 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5684 s.select_ranges([range]);
5685 });
5686 } else {
5687 cx.window_context().defer(move |cx| {
5688 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
5689 workspace.open_project_item(definition.target.buffer.clone(), cx)
5690 });
5691 target_editor.update(cx, |target_editor, cx| {
5692 // When selecting a definition in a different buffer, disable the nav history
5693 // to avoid creating a history entry at the previous cursor location.
5694 pane.update(cx, |pane, _| pane.disable_history());
5695 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
5696 s.select_ranges([range]);
5697 });
5698 pane.update(cx, |pane, _| pane.enable_history());
5699 });
5700 });
5701 }
5702 } else if !definitions.is_empty() {
5703 let replica_id = self.replica_id(cx);
5704 cx.window_context().defer(move |cx| {
5705 let title = definitions
5706 .iter()
5707 .find(|definition| definition.origin.is_some())
5708 .and_then(|definition| {
5709 definition.origin.as_ref().map(|origin| {
5710 let buffer = origin.buffer.read(cx);
5711 format!(
5712 "Definitions for {}",
5713 buffer
5714 .text_for_range(origin.range.clone())
5715 .collect::<String>()
5716 )
5717 })
5718 })
5719 .unwrap_or("Definitions".to_owned());
5720 let locations = definitions
5721 .into_iter()
5722 .map(|definition| definition.target)
5723 .collect();
5724 workspace.update(cx, |workspace, cx| {
5725 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5726 });
5727 });
5728 }
5729 }
5730
5731 pub fn find_all_references(
5732 workspace: &mut Workspace,
5733 _: &FindAllReferences,
5734 cx: &mut ViewContext<Workspace>,
5735 ) -> Option<Task<Result<()>>> {
5736 let active_item = workspace.active_item(cx)?;
5737 let editor_handle = active_item.act_as::<Self>(cx)?;
5738
5739 let editor = editor_handle.read(cx);
5740 let buffer = editor.buffer.read(cx);
5741 let head = editor.selections.newest::<usize>(cx).head();
5742 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5743 let replica_id = editor.replica_id(cx);
5744
5745 let project = workspace.project().clone();
5746 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5747 Some(cx.spawn_labeled(
5748 "Finding All References...",
5749 |workspace, mut cx| async move {
5750 let locations = references.await?;
5751 if locations.is_empty() {
5752 return Ok(());
5753 }
5754
5755 workspace.update(&mut cx, |workspace, cx| {
5756 let title = locations
5757 .first()
5758 .as_ref()
5759 .map(|location| {
5760 let buffer = location.buffer.read(cx);
5761 format!(
5762 "References to `{}`",
5763 buffer
5764 .text_for_range(location.range.clone())
5765 .collect::<String>()
5766 )
5767 })
5768 .unwrap();
5769 Self::open_locations_in_multibuffer(
5770 workspace, locations, replica_id, title, cx,
5771 );
5772 })?;
5773
5774 Ok(())
5775 },
5776 ))
5777 }
5778
5779 /// Opens a multibuffer with the given project locations in it
5780 pub fn open_locations_in_multibuffer(
5781 workspace: &mut Workspace,
5782 mut locations: Vec<Location>,
5783 replica_id: ReplicaId,
5784 title: String,
5785 cx: &mut ViewContext<Workspace>,
5786 ) {
5787 // If there are multiple definitions, open them in a multibuffer
5788 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
5789 let mut locations = locations.into_iter().peekable();
5790 let mut ranges_to_highlight = Vec::new();
5791
5792 let excerpt_buffer = cx.add_model(|cx| {
5793 let mut multibuffer = MultiBuffer::new(replica_id);
5794 while let Some(location) = locations.next() {
5795 let buffer = location.buffer.read(cx);
5796 let mut ranges_for_buffer = Vec::new();
5797 let range = location.range.to_offset(buffer);
5798 ranges_for_buffer.push(range.clone());
5799
5800 while let Some(next_location) = locations.peek() {
5801 if next_location.buffer == location.buffer {
5802 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5803 locations.next();
5804 } else {
5805 break;
5806 }
5807 }
5808
5809 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5810 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5811 location.buffer.clone(),
5812 ranges_for_buffer,
5813 1,
5814 cx,
5815 ))
5816 }
5817
5818 multibuffer.with_title(title)
5819 });
5820
5821 let editor = cx.add_view(|cx| {
5822 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5823 });
5824 editor.update(cx, |editor, cx| {
5825 editor.highlight_background::<Self>(
5826 ranges_to_highlight,
5827 |theme| theme.editor.highlighted_line_background,
5828 cx,
5829 );
5830 });
5831 workspace.add_item(Box::new(editor), cx);
5832 }
5833
5834 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5835 use language::ToOffset as _;
5836
5837 let project = self.project.clone()?;
5838 let selection = self.selections.newest_anchor().clone();
5839 let (cursor_buffer, cursor_buffer_position) = self
5840 .buffer
5841 .read(cx)
5842 .text_anchor_for_position(selection.head(), cx)?;
5843 let (tail_buffer, _) = self
5844 .buffer
5845 .read(cx)
5846 .text_anchor_for_position(selection.tail(), cx)?;
5847 if tail_buffer != cursor_buffer {
5848 return None;
5849 }
5850
5851 let snapshot = cursor_buffer.read(cx).snapshot();
5852 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5853 let prepare_rename = project.update(cx, |project, cx| {
5854 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5855 });
5856
5857 Some(cx.spawn(|this, mut cx| async move {
5858 let rename_range = if let Some(range) = prepare_rename.await? {
5859 Some(range)
5860 } else {
5861 this.read_with(&cx, |this, cx| {
5862 let buffer = this.buffer.read(cx).snapshot(cx);
5863 let mut buffer_highlights = this
5864 .document_highlights_for_position(selection.head(), &buffer)
5865 .filter(|highlight| {
5866 highlight.start.excerpt_id() == selection.head().excerpt_id()
5867 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5868 });
5869 buffer_highlights
5870 .next()
5871 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5872 })?
5873 };
5874 if let Some(rename_range) = rename_range {
5875 let rename_buffer_range = rename_range.to_offset(&snapshot);
5876 let cursor_offset_in_rename_range =
5877 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5878
5879 this.update(&mut cx, |this, cx| {
5880 this.take_rename(false, cx);
5881 let style = this.style(cx);
5882 let buffer = this.buffer.read(cx).read(cx);
5883 let cursor_offset = selection.head().to_offset(&buffer);
5884 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5885 let rename_end = rename_start + rename_buffer_range.len();
5886 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5887 let mut old_highlight_id = None;
5888 let old_name: Arc<str> = buffer
5889 .chunks(rename_start..rename_end, true)
5890 .map(|chunk| {
5891 if old_highlight_id.is_none() {
5892 old_highlight_id = chunk.syntax_highlight_id;
5893 }
5894 chunk.text
5895 })
5896 .collect::<String>()
5897 .into();
5898
5899 drop(buffer);
5900
5901 // Position the selection in the rename editor so that it matches the current selection.
5902 this.show_local_selections = false;
5903 let rename_editor = cx.add_view(|cx| {
5904 let mut editor = Editor::single_line(None, cx);
5905 if let Some(old_highlight_id) = old_highlight_id {
5906 editor.override_text_style =
5907 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5908 }
5909 editor.buffer.update(cx, |buffer, cx| {
5910 buffer.edit([(0..0, old_name.clone())], None, cx)
5911 });
5912 editor.select_all(&SelectAll, cx);
5913 editor
5914 });
5915
5916 let ranges = this
5917 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5918 .into_iter()
5919 .flat_map(|(_, ranges)| ranges)
5920 .chain(
5921 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5922 .into_iter()
5923 .flat_map(|(_, ranges)| ranges),
5924 )
5925 .collect();
5926
5927 this.highlight_text::<Rename>(
5928 ranges,
5929 HighlightStyle {
5930 fade_out: Some(style.rename_fade),
5931 ..Default::default()
5932 },
5933 cx,
5934 );
5935 cx.focus(&rename_editor);
5936 let block_id = this.insert_blocks(
5937 [BlockProperties {
5938 style: BlockStyle::Flex,
5939 position: range.start.clone(),
5940 height: 1,
5941 render: Arc::new({
5942 let editor = rename_editor.clone();
5943 move |cx: &mut BlockContext| {
5944 ChildView::new(&editor, cx)
5945 .contained()
5946 .with_padding_left(cx.anchor_x)
5947 .into_any()
5948 }
5949 }),
5950 disposition: BlockDisposition::Below,
5951 }],
5952 cx,
5953 )[0];
5954 this.pending_rename = Some(RenameState {
5955 range,
5956 old_name,
5957 editor: rename_editor,
5958 block_id,
5959 });
5960 })?;
5961 }
5962
5963 Ok(())
5964 }))
5965 }
5966
5967 pub fn confirm_rename(
5968 workspace: &mut Workspace,
5969 _: &ConfirmRename,
5970 cx: &mut ViewContext<Workspace>,
5971 ) -> Option<Task<Result<()>>> {
5972 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5973
5974 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5975 let rename = editor.take_rename(false, cx)?;
5976 let buffer = editor.buffer.read(cx);
5977 let (start_buffer, start) =
5978 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5979 let (end_buffer, end) =
5980 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5981 if start_buffer == end_buffer {
5982 let new_name = rename.editor.read(cx).text(cx);
5983 Some((start_buffer, start..end, rename.old_name, new_name))
5984 } else {
5985 None
5986 }
5987 })?;
5988
5989 let rename = workspace.project().clone().update(cx, |project, cx| {
5990 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5991 });
5992
5993 let editor = editor.downgrade();
5994 Some(cx.spawn(|workspace, mut cx| async move {
5995 let project_transaction = rename.await?;
5996 Self::open_project_transaction(
5997 &editor,
5998 workspace,
5999 project_transaction,
6000 format!("Rename: {} → {}", old_name, new_name),
6001 cx.clone(),
6002 )
6003 .await?;
6004
6005 editor.update(&mut cx, |editor, cx| {
6006 editor.refresh_document_highlights(cx);
6007 })?;
6008 Ok(())
6009 }))
6010 }
6011
6012 fn take_rename(
6013 &mut self,
6014 moving_cursor: bool,
6015 cx: &mut ViewContext<Self>,
6016 ) -> Option<RenameState> {
6017 let rename = self.pending_rename.take()?;
6018 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
6019 self.clear_text_highlights::<Rename>(cx);
6020 self.show_local_selections = true;
6021
6022 if moving_cursor {
6023 let rename_editor = rename.editor.read(cx);
6024 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6025
6026 // Update the selection to match the position of the selection inside
6027 // the rename editor.
6028 let snapshot = self.buffer.read(cx).read(cx);
6029 let rename_range = rename.range.to_offset(&snapshot);
6030 let cursor_in_editor = snapshot
6031 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6032 .min(rename_range.end);
6033 drop(snapshot);
6034
6035 self.change_selections(None, cx, |s| {
6036 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6037 });
6038 } else {
6039 self.refresh_document_highlights(cx);
6040 }
6041
6042 Some(rename)
6043 }
6044
6045 #[cfg(any(test, feature = "test-support"))]
6046 pub fn pending_rename(&self) -> Option<&RenameState> {
6047 self.pending_rename.as_ref()
6048 }
6049
6050 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6051 let project = match &self.project {
6052 Some(project) => project.clone(),
6053 None => return None,
6054 };
6055
6056 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6057 }
6058
6059 fn perform_format(
6060 &mut self,
6061 project: ModelHandle<Project>,
6062 trigger: FormatTrigger,
6063 cx: &mut ViewContext<Self>,
6064 ) -> Task<Result<()>> {
6065 let buffer = self.buffer().clone();
6066 let buffers = buffer.read(cx).all_buffers();
6067
6068 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6069 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6070
6071 cx.spawn(|_, mut cx| async move {
6072 let transaction = futures::select_biased! {
6073 _ = timeout => {
6074 log::warn!("timed out waiting for formatting");
6075 None
6076 }
6077 transaction = format.log_err().fuse() => transaction,
6078 };
6079
6080 buffer.update(&mut cx, |buffer, cx| {
6081 if let Some(transaction) = transaction {
6082 if !buffer.is_singleton() {
6083 buffer.push_transaction(&transaction.0, cx);
6084 }
6085 }
6086
6087 cx.notify();
6088 });
6089
6090 Ok(())
6091 })
6092 }
6093
6094 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6095 if let Some(project) = self.project.clone() {
6096 self.buffer.update(cx, |multi_buffer, cx| {
6097 project.update(cx, |project, cx| {
6098 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6099 });
6100 })
6101 }
6102 }
6103
6104 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6105 cx.show_character_palette();
6106 }
6107
6108 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6109 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6110 let buffer = self.buffer.read(cx).snapshot(cx);
6111 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6112 let is_valid = buffer
6113 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6114 .any(|entry| {
6115 entry.diagnostic.is_primary
6116 && !entry.range.is_empty()
6117 && entry.range.start == primary_range_start
6118 && entry.diagnostic.message == active_diagnostics.primary_message
6119 });
6120
6121 if is_valid != active_diagnostics.is_valid {
6122 active_diagnostics.is_valid = is_valid;
6123 let mut new_styles = HashMap::default();
6124 for (block_id, diagnostic) in &active_diagnostics.blocks {
6125 new_styles.insert(
6126 *block_id,
6127 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6128 );
6129 }
6130 self.display_map
6131 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6132 }
6133 }
6134 }
6135
6136 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6137 self.dismiss_diagnostics(cx);
6138 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6139 let buffer = self.buffer.read(cx).snapshot(cx);
6140
6141 let mut primary_range = None;
6142 let mut primary_message = None;
6143 let mut group_end = Point::zero();
6144 let diagnostic_group = buffer
6145 .diagnostic_group::<Point>(group_id)
6146 .map(|entry| {
6147 if entry.range.end > group_end {
6148 group_end = entry.range.end;
6149 }
6150 if entry.diagnostic.is_primary {
6151 primary_range = Some(entry.range.clone());
6152 primary_message = Some(entry.diagnostic.message.clone());
6153 }
6154 entry
6155 })
6156 .collect::<Vec<_>>();
6157 let primary_range = primary_range?;
6158 let primary_message = primary_message?;
6159 let primary_range =
6160 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6161
6162 let blocks = display_map
6163 .insert_blocks(
6164 diagnostic_group.iter().map(|entry| {
6165 let diagnostic = entry.diagnostic.clone();
6166 let message_height = diagnostic.message.lines().count() as u8;
6167 BlockProperties {
6168 style: BlockStyle::Fixed,
6169 position: buffer.anchor_after(entry.range.start),
6170 height: message_height,
6171 render: diagnostic_block_renderer(diagnostic, true),
6172 disposition: BlockDisposition::Below,
6173 }
6174 }),
6175 cx,
6176 )
6177 .into_iter()
6178 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6179 .collect();
6180
6181 Some(ActiveDiagnosticGroup {
6182 primary_range,
6183 primary_message,
6184 blocks,
6185 is_valid: true,
6186 })
6187 });
6188 self.active_diagnostics.is_some()
6189 }
6190
6191 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6192 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6193 self.display_map.update(cx, |display_map, cx| {
6194 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6195 });
6196 cx.notify();
6197 }
6198 }
6199
6200 pub fn set_selections_from_remote(
6201 &mut self,
6202 selections: Vec<Selection<Anchor>>,
6203 pending_selection: Option<Selection<Anchor>>,
6204 cx: &mut ViewContext<Self>,
6205 ) {
6206 let old_cursor_position = self.selections.newest_anchor().head();
6207 self.selections.change_with(cx, |s| {
6208 s.select_anchors(selections);
6209 if let Some(pending_selection) = pending_selection {
6210 s.set_pending(pending_selection, SelectMode::Character);
6211 } else {
6212 s.clear_pending();
6213 }
6214 });
6215 self.selections_did_change(false, &old_cursor_position, cx);
6216 }
6217
6218 fn push_to_selection_history(&mut self) {
6219 self.selection_history.push(SelectionHistoryEntry {
6220 selections: self.selections.disjoint_anchors(),
6221 select_next_state: self.select_next_state.clone(),
6222 add_selections_state: self.add_selections_state.clone(),
6223 });
6224 }
6225
6226 pub fn transact(
6227 &mut self,
6228 cx: &mut ViewContext<Self>,
6229 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6230 ) -> Option<TransactionId> {
6231 self.start_transaction_at(Instant::now(), cx);
6232 update(self, cx);
6233 self.end_transaction_at(Instant::now(), cx)
6234 }
6235
6236 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6237 self.end_selection(cx);
6238 if let Some(tx_id) = self
6239 .buffer
6240 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6241 {
6242 self.selection_history
6243 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6244 }
6245 }
6246
6247 fn end_transaction_at(
6248 &mut self,
6249 now: Instant,
6250 cx: &mut ViewContext<Self>,
6251 ) -> Option<TransactionId> {
6252 if let Some(tx_id) = self
6253 .buffer
6254 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6255 {
6256 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6257 *end_selections = Some(self.selections.disjoint_anchors());
6258 } else {
6259 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6260 }
6261
6262 cx.emit(Event::Edited);
6263 Some(tx_id)
6264 } else {
6265 None
6266 }
6267 }
6268
6269 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6270 let mut fold_ranges = Vec::new();
6271
6272 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6273
6274 let selections = self.selections.all::<Point>(cx);
6275 for selection in selections {
6276 let range = selection.range().sorted();
6277 let buffer_start_row = range.start.row;
6278
6279 for row in (0..=range.end.row).rev() {
6280 let fold_range = display_map.foldable_range(row);
6281
6282 if let Some(fold_range) = fold_range {
6283 if fold_range.end.row >= buffer_start_row {
6284 fold_ranges.push(fold_range);
6285 if row <= range.start.row {
6286 break;
6287 }
6288 }
6289 }
6290 }
6291 }
6292
6293 self.fold_ranges(fold_ranges, true, cx);
6294 }
6295
6296 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6297 let buffer_row = fold_at.buffer_row;
6298 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6299
6300 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6301 let autoscroll = self
6302 .selections
6303 .all::<Point>(cx)
6304 .iter()
6305 .any(|selection| fold_range.overlaps(&selection.range()));
6306
6307 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6308 }
6309 }
6310
6311 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6312 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6313 let buffer = &display_map.buffer_snapshot;
6314 let selections = self.selections.all::<Point>(cx);
6315 let ranges = selections
6316 .iter()
6317 .map(|s| {
6318 let range = s.display_range(&display_map).sorted();
6319 let mut start = range.start.to_point(&display_map);
6320 let mut end = range.end.to_point(&display_map);
6321 start.column = 0;
6322 end.column = buffer.line_len(end.row);
6323 start..end
6324 })
6325 .collect::<Vec<_>>();
6326
6327 self.unfold_ranges(ranges, true, true, cx);
6328 }
6329
6330 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6331 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6332
6333 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6334 ..Point::new(
6335 unfold_at.buffer_row,
6336 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6337 );
6338
6339 let autoscroll = self
6340 .selections
6341 .all::<Point>(cx)
6342 .iter()
6343 .any(|selection| selection.range().overlaps(&intersection_range));
6344
6345 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6346 }
6347
6348 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6349 let selections = self.selections.all::<Point>(cx);
6350 let ranges = selections.into_iter().map(|s| s.start..s.end);
6351 self.fold_ranges(ranges, true, cx);
6352 }
6353
6354 pub fn fold_ranges<T: ToOffset + Clone>(
6355 &mut self,
6356 ranges: impl IntoIterator<Item = Range<T>>,
6357 auto_scroll: bool,
6358 cx: &mut ViewContext<Self>,
6359 ) {
6360 let mut ranges = ranges.into_iter().peekable();
6361 if ranges.peek().is_some() {
6362 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6363
6364 if auto_scroll {
6365 self.request_autoscroll(Autoscroll::fit(), cx);
6366 }
6367
6368 cx.notify();
6369 }
6370 }
6371
6372 pub fn unfold_ranges<T: ToOffset + Clone>(
6373 &mut self,
6374 ranges: impl IntoIterator<Item = Range<T>>,
6375 inclusive: bool,
6376 auto_scroll: bool,
6377 cx: &mut ViewContext<Self>,
6378 ) {
6379 let mut ranges = ranges.into_iter().peekable();
6380 if ranges.peek().is_some() {
6381 self.display_map
6382 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6383 if auto_scroll {
6384 self.request_autoscroll(Autoscroll::fit(), cx);
6385 }
6386
6387 cx.notify();
6388 }
6389 }
6390
6391 pub fn gutter_hover(
6392 &mut self,
6393 GutterHover { hovered }: &GutterHover,
6394 cx: &mut ViewContext<Self>,
6395 ) {
6396 self.gutter_hovered = *hovered;
6397 cx.notify();
6398 }
6399
6400 pub fn insert_blocks(
6401 &mut self,
6402 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6403 cx: &mut ViewContext<Self>,
6404 ) -> Vec<BlockId> {
6405 let blocks = self
6406 .display_map
6407 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6408 self.request_autoscroll(Autoscroll::fit(), cx);
6409 blocks
6410 }
6411
6412 pub fn replace_blocks(
6413 &mut self,
6414 blocks: HashMap<BlockId, RenderBlock>,
6415 cx: &mut ViewContext<Self>,
6416 ) {
6417 self.display_map
6418 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6419 self.request_autoscroll(Autoscroll::fit(), cx);
6420 }
6421
6422 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6423 self.display_map.update(cx, |display_map, cx| {
6424 display_map.remove_blocks(block_ids, cx)
6425 });
6426 }
6427
6428 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6429 self.display_map
6430 .update(cx, |map, cx| map.snapshot(cx))
6431 .longest_row()
6432 }
6433
6434 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6435 self.display_map
6436 .update(cx, |map, cx| map.snapshot(cx))
6437 .max_point()
6438 }
6439
6440 pub fn text(&self, cx: &AppContext) -> String {
6441 self.buffer.read(cx).read(cx).text()
6442 }
6443
6444 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6445 self.transact(cx, |this, cx| {
6446 this.buffer
6447 .read(cx)
6448 .as_singleton()
6449 .expect("you can only call set_text on editors for singleton buffers")
6450 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6451 });
6452 }
6453
6454 pub fn display_text(&self, cx: &mut AppContext) -> String {
6455 self.display_map
6456 .update(cx, |map, cx| map.snapshot(cx))
6457 .text()
6458 }
6459
6460 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6461 let language_name = self
6462 .buffer
6463 .read(cx)
6464 .as_singleton()
6465 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
6466 .map(|l| l.name());
6467
6468 let settings = cx.global::<Settings>();
6469 let mode = self
6470 .soft_wrap_mode_override
6471 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
6472 match mode {
6473 settings::SoftWrap::None => SoftWrap::None,
6474 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6475 settings::SoftWrap::PreferredLineLength => {
6476 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
6477 }
6478 }
6479 }
6480
6481 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
6482 self.soft_wrap_mode_override = Some(mode);
6483 cx.notify();
6484 }
6485
6486 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6487 self.display_map
6488 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6489 }
6490
6491 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6492 if self.soft_wrap_mode_override.is_some() {
6493 self.soft_wrap_mode_override.take();
6494 } else {
6495 let soft_wrap = match self.soft_wrap_mode(cx) {
6496 SoftWrap::None => settings::SoftWrap::EditorWidth,
6497 SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
6498 };
6499 self.soft_wrap_mode_override = Some(soft_wrap);
6500 }
6501 cx.notify();
6502 }
6503
6504 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6505 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6506 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6507 cx.reveal_path(&file.abs_path(cx));
6508 }
6509 }
6510 }
6511
6512 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6513 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6514 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6515 if let Some(path) = file.abs_path(cx).to_str() {
6516 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6517 }
6518 }
6519 }
6520 }
6521
6522 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6523 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6524 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6525 if let Some(path) = file.path().to_str() {
6526 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6527 }
6528 }
6529 }
6530 }
6531
6532 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6533 self.highlighted_rows = rows;
6534 }
6535
6536 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6537 self.highlighted_rows.clone()
6538 }
6539
6540 pub fn highlight_background<T: 'static>(
6541 &mut self,
6542 ranges: Vec<Range<Anchor>>,
6543 color_fetcher: fn(&Theme) -> Color,
6544 cx: &mut ViewContext<Self>,
6545 ) {
6546 self.background_highlights
6547 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6548 cx.notify();
6549 }
6550
6551 #[allow(clippy::type_complexity)]
6552 pub fn clear_background_highlights<T: 'static>(
6553 &mut self,
6554 cx: &mut ViewContext<Self>,
6555 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6556 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6557 if highlights.is_some() {
6558 cx.notify();
6559 }
6560 highlights
6561 }
6562
6563 #[cfg(feature = "test-support")]
6564 pub fn all_background_highlights(
6565 &mut self,
6566 cx: &mut ViewContext<Self>,
6567 ) -> Vec<(Range<DisplayPoint>, Color)> {
6568 let snapshot = self.snapshot(cx);
6569 let buffer = &snapshot.buffer_snapshot;
6570 let start = buffer.anchor_before(0);
6571 let end = buffer.anchor_after(buffer.len());
6572 let theme = cx.global::<Settings>().theme.as_ref();
6573 self.background_highlights_in_range(start..end, &snapshot, theme)
6574 }
6575
6576 fn document_highlights_for_position<'a>(
6577 &'a self,
6578 position: Anchor,
6579 buffer: &'a MultiBufferSnapshot,
6580 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6581 let read_highlights = self
6582 .background_highlights
6583 .get(&TypeId::of::<DocumentHighlightRead>())
6584 .map(|h| &h.1);
6585 let write_highlights = self
6586 .background_highlights
6587 .get(&TypeId::of::<DocumentHighlightWrite>())
6588 .map(|h| &h.1);
6589 let left_position = position.bias_left(buffer);
6590 let right_position = position.bias_right(buffer);
6591 read_highlights
6592 .into_iter()
6593 .chain(write_highlights)
6594 .flat_map(move |ranges| {
6595 let start_ix = match ranges.binary_search_by(|probe| {
6596 let cmp = probe.end.cmp(&left_position, buffer);
6597 if cmp.is_ge() {
6598 Ordering::Greater
6599 } else {
6600 Ordering::Less
6601 }
6602 }) {
6603 Ok(i) | Err(i) => i,
6604 };
6605
6606 let right_position = right_position.clone();
6607 ranges[start_ix..]
6608 .iter()
6609 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6610 })
6611 }
6612
6613 pub fn background_highlights_in_range(
6614 &self,
6615 search_range: Range<Anchor>,
6616 display_snapshot: &DisplaySnapshot,
6617 theme: &Theme,
6618 ) -> Vec<(Range<DisplayPoint>, Color)> {
6619 let mut results = Vec::new();
6620 let buffer = &display_snapshot.buffer_snapshot;
6621 for (color_fetcher, ranges) in self.background_highlights.values() {
6622 let color = color_fetcher(theme);
6623 let start_ix = match ranges.binary_search_by(|probe| {
6624 let cmp = probe.end.cmp(&search_range.start, buffer);
6625 if cmp.is_gt() {
6626 Ordering::Greater
6627 } else {
6628 Ordering::Less
6629 }
6630 }) {
6631 Ok(i) | Err(i) => i,
6632 };
6633 for range in &ranges[start_ix..] {
6634 if range.start.cmp(&search_range.end, buffer).is_ge() {
6635 break;
6636 }
6637 let start = range
6638 .start
6639 .to_point(buffer)
6640 .to_display_point(display_snapshot);
6641 let end = range
6642 .end
6643 .to_point(buffer)
6644 .to_display_point(display_snapshot);
6645 results.push((start..end, color))
6646 }
6647 }
6648 results
6649 }
6650
6651 pub fn highlight_text<T: 'static>(
6652 &mut self,
6653 ranges: Vec<Range<Anchor>>,
6654 style: HighlightStyle,
6655 cx: &mut ViewContext<Self>,
6656 ) {
6657 self.display_map.update(cx, |map, _| {
6658 map.highlight_text(TypeId::of::<T>(), ranges, style)
6659 });
6660 cx.notify();
6661 }
6662
6663 pub fn text_highlights<'a, T: 'static>(
6664 &'a self,
6665 cx: &'a AppContext,
6666 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6667 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6668 }
6669
6670 pub fn clear_text_highlights<T: 'static>(
6671 &mut self,
6672 cx: &mut ViewContext<Self>,
6673 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6674 let highlights = self
6675 .display_map
6676 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6677 if highlights.is_some() {
6678 cx.notify();
6679 }
6680 highlights
6681 }
6682
6683 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6684 self.blink_manager.read(cx).visible() && self.focused
6685 }
6686
6687 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6688 cx.notify();
6689 }
6690
6691 fn on_buffer_event(
6692 &mut self,
6693 _: ModelHandle<MultiBuffer>,
6694 event: &multi_buffer::Event,
6695 cx: &mut ViewContext<Self>,
6696 ) {
6697 match event {
6698 multi_buffer::Event::Edited => {
6699 self.refresh_active_diagnostics(cx);
6700 self.refresh_code_actions(cx);
6701 if self.has_active_copilot_suggestion(cx) {
6702 self.update_visible_copilot_suggestion(cx);
6703 }
6704 cx.emit(Event::BufferEdited);
6705 }
6706 multi_buffer::Event::ExcerptsAdded {
6707 buffer,
6708 predecessor,
6709 excerpts,
6710 } => cx.emit(Event::ExcerptsAdded {
6711 buffer: buffer.clone(),
6712 predecessor: *predecessor,
6713 excerpts: excerpts.clone(),
6714 }),
6715 multi_buffer::Event::ExcerptsRemoved { ids } => {
6716 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6717 }
6718 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6719 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6720 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6721 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6722 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6723 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6724 multi_buffer::Event::DiagnosticsUpdated => {
6725 self.refresh_active_diagnostics(cx);
6726 }
6727 multi_buffer::Event::LanguageChanged => {}
6728 }
6729 }
6730
6731 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6732 cx.notify();
6733 }
6734
6735 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
6736 self.refresh_copilot_suggestions(true, cx);
6737 }
6738
6739 pub fn set_searchable(&mut self, searchable: bool) {
6740 self.searchable = searchable;
6741 }
6742
6743 pub fn searchable(&self) -> bool {
6744 self.searchable
6745 }
6746
6747 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6748 let active_item = workspace.active_item(cx);
6749 let editor_handle = if let Some(editor) = active_item
6750 .as_ref()
6751 .and_then(|item| item.act_as::<Self>(cx))
6752 {
6753 editor
6754 } else {
6755 cx.propagate_action();
6756 return;
6757 };
6758
6759 let editor = editor_handle.read(cx);
6760 let buffer = editor.buffer.read(cx);
6761 if buffer.is_singleton() {
6762 cx.propagate_action();
6763 return;
6764 }
6765
6766 let mut new_selections_by_buffer = HashMap::default();
6767 for selection in editor.selections.all::<usize>(cx) {
6768 for (buffer, mut range) in
6769 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6770 {
6771 if selection.reversed {
6772 mem::swap(&mut range.start, &mut range.end);
6773 }
6774 new_selections_by_buffer
6775 .entry(buffer)
6776 .or_insert(Vec::new())
6777 .push(range)
6778 }
6779 }
6780
6781 editor_handle.update(cx, |editor, cx| {
6782 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6783 });
6784 let pane = workspace.active_pane().clone();
6785 pane.update(cx, |pane, _| pane.disable_history());
6786
6787 // We defer the pane interaction because we ourselves are a workspace item
6788 // and activating a new item causes the pane to call a method on us reentrantly,
6789 // which panics if we're on the stack.
6790 cx.defer(move |workspace, cx| {
6791 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6792 let editor = workspace.open_project_item::<Self>(buffer, cx);
6793 editor.update(cx, |editor, cx| {
6794 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6795 s.select_ranges(ranges);
6796 });
6797 });
6798 }
6799
6800 pane.update(cx, |pane, _| pane.enable_history());
6801 });
6802 }
6803
6804 fn jump(
6805 workspace: &mut Workspace,
6806 path: ProjectPath,
6807 position: Point,
6808 anchor: language::Anchor,
6809 cx: &mut ViewContext<Workspace>,
6810 ) {
6811 let editor = workspace.open_path(path, None, true, cx);
6812 cx.spawn(|_, mut cx| async move {
6813 let editor = editor
6814 .await?
6815 .downcast::<Editor>()
6816 .ok_or_else(|| anyhow!("opened item was not an editor"))?
6817 .downgrade();
6818 editor.update(&mut cx, |editor, cx| {
6819 let buffer = editor
6820 .buffer()
6821 .read(cx)
6822 .as_singleton()
6823 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
6824 let buffer = buffer.read(cx);
6825 let cursor = if buffer.can_resolve(&anchor) {
6826 language::ToPoint::to_point(&anchor, buffer)
6827 } else {
6828 buffer.clip_point(position, Bias::Left)
6829 };
6830
6831 let nav_history = editor.nav_history.take();
6832 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6833 s.select_ranges([cursor..cursor]);
6834 });
6835 editor.nav_history = nav_history;
6836
6837 anyhow::Ok(())
6838 })??;
6839
6840 anyhow::Ok(())
6841 })
6842 .detach_and_log_err(cx);
6843 }
6844
6845 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6846 let snapshot = self.buffer.read(cx).read(cx);
6847 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6848 Some(
6849 ranges
6850 .iter()
6851 .map(move |range| {
6852 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6853 })
6854 .collect(),
6855 )
6856 }
6857
6858 fn selection_replacement_ranges(
6859 &self,
6860 range: Range<OffsetUtf16>,
6861 cx: &AppContext,
6862 ) -> Vec<Range<OffsetUtf16>> {
6863 let selections = self.selections.all::<OffsetUtf16>(cx);
6864 let newest_selection = selections
6865 .iter()
6866 .max_by_key(|selection| selection.id)
6867 .unwrap();
6868 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6869 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6870 let snapshot = self.buffer.read(cx).read(cx);
6871 selections
6872 .into_iter()
6873 .map(|mut selection| {
6874 selection.start.0 =
6875 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6876 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6877 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6878 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6879 })
6880 .collect()
6881 }
6882
6883 fn report_copilot_event(
6884 &self,
6885 suggestion_id: Option<String>,
6886 suggestion_accepted: bool,
6887 cx: &AppContext,
6888 ) {
6889 let Some(project) = &self.project else {
6890 return
6891 };
6892
6893 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
6894 let file_extension = self
6895 .buffer
6896 .read(cx)
6897 .as_singleton()
6898 .and_then(|b| b.read(cx).file())
6899 .and_then(|file| Path::new(file.file_name(cx)).extension())
6900 .and_then(|e| e.to_str())
6901 .map(|a| a.to_string());
6902
6903 let telemetry = project.read(cx).client().telemetry().clone();
6904 let telemetry_settings = cx.global::<Settings>().telemetry();
6905
6906 let event = ClickhouseEvent::Copilot {
6907 suggestion_id,
6908 suggestion_accepted,
6909 file_extension,
6910 };
6911 telemetry.report_clickhouse_event(event, telemetry_settings);
6912 }
6913
6914 fn report_editor_event(
6915 &self,
6916 name: &'static str,
6917 file_extension: Option<String>,
6918 cx: &AppContext,
6919 ) {
6920 let Some(project) = &self.project else {
6921 return
6922 };
6923
6924 // If None, we are in a file without an extension
6925 let file_extension = file_extension.or(self
6926 .buffer
6927 .read(cx)
6928 .as_singleton()
6929 .and_then(|b| b.read(cx).file())
6930 .and_then(|file| Path::new(file.file_name(cx)).extension())
6931 .and_then(|e| e.to_str())
6932 .map(|a| a.to_string()));
6933
6934 let settings = cx.global::<Settings>();
6935
6936 let telemetry = project.read(cx).client().telemetry().clone();
6937 telemetry.report_mixpanel_event(
6938 match name {
6939 "open" => "open editor",
6940 "save" => "save editor",
6941 _ => name,
6942 },
6943 json!({ "File Extension": file_extension, "Vim Mode": settings.vim_mode, "In Clickhouse": true }),
6944 settings.telemetry(),
6945 );
6946 let event = ClickhouseEvent::Editor {
6947 file_extension,
6948 vim_mode: settings.vim_mode,
6949 operation: name,
6950 copilot_enabled: settings.features.copilot,
6951 copilot_enabled_for_language: settings.show_copilot_suggestions(
6952 self.language_at(0, cx)
6953 .map(|language| language.name())
6954 .as_deref(),
6955 self.file_at(0, cx)
6956 .map(|file| file.path().clone())
6957 .as_deref(),
6958 ),
6959 };
6960 telemetry.report_clickhouse_event(event, settings.telemetry())
6961 }
6962
6963 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
6964 /// with each line being an array of {text, highlight} objects.
6965 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
6966 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
6967 return;
6968 };
6969
6970 #[derive(Serialize)]
6971 struct Chunk<'a> {
6972 text: String,
6973 highlight: Option<&'a str>,
6974 }
6975
6976 let snapshot = buffer.read(cx).snapshot();
6977 let range = self
6978 .selected_text_range(cx)
6979 .and_then(|selected_range| {
6980 if selected_range.is_empty() {
6981 None
6982 } else {
6983 Some(selected_range)
6984 }
6985 })
6986 .unwrap_or_else(|| 0..snapshot.len());
6987
6988 let chunks = snapshot.chunks(range, true);
6989 let mut lines = Vec::new();
6990 let mut line: VecDeque<Chunk> = VecDeque::new();
6991
6992 let theme = &cx.global::<Settings>().theme.editor.syntax;
6993
6994 for chunk in chunks {
6995 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
6996 let mut chunk_lines = chunk.text.split("\n").peekable();
6997 while let Some(text) = chunk_lines.next() {
6998 let mut merged_with_last_token = false;
6999 if let Some(last_token) = line.back_mut() {
7000 if last_token.highlight == highlight {
7001 last_token.text.push_str(text);
7002 merged_with_last_token = true;
7003 }
7004 }
7005
7006 if !merged_with_last_token {
7007 line.push_back(Chunk {
7008 text: text.into(),
7009 highlight,
7010 });
7011 }
7012
7013 if chunk_lines.peek().is_some() {
7014 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7015 line.pop_front();
7016 }
7017 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7018 line.pop_back();
7019 }
7020
7021 lines.push(mem::take(&mut line));
7022 }
7023 }
7024 }
7025
7026 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7027 cx.write_to_clipboard(ClipboardItem::new(lines));
7028 }
7029}
7030
7031fn consume_contiguous_rows(
7032 contiguous_row_selections: &mut Vec<Selection<Point>>,
7033 selection: &Selection<Point>,
7034 display_map: &DisplaySnapshot,
7035 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7036) -> (u32, u32) {
7037 contiguous_row_selections.push(selection.clone());
7038 let start_row = selection.start.row;
7039 let mut end_row = ending_row(selection, display_map);
7040
7041 while let Some(next_selection) = selections.peek() {
7042 if next_selection.start.row <= end_row {
7043 end_row = ending_row(next_selection, display_map);
7044 contiguous_row_selections.push(selections.next().unwrap().clone());
7045 } else {
7046 break;
7047 }
7048 }
7049 (start_row, end_row)
7050}
7051
7052fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7053 if next_selection.end.column > 0 || next_selection.is_empty() {
7054 display_map.next_line_boundary(next_selection.end).0.row + 1
7055 } else {
7056 next_selection.end.row
7057 }
7058}
7059
7060impl EditorSnapshot {
7061 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7062 self.display_snapshot.buffer_snapshot.language_at(position)
7063 }
7064
7065 pub fn is_focused(&self) -> bool {
7066 self.is_focused
7067 }
7068
7069 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7070 self.placeholder_text.as_ref()
7071 }
7072
7073 pub fn scroll_position(&self) -> Vector2F {
7074 self.scroll_anchor.scroll_position(&self.display_snapshot)
7075 }
7076}
7077
7078impl Deref for EditorSnapshot {
7079 type Target = DisplaySnapshot;
7080
7081 fn deref(&self) -> &Self::Target {
7082 &self.display_snapshot
7083 }
7084}
7085
7086#[derive(Clone, Debug, PartialEq, Eq)]
7087pub enum Event {
7088 InputIgnored {
7089 text: Arc<str>,
7090 },
7091 ExcerptsAdded {
7092 buffer: ModelHandle<Buffer>,
7093 predecessor: ExcerptId,
7094 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7095 },
7096 ExcerptsRemoved {
7097 ids: Vec<ExcerptId>,
7098 },
7099 BufferEdited,
7100 Edited,
7101 Reparsed,
7102 Blurred,
7103 DirtyChanged,
7104 Saved,
7105 TitleChanged,
7106 SelectionsChanged {
7107 local: bool,
7108 },
7109 ScrollPositionChanged {
7110 local: bool,
7111 },
7112 Closed,
7113}
7114
7115pub struct EditorFocused(pub ViewHandle<Editor>);
7116pub struct EditorBlurred(pub ViewHandle<Editor>);
7117pub struct EditorReleased(pub WeakViewHandle<Editor>);
7118
7119impl Entity for Editor {
7120 type Event = Event;
7121
7122 fn release(&mut self, cx: &mut AppContext) {
7123 cx.emit_global(EditorReleased(self.handle.clone()));
7124 }
7125}
7126
7127impl View for Editor {
7128 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7129 let style = self.style(cx);
7130 let font_changed = self.display_map.update(cx, |map, cx| {
7131 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7132 map.set_font(style.text.font_id, style.text.font_size, cx)
7133 });
7134
7135 if font_changed {
7136 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7137 hide_hover(editor, cx);
7138 hide_link_definition(editor, cx);
7139 });
7140 }
7141
7142 Stack::new()
7143 .with_child(EditorElement::new(style.clone()))
7144 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7145 .into_any()
7146 }
7147
7148 fn ui_name() -> &'static str {
7149 "Editor"
7150 }
7151
7152 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7153 if cx.is_self_focused() {
7154 let focused_event = EditorFocused(cx.handle());
7155 cx.emit_global(focused_event);
7156 }
7157 if let Some(rename) = self.pending_rename.as_ref() {
7158 cx.focus(&rename.editor);
7159 } else {
7160 if !self.focused {
7161 self.blink_manager.update(cx, BlinkManager::enable);
7162 }
7163 self.focused = true;
7164 self.buffer.update(cx, |buffer, cx| {
7165 buffer.finalize_last_transaction(cx);
7166 if self.leader_replica_id.is_none() {
7167 buffer.set_active_selections(
7168 &self.selections.disjoint_anchors(),
7169 self.selections.line_mode,
7170 self.cursor_shape,
7171 cx,
7172 );
7173 }
7174 });
7175 }
7176 }
7177
7178 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7179 let blurred_event = EditorBlurred(cx.handle());
7180 cx.emit_global(blurred_event);
7181 self.focused = false;
7182 self.blink_manager.update(cx, BlinkManager::disable);
7183 self.buffer
7184 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7185 self.hide_context_menu(cx);
7186 hide_hover(self, cx);
7187 cx.emit(Event::Blurred);
7188 cx.notify();
7189 }
7190
7191 fn modifiers_changed(
7192 &mut self,
7193 event: &gpui::platform::ModifiersChangedEvent,
7194 cx: &mut ViewContext<Self>,
7195 ) -> bool {
7196 let pending_selection = self.has_pending_selection();
7197
7198 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7199 if event.cmd && !pending_selection {
7200 let snapshot = self.snapshot(cx);
7201 let kind = if event.shift {
7202 LinkDefinitionKind::Type
7203 } else {
7204 LinkDefinitionKind::Symbol
7205 };
7206
7207 show_link_definition(kind, self, point, snapshot, cx);
7208 return false;
7209 }
7210 }
7211
7212 {
7213 if self.link_go_to_definition_state.symbol_range.is_some()
7214 || !self.link_go_to_definition_state.definitions.is_empty()
7215 {
7216 self.link_go_to_definition_state.symbol_range.take();
7217 self.link_go_to_definition_state.definitions.clear();
7218 cx.notify();
7219 }
7220
7221 self.link_go_to_definition_state.task = None;
7222
7223 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7224 }
7225
7226 false
7227 }
7228
7229 fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
7230 Self::reset_to_default_keymap_context(keymap);
7231 let mode = match self.mode {
7232 EditorMode::SingleLine => "single_line",
7233 EditorMode::AutoHeight { .. } => "auto_height",
7234 EditorMode::Full => "full",
7235 };
7236 keymap.add_key("mode", mode);
7237 if self.pending_rename.is_some() {
7238 keymap.add_identifier("renaming");
7239 }
7240 match self.context_menu.as_ref() {
7241 Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
7242 Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
7243 None => {}
7244 }
7245
7246 for layer in self.keymap_context_layers.values() {
7247 keymap.extend(layer);
7248 }
7249 }
7250
7251 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7252 Some(
7253 self.buffer
7254 .read(cx)
7255 .read(cx)
7256 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7257 .collect(),
7258 )
7259 }
7260
7261 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7262 // Prevent the IME menu from appearing when holding down an alphabetic key
7263 // while input is disabled.
7264 if !self.input_enabled {
7265 return None;
7266 }
7267
7268 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7269 Some(range.start.0..range.end.0)
7270 }
7271
7272 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7273 let snapshot = self.buffer.read(cx).read(cx);
7274 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7275 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7276 }
7277
7278 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7279 self.clear_text_highlights::<InputComposition>(cx);
7280 self.ime_transaction.take();
7281 }
7282
7283 fn replace_text_in_range(
7284 &mut self,
7285 range_utf16: Option<Range<usize>>,
7286 text: &str,
7287 cx: &mut ViewContext<Self>,
7288 ) {
7289 self.transact(cx, |this, cx| {
7290 if this.input_enabled {
7291 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7292 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7293 Some(this.selection_replacement_ranges(range_utf16, cx))
7294 } else {
7295 this.marked_text_ranges(cx)
7296 };
7297
7298 if let Some(new_selected_ranges) = new_selected_ranges {
7299 this.change_selections(None, cx, |selections| {
7300 selections.select_ranges(new_selected_ranges)
7301 });
7302 }
7303 }
7304
7305 this.handle_input(text, cx);
7306 });
7307
7308 if !self.input_enabled {
7309 return;
7310 }
7311
7312 if let Some(transaction) = self.ime_transaction {
7313 self.buffer.update(cx, |buffer, cx| {
7314 buffer.group_until_transaction(transaction, cx);
7315 });
7316 }
7317
7318 self.unmark_text(cx);
7319 }
7320
7321 fn replace_and_mark_text_in_range(
7322 &mut self,
7323 range_utf16: Option<Range<usize>>,
7324 text: &str,
7325 new_selected_range_utf16: Option<Range<usize>>,
7326 cx: &mut ViewContext<Self>,
7327 ) {
7328 if !self.input_enabled {
7329 return;
7330 }
7331
7332 let transaction = self.transact(cx, |this, cx| {
7333 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7334 let snapshot = this.buffer.read(cx).read(cx);
7335 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7336 for marked_range in &mut marked_ranges {
7337 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7338 marked_range.start.0 += relative_range_utf16.start;
7339 marked_range.start =
7340 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7341 marked_range.end =
7342 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7343 }
7344 }
7345 Some(marked_ranges)
7346 } else if let Some(range_utf16) = range_utf16 {
7347 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7348 Some(this.selection_replacement_ranges(range_utf16, cx))
7349 } else {
7350 None
7351 };
7352
7353 if let Some(ranges) = ranges_to_replace {
7354 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7355 }
7356
7357 let marked_ranges = {
7358 let snapshot = this.buffer.read(cx).read(cx);
7359 this.selections
7360 .disjoint_anchors()
7361 .iter()
7362 .map(|selection| {
7363 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7364 })
7365 .collect::<Vec<_>>()
7366 };
7367
7368 if text.is_empty() {
7369 this.unmark_text(cx);
7370 } else {
7371 this.highlight_text::<InputComposition>(
7372 marked_ranges.clone(),
7373 this.style(cx).composition_mark,
7374 cx,
7375 );
7376 }
7377
7378 this.handle_input(text, cx);
7379
7380 if let Some(new_selected_range) = new_selected_range_utf16 {
7381 let snapshot = this.buffer.read(cx).read(cx);
7382 let new_selected_ranges = marked_ranges
7383 .into_iter()
7384 .map(|marked_range| {
7385 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7386 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7387 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7388 snapshot.clip_offset_utf16(new_start, Bias::Left)
7389 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7390 })
7391 .collect::<Vec<_>>();
7392
7393 drop(snapshot);
7394 this.change_selections(None, cx, |selections| {
7395 selections.select_ranges(new_selected_ranges)
7396 });
7397 }
7398 });
7399
7400 self.ime_transaction = self.ime_transaction.or(transaction);
7401 if let Some(transaction) = self.ime_transaction {
7402 self.buffer.update(cx, |buffer, cx| {
7403 buffer.group_until_transaction(transaction, cx);
7404 });
7405 }
7406
7407 if self.text_highlights::<InputComposition>(cx).is_none() {
7408 self.ime_transaction.take();
7409 }
7410 }
7411}
7412
7413fn build_style(
7414 settings: &Settings,
7415 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7416 override_text_style: Option<&OverrideTextStyle>,
7417 cx: &AppContext,
7418) -> EditorStyle {
7419 let font_cache = cx.font_cache();
7420
7421 let theme_id = settings.theme.meta.id;
7422 let mut theme = settings.theme.editor.clone();
7423 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7424 let field_editor_theme = get_field_editor_theme(&settings.theme);
7425 theme.text_color = field_editor_theme.text.color;
7426 theme.selection = field_editor_theme.selection;
7427 theme.background = field_editor_theme
7428 .container
7429 .background_color
7430 .unwrap_or_default();
7431 EditorStyle {
7432 text: field_editor_theme.text,
7433 placeholder_text: field_editor_theme.placeholder_text,
7434 theme,
7435 theme_id,
7436 }
7437 } else {
7438 let font_family_id = settings.buffer_font_family;
7439 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7440 let font_properties = Default::default();
7441 let font_id = font_cache
7442 .select_font(font_family_id, &font_properties)
7443 .unwrap();
7444 let font_size = settings.buffer_font_size;
7445 EditorStyle {
7446 text: TextStyle {
7447 color: settings.theme.editor.text_color,
7448 font_family_name,
7449 font_family_id,
7450 font_id,
7451 font_size,
7452 font_properties,
7453 underline: Default::default(),
7454 },
7455 placeholder_text: None,
7456 theme,
7457 theme_id,
7458 }
7459 };
7460
7461 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7462 if let Some(highlighted) = style
7463 .text
7464 .clone()
7465 .highlight(highlight_style, font_cache)
7466 .log_err()
7467 {
7468 style.text = highlighted;
7469 }
7470 }
7471
7472 style
7473}
7474
7475trait SelectionExt {
7476 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7477 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7478 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7479 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7480 -> Range<u32>;
7481}
7482
7483impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7484 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7485 let start = self.start.to_point(buffer);
7486 let end = self.end.to_point(buffer);
7487 if self.reversed {
7488 end..start
7489 } else {
7490 start..end
7491 }
7492 }
7493
7494 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7495 let start = self.start.to_offset(buffer);
7496 let end = self.end.to_offset(buffer);
7497 if self.reversed {
7498 end..start
7499 } else {
7500 start..end
7501 }
7502 }
7503
7504 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7505 let start = self
7506 .start
7507 .to_point(&map.buffer_snapshot)
7508 .to_display_point(map);
7509 let end = self
7510 .end
7511 .to_point(&map.buffer_snapshot)
7512 .to_display_point(map);
7513 if self.reversed {
7514 end..start
7515 } else {
7516 start..end
7517 }
7518 }
7519
7520 fn spanned_rows(
7521 &self,
7522 include_end_if_at_line_start: bool,
7523 map: &DisplaySnapshot,
7524 ) -> Range<u32> {
7525 let start = self.start.to_point(&map.buffer_snapshot);
7526 let mut end = self.end.to_point(&map.buffer_snapshot);
7527 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7528 end.row -= 1;
7529 }
7530
7531 let buffer_start = map.prev_line_boundary(start).0;
7532 let buffer_end = map.next_line_boundary(end).0;
7533 buffer_start.row..buffer_end.row + 1
7534 }
7535}
7536
7537impl<T: InvalidationRegion> InvalidationStack<T> {
7538 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7539 where
7540 S: Clone + ToOffset,
7541 {
7542 while let Some(region) = self.last() {
7543 let all_selections_inside_invalidation_ranges =
7544 if selections.len() == region.ranges().len() {
7545 selections
7546 .iter()
7547 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7548 .all(|(selection, invalidation_range)| {
7549 let head = selection.head().to_offset(buffer);
7550 invalidation_range.start <= head && invalidation_range.end >= head
7551 })
7552 } else {
7553 false
7554 };
7555
7556 if all_selections_inside_invalidation_ranges {
7557 break;
7558 } else {
7559 self.pop();
7560 }
7561 }
7562 }
7563}
7564
7565impl<T> Default for InvalidationStack<T> {
7566 fn default() -> Self {
7567 Self(Default::default())
7568 }
7569}
7570
7571impl<T> Deref for InvalidationStack<T> {
7572 type Target = Vec<T>;
7573
7574 fn deref(&self) -> &Self::Target {
7575 &self.0
7576 }
7577}
7578
7579impl<T> DerefMut for InvalidationStack<T> {
7580 fn deref_mut(&mut self) -> &mut Self::Target {
7581 &mut self.0
7582 }
7583}
7584
7585impl InvalidationRegion for SnippetState {
7586 fn ranges(&self) -> &[Range<Anchor>] {
7587 &self.ranges[self.active_index]
7588 }
7589}
7590
7591impl Deref for EditorStyle {
7592 type Target = theme::Editor;
7593
7594 fn deref(&self) -> &Self::Target {
7595 &self.theme
7596 }
7597}
7598
7599pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7600 let mut highlighted_lines = Vec::new();
7601 for (index, line) in diagnostic.message.lines().enumerate() {
7602 let line = match &diagnostic.source {
7603 Some(source) if index == 0 => {
7604 let source_highlight = Vec::from_iter(0..source.len());
7605 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
7606 }
7607
7608 _ => highlight_diagnostic_message(Vec::new(), line),
7609 };
7610 highlighted_lines.push(line);
7611 }
7612
7613 Arc::new(move |cx: &mut BlockContext| {
7614 let settings = cx.global::<Settings>();
7615 let theme = &settings.theme.editor;
7616 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7617 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
7618 Flex::column()
7619 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7620 Label::new(
7621 line.clone(),
7622 style.message.clone().with_font_size(font_size),
7623 )
7624 .with_highlights(highlights.clone())
7625 .contained()
7626 .with_margin_left(cx.anchor_x)
7627 }))
7628 .aligned()
7629 .left()
7630 .into_any()
7631 })
7632}
7633
7634pub fn highlight_diagnostic_message(
7635 inital_highlights: Vec<usize>,
7636 message: &str,
7637) -> (String, Vec<usize>) {
7638 let mut message_without_backticks = String::new();
7639 let mut prev_offset = 0;
7640 let mut inside_block = false;
7641 let mut highlights = inital_highlights;
7642 for (match_ix, (offset, _)) in message
7643 .match_indices('`')
7644 .chain([(message.len(), "")])
7645 .enumerate()
7646 {
7647 message_without_backticks.push_str(&message[prev_offset..offset]);
7648 if inside_block {
7649 highlights.extend(prev_offset - match_ix..offset - match_ix);
7650 }
7651
7652 inside_block = !inside_block;
7653 prev_offset = offset + 1;
7654 }
7655
7656 (message_without_backticks, highlights)
7657}
7658
7659pub fn diagnostic_style(
7660 severity: DiagnosticSeverity,
7661 valid: bool,
7662 theme: &theme::Editor,
7663) -> DiagnosticStyle {
7664 match (severity, valid) {
7665 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
7666 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
7667 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
7668 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
7669 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
7670 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
7671 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
7672 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
7673 _ => theme.invalid_hint_diagnostic.clone(),
7674 }
7675}
7676
7677pub fn combine_syntax_and_fuzzy_match_highlights(
7678 text: &str,
7679 default_style: HighlightStyle,
7680 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
7681 match_indices: &[usize],
7682) -> Vec<(Range<usize>, HighlightStyle)> {
7683 let mut result = Vec::new();
7684 let mut match_indices = match_indices.iter().copied().peekable();
7685
7686 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
7687 {
7688 syntax_highlight.weight = None;
7689
7690 // Add highlights for any fuzzy match characters before the next
7691 // syntax highlight range.
7692 while let Some(&match_index) = match_indices.peek() {
7693 if match_index >= range.start {
7694 break;
7695 }
7696 match_indices.next();
7697 let end_index = char_ix_after(match_index, text);
7698 let mut match_style = default_style;
7699 match_style.weight = Some(fonts::Weight::BOLD);
7700 result.push((match_index..end_index, match_style));
7701 }
7702
7703 if range.start == usize::MAX {
7704 break;
7705 }
7706
7707 // Add highlights for any fuzzy match characters within the
7708 // syntax highlight range.
7709 let mut offset = range.start;
7710 while let Some(&match_index) = match_indices.peek() {
7711 if match_index >= range.end {
7712 break;
7713 }
7714
7715 match_indices.next();
7716 if match_index > offset {
7717 result.push((offset..match_index, syntax_highlight));
7718 }
7719
7720 let mut end_index = char_ix_after(match_index, text);
7721 while let Some(&next_match_index) = match_indices.peek() {
7722 if next_match_index == end_index && next_match_index < range.end {
7723 end_index = char_ix_after(next_match_index, text);
7724 match_indices.next();
7725 } else {
7726 break;
7727 }
7728 }
7729
7730 let mut match_style = syntax_highlight;
7731 match_style.weight = Some(fonts::Weight::BOLD);
7732 result.push((match_index..end_index, match_style));
7733 offset = end_index;
7734 }
7735
7736 if offset < range.end {
7737 result.push((offset..range.end, syntax_highlight));
7738 }
7739 }
7740
7741 fn char_ix_after(ix: usize, text: &str) -> usize {
7742 ix + text[ix..].chars().next().unwrap().len_utf8()
7743 }
7744
7745 result
7746}
7747
7748pub fn styled_runs_for_code_label<'a>(
7749 label: &'a CodeLabel,
7750 syntax_theme: &'a theme::SyntaxTheme,
7751) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
7752 let fade_out = HighlightStyle {
7753 fade_out: Some(0.35),
7754 ..Default::default()
7755 };
7756
7757 let mut prev_end = label.filter_range.end;
7758 label
7759 .runs
7760 .iter()
7761 .enumerate()
7762 .flat_map(move |(ix, (range, highlight_id))| {
7763 let style = if let Some(style) = highlight_id.style(syntax_theme) {
7764 style
7765 } else {
7766 return Default::default();
7767 };
7768 let mut muted_style = style;
7769 muted_style.highlight(fade_out);
7770
7771 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
7772 if range.start >= label.filter_range.end {
7773 if range.start > prev_end {
7774 runs.push((prev_end..range.start, fade_out));
7775 }
7776 runs.push((range.clone(), muted_style));
7777 } else if range.end <= label.filter_range.end {
7778 runs.push((range.clone(), style));
7779 } else {
7780 runs.push((range.start..label.filter_range.end, style));
7781 runs.push((label.filter_range.end..range.end, muted_style));
7782 }
7783 prev_end = cmp::max(prev_end, range.end);
7784
7785 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
7786 runs.push((prev_end..label.text.len(), fade_out));
7787 }
7788
7789 runs
7790 })
7791}
7792
7793pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
7794 let mut index = 0;
7795 let mut codepoints = text.char_indices().peekable();
7796
7797 std::iter::from_fn(move || {
7798 let start_index = index;
7799 while let Some((new_index, codepoint)) = codepoints.next() {
7800 index = new_index + codepoint.len_utf8();
7801 let current_upper = codepoint.is_uppercase();
7802 let next_upper = codepoints
7803 .peek()
7804 .map(|(_, c)| c.is_uppercase())
7805 .unwrap_or(false);
7806
7807 if !current_upper && next_upper {
7808 return Some(&text[start_index..index]);
7809 }
7810 }
7811
7812 index = text.len();
7813 if start_index < text.len() {
7814 return Some(&text[start_index..]);
7815 }
7816 None
7817 })
7818 .flat_map(|word| word.split_inclusive('_'))
7819}
7820
7821trait RangeToAnchorExt {
7822 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
7823}
7824
7825impl<T: ToOffset> RangeToAnchorExt for Range<T> {
7826 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
7827 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
7828 }
7829}