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