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