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