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