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