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