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 cx.window_context().defer(move |cx| {
5684 let title = definitions
5685 .iter()
5686 .find(|definition| definition.origin.is_some())
5687 .and_then(|definition| {
5688 definition.origin.as_ref().map(|origin| {
5689 let buffer = origin.buffer.read(cx);
5690 format!(
5691 "Definitions for {}",
5692 buffer
5693 .text_for_range(origin.range.clone())
5694 .collect::<String>()
5695 )
5696 })
5697 })
5698 .unwrap_or("Definitions".to_owned());
5699 let locations = definitions
5700 .into_iter()
5701 .map(|definition| definition.target)
5702 .collect();
5703 workspace.update(cx, |workspace, cx| {
5704 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5705 });
5706 });
5707 }
5708 }
5709
5710 pub fn find_all_references(
5711 workspace: &mut Workspace,
5712 _: &FindAllReferences,
5713 cx: &mut ViewContext<Workspace>,
5714 ) -> Option<Task<Result<()>>> {
5715 let active_item = workspace.active_item(cx)?;
5716 let editor_handle = active_item.act_as::<Self>(cx)?;
5717
5718 let editor = editor_handle.read(cx);
5719 let buffer = editor.buffer.read(cx);
5720 let head = editor.selections.newest::<usize>(cx).head();
5721 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5722 let replica_id = editor.replica_id(cx);
5723
5724 let project = workspace.project().clone();
5725 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5726 Some(cx.spawn_labeled(
5727 "Finding All References...",
5728 |workspace, mut cx| async move {
5729 let locations = references.await?;
5730 if locations.is_empty() {
5731 return Ok(());
5732 }
5733
5734 workspace.update(&mut cx, |workspace, cx| {
5735 let title = locations
5736 .first()
5737 .as_ref()
5738 .map(|location| {
5739 let buffer = location.buffer.read(cx);
5740 format!(
5741 "References to `{}`",
5742 buffer
5743 .text_for_range(location.range.clone())
5744 .collect::<String>()
5745 )
5746 })
5747 .unwrap();
5748 Self::open_locations_in_multibuffer(
5749 workspace, locations, replica_id, title, cx,
5750 );
5751 })?;
5752
5753 Ok(())
5754 },
5755 ))
5756 }
5757
5758 /// Opens a multibuffer with the given project locations in it
5759 pub fn open_locations_in_multibuffer(
5760 workspace: &mut Workspace,
5761 mut locations: Vec<Location>,
5762 replica_id: ReplicaId,
5763 title: String,
5764 cx: &mut ViewContext<Workspace>,
5765 ) {
5766 // If there are multiple definitions, open them in a multibuffer
5767 locations.sort_by_key(|location| location.buffer.id());
5768 let mut locations = locations.into_iter().peekable();
5769 let mut ranges_to_highlight = Vec::new();
5770
5771 let excerpt_buffer = cx.add_model(|cx| {
5772 let mut multibuffer = MultiBuffer::new(replica_id);
5773 while let Some(location) = locations.next() {
5774 let buffer = location.buffer.read(cx);
5775 let mut ranges_for_buffer = Vec::new();
5776 let range = location.range.to_offset(buffer);
5777 ranges_for_buffer.push(range.clone());
5778
5779 while let Some(next_location) = locations.peek() {
5780 if next_location.buffer == location.buffer {
5781 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5782 locations.next();
5783 } else {
5784 break;
5785 }
5786 }
5787
5788 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5789 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5790 location.buffer.clone(),
5791 ranges_for_buffer,
5792 1,
5793 cx,
5794 ))
5795 }
5796
5797 multibuffer.with_title(title)
5798 });
5799
5800 let editor = cx.add_view(|cx| {
5801 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5802 });
5803 editor.update(cx, |editor, cx| {
5804 editor.highlight_background::<Self>(
5805 ranges_to_highlight,
5806 |theme| theme.editor.highlighted_line_background,
5807 cx,
5808 );
5809 });
5810 workspace.add_item(Box::new(editor), cx);
5811 }
5812
5813 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5814 use language::ToOffset as _;
5815
5816 let project = self.project.clone()?;
5817 let selection = self.selections.newest_anchor().clone();
5818 let (cursor_buffer, cursor_buffer_position) = self
5819 .buffer
5820 .read(cx)
5821 .text_anchor_for_position(selection.head(), cx)?;
5822 let (tail_buffer, _) = self
5823 .buffer
5824 .read(cx)
5825 .text_anchor_for_position(selection.tail(), cx)?;
5826 if tail_buffer != cursor_buffer {
5827 return None;
5828 }
5829
5830 let snapshot = cursor_buffer.read(cx).snapshot();
5831 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5832 let prepare_rename = project.update(cx, |project, cx| {
5833 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5834 });
5835
5836 Some(cx.spawn(|this, mut cx| async move {
5837 let rename_range = if let Some(range) = prepare_rename.await? {
5838 Some(range)
5839 } else {
5840 this.read_with(&cx, |this, cx| {
5841 let buffer = this.buffer.read(cx).snapshot(cx);
5842 let mut buffer_highlights = this
5843 .document_highlights_for_position(selection.head(), &buffer)
5844 .filter(|highlight| {
5845 highlight.start.excerpt_id() == selection.head().excerpt_id()
5846 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5847 });
5848 buffer_highlights
5849 .next()
5850 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5851 })?
5852 };
5853 if let Some(rename_range) = rename_range {
5854 let rename_buffer_range = rename_range.to_offset(&snapshot);
5855 let cursor_offset_in_rename_range =
5856 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5857
5858 this.update(&mut cx, |this, cx| {
5859 this.take_rename(false, cx);
5860 let style = this.style(cx);
5861 let buffer = this.buffer.read(cx).read(cx);
5862 let cursor_offset = selection.head().to_offset(&buffer);
5863 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5864 let rename_end = rename_start + rename_buffer_range.len();
5865 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5866 let mut old_highlight_id = None;
5867 let old_name: Arc<str> = buffer
5868 .chunks(rename_start..rename_end, true)
5869 .map(|chunk| {
5870 if old_highlight_id.is_none() {
5871 old_highlight_id = chunk.syntax_highlight_id;
5872 }
5873 chunk.text
5874 })
5875 .collect::<String>()
5876 .into();
5877
5878 drop(buffer);
5879
5880 // Position the selection in the rename editor so that it matches the current selection.
5881 this.show_local_selections = false;
5882 let rename_editor = cx.add_view(|cx| {
5883 let mut editor = Editor::single_line(None, cx);
5884 if let Some(old_highlight_id) = old_highlight_id {
5885 editor.override_text_style =
5886 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5887 }
5888 editor.buffer.update(cx, |buffer, cx| {
5889 buffer.edit([(0..0, old_name.clone())], None, cx)
5890 });
5891 editor.select_all(&SelectAll, cx);
5892 editor
5893 });
5894
5895 let ranges = this
5896 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5897 .into_iter()
5898 .flat_map(|(_, ranges)| ranges)
5899 .chain(
5900 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5901 .into_iter()
5902 .flat_map(|(_, ranges)| ranges),
5903 )
5904 .collect();
5905
5906 this.highlight_text::<Rename>(
5907 ranges,
5908 HighlightStyle {
5909 fade_out: Some(style.rename_fade),
5910 ..Default::default()
5911 },
5912 cx,
5913 );
5914 cx.focus(&rename_editor);
5915 let block_id = this.insert_blocks(
5916 [BlockProperties {
5917 style: BlockStyle::Flex,
5918 position: range.start.clone(),
5919 height: 1,
5920 render: Arc::new({
5921 let editor = rename_editor.clone();
5922 move |cx: &mut BlockContext| {
5923 ChildView::new(&editor, cx)
5924 .contained()
5925 .with_padding_left(cx.anchor_x)
5926 .into_any()
5927 }
5928 }),
5929 disposition: BlockDisposition::Below,
5930 }],
5931 cx,
5932 )[0];
5933 this.pending_rename = Some(RenameState {
5934 range,
5935 old_name,
5936 editor: rename_editor,
5937 block_id,
5938 });
5939 })?;
5940 }
5941
5942 Ok(())
5943 }))
5944 }
5945
5946 pub fn confirm_rename(
5947 workspace: &mut Workspace,
5948 _: &ConfirmRename,
5949 cx: &mut ViewContext<Workspace>,
5950 ) -> Option<Task<Result<()>>> {
5951 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5952
5953 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5954 let rename = editor.take_rename(false, cx)?;
5955 let buffer = editor.buffer.read(cx);
5956 let (start_buffer, start) =
5957 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5958 let (end_buffer, end) =
5959 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5960 if start_buffer == end_buffer {
5961 let new_name = rename.editor.read(cx).text(cx);
5962 Some((start_buffer, start..end, rename.old_name, new_name))
5963 } else {
5964 None
5965 }
5966 })?;
5967
5968 let rename = workspace.project().clone().update(cx, |project, cx| {
5969 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5970 });
5971
5972 let editor = editor.downgrade();
5973 Some(cx.spawn(|workspace, mut cx| async move {
5974 let project_transaction = rename.await?;
5975 Self::open_project_transaction(
5976 &editor,
5977 workspace,
5978 project_transaction,
5979 format!("Rename: {} → {}", old_name, new_name),
5980 cx.clone(),
5981 )
5982 .await?;
5983
5984 editor.update(&mut cx, |editor, cx| {
5985 editor.refresh_document_highlights(cx);
5986 })?;
5987 Ok(())
5988 }))
5989 }
5990
5991 fn take_rename(
5992 &mut self,
5993 moving_cursor: bool,
5994 cx: &mut ViewContext<Self>,
5995 ) -> Option<RenameState> {
5996 let rename = self.pending_rename.take()?;
5997 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5998 self.clear_text_highlights::<Rename>(cx);
5999 self.show_local_selections = true;
6000
6001 if moving_cursor {
6002 let rename_editor = rename.editor.read(cx);
6003 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6004
6005 // Update the selection to match the position of the selection inside
6006 // the rename editor.
6007 let snapshot = self.buffer.read(cx).read(cx);
6008 let rename_range = rename.range.to_offset(&snapshot);
6009 let cursor_in_editor = snapshot
6010 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6011 .min(rename_range.end);
6012 drop(snapshot);
6013
6014 self.change_selections(None, cx, |s| {
6015 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6016 });
6017 } else {
6018 self.refresh_document_highlights(cx);
6019 }
6020
6021 Some(rename)
6022 }
6023
6024 #[cfg(any(test, feature = "test-support"))]
6025 pub fn pending_rename(&self) -> Option<&RenameState> {
6026 self.pending_rename.as_ref()
6027 }
6028
6029 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6030 let project = match &self.project {
6031 Some(project) => project.clone(),
6032 None => return None,
6033 };
6034
6035 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6036 }
6037
6038 fn perform_format(
6039 &mut self,
6040 project: ModelHandle<Project>,
6041 trigger: FormatTrigger,
6042 cx: &mut ViewContext<Self>,
6043 ) -> Task<Result<()>> {
6044 let buffer = self.buffer().clone();
6045 let buffers = buffer.read(cx).all_buffers();
6046
6047 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6048 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6049
6050 cx.spawn(|_, mut cx| async move {
6051 let transaction = futures::select_biased! {
6052 _ = timeout => {
6053 log::warn!("timed out waiting for formatting");
6054 None
6055 }
6056 transaction = format.log_err().fuse() => transaction,
6057 };
6058
6059 buffer.update(&mut cx, |buffer, cx| {
6060 if let Some(transaction) = transaction {
6061 if !buffer.is_singleton() {
6062 buffer.push_transaction(&transaction.0);
6063 }
6064 }
6065
6066 cx.notify();
6067 });
6068
6069 Ok(())
6070 })
6071 }
6072
6073 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6074 if let Some(project) = self.project.clone() {
6075 self.buffer.update(cx, |multi_buffer, cx| {
6076 project.update(cx, |project, cx| {
6077 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6078 });
6079 })
6080 }
6081 }
6082
6083 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6084 cx.show_character_palette();
6085 }
6086
6087 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6088 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6089 let buffer = self.buffer.read(cx).snapshot(cx);
6090 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6091 let is_valid = buffer
6092 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6093 .any(|entry| {
6094 entry.diagnostic.is_primary
6095 && !entry.range.is_empty()
6096 && entry.range.start == primary_range_start
6097 && entry.diagnostic.message == active_diagnostics.primary_message
6098 });
6099
6100 if is_valid != active_diagnostics.is_valid {
6101 active_diagnostics.is_valid = is_valid;
6102 let mut new_styles = HashMap::default();
6103 for (block_id, diagnostic) in &active_diagnostics.blocks {
6104 new_styles.insert(
6105 *block_id,
6106 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6107 );
6108 }
6109 self.display_map
6110 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6111 }
6112 }
6113 }
6114
6115 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6116 self.dismiss_diagnostics(cx);
6117 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6118 let buffer = self.buffer.read(cx).snapshot(cx);
6119
6120 let mut primary_range = None;
6121 let mut primary_message = None;
6122 let mut group_end = Point::zero();
6123 let diagnostic_group = buffer
6124 .diagnostic_group::<Point>(group_id)
6125 .map(|entry| {
6126 if entry.range.end > group_end {
6127 group_end = entry.range.end;
6128 }
6129 if entry.diagnostic.is_primary {
6130 primary_range = Some(entry.range.clone());
6131 primary_message = Some(entry.diagnostic.message.clone());
6132 }
6133 entry
6134 })
6135 .collect::<Vec<_>>();
6136 let primary_range = primary_range?;
6137 let primary_message = primary_message?;
6138 let primary_range =
6139 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6140
6141 let blocks = display_map
6142 .insert_blocks(
6143 diagnostic_group.iter().map(|entry| {
6144 let diagnostic = entry.diagnostic.clone();
6145 let message_height = diagnostic.message.lines().count() as u8;
6146 BlockProperties {
6147 style: BlockStyle::Fixed,
6148 position: buffer.anchor_after(entry.range.start),
6149 height: message_height,
6150 render: diagnostic_block_renderer(diagnostic, true),
6151 disposition: BlockDisposition::Below,
6152 }
6153 }),
6154 cx,
6155 )
6156 .into_iter()
6157 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6158 .collect();
6159
6160 Some(ActiveDiagnosticGroup {
6161 primary_range,
6162 primary_message,
6163 blocks,
6164 is_valid: true,
6165 })
6166 });
6167 self.active_diagnostics.is_some()
6168 }
6169
6170 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6171 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6172 self.display_map.update(cx, |display_map, cx| {
6173 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6174 });
6175 cx.notify();
6176 }
6177 }
6178
6179 pub fn set_selections_from_remote(
6180 &mut self,
6181 selections: Vec<Selection<Anchor>>,
6182 pending_selection: Option<Selection<Anchor>>,
6183 cx: &mut ViewContext<Self>,
6184 ) {
6185 let old_cursor_position = self.selections.newest_anchor().head();
6186 self.selections.change_with(cx, |s| {
6187 s.select_anchors(selections);
6188 if let Some(pending_selection) = pending_selection {
6189 s.set_pending(pending_selection, SelectMode::Character);
6190 } else {
6191 s.clear_pending();
6192 }
6193 });
6194 self.selections_did_change(false, &old_cursor_position, cx);
6195 }
6196
6197 fn push_to_selection_history(&mut self) {
6198 self.selection_history.push(SelectionHistoryEntry {
6199 selections: self.selections.disjoint_anchors(),
6200 select_next_state: self.select_next_state.clone(),
6201 add_selections_state: self.add_selections_state.clone(),
6202 });
6203 }
6204
6205 pub fn transact(
6206 &mut self,
6207 cx: &mut ViewContext<Self>,
6208 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6209 ) -> Option<TransactionId> {
6210 self.start_transaction_at(Instant::now(), cx);
6211 update(self, cx);
6212 self.end_transaction_at(Instant::now(), cx)
6213 }
6214
6215 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6216 self.end_selection(cx);
6217 if let Some(tx_id) = self
6218 .buffer
6219 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6220 {
6221 self.selection_history
6222 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6223 }
6224 }
6225
6226 fn end_transaction_at(
6227 &mut self,
6228 now: Instant,
6229 cx: &mut ViewContext<Self>,
6230 ) -> Option<TransactionId> {
6231 if let Some(tx_id) = self
6232 .buffer
6233 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6234 {
6235 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6236 *end_selections = Some(self.selections.disjoint_anchors());
6237 } else {
6238 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6239 }
6240
6241 cx.emit(Event::Edited);
6242 Some(tx_id)
6243 } else {
6244 None
6245 }
6246 }
6247
6248 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6249 let mut fold_ranges = Vec::new();
6250
6251 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6252
6253 let selections = self.selections.all::<Point>(cx);
6254 for selection in selections {
6255 let range = selection.range().sorted();
6256 let buffer_start_row = range.start.row;
6257
6258 for row in (0..=range.end.row).rev() {
6259 let fold_range = display_map.foldable_range(row);
6260
6261 if let Some(fold_range) = fold_range {
6262 if fold_range.end.row >= buffer_start_row {
6263 fold_ranges.push(fold_range);
6264 if row <= range.start.row {
6265 break;
6266 }
6267 }
6268 }
6269 }
6270 }
6271
6272 self.fold_ranges(fold_ranges, true, cx);
6273 }
6274
6275 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6276 let buffer_row = fold_at.buffer_row;
6277 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6278
6279 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6280 let autoscroll = self
6281 .selections
6282 .all::<Point>(cx)
6283 .iter()
6284 .any(|selection| fold_range.overlaps(&selection.range()));
6285
6286 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6287 }
6288 }
6289
6290 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6291 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6292 let buffer = &display_map.buffer_snapshot;
6293 let selections = self.selections.all::<Point>(cx);
6294 let ranges = selections
6295 .iter()
6296 .map(|s| {
6297 let range = s.display_range(&display_map).sorted();
6298 let mut start = range.start.to_point(&display_map);
6299 let mut end = range.end.to_point(&display_map);
6300 start.column = 0;
6301 end.column = buffer.line_len(end.row);
6302 start..end
6303 })
6304 .collect::<Vec<_>>();
6305
6306 self.unfold_ranges(ranges, true, true, cx);
6307 }
6308
6309 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6310 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6311
6312 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6313 ..Point::new(
6314 unfold_at.buffer_row,
6315 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6316 );
6317
6318 let autoscroll = self
6319 .selections
6320 .all::<Point>(cx)
6321 .iter()
6322 .any(|selection| selection.range().overlaps(&intersection_range));
6323
6324 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6325 }
6326
6327 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6328 let selections = self.selections.all::<Point>(cx);
6329 let ranges = selections.into_iter().map(|s| s.start..s.end);
6330 self.fold_ranges(ranges, true, cx);
6331 }
6332
6333 pub fn fold_ranges<T: ToOffset + Clone>(
6334 &mut self,
6335 ranges: impl IntoIterator<Item = Range<T>>,
6336 auto_scroll: bool,
6337 cx: &mut ViewContext<Self>,
6338 ) {
6339 let mut ranges = ranges.into_iter().peekable();
6340 if ranges.peek().is_some() {
6341 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6342
6343 if auto_scroll {
6344 self.request_autoscroll(Autoscroll::fit(), cx);
6345 }
6346
6347 cx.notify();
6348 }
6349 }
6350
6351 pub fn unfold_ranges<T: ToOffset + Clone>(
6352 &mut self,
6353 ranges: impl IntoIterator<Item = Range<T>>,
6354 inclusive: bool,
6355 auto_scroll: bool,
6356 cx: &mut ViewContext<Self>,
6357 ) {
6358 let mut ranges = ranges.into_iter().peekable();
6359 if ranges.peek().is_some() {
6360 self.display_map
6361 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6362 if auto_scroll {
6363 self.request_autoscroll(Autoscroll::fit(), cx);
6364 }
6365
6366 cx.notify();
6367 }
6368 }
6369
6370 pub fn gutter_hover(
6371 &mut self,
6372 GutterHover { hovered }: &GutterHover,
6373 cx: &mut ViewContext<Self>,
6374 ) {
6375 self.gutter_hovered = *hovered;
6376 cx.notify();
6377 }
6378
6379 pub fn insert_blocks(
6380 &mut self,
6381 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6382 cx: &mut ViewContext<Self>,
6383 ) -> Vec<BlockId> {
6384 let blocks = self
6385 .display_map
6386 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6387 self.request_autoscroll(Autoscroll::fit(), cx);
6388 blocks
6389 }
6390
6391 pub fn replace_blocks(
6392 &mut self,
6393 blocks: HashMap<BlockId, RenderBlock>,
6394 cx: &mut ViewContext<Self>,
6395 ) {
6396 self.display_map
6397 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6398 self.request_autoscroll(Autoscroll::fit(), cx);
6399 }
6400
6401 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6402 self.display_map.update(cx, |display_map, cx| {
6403 display_map.remove_blocks(block_ids, cx)
6404 });
6405 }
6406
6407 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6408 self.display_map
6409 .update(cx, |map, cx| map.snapshot(cx))
6410 .longest_row()
6411 }
6412
6413 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6414 self.display_map
6415 .update(cx, |map, cx| map.snapshot(cx))
6416 .max_point()
6417 }
6418
6419 pub fn text(&self, cx: &AppContext) -> String {
6420 self.buffer.read(cx).read(cx).text()
6421 }
6422
6423 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6424 self.transact(cx, |this, cx| {
6425 this.buffer
6426 .read(cx)
6427 .as_singleton()
6428 .expect("you can only call set_text on editors for singleton buffers")
6429 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6430 });
6431 }
6432
6433 pub fn display_text(&self, cx: &mut AppContext) -> String {
6434 self.display_map
6435 .update(cx, |map, cx| map.snapshot(cx))
6436 .text()
6437 }
6438
6439 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6440 let language_name = self
6441 .buffer
6442 .read(cx)
6443 .as_singleton()
6444 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
6445 .map(|l| l.name());
6446
6447 let settings = cx.global::<Settings>();
6448 let mode = self
6449 .soft_wrap_mode_override
6450 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
6451 match mode {
6452 settings::SoftWrap::None => SoftWrap::None,
6453 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6454 settings::SoftWrap::PreferredLineLength => {
6455 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
6456 }
6457 }
6458 }
6459
6460 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
6461 self.soft_wrap_mode_override = Some(mode);
6462 cx.notify();
6463 }
6464
6465 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6466 self.display_map
6467 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6468 }
6469
6470 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6471 if self.soft_wrap_mode_override.is_some() {
6472 self.soft_wrap_mode_override.take();
6473 } else {
6474 let soft_wrap = match self.soft_wrap_mode(cx) {
6475 SoftWrap::None => settings::SoftWrap::EditorWidth,
6476 SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
6477 };
6478 self.soft_wrap_mode_override = Some(soft_wrap);
6479 }
6480 cx.notify();
6481 }
6482
6483 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6484 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6485 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6486 cx.reveal_path(&file.abs_path(cx));
6487 }
6488 }
6489 }
6490
6491 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6492 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6493 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6494 if let Some(path) = file.abs_path(cx).to_str() {
6495 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6496 }
6497 }
6498 }
6499 }
6500
6501 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6502 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6503 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6504 if let Some(path) = file.path().to_str() {
6505 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6506 }
6507 }
6508 }
6509 }
6510
6511 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6512 self.highlighted_rows = rows;
6513 }
6514
6515 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6516 self.highlighted_rows.clone()
6517 }
6518
6519 pub fn highlight_background<T: 'static>(
6520 &mut self,
6521 ranges: Vec<Range<Anchor>>,
6522 color_fetcher: fn(&Theme) -> Color,
6523 cx: &mut ViewContext<Self>,
6524 ) {
6525 self.background_highlights
6526 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6527 cx.notify();
6528 }
6529
6530 #[allow(clippy::type_complexity)]
6531 pub fn clear_background_highlights<T: 'static>(
6532 &mut self,
6533 cx: &mut ViewContext<Self>,
6534 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6535 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6536 if highlights.is_some() {
6537 cx.notify();
6538 }
6539 highlights
6540 }
6541
6542 #[cfg(feature = "test-support")]
6543 pub fn all_background_highlights(
6544 &mut self,
6545 cx: &mut ViewContext<Self>,
6546 ) -> Vec<(Range<DisplayPoint>, Color)> {
6547 let snapshot = self.snapshot(cx);
6548 let buffer = &snapshot.buffer_snapshot;
6549 let start = buffer.anchor_before(0);
6550 let end = buffer.anchor_after(buffer.len());
6551 let theme = cx.global::<Settings>().theme.as_ref();
6552 self.background_highlights_in_range(start..end, &snapshot, theme)
6553 }
6554
6555 fn document_highlights_for_position<'a>(
6556 &'a self,
6557 position: Anchor,
6558 buffer: &'a MultiBufferSnapshot,
6559 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6560 let read_highlights = self
6561 .background_highlights
6562 .get(&TypeId::of::<DocumentHighlightRead>())
6563 .map(|h| &h.1);
6564 let write_highlights = self
6565 .background_highlights
6566 .get(&TypeId::of::<DocumentHighlightWrite>())
6567 .map(|h| &h.1);
6568 let left_position = position.bias_left(buffer);
6569 let right_position = position.bias_right(buffer);
6570 read_highlights
6571 .into_iter()
6572 .chain(write_highlights)
6573 .flat_map(move |ranges| {
6574 let start_ix = match ranges.binary_search_by(|probe| {
6575 let cmp = probe.end.cmp(&left_position, buffer);
6576 if cmp.is_ge() {
6577 Ordering::Greater
6578 } else {
6579 Ordering::Less
6580 }
6581 }) {
6582 Ok(i) | Err(i) => i,
6583 };
6584
6585 let right_position = right_position.clone();
6586 ranges[start_ix..]
6587 .iter()
6588 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6589 })
6590 }
6591
6592 pub fn background_highlights_in_range(
6593 &self,
6594 search_range: Range<Anchor>,
6595 display_snapshot: &DisplaySnapshot,
6596 theme: &Theme,
6597 ) -> Vec<(Range<DisplayPoint>, Color)> {
6598 let mut results = Vec::new();
6599 let buffer = &display_snapshot.buffer_snapshot;
6600 for (color_fetcher, ranges) in self.background_highlights.values() {
6601 let color = color_fetcher(theme);
6602 let start_ix = match ranges.binary_search_by(|probe| {
6603 let cmp = probe.end.cmp(&search_range.start, buffer);
6604 if cmp.is_gt() {
6605 Ordering::Greater
6606 } else {
6607 Ordering::Less
6608 }
6609 }) {
6610 Ok(i) | Err(i) => i,
6611 };
6612 for range in &ranges[start_ix..] {
6613 if range.start.cmp(&search_range.end, buffer).is_ge() {
6614 break;
6615 }
6616 let start = range
6617 .start
6618 .to_point(buffer)
6619 .to_display_point(display_snapshot);
6620 let end = range
6621 .end
6622 .to_point(buffer)
6623 .to_display_point(display_snapshot);
6624 results.push((start..end, color))
6625 }
6626 }
6627 results
6628 }
6629
6630 pub fn highlight_text<T: 'static>(
6631 &mut self,
6632 ranges: Vec<Range<Anchor>>,
6633 style: HighlightStyle,
6634 cx: &mut ViewContext<Self>,
6635 ) {
6636 self.display_map.update(cx, |map, _| {
6637 map.highlight_text(TypeId::of::<T>(), ranges, style)
6638 });
6639 cx.notify();
6640 }
6641
6642 pub fn text_highlights<'a, T: 'static>(
6643 &'a self,
6644 cx: &'a AppContext,
6645 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6646 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6647 }
6648
6649 pub fn clear_text_highlights<T: 'static>(
6650 &mut self,
6651 cx: &mut ViewContext<Self>,
6652 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6653 let highlights = self
6654 .display_map
6655 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6656 if highlights.is_some() {
6657 cx.notify();
6658 }
6659 highlights
6660 }
6661
6662 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6663 self.blink_manager.read(cx).visible() && self.focused
6664 }
6665
6666 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6667 cx.notify();
6668 }
6669
6670 fn on_buffer_event(
6671 &mut self,
6672 _: ModelHandle<MultiBuffer>,
6673 event: &multi_buffer::Event,
6674 cx: &mut ViewContext<Self>,
6675 ) {
6676 match event {
6677 multi_buffer::Event::Edited => {
6678 self.refresh_active_diagnostics(cx);
6679 self.refresh_code_actions(cx);
6680 if self.has_active_copilot_suggestion(cx) {
6681 self.update_visible_copilot_suggestion(cx);
6682 }
6683 cx.emit(Event::BufferEdited);
6684 }
6685 multi_buffer::Event::ExcerptsAdded {
6686 buffer,
6687 predecessor,
6688 excerpts,
6689 } => cx.emit(Event::ExcerptsAdded {
6690 buffer: buffer.clone(),
6691 predecessor: *predecessor,
6692 excerpts: excerpts.clone(),
6693 }),
6694 multi_buffer::Event::ExcerptsRemoved { ids } => {
6695 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6696 }
6697 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6698 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6699 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6700 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6701 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6702 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6703 multi_buffer::Event::DiagnosticsUpdated => {
6704 self.refresh_active_diagnostics(cx);
6705 }
6706 multi_buffer::Event::LanguageChanged => {}
6707 }
6708 }
6709
6710 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6711 cx.notify();
6712 }
6713
6714 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
6715 self.refresh_copilot_suggestions(true, cx);
6716 }
6717
6718 pub fn set_searchable(&mut self, searchable: bool) {
6719 self.searchable = searchable;
6720 }
6721
6722 pub fn searchable(&self) -> bool {
6723 self.searchable
6724 }
6725
6726 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6727 let active_item = workspace.active_item(cx);
6728 let editor_handle = if let Some(editor) = active_item
6729 .as_ref()
6730 .and_then(|item| item.act_as::<Self>(cx))
6731 {
6732 editor
6733 } else {
6734 cx.propagate_action();
6735 return;
6736 };
6737
6738 let editor = editor_handle.read(cx);
6739 let buffer = editor.buffer.read(cx);
6740 if buffer.is_singleton() {
6741 cx.propagate_action();
6742 return;
6743 }
6744
6745 let mut new_selections_by_buffer = HashMap::default();
6746 for selection in editor.selections.all::<usize>(cx) {
6747 for (buffer, mut range) in
6748 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6749 {
6750 if selection.reversed {
6751 mem::swap(&mut range.start, &mut range.end);
6752 }
6753 new_selections_by_buffer
6754 .entry(buffer)
6755 .or_insert(Vec::new())
6756 .push(range)
6757 }
6758 }
6759
6760 editor_handle.update(cx, |editor, cx| {
6761 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6762 });
6763 let pane = workspace.active_pane().clone();
6764 pane.update(cx, |pane, _| pane.disable_history());
6765
6766 // We defer the pane interaction because we ourselves are a workspace item
6767 // and activating a new item causes the pane to call a method on us reentrantly,
6768 // which panics if we're on the stack.
6769 cx.defer(move |workspace, cx| {
6770 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6771 let editor = workspace.open_project_item::<Self>(buffer, cx);
6772 editor.update(cx, |editor, cx| {
6773 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6774 s.select_ranges(ranges);
6775 });
6776 });
6777 }
6778
6779 pane.update(cx, |pane, _| pane.enable_history());
6780 });
6781 }
6782
6783 fn jump(
6784 workspace: &mut Workspace,
6785 path: ProjectPath,
6786 position: Point,
6787 anchor: language::Anchor,
6788 cx: &mut ViewContext<Workspace>,
6789 ) {
6790 let editor = workspace.open_path(path, None, true, cx);
6791 cx.spawn(|_, mut cx| async move {
6792 let editor = editor
6793 .await?
6794 .downcast::<Editor>()
6795 .ok_or_else(|| anyhow!("opened item was not an editor"))?
6796 .downgrade();
6797 editor.update(&mut cx, |editor, cx| {
6798 let buffer = editor
6799 .buffer()
6800 .read(cx)
6801 .as_singleton()
6802 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
6803 let buffer = buffer.read(cx);
6804 let cursor = if buffer.can_resolve(&anchor) {
6805 language::ToPoint::to_point(&anchor, buffer)
6806 } else {
6807 buffer.clip_point(position, Bias::Left)
6808 };
6809
6810 let nav_history = editor.nav_history.take();
6811 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6812 s.select_ranges([cursor..cursor]);
6813 });
6814 editor.nav_history = nav_history;
6815
6816 anyhow::Ok(())
6817 })??;
6818
6819 anyhow::Ok(())
6820 })
6821 .detach_and_log_err(cx);
6822 }
6823
6824 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6825 let snapshot = self.buffer.read(cx).read(cx);
6826 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6827 Some(
6828 ranges
6829 .iter()
6830 .map(move |range| {
6831 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6832 })
6833 .collect(),
6834 )
6835 }
6836
6837 fn selection_replacement_ranges(
6838 &self,
6839 range: Range<OffsetUtf16>,
6840 cx: &AppContext,
6841 ) -> Vec<Range<OffsetUtf16>> {
6842 let selections = self.selections.all::<OffsetUtf16>(cx);
6843 let newest_selection = selections
6844 .iter()
6845 .max_by_key(|selection| selection.id)
6846 .unwrap();
6847 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6848 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6849 let snapshot = self.buffer.read(cx).read(cx);
6850 selections
6851 .into_iter()
6852 .map(|mut selection| {
6853 selection.start.0 =
6854 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6855 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6856 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6857 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6858 })
6859 .collect()
6860 }
6861
6862 fn report_editor_event(&self, name: &'static str, cx: &AppContext) {
6863 if let Some((project, file)) = self.project.as_ref().zip(
6864 self.buffer
6865 .read(cx)
6866 .as_singleton()
6867 .and_then(|b| b.read(cx).file()),
6868 ) {
6869 let settings = cx.global::<Settings>();
6870
6871 let extension = Path::new(file.file_name(cx))
6872 .extension()
6873 .and_then(|e| e.to_str());
6874 let telemetry = project.read(cx).client().telemetry().clone();
6875 telemetry.report_mixpanel_event(
6876 match name {
6877 "open" => "open editor",
6878 "save" => "save editor",
6879 _ => name,
6880 },
6881 json!({ "File Extension": extension, "Vim Mode": settings.vim_mode, "In Clickhouse": true }),
6882 settings.telemetry(),
6883 );
6884 let event = ClickhouseEvent::Editor {
6885 file_extension: extension.map(ToString::to_string),
6886 vim_mode: settings.vim_mode,
6887 operation: name,
6888 copilot_enabled: settings.features.copilot,
6889 copilot_enabled_for_language: settings.show_copilot_suggestions(
6890 self.language_at(0, cx)
6891 .map(|language| language.name())
6892 .as_deref(),
6893 self.file_at(0, cx)
6894 .map(|file| file.path().clone())
6895 .as_deref(),
6896 ),
6897 };
6898 telemetry.report_clickhouse_event(event, settings.telemetry())
6899 }
6900 }
6901
6902 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
6903 /// with each line being an array of {text, highlight} objects.
6904 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
6905 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
6906 return;
6907 };
6908
6909 #[derive(Serialize)]
6910 struct Chunk<'a> {
6911 text: String,
6912 highlight: Option<&'a str>,
6913 }
6914
6915 let snapshot = buffer.read(cx).snapshot();
6916 let range = self
6917 .selected_text_range(cx)
6918 .and_then(|selected_range| {
6919 if selected_range.is_empty() {
6920 None
6921 } else {
6922 Some(selected_range)
6923 }
6924 })
6925 .unwrap_or_else(|| 0..snapshot.len());
6926
6927 let chunks = snapshot.chunks(range, true);
6928 let mut lines = Vec::new();
6929 let mut line: VecDeque<Chunk> = VecDeque::new();
6930
6931 let theme = &cx.global::<Settings>().theme.editor.syntax;
6932
6933 for chunk in chunks {
6934 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
6935 let mut chunk_lines = chunk.text.split("\n").peekable();
6936 while let Some(text) = chunk_lines.next() {
6937 let mut merged_with_last_token = false;
6938 if let Some(last_token) = line.back_mut() {
6939 if last_token.highlight == highlight {
6940 last_token.text.push_str(text);
6941 merged_with_last_token = true;
6942 }
6943 }
6944
6945 if !merged_with_last_token {
6946 line.push_back(Chunk {
6947 text: text.into(),
6948 highlight,
6949 });
6950 }
6951
6952 if chunk_lines.peek().is_some() {
6953 if line.len() > 1 && line.front().unwrap().text.is_empty() {
6954 line.pop_front();
6955 }
6956 if line.len() > 1 && line.back().unwrap().text.is_empty() {
6957 line.pop_back();
6958 }
6959
6960 lines.push(mem::take(&mut line));
6961 }
6962 }
6963 }
6964
6965 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
6966 cx.write_to_clipboard(ClipboardItem::new(lines));
6967 }
6968}
6969
6970fn consume_contiguous_rows(
6971 contiguous_row_selections: &mut Vec<Selection<Point>>,
6972 selection: &Selection<Point>,
6973 display_map: &DisplaySnapshot,
6974 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
6975) -> (u32, u32) {
6976 contiguous_row_selections.push(selection.clone());
6977 let start_row = selection.start.row;
6978 let mut end_row = ending_row(selection, display_map);
6979
6980 while let Some(next_selection) = selections.peek() {
6981 if next_selection.start.row <= end_row {
6982 end_row = ending_row(next_selection, display_map);
6983 contiguous_row_selections.push(selections.next().unwrap().clone());
6984 } else {
6985 break;
6986 }
6987 }
6988 (start_row, end_row)
6989}
6990
6991fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
6992 if next_selection.end.column > 0 || next_selection.is_empty() {
6993 display_map.next_line_boundary(next_selection.end).0.row + 1
6994 } else {
6995 next_selection.end.row
6996 }
6997}
6998
6999impl EditorSnapshot {
7000 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7001 self.display_snapshot.buffer_snapshot.language_at(position)
7002 }
7003
7004 pub fn is_focused(&self) -> bool {
7005 self.is_focused
7006 }
7007
7008 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7009 self.placeholder_text.as_ref()
7010 }
7011
7012 pub fn scroll_position(&self) -> Vector2F {
7013 self.scroll_anchor.scroll_position(&self.display_snapshot)
7014 }
7015}
7016
7017impl Deref for EditorSnapshot {
7018 type Target = DisplaySnapshot;
7019
7020 fn deref(&self) -> &Self::Target {
7021 &self.display_snapshot
7022 }
7023}
7024
7025#[derive(Clone, Debug, PartialEq, Eq)]
7026pub enum Event {
7027 InputIgnored {
7028 text: Arc<str>,
7029 },
7030 ExcerptsAdded {
7031 buffer: ModelHandle<Buffer>,
7032 predecessor: ExcerptId,
7033 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7034 },
7035 ExcerptsRemoved {
7036 ids: Vec<ExcerptId>,
7037 },
7038 BufferEdited,
7039 Edited,
7040 Reparsed,
7041 Blurred,
7042 DirtyChanged,
7043 Saved,
7044 TitleChanged,
7045 SelectionsChanged {
7046 local: bool,
7047 },
7048 ScrollPositionChanged {
7049 local: bool,
7050 },
7051 Closed,
7052}
7053
7054pub struct EditorFocused(pub ViewHandle<Editor>);
7055pub struct EditorBlurred(pub ViewHandle<Editor>);
7056pub struct EditorReleased(pub WeakViewHandle<Editor>);
7057
7058impl Entity for Editor {
7059 type Event = Event;
7060
7061 fn release(&mut self, cx: &mut AppContext) {
7062 cx.emit_global(EditorReleased(self.handle.clone()));
7063 }
7064}
7065
7066impl View for Editor {
7067 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7068 let style = self.style(cx);
7069 let font_changed = self.display_map.update(cx, |map, cx| {
7070 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7071 map.set_font(style.text.font_id, style.text.font_size, cx)
7072 });
7073
7074 if font_changed {
7075 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7076 hide_hover(editor, cx);
7077 hide_link_definition(editor, cx);
7078 });
7079 }
7080
7081 Stack::new()
7082 .with_child(EditorElement::new(style.clone()))
7083 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7084 .into_any()
7085 }
7086
7087 fn ui_name() -> &'static str {
7088 "Editor"
7089 }
7090
7091 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7092 if cx.is_self_focused() {
7093 let focused_event = EditorFocused(cx.handle());
7094 cx.emit_global(focused_event);
7095 }
7096 if let Some(rename) = self.pending_rename.as_ref() {
7097 cx.focus(&rename.editor);
7098 } else {
7099 if !self.focused {
7100 self.blink_manager.update(cx, BlinkManager::enable);
7101 }
7102 self.focused = true;
7103 self.buffer.update(cx, |buffer, cx| {
7104 buffer.finalize_last_transaction(cx);
7105 if self.leader_replica_id.is_none() {
7106 buffer.set_active_selections(
7107 &self.selections.disjoint_anchors(),
7108 self.selections.line_mode,
7109 self.cursor_shape,
7110 cx,
7111 );
7112 }
7113 });
7114 }
7115 }
7116
7117 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7118 let blurred_event = EditorBlurred(cx.handle());
7119 cx.emit_global(blurred_event);
7120 self.focused = false;
7121 self.blink_manager.update(cx, BlinkManager::disable);
7122 self.buffer
7123 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7124 self.hide_context_menu(cx);
7125 hide_hover(self, cx);
7126 cx.emit(Event::Blurred);
7127 cx.notify();
7128 }
7129
7130 fn modifiers_changed(
7131 &mut self,
7132 event: &gpui::platform::ModifiersChangedEvent,
7133 cx: &mut ViewContext<Self>,
7134 ) -> bool {
7135 let pending_selection = self.has_pending_selection();
7136
7137 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7138 if event.cmd && !pending_selection {
7139 let snapshot = self.snapshot(cx);
7140 let kind = if event.shift {
7141 LinkDefinitionKind::Type
7142 } else {
7143 LinkDefinitionKind::Symbol
7144 };
7145
7146 show_link_definition(kind, self, point, snapshot, cx);
7147 return false;
7148 }
7149 }
7150
7151 {
7152 if self.link_go_to_definition_state.symbol_range.is_some()
7153 || !self.link_go_to_definition_state.definitions.is_empty()
7154 {
7155 self.link_go_to_definition_state.symbol_range.take();
7156 self.link_go_to_definition_state.definitions.clear();
7157 cx.notify();
7158 }
7159
7160 self.link_go_to_definition_state.task = None;
7161
7162 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7163 }
7164
7165 false
7166 }
7167
7168 fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
7169 Self::reset_to_default_keymap_context(keymap);
7170 let mode = match self.mode {
7171 EditorMode::SingleLine => "single_line",
7172 EditorMode::AutoHeight { .. } => "auto_height",
7173 EditorMode::Full => "full",
7174 };
7175 keymap.add_key("mode", mode);
7176 if self.pending_rename.is_some() {
7177 keymap.add_identifier("renaming");
7178 }
7179 match self.context_menu.as_ref() {
7180 Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
7181 Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
7182 None => {}
7183 }
7184
7185 for layer in self.keymap_context_layers.values() {
7186 keymap.extend(layer);
7187 }
7188 }
7189
7190 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7191 Some(
7192 self.buffer
7193 .read(cx)
7194 .read(cx)
7195 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7196 .collect(),
7197 )
7198 }
7199
7200 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7201 // Prevent the IME menu from appearing when holding down an alphabetic key
7202 // while input is disabled.
7203 if !self.input_enabled {
7204 return None;
7205 }
7206
7207 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7208 Some(range.start.0..range.end.0)
7209 }
7210
7211 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7212 let snapshot = self.buffer.read(cx).read(cx);
7213 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7214 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7215 }
7216
7217 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7218 self.clear_text_highlights::<InputComposition>(cx);
7219 self.ime_transaction.take();
7220 }
7221
7222 fn replace_text_in_range(
7223 &mut self,
7224 range_utf16: Option<Range<usize>>,
7225 text: &str,
7226 cx: &mut ViewContext<Self>,
7227 ) {
7228 self.transact(cx, |this, cx| {
7229 if this.input_enabled {
7230 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7231 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7232 Some(this.selection_replacement_ranges(range_utf16, cx))
7233 } else {
7234 this.marked_text_ranges(cx)
7235 };
7236
7237 if let Some(new_selected_ranges) = new_selected_ranges {
7238 this.change_selections(None, cx, |selections| {
7239 selections.select_ranges(new_selected_ranges)
7240 });
7241 }
7242 }
7243
7244 this.handle_input(text, cx);
7245 });
7246
7247 if !self.input_enabled {
7248 return;
7249 }
7250
7251 if let Some(transaction) = self.ime_transaction {
7252 self.buffer.update(cx, |buffer, cx| {
7253 buffer.group_until_transaction(transaction, cx);
7254 });
7255 }
7256
7257 self.unmark_text(cx);
7258 }
7259
7260 fn replace_and_mark_text_in_range(
7261 &mut self,
7262 range_utf16: Option<Range<usize>>,
7263 text: &str,
7264 new_selected_range_utf16: Option<Range<usize>>,
7265 cx: &mut ViewContext<Self>,
7266 ) {
7267 if !self.input_enabled {
7268 return;
7269 }
7270
7271 let transaction = self.transact(cx, |this, cx| {
7272 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7273 let snapshot = this.buffer.read(cx).read(cx);
7274 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7275 for marked_range in &mut marked_ranges {
7276 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7277 marked_range.start.0 += relative_range_utf16.start;
7278 marked_range.start =
7279 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7280 marked_range.end =
7281 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7282 }
7283 }
7284 Some(marked_ranges)
7285 } else if let Some(range_utf16) = range_utf16 {
7286 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7287 Some(this.selection_replacement_ranges(range_utf16, cx))
7288 } else {
7289 None
7290 };
7291
7292 if let Some(ranges) = ranges_to_replace {
7293 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7294 }
7295
7296 let marked_ranges = {
7297 let snapshot = this.buffer.read(cx).read(cx);
7298 this.selections
7299 .disjoint_anchors()
7300 .iter()
7301 .map(|selection| {
7302 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7303 })
7304 .collect::<Vec<_>>()
7305 };
7306
7307 if text.is_empty() {
7308 this.unmark_text(cx);
7309 } else {
7310 this.highlight_text::<InputComposition>(
7311 marked_ranges.clone(),
7312 this.style(cx).composition_mark,
7313 cx,
7314 );
7315 }
7316
7317 this.handle_input(text, cx);
7318
7319 if let Some(new_selected_range) = new_selected_range_utf16 {
7320 let snapshot = this.buffer.read(cx).read(cx);
7321 let new_selected_ranges = marked_ranges
7322 .into_iter()
7323 .map(|marked_range| {
7324 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7325 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7326 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7327 snapshot.clip_offset_utf16(new_start, Bias::Left)
7328 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7329 })
7330 .collect::<Vec<_>>();
7331
7332 drop(snapshot);
7333 this.change_selections(None, cx, |selections| {
7334 selections.select_ranges(new_selected_ranges)
7335 });
7336 }
7337 });
7338
7339 self.ime_transaction = self.ime_transaction.or(transaction);
7340 if let Some(transaction) = self.ime_transaction {
7341 self.buffer.update(cx, |buffer, cx| {
7342 buffer.group_until_transaction(transaction, cx);
7343 });
7344 }
7345
7346 if self.text_highlights::<InputComposition>(cx).is_none() {
7347 self.ime_transaction.take();
7348 }
7349 }
7350}
7351
7352fn build_style(
7353 settings: &Settings,
7354 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7355 override_text_style: Option<&OverrideTextStyle>,
7356 cx: &AppContext,
7357) -> EditorStyle {
7358 let font_cache = cx.font_cache();
7359
7360 let theme_id = settings.theme.meta.id;
7361 let mut theme = settings.theme.editor.clone();
7362 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7363 let field_editor_theme = get_field_editor_theme(&settings.theme);
7364 theme.text_color = field_editor_theme.text.color;
7365 theme.selection = field_editor_theme.selection;
7366 theme.background = field_editor_theme
7367 .container
7368 .background_color
7369 .unwrap_or_default();
7370 EditorStyle {
7371 text: field_editor_theme.text,
7372 placeholder_text: field_editor_theme.placeholder_text,
7373 theme,
7374 theme_id,
7375 }
7376 } else {
7377 let font_family_id = settings.buffer_font_family;
7378 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7379 let font_properties = Default::default();
7380 let font_id = font_cache
7381 .select_font(font_family_id, &font_properties)
7382 .unwrap();
7383 let font_size = settings.buffer_font_size;
7384 EditorStyle {
7385 text: TextStyle {
7386 color: settings.theme.editor.text_color,
7387 font_family_name,
7388 font_family_id,
7389 font_id,
7390 font_size,
7391 font_properties,
7392 underline: Default::default(),
7393 },
7394 placeholder_text: None,
7395 theme,
7396 theme_id,
7397 }
7398 };
7399
7400 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7401 if let Some(highlighted) = style
7402 .text
7403 .clone()
7404 .highlight(highlight_style, font_cache)
7405 .log_err()
7406 {
7407 style.text = highlighted;
7408 }
7409 }
7410
7411 style
7412}
7413
7414trait SelectionExt {
7415 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7416 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7417 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7418 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7419 -> Range<u32>;
7420}
7421
7422impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7423 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7424 let start = self.start.to_point(buffer);
7425 let end = self.end.to_point(buffer);
7426 if self.reversed {
7427 end..start
7428 } else {
7429 start..end
7430 }
7431 }
7432
7433 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7434 let start = self.start.to_offset(buffer);
7435 let end = self.end.to_offset(buffer);
7436 if self.reversed {
7437 end..start
7438 } else {
7439 start..end
7440 }
7441 }
7442
7443 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7444 let start = self
7445 .start
7446 .to_point(&map.buffer_snapshot)
7447 .to_display_point(map);
7448 let end = self
7449 .end
7450 .to_point(&map.buffer_snapshot)
7451 .to_display_point(map);
7452 if self.reversed {
7453 end..start
7454 } else {
7455 start..end
7456 }
7457 }
7458
7459 fn spanned_rows(
7460 &self,
7461 include_end_if_at_line_start: bool,
7462 map: &DisplaySnapshot,
7463 ) -> Range<u32> {
7464 let start = self.start.to_point(&map.buffer_snapshot);
7465 let mut end = self.end.to_point(&map.buffer_snapshot);
7466 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7467 end.row -= 1;
7468 }
7469
7470 let buffer_start = map.prev_line_boundary(start).0;
7471 let buffer_end = map.next_line_boundary(end).0;
7472 buffer_start.row..buffer_end.row + 1
7473 }
7474}
7475
7476impl<T: InvalidationRegion> InvalidationStack<T> {
7477 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7478 where
7479 S: Clone + ToOffset,
7480 {
7481 while let Some(region) = self.last() {
7482 let all_selections_inside_invalidation_ranges =
7483 if selections.len() == region.ranges().len() {
7484 selections
7485 .iter()
7486 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7487 .all(|(selection, invalidation_range)| {
7488 let head = selection.head().to_offset(buffer);
7489 invalidation_range.start <= head && invalidation_range.end >= head
7490 })
7491 } else {
7492 false
7493 };
7494
7495 if all_selections_inside_invalidation_ranges {
7496 break;
7497 } else {
7498 self.pop();
7499 }
7500 }
7501 }
7502}
7503
7504impl<T> Default for InvalidationStack<T> {
7505 fn default() -> Self {
7506 Self(Default::default())
7507 }
7508}
7509
7510impl<T> Deref for InvalidationStack<T> {
7511 type Target = Vec<T>;
7512
7513 fn deref(&self) -> &Self::Target {
7514 &self.0
7515 }
7516}
7517
7518impl<T> DerefMut for InvalidationStack<T> {
7519 fn deref_mut(&mut self) -> &mut Self::Target {
7520 &mut self.0
7521 }
7522}
7523
7524impl InvalidationRegion for SnippetState {
7525 fn ranges(&self) -> &[Range<Anchor>] {
7526 &self.ranges[self.active_index]
7527 }
7528}
7529
7530impl Deref for EditorStyle {
7531 type Target = theme::Editor;
7532
7533 fn deref(&self) -> &Self::Target {
7534 &self.theme
7535 }
7536}
7537
7538pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7539 let mut highlighted_lines = Vec::new();
7540 for (index, line) in diagnostic.message.lines().enumerate() {
7541 let line = match &diagnostic.source {
7542 Some(source) if index == 0 => {
7543 let source_highlight = Vec::from_iter(0..source.len());
7544 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
7545 }
7546
7547 _ => highlight_diagnostic_message(Vec::new(), line),
7548 };
7549 highlighted_lines.push(line);
7550 }
7551
7552 Arc::new(move |cx: &mut BlockContext| {
7553 let settings = cx.global::<Settings>();
7554 let theme = &settings.theme.editor;
7555 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7556 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
7557 Flex::column()
7558 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7559 Label::new(
7560 line.clone(),
7561 style.message.clone().with_font_size(font_size),
7562 )
7563 .with_highlights(highlights.clone())
7564 .contained()
7565 .with_margin_left(cx.anchor_x)
7566 }))
7567 .aligned()
7568 .left()
7569 .into_any()
7570 })
7571}
7572
7573pub fn highlight_diagnostic_message(
7574 inital_highlights: Vec<usize>,
7575 message: &str,
7576) -> (String, Vec<usize>) {
7577 let mut message_without_backticks = String::new();
7578 let mut prev_offset = 0;
7579 let mut inside_block = false;
7580 let mut highlights = inital_highlights;
7581 for (match_ix, (offset, _)) in message
7582 .match_indices('`')
7583 .chain([(message.len(), "")])
7584 .enumerate()
7585 {
7586 message_without_backticks.push_str(&message[prev_offset..offset]);
7587 if inside_block {
7588 highlights.extend(prev_offset - match_ix..offset - match_ix);
7589 }
7590
7591 inside_block = !inside_block;
7592 prev_offset = offset + 1;
7593 }
7594
7595 (message_without_backticks, highlights)
7596}
7597
7598pub fn diagnostic_style(
7599 severity: DiagnosticSeverity,
7600 valid: bool,
7601 theme: &theme::Editor,
7602) -> DiagnosticStyle {
7603 match (severity, valid) {
7604 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
7605 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
7606 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
7607 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
7608 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
7609 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
7610 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
7611 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
7612 _ => theme.invalid_hint_diagnostic.clone(),
7613 }
7614}
7615
7616pub fn combine_syntax_and_fuzzy_match_highlights(
7617 text: &str,
7618 default_style: HighlightStyle,
7619 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
7620 match_indices: &[usize],
7621) -> Vec<(Range<usize>, HighlightStyle)> {
7622 let mut result = Vec::new();
7623 let mut match_indices = match_indices.iter().copied().peekable();
7624
7625 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
7626 {
7627 syntax_highlight.weight = None;
7628
7629 // Add highlights for any fuzzy match characters before the next
7630 // syntax highlight range.
7631 while let Some(&match_index) = match_indices.peek() {
7632 if match_index >= range.start {
7633 break;
7634 }
7635 match_indices.next();
7636 let end_index = char_ix_after(match_index, text);
7637 let mut match_style = default_style;
7638 match_style.weight = Some(fonts::Weight::BOLD);
7639 result.push((match_index..end_index, match_style));
7640 }
7641
7642 if range.start == usize::MAX {
7643 break;
7644 }
7645
7646 // Add highlights for any fuzzy match characters within the
7647 // syntax highlight range.
7648 let mut offset = range.start;
7649 while let Some(&match_index) = match_indices.peek() {
7650 if match_index >= range.end {
7651 break;
7652 }
7653
7654 match_indices.next();
7655 if match_index > offset {
7656 result.push((offset..match_index, syntax_highlight));
7657 }
7658
7659 let mut end_index = char_ix_after(match_index, text);
7660 while let Some(&next_match_index) = match_indices.peek() {
7661 if next_match_index == end_index && next_match_index < range.end {
7662 end_index = char_ix_after(next_match_index, text);
7663 match_indices.next();
7664 } else {
7665 break;
7666 }
7667 }
7668
7669 let mut match_style = syntax_highlight;
7670 match_style.weight = Some(fonts::Weight::BOLD);
7671 result.push((match_index..end_index, match_style));
7672 offset = end_index;
7673 }
7674
7675 if offset < range.end {
7676 result.push((offset..range.end, syntax_highlight));
7677 }
7678 }
7679
7680 fn char_ix_after(ix: usize, text: &str) -> usize {
7681 ix + text[ix..].chars().next().unwrap().len_utf8()
7682 }
7683
7684 result
7685}
7686
7687pub fn styled_runs_for_code_label<'a>(
7688 label: &'a CodeLabel,
7689 syntax_theme: &'a theme::SyntaxTheme,
7690) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
7691 let fade_out = HighlightStyle {
7692 fade_out: Some(0.35),
7693 ..Default::default()
7694 };
7695
7696 let mut prev_end = label.filter_range.end;
7697 label
7698 .runs
7699 .iter()
7700 .enumerate()
7701 .flat_map(move |(ix, (range, highlight_id))| {
7702 let style = if let Some(style) = highlight_id.style(syntax_theme) {
7703 style
7704 } else {
7705 return Default::default();
7706 };
7707 let mut muted_style = style;
7708 muted_style.highlight(fade_out);
7709
7710 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
7711 if range.start >= label.filter_range.end {
7712 if range.start > prev_end {
7713 runs.push((prev_end..range.start, fade_out));
7714 }
7715 runs.push((range.clone(), muted_style));
7716 } else if range.end <= label.filter_range.end {
7717 runs.push((range.clone(), style));
7718 } else {
7719 runs.push((range.start..label.filter_range.end, style));
7720 runs.push((label.filter_range.end..range.end, muted_style));
7721 }
7722 prev_end = cmp::max(prev_end, range.end);
7723
7724 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
7725 runs.push((prev_end..label.text.len(), fade_out));
7726 }
7727
7728 runs
7729 })
7730}
7731
7732pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
7733 let mut index = 0;
7734 let mut codepoints = text.char_indices().peekable();
7735
7736 std::iter::from_fn(move || {
7737 let start_index = index;
7738 while let Some((new_index, codepoint)) = codepoints.next() {
7739 index = new_index + codepoint.len_utf8();
7740 let current_upper = codepoint.is_uppercase();
7741 let next_upper = codepoints
7742 .peek()
7743 .map(|(_, c)| c.is_uppercase())
7744 .unwrap_or(false);
7745
7746 if !current_upper && next_upper {
7747 return Some(&text[start_index..index]);
7748 }
7749 }
7750
7751 index = text.len();
7752 if start_index < text.len() {
7753 return Some(&text[start_index..]);
7754 }
7755 None
7756 })
7757 .flat_map(|word| word.split_inclusive('_'))
7758}
7759
7760trait RangeToAnchorExt {
7761 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
7762}
7763
7764impl<T: ToOffset> RangeToAnchorExt for Range<T> {
7765 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
7766 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
7767 }
7768}