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