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