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