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