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