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