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