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 self.inlay_hint_cache.refresh_inlay_hints(
2663 self.excerpt_visible_offsets(required_languages.as_ref(), cx),
2664 invalidate_cache,
2665 cx,
2666 )
2667 }
2668
2669 fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
2670 self.display_map
2671 .read(cx)
2672 .current_inlays()
2673 .filter(move |inlay| {
2674 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
2675 })
2676 .cloned()
2677 .collect()
2678 }
2679
2680 fn excerpt_visible_offsets(
2681 &self,
2682 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
2683 cx: &mut ViewContext<'_, '_, Editor>,
2684 ) -> HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)> {
2685 let multi_buffer = self.buffer().read(cx);
2686 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
2687 let multi_buffer_visible_start = self
2688 .scroll_manager
2689 .anchor()
2690 .anchor
2691 .to_point(&multi_buffer_snapshot);
2692 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
2693 multi_buffer_visible_start
2694 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
2695 Bias::Left,
2696 );
2697 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
2698 multi_buffer
2699 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
2700 .into_iter()
2701 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
2702 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
2703 let buffer = buffer_handle.read(cx);
2704 let language = buffer.language()?;
2705 if let Some(restrict_to_languages) = restrict_to_languages {
2706 if !restrict_to_languages.contains(language) {
2707 return None;
2708 }
2709 }
2710 Some((
2711 excerpt_id,
2712 (
2713 buffer_handle,
2714 buffer.version().clone(),
2715 excerpt_visible_range,
2716 ),
2717 ))
2718 })
2719 .collect()
2720 }
2721
2722 fn splice_inlay_hints(
2723 &self,
2724 to_remove: Vec<InlayId>,
2725 to_insert: Vec<Inlay>,
2726 cx: &mut ViewContext<Self>,
2727 ) {
2728 self.display_map.update(cx, |display_map, cx| {
2729 display_map.splice_inlays(to_remove, to_insert, cx);
2730 });
2731 }
2732
2733 fn trigger_on_type_formatting(
2734 &self,
2735 input: String,
2736 cx: &mut ViewContext<Self>,
2737 ) -> Option<Task<Result<()>>> {
2738 if input.len() != 1 {
2739 return None;
2740 }
2741
2742 let project = self.project.as_ref()?;
2743 let position = self.selections.newest_anchor().head();
2744 let (buffer, buffer_position) = self
2745 .buffer
2746 .read(cx)
2747 .text_anchor_for_position(position.clone(), cx)?;
2748
2749 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2750 // hence we do LSP request & edit on host side only — add formats to host's history.
2751 let push_to_lsp_host_history = true;
2752 // If this is not the host, append its history with new edits.
2753 let push_to_client_history = project.read(cx).is_remote();
2754
2755 let on_type_formatting = project.update(cx, |project, cx| {
2756 project.on_type_format(
2757 buffer.clone(),
2758 buffer_position,
2759 input,
2760 push_to_lsp_host_history,
2761 cx,
2762 )
2763 });
2764 Some(cx.spawn(|editor, mut cx| async move {
2765 if let Some(transaction) = on_type_formatting.await? {
2766 if push_to_client_history {
2767 buffer.update(&mut cx, |buffer, _| {
2768 buffer.push_transaction(transaction, Instant::now());
2769 });
2770 }
2771 editor.update(&mut cx, |editor, cx| {
2772 editor.refresh_document_highlights(cx);
2773 })?;
2774 }
2775 Ok(())
2776 }))
2777 }
2778
2779 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2780 if self.pending_rename.is_some() {
2781 return;
2782 }
2783
2784 let project = if let Some(project) = self.project.clone() {
2785 project
2786 } else {
2787 return;
2788 };
2789
2790 let position = self.selections.newest_anchor().head();
2791 let (buffer, buffer_position) = if let Some(output) = self
2792 .buffer
2793 .read(cx)
2794 .text_anchor_for_position(position.clone(), cx)
2795 {
2796 output
2797 } else {
2798 return;
2799 };
2800
2801 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2802 let completions = project.update(cx, |project, cx| {
2803 project.completions(&buffer, buffer_position, cx)
2804 });
2805
2806 let id = post_inc(&mut self.next_completion_id);
2807 let task = cx.spawn(|this, mut cx| {
2808 async move {
2809 let menu = if let Some(completions) = completions.await.log_err() {
2810 let mut menu = CompletionsMenu {
2811 id,
2812 initial_position: position,
2813 match_candidates: completions
2814 .iter()
2815 .enumerate()
2816 .map(|(id, completion)| {
2817 StringMatchCandidate::new(
2818 id,
2819 completion.label.text[completion.label.filter_range.clone()]
2820 .into(),
2821 )
2822 })
2823 .collect(),
2824 buffer,
2825 completions: completions.into(),
2826 matches: Vec::new().into(),
2827 selected_item: 0,
2828 list: Default::default(),
2829 };
2830 menu.filter(query.as_deref(), cx.background()).await;
2831 if menu.matches.is_empty() {
2832 None
2833 } else {
2834 Some(menu)
2835 }
2836 } else {
2837 None
2838 };
2839
2840 this.update(&mut cx, |this, cx| {
2841 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2842
2843 match this.context_menu.as_ref() {
2844 None => {}
2845 Some(ContextMenu::Completions(prev_menu)) => {
2846 if prev_menu.id > id {
2847 return;
2848 }
2849 }
2850 _ => return,
2851 }
2852
2853 if this.focused && menu.is_some() {
2854 let menu = menu.unwrap();
2855 this.show_context_menu(ContextMenu::Completions(menu), cx);
2856 } else if this.completion_tasks.is_empty() {
2857 // If there are no more completion tasks and the last menu was
2858 // empty, we should hide it. If it was already hidden, we should
2859 // also show the copilot suggestion when available.
2860 if this.hide_context_menu(cx).is_none() {
2861 this.update_visible_copilot_suggestion(cx);
2862 }
2863 }
2864 })?;
2865
2866 Ok::<_, anyhow::Error>(())
2867 }
2868 .log_err()
2869 });
2870 self.completion_tasks.push((id, task));
2871 }
2872
2873 pub fn confirm_completion(
2874 &mut self,
2875 action: &ConfirmCompletion,
2876 cx: &mut ViewContext<Self>,
2877 ) -> Option<Task<Result<()>>> {
2878 use language::ToOffset as _;
2879
2880 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2881 menu
2882 } else {
2883 return None;
2884 };
2885
2886 let mat = completions_menu
2887 .matches
2888 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2889 let buffer_handle = completions_menu.buffer;
2890 let completion = completions_menu.completions.get(mat.candidate_id)?;
2891
2892 let snippet;
2893 let text;
2894 if completion.is_snippet() {
2895 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2896 text = snippet.as_ref().unwrap().text.clone();
2897 } else {
2898 snippet = None;
2899 text = completion.new_text.clone();
2900 };
2901 let selections = self.selections.all::<usize>(cx);
2902 let buffer = buffer_handle.read(cx);
2903 let old_range = completion.old_range.to_offset(buffer);
2904 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2905
2906 let newest_selection = self.selections.newest_anchor();
2907 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2908 return None;
2909 }
2910
2911 let lookbehind = newest_selection
2912 .start
2913 .text_anchor
2914 .to_offset(buffer)
2915 .saturating_sub(old_range.start);
2916 let lookahead = old_range
2917 .end
2918 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2919 let mut common_prefix_len = old_text
2920 .bytes()
2921 .zip(text.bytes())
2922 .take_while(|(a, b)| a == b)
2923 .count();
2924
2925 let snapshot = self.buffer.read(cx).snapshot(cx);
2926 let mut ranges = Vec::new();
2927 for selection in &selections {
2928 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2929 let start = selection.start.saturating_sub(lookbehind);
2930 let end = selection.end + lookahead;
2931 ranges.push(start + common_prefix_len..end);
2932 } else {
2933 common_prefix_len = 0;
2934 ranges.clear();
2935 ranges.extend(selections.iter().map(|s| {
2936 if s.id == newest_selection.id {
2937 old_range.clone()
2938 } else {
2939 s.start..s.end
2940 }
2941 }));
2942 break;
2943 }
2944 }
2945 let text = &text[common_prefix_len..];
2946
2947 self.transact(cx, |this, cx| {
2948 if let Some(mut snippet) = snippet {
2949 snippet.text = text.to_string();
2950 for tabstop in snippet.tabstops.iter_mut().flatten() {
2951 tabstop.start -= common_prefix_len as isize;
2952 tabstop.end -= common_prefix_len as isize;
2953 }
2954
2955 this.insert_snippet(&ranges, snippet, cx).log_err();
2956 } else {
2957 this.buffer.update(cx, |buffer, cx| {
2958 buffer.edit(
2959 ranges.iter().map(|range| (range.clone(), text)),
2960 Some(AutoindentMode::EachLine),
2961 cx,
2962 );
2963 });
2964 }
2965
2966 this.refresh_copilot_suggestions(true, cx);
2967 });
2968
2969 let project = self.project.clone()?;
2970 let apply_edits = project.update(cx, |project, cx| {
2971 project.apply_additional_edits_for_completion(
2972 buffer_handle,
2973 completion.clone(),
2974 true,
2975 cx,
2976 )
2977 });
2978 Some(cx.foreground().spawn(async move {
2979 apply_edits.await?;
2980 Ok(())
2981 }))
2982 }
2983
2984 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2985 if matches!(
2986 self.context_menu.as_ref(),
2987 Some(ContextMenu::CodeActions(_))
2988 ) {
2989 self.context_menu.take();
2990 cx.notify();
2991 return;
2992 }
2993
2994 let deployed_from_indicator = action.deployed_from_indicator;
2995 let mut task = self.code_actions_task.take();
2996 cx.spawn(|this, mut cx| async move {
2997 while let Some(prev_task) = task {
2998 prev_task.await;
2999 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3000 }
3001
3002 this.update(&mut cx, |this, cx| {
3003 if this.focused {
3004 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3005 this.show_context_menu(
3006 ContextMenu::CodeActions(CodeActionsMenu {
3007 buffer,
3008 actions,
3009 selected_item: Default::default(),
3010 list: Default::default(),
3011 deployed_from_indicator,
3012 }),
3013 cx,
3014 );
3015 }
3016 }
3017 })?;
3018
3019 Ok::<_, anyhow::Error>(())
3020 })
3021 .detach_and_log_err(cx);
3022 }
3023
3024 pub fn confirm_code_action(
3025 workspace: &mut Workspace,
3026 action: &ConfirmCodeAction,
3027 cx: &mut ViewContext<Workspace>,
3028 ) -> Option<Task<Result<()>>> {
3029 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3030 let actions_menu = if let ContextMenu::CodeActions(menu) =
3031 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3032 {
3033 menu
3034 } else {
3035 return None;
3036 };
3037 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3038 let action = actions_menu.actions.get(action_ix)?.clone();
3039 let title = action.lsp_action.title.clone();
3040 let buffer = actions_menu.buffer;
3041
3042 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3043 project.apply_code_action(buffer, action, true, cx)
3044 });
3045 let editor = editor.downgrade();
3046 Some(cx.spawn(|workspace, cx| async move {
3047 let project_transaction = apply_code_actions.await?;
3048 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3049 }))
3050 }
3051
3052 async fn open_project_transaction(
3053 this: &WeakViewHandle<Editor>,
3054 workspace: WeakViewHandle<Workspace>,
3055 transaction: ProjectTransaction,
3056 title: String,
3057 mut cx: AsyncAppContext,
3058 ) -> Result<()> {
3059 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3060
3061 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3062 entries.sort_unstable_by_key(|(buffer, _)| {
3063 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3064 });
3065
3066 // If the project transaction's edits are all contained within this editor, then
3067 // avoid opening a new editor to display them.
3068
3069 if let Some((buffer, transaction)) = entries.first() {
3070 if entries.len() == 1 {
3071 let excerpt = this.read_with(&cx, |editor, cx| {
3072 editor
3073 .buffer()
3074 .read(cx)
3075 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3076 })?;
3077 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3078 if excerpted_buffer == *buffer {
3079 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3080 let excerpt_range = excerpt_range.to_offset(buffer);
3081 buffer
3082 .edited_ranges_for_transaction::<usize>(transaction)
3083 .all(|range| {
3084 excerpt_range.start <= range.start
3085 && excerpt_range.end >= range.end
3086 })
3087 });
3088
3089 if all_edits_within_excerpt {
3090 return Ok(());
3091 }
3092 }
3093 }
3094 }
3095 } else {
3096 return Ok(());
3097 }
3098
3099 let mut ranges_to_highlight = Vec::new();
3100 let excerpt_buffer = cx.add_model(|cx| {
3101 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3102 for (buffer_handle, transaction) in &entries {
3103 let buffer = buffer_handle.read(cx);
3104 ranges_to_highlight.extend(
3105 multibuffer.push_excerpts_with_context_lines(
3106 buffer_handle.clone(),
3107 buffer
3108 .edited_ranges_for_transaction::<usize>(transaction)
3109 .collect(),
3110 1,
3111 cx,
3112 ),
3113 );
3114 }
3115 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3116 multibuffer
3117 });
3118
3119 workspace.update(&mut cx, |workspace, cx| {
3120 let project = workspace.project().clone();
3121 let editor =
3122 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3123 workspace.add_item(Box::new(editor.clone()), cx);
3124 editor.update(cx, |editor, cx| {
3125 editor.highlight_background::<Self>(
3126 ranges_to_highlight,
3127 |theme| theme.editor.highlighted_line_background,
3128 cx,
3129 );
3130 });
3131 })?;
3132
3133 Ok(())
3134 }
3135
3136 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3137 let project = self.project.as_ref()?;
3138 let buffer = self.buffer.read(cx);
3139 let newest_selection = self.selections.newest_anchor().clone();
3140 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3141 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3142 if start_buffer != end_buffer {
3143 return None;
3144 }
3145
3146 let actions = project.update(cx, |project, cx| {
3147 project.code_actions(&start_buffer, start..end, cx)
3148 });
3149 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3150 let actions = actions.await;
3151 this.update(&mut cx, |this, cx| {
3152 this.available_code_actions = actions.log_err().and_then(|actions| {
3153 if actions.is_empty() {
3154 None
3155 } else {
3156 Some((start_buffer, actions.into()))
3157 }
3158 });
3159 cx.notify();
3160 })
3161 .log_err();
3162 }));
3163 None
3164 }
3165
3166 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3167 if self.pending_rename.is_some() {
3168 return None;
3169 }
3170
3171 let project = self.project.as_ref()?;
3172 let buffer = self.buffer.read(cx);
3173 let newest_selection = self.selections.newest_anchor().clone();
3174 let cursor_position = newest_selection.head();
3175 let (cursor_buffer, cursor_buffer_position) =
3176 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3177 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3178 if cursor_buffer != tail_buffer {
3179 return None;
3180 }
3181
3182 let highlights = project.update(cx, |project, cx| {
3183 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3184 });
3185
3186 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3187 if let Some(highlights) = highlights.await.log_err() {
3188 this.update(&mut cx, |this, cx| {
3189 if this.pending_rename.is_some() {
3190 return;
3191 }
3192
3193 let buffer_id = cursor_position.buffer_id;
3194 let buffer = this.buffer.read(cx);
3195 if !buffer
3196 .text_anchor_for_position(cursor_position, cx)
3197 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3198 {
3199 return;
3200 }
3201
3202 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3203 let mut write_ranges = Vec::new();
3204 let mut read_ranges = Vec::new();
3205 for highlight in highlights {
3206 for (excerpt_id, excerpt_range) in
3207 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3208 {
3209 let start = highlight
3210 .range
3211 .start
3212 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3213 let end = highlight
3214 .range
3215 .end
3216 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3217 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3218 continue;
3219 }
3220
3221 let range = Anchor {
3222 buffer_id,
3223 excerpt_id: excerpt_id.clone(),
3224 text_anchor: start,
3225 }..Anchor {
3226 buffer_id,
3227 excerpt_id,
3228 text_anchor: end,
3229 };
3230 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3231 write_ranges.push(range);
3232 } else {
3233 read_ranges.push(range);
3234 }
3235 }
3236 }
3237
3238 this.highlight_background::<DocumentHighlightRead>(
3239 read_ranges,
3240 |theme| theme.editor.document_highlight_read_background,
3241 cx,
3242 );
3243 this.highlight_background::<DocumentHighlightWrite>(
3244 write_ranges,
3245 |theme| theme.editor.document_highlight_write_background,
3246 cx,
3247 );
3248 cx.notify();
3249 })
3250 .log_err();
3251 }
3252 }));
3253 None
3254 }
3255
3256 fn refresh_copilot_suggestions(
3257 &mut self,
3258 debounce: bool,
3259 cx: &mut ViewContext<Self>,
3260 ) -> Option<()> {
3261 let copilot = Copilot::global(cx)?;
3262 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3263 self.clear_copilot_suggestions(cx);
3264 return None;
3265 }
3266 self.update_visible_copilot_suggestion(cx);
3267
3268 let snapshot = self.buffer.read(cx).snapshot(cx);
3269 let cursor = self.selections.newest_anchor().head();
3270 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3271 self.clear_copilot_suggestions(cx);
3272 return None;
3273 }
3274
3275 let (buffer, buffer_position) =
3276 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3277 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3278 if debounce {
3279 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3280 }
3281
3282 let completions = copilot
3283 .update(&mut cx, |copilot, cx| {
3284 copilot.completions(&buffer, buffer_position, cx)
3285 })
3286 .await
3287 .log_err()
3288 .into_iter()
3289 .flatten()
3290 .collect_vec();
3291
3292 this.update(&mut cx, |this, cx| {
3293 if !completions.is_empty() {
3294 this.copilot_state.cycled = false;
3295 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3296 this.copilot_state.completions.clear();
3297 this.copilot_state.active_completion_index = 0;
3298 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3299 for completion in completions {
3300 this.copilot_state.push_completion(completion);
3301 }
3302 this.update_visible_copilot_suggestion(cx);
3303 }
3304 })
3305 .log_err()?;
3306 Some(())
3307 });
3308
3309 Some(())
3310 }
3311
3312 fn cycle_copilot_suggestions(
3313 &mut self,
3314 direction: Direction,
3315 cx: &mut ViewContext<Self>,
3316 ) -> Option<()> {
3317 let copilot = Copilot::global(cx)?;
3318 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3319 return None;
3320 }
3321
3322 if self.copilot_state.cycled {
3323 self.copilot_state.cycle_completions(direction);
3324 self.update_visible_copilot_suggestion(cx);
3325 } else {
3326 let cursor = self.selections.newest_anchor().head();
3327 let (buffer, buffer_position) =
3328 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3329 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3330 let completions = copilot
3331 .update(&mut cx, |copilot, cx| {
3332 copilot.completions_cycling(&buffer, buffer_position, cx)
3333 })
3334 .await;
3335
3336 this.update(&mut cx, |this, cx| {
3337 this.copilot_state.cycled = true;
3338 for completion in completions.log_err().into_iter().flatten() {
3339 this.copilot_state.push_completion(completion);
3340 }
3341 this.copilot_state.cycle_completions(direction);
3342 this.update_visible_copilot_suggestion(cx);
3343 })
3344 .log_err()?;
3345
3346 Some(())
3347 });
3348 }
3349
3350 Some(())
3351 }
3352
3353 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3354 if !self.has_active_copilot_suggestion(cx) {
3355 self.refresh_copilot_suggestions(false, cx);
3356 return;
3357 }
3358
3359 self.update_visible_copilot_suggestion(cx);
3360 }
3361
3362 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3363 if self.has_active_copilot_suggestion(cx) {
3364 self.cycle_copilot_suggestions(Direction::Next, cx);
3365 } else {
3366 self.refresh_copilot_suggestions(false, cx);
3367 }
3368 }
3369
3370 fn previous_copilot_suggestion(
3371 &mut self,
3372 _: &copilot::PreviousSuggestion,
3373 cx: &mut ViewContext<Self>,
3374 ) {
3375 if self.has_active_copilot_suggestion(cx) {
3376 self.cycle_copilot_suggestions(Direction::Prev, cx);
3377 } else {
3378 self.refresh_copilot_suggestions(false, cx);
3379 }
3380 }
3381
3382 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3383 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3384 if let Some((copilot, completion)) =
3385 Copilot::global(cx).zip(self.copilot_state.active_completion())
3386 {
3387 copilot
3388 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3389 .detach_and_log_err(cx);
3390
3391 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3392 }
3393 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3394 cx.notify();
3395 true
3396 } else {
3397 false
3398 }
3399 }
3400
3401 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3402 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3403 if let Some(copilot) = Copilot::global(cx) {
3404 copilot
3405 .update(cx, |copilot, cx| {
3406 copilot.discard_completions(&self.copilot_state.completions, cx)
3407 })
3408 .detach_and_log_err(cx);
3409
3410 self.report_copilot_event(None, false, cx)
3411 }
3412
3413 self.display_map.update(cx, |map, cx| {
3414 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3415 });
3416 cx.notify();
3417 true
3418 } else {
3419 false
3420 }
3421 }
3422
3423 fn is_copilot_enabled_at(
3424 &self,
3425 location: Anchor,
3426 snapshot: &MultiBufferSnapshot,
3427 cx: &mut ViewContext<Self>,
3428 ) -> bool {
3429 let file = snapshot.file_at(location);
3430 let language = snapshot.language_at(location);
3431 let settings = all_language_settings(file, cx);
3432 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3433 }
3434
3435 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3436 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3437 let buffer = self.buffer.read(cx).read(cx);
3438 suggestion.position.is_valid(&buffer)
3439 } else {
3440 false
3441 }
3442 }
3443
3444 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3445 let suggestion = self.copilot_state.suggestion.take()?;
3446 self.display_map.update(cx, |map, cx| {
3447 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3448 });
3449 let buffer = self.buffer.read(cx).read(cx);
3450
3451 if suggestion.position.is_valid(&buffer) {
3452 Some(suggestion)
3453 } else {
3454 None
3455 }
3456 }
3457
3458 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3459 let snapshot = self.buffer.read(cx).snapshot(cx);
3460 let selection = self.selections.newest_anchor();
3461 let cursor = selection.head();
3462
3463 if self.context_menu.is_some()
3464 || !self.completion_tasks.is_empty()
3465 || selection.start != selection.end
3466 {
3467 self.discard_copilot_suggestion(cx);
3468 } else if let Some(text) = self
3469 .copilot_state
3470 .text_for_active_completion(cursor, &snapshot)
3471 {
3472 let text = Rope::from(text);
3473 let mut to_remove = Vec::new();
3474 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3475 to_remove.push(suggestion.id);
3476 }
3477
3478 let suggestion_inlay =
3479 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3480 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3481 self.display_map.update(cx, move |map, cx| {
3482 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3483 });
3484 cx.notify();
3485 } else {
3486 self.discard_copilot_suggestion(cx);
3487 }
3488 }
3489
3490 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3491 self.copilot_state = Default::default();
3492 self.discard_copilot_suggestion(cx);
3493 }
3494
3495 pub fn render_code_actions_indicator(
3496 &self,
3497 style: &EditorStyle,
3498 is_active: bool,
3499 cx: &mut ViewContext<Self>,
3500 ) -> Option<AnyElement<Self>> {
3501 if self.available_code_actions.is_some() {
3502 enum CodeActions {}
3503 Some(
3504 MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
3505 Svg::new("icons/bolt_8.svg").with_color(
3506 style
3507 .code_actions
3508 .indicator
3509 .in_state(is_active)
3510 .style_for(state)
3511 .color,
3512 )
3513 })
3514 .with_cursor_style(CursorStyle::PointingHand)
3515 .with_padding(Padding::uniform(3.))
3516 .on_down(MouseButton::Left, |_, this, cx| {
3517 this.toggle_code_actions(
3518 &ToggleCodeActions {
3519 deployed_from_indicator: true,
3520 },
3521 cx,
3522 );
3523 })
3524 .into_any(),
3525 )
3526 } else {
3527 None
3528 }
3529 }
3530
3531 pub fn render_fold_indicators(
3532 &self,
3533 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3534 style: &EditorStyle,
3535 gutter_hovered: bool,
3536 line_height: f32,
3537 gutter_margin: f32,
3538 cx: &mut ViewContext<Self>,
3539 ) -> Vec<Option<AnyElement<Self>>> {
3540 enum FoldIndicators {}
3541
3542 let style = style.folds.clone();
3543
3544 fold_data
3545 .iter()
3546 .enumerate()
3547 .map(|(ix, fold_data)| {
3548 fold_data
3549 .map(|(fold_status, buffer_row, active)| {
3550 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3551 MouseEventHandler::<FoldIndicators, _>::new(
3552 ix as usize,
3553 cx,
3554 |mouse_state, _| {
3555 Svg::new(match fold_status {
3556 FoldStatus::Folded => style.folded_icon.clone(),
3557 FoldStatus::Foldable => style.foldable_icon.clone(),
3558 })
3559 .with_color(
3560 style
3561 .indicator
3562 .in_state(fold_status == FoldStatus::Folded)
3563 .style_for(mouse_state)
3564 .color,
3565 )
3566 .constrained()
3567 .with_width(gutter_margin * style.icon_margin_scale)
3568 .aligned()
3569 .constrained()
3570 .with_height(line_height)
3571 .with_width(gutter_margin)
3572 .aligned()
3573 },
3574 )
3575 .with_cursor_style(CursorStyle::PointingHand)
3576 .with_padding(Padding::uniform(3.))
3577 .on_click(MouseButton::Left, {
3578 move |_, editor, cx| match fold_status {
3579 FoldStatus::Folded => {
3580 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3581 }
3582 FoldStatus::Foldable => {
3583 editor.fold_at(&FoldAt { buffer_row }, cx);
3584 }
3585 }
3586 })
3587 .into_any()
3588 })
3589 })
3590 .flatten()
3591 })
3592 .collect()
3593 }
3594
3595 pub fn context_menu_visible(&self) -> bool {
3596 self.context_menu
3597 .as_ref()
3598 .map_or(false, |menu| menu.visible())
3599 }
3600
3601 pub fn render_context_menu(
3602 &self,
3603 cursor_position: DisplayPoint,
3604 style: EditorStyle,
3605 cx: &mut ViewContext<Editor>,
3606 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3607 self.context_menu
3608 .as_ref()
3609 .map(|menu| menu.render(cursor_position, style, cx))
3610 }
3611
3612 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3613 if !matches!(menu, ContextMenu::Completions(_)) {
3614 self.completion_tasks.clear();
3615 }
3616 self.context_menu = Some(menu);
3617 self.discard_copilot_suggestion(cx);
3618 cx.notify();
3619 }
3620
3621 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3622 cx.notify();
3623 self.completion_tasks.clear();
3624 let context_menu = self.context_menu.take();
3625 if context_menu.is_some() {
3626 self.update_visible_copilot_suggestion(cx);
3627 }
3628 context_menu
3629 }
3630
3631 pub fn insert_snippet(
3632 &mut self,
3633 insertion_ranges: &[Range<usize>],
3634 snippet: Snippet,
3635 cx: &mut ViewContext<Self>,
3636 ) -> Result<()> {
3637 let tabstops = self.buffer.update(cx, |buffer, cx| {
3638 let snippet_text: Arc<str> = snippet.text.clone().into();
3639 buffer.edit(
3640 insertion_ranges
3641 .iter()
3642 .cloned()
3643 .map(|range| (range, snippet_text.clone())),
3644 Some(AutoindentMode::EachLine),
3645 cx,
3646 );
3647
3648 let snapshot = &*buffer.read(cx);
3649 let snippet = &snippet;
3650 snippet
3651 .tabstops
3652 .iter()
3653 .map(|tabstop| {
3654 let mut tabstop_ranges = tabstop
3655 .iter()
3656 .flat_map(|tabstop_range| {
3657 let mut delta = 0_isize;
3658 insertion_ranges.iter().map(move |insertion_range| {
3659 let insertion_start = insertion_range.start as isize + delta;
3660 delta +=
3661 snippet.text.len() as isize - insertion_range.len() as isize;
3662
3663 let start = snapshot.anchor_before(
3664 (insertion_start + tabstop_range.start) as usize,
3665 );
3666 let end = snapshot
3667 .anchor_after((insertion_start + tabstop_range.end) as usize);
3668 start..end
3669 })
3670 })
3671 .collect::<Vec<_>>();
3672 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3673 tabstop_ranges
3674 })
3675 .collect::<Vec<_>>()
3676 });
3677
3678 if let Some(tabstop) = tabstops.first() {
3679 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3680 s.select_ranges(tabstop.iter().cloned());
3681 });
3682 self.snippet_stack.push(SnippetState {
3683 active_index: 0,
3684 ranges: tabstops,
3685 });
3686 }
3687
3688 Ok(())
3689 }
3690
3691 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3692 self.move_to_snippet_tabstop(Bias::Right, cx)
3693 }
3694
3695 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3696 self.move_to_snippet_tabstop(Bias::Left, cx)
3697 }
3698
3699 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3700 if let Some(mut snippet) = self.snippet_stack.pop() {
3701 match bias {
3702 Bias::Left => {
3703 if snippet.active_index > 0 {
3704 snippet.active_index -= 1;
3705 } else {
3706 self.snippet_stack.push(snippet);
3707 return false;
3708 }
3709 }
3710 Bias::Right => {
3711 if snippet.active_index + 1 < snippet.ranges.len() {
3712 snippet.active_index += 1;
3713 } else {
3714 self.snippet_stack.push(snippet);
3715 return false;
3716 }
3717 }
3718 }
3719 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3720 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3721 s.select_anchor_ranges(current_ranges.iter().cloned())
3722 });
3723 // If snippet state is not at the last tabstop, push it back on the stack
3724 if snippet.active_index + 1 < snippet.ranges.len() {
3725 self.snippet_stack.push(snippet);
3726 }
3727 return true;
3728 }
3729 }
3730
3731 false
3732 }
3733
3734 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3735 self.transact(cx, |this, cx| {
3736 this.select_all(&SelectAll, cx);
3737 this.insert("", cx);
3738 });
3739 }
3740
3741 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3742 self.transact(cx, |this, cx| {
3743 this.select_autoclose_pair(cx);
3744 let mut selections = this.selections.all::<Point>(cx);
3745 if !this.selections.line_mode {
3746 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3747 for selection in &mut selections {
3748 if selection.is_empty() {
3749 let old_head = selection.head();
3750 let mut new_head =
3751 movement::left(&display_map, old_head.to_display_point(&display_map))
3752 .to_point(&display_map);
3753 if let Some((buffer, line_buffer_range)) = display_map
3754 .buffer_snapshot
3755 .buffer_line_for_row(old_head.row)
3756 {
3757 let indent_size =
3758 buffer.indent_size_for_line(line_buffer_range.start.row);
3759 let indent_len = match indent_size.kind {
3760 IndentKind::Space => {
3761 buffer.settings_at(line_buffer_range.start, cx).tab_size
3762 }
3763 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3764 };
3765 if old_head.column <= indent_size.len && old_head.column > 0 {
3766 let indent_len = indent_len.get();
3767 new_head = cmp::min(
3768 new_head,
3769 Point::new(
3770 old_head.row,
3771 ((old_head.column - 1) / indent_len) * indent_len,
3772 ),
3773 );
3774 }
3775 }
3776
3777 selection.set_head(new_head, SelectionGoal::None);
3778 }
3779 }
3780 }
3781
3782 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3783 this.insert("", cx);
3784 this.refresh_copilot_suggestions(true, cx);
3785 });
3786 }
3787
3788 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3789 self.transact(cx, |this, cx| {
3790 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3791 let line_mode = s.line_mode;
3792 s.move_with(|map, selection| {
3793 if selection.is_empty() && !line_mode {
3794 let cursor = movement::right(map, selection.head());
3795 selection.end = cursor;
3796 selection.reversed = true;
3797 selection.goal = SelectionGoal::None;
3798 }
3799 })
3800 });
3801 this.insert("", cx);
3802 this.refresh_copilot_suggestions(true, cx);
3803 });
3804 }
3805
3806 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3807 if self.move_to_prev_snippet_tabstop(cx) {
3808 return;
3809 }
3810
3811 self.outdent(&Outdent, cx);
3812 }
3813
3814 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3815 if self.move_to_next_snippet_tabstop(cx) {
3816 return;
3817 }
3818
3819 let mut selections = self.selections.all_adjusted(cx);
3820 let buffer = self.buffer.read(cx);
3821 let snapshot = buffer.snapshot(cx);
3822 let rows_iter = selections.iter().map(|s| s.head().row);
3823 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3824
3825 let mut edits = Vec::new();
3826 let mut prev_edited_row = 0;
3827 let mut row_delta = 0;
3828 for selection in &mut selections {
3829 if selection.start.row != prev_edited_row {
3830 row_delta = 0;
3831 }
3832 prev_edited_row = selection.end.row;
3833
3834 // If the selection is non-empty, then increase the indentation of the selected lines.
3835 if !selection.is_empty() {
3836 row_delta =
3837 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3838 continue;
3839 }
3840
3841 // If the selection is empty and the cursor is in the leading whitespace before the
3842 // suggested indentation, then auto-indent the line.
3843 let cursor = selection.head();
3844 let current_indent = snapshot.indent_size_for_line(cursor.row);
3845 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3846 if cursor.column < suggested_indent.len
3847 && cursor.column <= current_indent.len
3848 && current_indent.len <= suggested_indent.len
3849 {
3850 selection.start = Point::new(cursor.row, suggested_indent.len);
3851 selection.end = selection.start;
3852 if row_delta == 0 {
3853 edits.extend(Buffer::edit_for_indent_size_adjustment(
3854 cursor.row,
3855 current_indent,
3856 suggested_indent,
3857 ));
3858 row_delta = suggested_indent.len - current_indent.len;
3859 }
3860 continue;
3861 }
3862 }
3863
3864 // Accept copilot suggestion if there is only one selection and the cursor is not
3865 // in the leading whitespace.
3866 if self.selections.count() == 1
3867 && cursor.column >= current_indent.len
3868 && self.has_active_copilot_suggestion(cx)
3869 {
3870 self.accept_copilot_suggestion(cx);
3871 return;
3872 }
3873
3874 // Otherwise, insert a hard or soft tab.
3875 let settings = buffer.settings_at(cursor, cx);
3876 let tab_size = if settings.hard_tabs {
3877 IndentSize::tab()
3878 } else {
3879 let tab_size = settings.tab_size.get();
3880 let char_column = snapshot
3881 .text_for_range(Point::new(cursor.row, 0)..cursor)
3882 .flat_map(str::chars)
3883 .count()
3884 + row_delta as usize;
3885 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3886 IndentSize::spaces(chars_to_next_tab_stop)
3887 };
3888 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3889 selection.end = selection.start;
3890 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3891 row_delta += tab_size.len;
3892 }
3893
3894 self.transact(cx, |this, cx| {
3895 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3896 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3897 this.refresh_copilot_suggestions(true, cx);
3898 });
3899 }
3900
3901 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3902 let mut selections = self.selections.all::<Point>(cx);
3903 let mut prev_edited_row = 0;
3904 let mut row_delta = 0;
3905 let mut edits = Vec::new();
3906 let buffer = self.buffer.read(cx);
3907 let snapshot = buffer.snapshot(cx);
3908 for selection in &mut selections {
3909 if selection.start.row != prev_edited_row {
3910 row_delta = 0;
3911 }
3912 prev_edited_row = selection.end.row;
3913
3914 row_delta =
3915 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3916 }
3917
3918 self.transact(cx, |this, cx| {
3919 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3920 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3921 });
3922 }
3923
3924 fn indent_selection(
3925 buffer: &MultiBuffer,
3926 snapshot: &MultiBufferSnapshot,
3927 selection: &mut Selection<Point>,
3928 edits: &mut Vec<(Range<Point>, String)>,
3929 delta_for_start_row: u32,
3930 cx: &AppContext,
3931 ) -> u32 {
3932 let settings = buffer.settings_at(selection.start, cx);
3933 let tab_size = settings.tab_size.get();
3934 let indent_kind = if settings.hard_tabs {
3935 IndentKind::Tab
3936 } else {
3937 IndentKind::Space
3938 };
3939 let mut start_row = selection.start.row;
3940 let mut end_row = selection.end.row + 1;
3941
3942 // If a selection ends at the beginning of a line, don't indent
3943 // that last line.
3944 if selection.end.column == 0 {
3945 end_row -= 1;
3946 }
3947
3948 // Avoid re-indenting a row that has already been indented by a
3949 // previous selection, but still update this selection's column
3950 // to reflect that indentation.
3951 if delta_for_start_row > 0 {
3952 start_row += 1;
3953 selection.start.column += delta_for_start_row;
3954 if selection.end.row == selection.start.row {
3955 selection.end.column += delta_for_start_row;
3956 }
3957 }
3958
3959 let mut delta_for_end_row = 0;
3960 for row in start_row..end_row {
3961 let current_indent = snapshot.indent_size_for_line(row);
3962 let indent_delta = match (current_indent.kind, indent_kind) {
3963 (IndentKind::Space, IndentKind::Space) => {
3964 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3965 IndentSize::spaces(columns_to_next_tab_stop)
3966 }
3967 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3968 (_, IndentKind::Tab) => IndentSize::tab(),
3969 };
3970
3971 let row_start = Point::new(row, 0);
3972 edits.push((
3973 row_start..row_start,
3974 indent_delta.chars().collect::<String>(),
3975 ));
3976
3977 // Update this selection's endpoints to reflect the indentation.
3978 if row == selection.start.row {
3979 selection.start.column += indent_delta.len;
3980 }
3981 if row == selection.end.row {
3982 selection.end.column += indent_delta.len;
3983 delta_for_end_row = indent_delta.len;
3984 }
3985 }
3986
3987 if selection.start.row == selection.end.row {
3988 delta_for_start_row + delta_for_end_row
3989 } else {
3990 delta_for_end_row
3991 }
3992 }
3993
3994 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3995 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3996 let selections = self.selections.all::<Point>(cx);
3997 let mut deletion_ranges = Vec::new();
3998 let mut last_outdent = None;
3999 {
4000 let buffer = self.buffer.read(cx);
4001 let snapshot = buffer.snapshot(cx);
4002 for selection in &selections {
4003 let settings = buffer.settings_at(selection.start, cx);
4004 let tab_size = settings.tab_size.get();
4005 let mut rows = selection.spanned_rows(false, &display_map);
4006
4007 // Avoid re-outdenting a row that has already been outdented by a
4008 // previous selection.
4009 if let Some(last_row) = last_outdent {
4010 if last_row == rows.start {
4011 rows.start += 1;
4012 }
4013 }
4014
4015 for row in rows {
4016 let indent_size = snapshot.indent_size_for_line(row);
4017 if indent_size.len > 0 {
4018 let deletion_len = match indent_size.kind {
4019 IndentKind::Space => {
4020 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4021 if columns_to_prev_tab_stop == 0 {
4022 tab_size
4023 } else {
4024 columns_to_prev_tab_stop
4025 }
4026 }
4027 IndentKind::Tab => 1,
4028 };
4029 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4030 last_outdent = Some(row);
4031 }
4032 }
4033 }
4034 }
4035
4036 self.transact(cx, |this, cx| {
4037 this.buffer.update(cx, |buffer, cx| {
4038 let empty_str: Arc<str> = "".into();
4039 buffer.edit(
4040 deletion_ranges
4041 .into_iter()
4042 .map(|range| (range, empty_str.clone())),
4043 None,
4044 cx,
4045 );
4046 });
4047 let selections = this.selections.all::<usize>(cx);
4048 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4049 });
4050 }
4051
4052 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4053 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4054 let selections = self.selections.all::<Point>(cx);
4055
4056 let mut new_cursors = Vec::new();
4057 let mut edit_ranges = Vec::new();
4058 let mut selections = selections.iter().peekable();
4059 while let Some(selection) = selections.next() {
4060 let mut rows = selection.spanned_rows(false, &display_map);
4061 let goal_display_column = selection.head().to_display_point(&display_map).column();
4062
4063 // Accumulate contiguous regions of rows that we want to delete.
4064 while let Some(next_selection) = selections.peek() {
4065 let next_rows = next_selection.spanned_rows(false, &display_map);
4066 if next_rows.start <= rows.end {
4067 rows.end = next_rows.end;
4068 selections.next().unwrap();
4069 } else {
4070 break;
4071 }
4072 }
4073
4074 let buffer = &display_map.buffer_snapshot;
4075 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4076 let edit_end;
4077 let cursor_buffer_row;
4078 if buffer.max_point().row >= rows.end {
4079 // If there's a line after the range, delete the \n from the end of the row range
4080 // and position the cursor on the next line.
4081 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4082 cursor_buffer_row = rows.end;
4083 } else {
4084 // If there isn't a line after the range, delete the \n from the line before the
4085 // start of the row range and position the cursor there.
4086 edit_start = edit_start.saturating_sub(1);
4087 edit_end = buffer.len();
4088 cursor_buffer_row = rows.start.saturating_sub(1);
4089 }
4090
4091 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4092 *cursor.column_mut() =
4093 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4094
4095 new_cursors.push((
4096 selection.id,
4097 buffer.anchor_after(cursor.to_point(&display_map)),
4098 ));
4099 edit_ranges.push(edit_start..edit_end);
4100 }
4101
4102 self.transact(cx, |this, cx| {
4103 let buffer = this.buffer.update(cx, |buffer, cx| {
4104 let empty_str: Arc<str> = "".into();
4105 buffer.edit(
4106 edit_ranges
4107 .into_iter()
4108 .map(|range| (range, empty_str.clone())),
4109 None,
4110 cx,
4111 );
4112 buffer.snapshot(cx)
4113 });
4114 let new_selections = new_cursors
4115 .into_iter()
4116 .map(|(id, cursor)| {
4117 let cursor = cursor.to_point(&buffer);
4118 Selection {
4119 id,
4120 start: cursor,
4121 end: cursor,
4122 reversed: false,
4123 goal: SelectionGoal::None,
4124 }
4125 })
4126 .collect();
4127
4128 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4129 s.select(new_selections);
4130 });
4131 });
4132 }
4133
4134 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4135 let mut row_ranges = Vec::<Range<u32>>::new();
4136 for selection in self.selections.all::<Point>(cx) {
4137 let start = selection.start.row;
4138 let end = if selection.start.row == selection.end.row {
4139 selection.start.row + 1
4140 } else {
4141 selection.end.row
4142 };
4143
4144 if let Some(last_row_range) = row_ranges.last_mut() {
4145 if start <= last_row_range.end {
4146 last_row_range.end = end;
4147 continue;
4148 }
4149 }
4150 row_ranges.push(start..end);
4151 }
4152
4153 let snapshot = self.buffer.read(cx).snapshot(cx);
4154 let mut cursor_positions = Vec::new();
4155 for row_range in &row_ranges {
4156 let anchor = snapshot.anchor_before(Point::new(
4157 row_range.end - 1,
4158 snapshot.line_len(row_range.end - 1),
4159 ));
4160 cursor_positions.push(anchor.clone()..anchor);
4161 }
4162
4163 self.transact(cx, |this, cx| {
4164 for row_range in row_ranges.into_iter().rev() {
4165 for row in row_range.rev() {
4166 let end_of_line = Point::new(row, snapshot.line_len(row));
4167 let indent = snapshot.indent_size_for_line(row + 1);
4168 let start_of_next_line = Point::new(row + 1, indent.len);
4169
4170 let replace = if snapshot.line_len(row + 1) > indent.len {
4171 " "
4172 } else {
4173 ""
4174 };
4175
4176 this.buffer.update(cx, |buffer, cx| {
4177 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4178 });
4179 }
4180 }
4181
4182 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4183 s.select_anchor_ranges(cursor_positions)
4184 });
4185 });
4186 }
4187
4188 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4189 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4190 let buffer = &display_map.buffer_snapshot;
4191 let selections = self.selections.all::<Point>(cx);
4192
4193 let mut edits = Vec::new();
4194 let mut selections_iter = selections.iter().peekable();
4195 while let Some(selection) = selections_iter.next() {
4196 // Avoid duplicating the same lines twice.
4197 let mut rows = selection.spanned_rows(false, &display_map);
4198
4199 while let Some(next_selection) = selections_iter.peek() {
4200 let next_rows = next_selection.spanned_rows(false, &display_map);
4201 if next_rows.start < rows.end {
4202 rows.end = next_rows.end;
4203 selections_iter.next().unwrap();
4204 } else {
4205 break;
4206 }
4207 }
4208
4209 // Copy the text from the selected row region and splice it at the start of the region.
4210 let start = Point::new(rows.start, 0);
4211 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4212 let text = buffer
4213 .text_for_range(start..end)
4214 .chain(Some("\n"))
4215 .collect::<String>();
4216 edits.push((start..start, text));
4217 }
4218
4219 self.transact(cx, |this, cx| {
4220 this.buffer.update(cx, |buffer, cx| {
4221 buffer.edit(edits, None, cx);
4222 });
4223
4224 this.request_autoscroll(Autoscroll::fit(), cx);
4225 });
4226 }
4227
4228 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4229 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4230 let buffer = self.buffer.read(cx).snapshot(cx);
4231
4232 let mut edits = Vec::new();
4233 let mut unfold_ranges = Vec::new();
4234 let mut refold_ranges = Vec::new();
4235
4236 let selections = self.selections.all::<Point>(cx);
4237 let mut selections = selections.iter().peekable();
4238 let mut contiguous_row_selections = Vec::new();
4239 let mut new_selections = Vec::new();
4240
4241 while let Some(selection) = selections.next() {
4242 // Find all the selections that span a contiguous row range
4243 let (start_row, end_row) = consume_contiguous_rows(
4244 &mut contiguous_row_selections,
4245 selection,
4246 &display_map,
4247 &mut selections,
4248 );
4249
4250 // Move the text spanned by the row range to be before the line preceding the row range
4251 if start_row > 0 {
4252 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4253 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4254 let insertion_point = display_map
4255 .prev_line_boundary(Point::new(start_row - 1, 0))
4256 .0;
4257
4258 // Don't move lines across excerpts
4259 if buffer
4260 .excerpt_boundaries_in_range((
4261 Bound::Excluded(insertion_point),
4262 Bound::Included(range_to_move.end),
4263 ))
4264 .next()
4265 .is_none()
4266 {
4267 let text = buffer
4268 .text_for_range(range_to_move.clone())
4269 .flat_map(|s| s.chars())
4270 .skip(1)
4271 .chain(['\n'])
4272 .collect::<String>();
4273
4274 edits.push((
4275 buffer.anchor_after(range_to_move.start)
4276 ..buffer.anchor_before(range_to_move.end),
4277 String::new(),
4278 ));
4279 let insertion_anchor = buffer.anchor_after(insertion_point);
4280 edits.push((insertion_anchor..insertion_anchor, text));
4281
4282 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4283
4284 // Move selections up
4285 new_selections.extend(contiguous_row_selections.drain(..).map(
4286 |mut selection| {
4287 selection.start.row -= row_delta;
4288 selection.end.row -= row_delta;
4289 selection
4290 },
4291 ));
4292
4293 // Move folds up
4294 unfold_ranges.push(range_to_move.clone());
4295 for fold in display_map.folds_in_range(
4296 buffer.anchor_before(range_to_move.start)
4297 ..buffer.anchor_after(range_to_move.end),
4298 ) {
4299 let mut start = fold.start.to_point(&buffer);
4300 let mut end = fold.end.to_point(&buffer);
4301 start.row -= row_delta;
4302 end.row -= row_delta;
4303 refold_ranges.push(start..end);
4304 }
4305 }
4306 }
4307
4308 // If we didn't move line(s), preserve the existing selections
4309 new_selections.append(&mut contiguous_row_selections);
4310 }
4311
4312 self.transact(cx, |this, cx| {
4313 this.unfold_ranges(unfold_ranges, true, true, cx);
4314 this.buffer.update(cx, |buffer, cx| {
4315 for (range, text) in edits {
4316 buffer.edit([(range, text)], None, cx);
4317 }
4318 });
4319 this.fold_ranges(refold_ranges, true, cx);
4320 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4321 s.select(new_selections);
4322 })
4323 });
4324 }
4325
4326 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4327 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4328 let buffer = self.buffer.read(cx).snapshot(cx);
4329
4330 let mut edits = Vec::new();
4331 let mut unfold_ranges = Vec::new();
4332 let mut refold_ranges = Vec::new();
4333
4334 let selections = self.selections.all::<Point>(cx);
4335 let mut selections = selections.iter().peekable();
4336 let mut contiguous_row_selections = Vec::new();
4337 let mut new_selections = Vec::new();
4338
4339 while let Some(selection) = selections.next() {
4340 // Find all the selections that span a contiguous row range
4341 let (start_row, end_row) = consume_contiguous_rows(
4342 &mut contiguous_row_selections,
4343 selection,
4344 &display_map,
4345 &mut selections,
4346 );
4347
4348 // Move the text spanned by the row range to be after the last line of the row range
4349 if end_row <= buffer.max_point().row {
4350 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4351 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4352
4353 // Don't move lines across excerpt boundaries
4354 if buffer
4355 .excerpt_boundaries_in_range((
4356 Bound::Excluded(range_to_move.start),
4357 Bound::Included(insertion_point),
4358 ))
4359 .next()
4360 .is_none()
4361 {
4362 let mut text = String::from("\n");
4363 text.extend(buffer.text_for_range(range_to_move.clone()));
4364 text.pop(); // Drop trailing newline
4365 edits.push((
4366 buffer.anchor_after(range_to_move.start)
4367 ..buffer.anchor_before(range_to_move.end),
4368 String::new(),
4369 ));
4370 let insertion_anchor = buffer.anchor_after(insertion_point);
4371 edits.push((insertion_anchor..insertion_anchor, text));
4372
4373 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4374
4375 // Move selections down
4376 new_selections.extend(contiguous_row_selections.drain(..).map(
4377 |mut selection| {
4378 selection.start.row += row_delta;
4379 selection.end.row += row_delta;
4380 selection
4381 },
4382 ));
4383
4384 // Move folds down
4385 unfold_ranges.push(range_to_move.clone());
4386 for fold in display_map.folds_in_range(
4387 buffer.anchor_before(range_to_move.start)
4388 ..buffer.anchor_after(range_to_move.end),
4389 ) {
4390 let mut start = fold.start.to_point(&buffer);
4391 let mut end = fold.end.to_point(&buffer);
4392 start.row += row_delta;
4393 end.row += row_delta;
4394 refold_ranges.push(start..end);
4395 }
4396 }
4397 }
4398
4399 // If we didn't move line(s), preserve the existing selections
4400 new_selections.append(&mut contiguous_row_selections);
4401 }
4402
4403 self.transact(cx, |this, cx| {
4404 this.unfold_ranges(unfold_ranges, true, true, cx);
4405 this.buffer.update(cx, |buffer, cx| {
4406 for (range, text) in edits {
4407 buffer.edit([(range, text)], None, cx);
4408 }
4409 });
4410 this.fold_ranges(refold_ranges, true, cx);
4411 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4412 });
4413 }
4414
4415 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4416 self.transact(cx, |this, cx| {
4417 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4418 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4419 let line_mode = s.line_mode;
4420 s.move_with(|display_map, selection| {
4421 if !selection.is_empty() || line_mode {
4422 return;
4423 }
4424
4425 let mut head = selection.head();
4426 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4427 if head.column() == display_map.line_len(head.row()) {
4428 transpose_offset = display_map
4429 .buffer_snapshot
4430 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4431 }
4432
4433 if transpose_offset == 0 {
4434 return;
4435 }
4436
4437 *head.column_mut() += 1;
4438 head = display_map.clip_point(head, Bias::Right);
4439 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4440
4441 let transpose_start = display_map
4442 .buffer_snapshot
4443 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4444 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4445 let transpose_end = display_map
4446 .buffer_snapshot
4447 .clip_offset(transpose_offset + 1, Bias::Right);
4448 if let Some(ch) =
4449 display_map.buffer_snapshot.chars_at(transpose_start).next()
4450 {
4451 edits.push((transpose_start..transpose_offset, String::new()));
4452 edits.push((transpose_end..transpose_end, ch.to_string()));
4453 }
4454 }
4455 });
4456 edits
4457 });
4458 this.buffer
4459 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4460 let selections = this.selections.all::<usize>(cx);
4461 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4462 s.select(selections);
4463 });
4464 });
4465 }
4466
4467 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4468 let mut text = String::new();
4469 let buffer = self.buffer.read(cx).snapshot(cx);
4470 let mut selections = self.selections.all::<Point>(cx);
4471 let mut clipboard_selections = Vec::with_capacity(selections.len());
4472 {
4473 let max_point = buffer.max_point();
4474 for selection in &mut selections {
4475 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4476 if is_entire_line {
4477 selection.start = Point::new(selection.start.row, 0);
4478 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4479 selection.goal = SelectionGoal::None;
4480 }
4481 let mut len = 0;
4482 for chunk in buffer.text_for_range(selection.start..selection.end) {
4483 text.push_str(chunk);
4484 len += chunk.len();
4485 }
4486 clipboard_selections.push(ClipboardSelection {
4487 len,
4488 is_entire_line,
4489 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4490 });
4491 }
4492 }
4493
4494 self.transact(cx, |this, cx| {
4495 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4496 s.select(selections);
4497 });
4498 this.insert("", cx);
4499 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4500 });
4501 }
4502
4503 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4504 let selections = self.selections.all::<Point>(cx);
4505 let buffer = self.buffer.read(cx).read(cx);
4506 let mut text = String::new();
4507
4508 let mut clipboard_selections = Vec::with_capacity(selections.len());
4509 {
4510 let max_point = buffer.max_point();
4511 for selection in selections.iter() {
4512 let mut start = selection.start;
4513 let mut end = selection.end;
4514 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4515 if is_entire_line {
4516 start = Point::new(start.row, 0);
4517 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4518 }
4519 let mut len = 0;
4520 for chunk in buffer.text_for_range(start..end) {
4521 text.push_str(chunk);
4522 len += chunk.len();
4523 }
4524 clipboard_selections.push(ClipboardSelection {
4525 len,
4526 is_entire_line,
4527 first_line_indent: buffer.indent_size_for_line(start.row).len,
4528 });
4529 }
4530 }
4531
4532 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4533 }
4534
4535 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4536 self.transact(cx, |this, cx| {
4537 if let Some(item) = cx.read_from_clipboard() {
4538 let mut clipboard_text = Cow::Borrowed(item.text());
4539 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4540 let old_selections = this.selections.all::<usize>(cx);
4541 let all_selections_were_entire_line =
4542 clipboard_selections.iter().all(|s| s.is_entire_line);
4543 let first_selection_indent_column =
4544 clipboard_selections.first().map(|s| s.first_line_indent);
4545 if clipboard_selections.len() != old_selections.len() {
4546 let mut newline_separated_text = String::new();
4547 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4548 let mut ix = 0;
4549 while let Some(clipboard_selection) = clipboard_selections.next() {
4550 newline_separated_text
4551 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4552 ix += clipboard_selection.len;
4553 if clipboard_selections.peek().is_some() {
4554 newline_separated_text.push('\n');
4555 }
4556 }
4557 clipboard_text = Cow::Owned(newline_separated_text);
4558 }
4559
4560 this.buffer.update(cx, |buffer, cx| {
4561 let snapshot = buffer.read(cx);
4562 let mut start_offset = 0;
4563 let mut edits = Vec::new();
4564 let mut original_indent_columns = Vec::new();
4565 let line_mode = this.selections.line_mode;
4566 for (ix, selection) in old_selections.iter().enumerate() {
4567 let to_insert;
4568 let entire_line;
4569 let original_indent_column;
4570 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4571 let end_offset = start_offset + clipboard_selection.len;
4572 to_insert = &clipboard_text[start_offset..end_offset];
4573 entire_line = clipboard_selection.is_entire_line;
4574 start_offset = end_offset;
4575 original_indent_column =
4576 Some(clipboard_selection.first_line_indent);
4577 } else {
4578 to_insert = clipboard_text.as_str();
4579 entire_line = all_selections_were_entire_line;
4580 original_indent_column = first_selection_indent_column
4581 }
4582
4583 // If the corresponding selection was empty when this slice of the
4584 // clipboard text was written, then the entire line containing the
4585 // selection was copied. If this selection is also currently empty,
4586 // then paste the line before the current line of the buffer.
4587 let range = if selection.is_empty() && !line_mode && entire_line {
4588 let column = selection.start.to_point(&snapshot).column as usize;
4589 let line_start = selection.start - column;
4590 line_start..line_start
4591 } else {
4592 selection.range()
4593 };
4594
4595 edits.push((range, to_insert));
4596 original_indent_columns.extend(original_indent_column);
4597 }
4598 drop(snapshot);
4599
4600 buffer.edit(
4601 edits,
4602 Some(AutoindentMode::Block {
4603 original_indent_columns,
4604 }),
4605 cx,
4606 );
4607 });
4608
4609 let selections = this.selections.all::<usize>(cx);
4610 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4611 } else {
4612 this.insert(&clipboard_text, cx);
4613 }
4614 }
4615 });
4616 }
4617
4618 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4619 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4620 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4621 self.change_selections(None, cx, |s| {
4622 s.select_anchors(selections.to_vec());
4623 });
4624 }
4625 self.request_autoscroll(Autoscroll::fit(), cx);
4626 self.unmark_text(cx);
4627 self.refresh_copilot_suggestions(true, cx);
4628 cx.emit(Event::Edited);
4629 }
4630 }
4631
4632 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4633 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4634 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4635 {
4636 self.change_selections(None, cx, |s| {
4637 s.select_anchors(selections.to_vec());
4638 });
4639 }
4640 self.request_autoscroll(Autoscroll::fit(), cx);
4641 self.unmark_text(cx);
4642 self.refresh_copilot_suggestions(true, cx);
4643 cx.emit(Event::Edited);
4644 }
4645 }
4646
4647 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4648 self.buffer
4649 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4650 }
4651
4652 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4653 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4654 let line_mode = s.line_mode;
4655 s.move_with(|map, selection| {
4656 let cursor = if selection.is_empty() && !line_mode {
4657 movement::left(map, selection.start)
4658 } else {
4659 selection.start
4660 };
4661 selection.collapse_to(cursor, SelectionGoal::None);
4662 });
4663 })
4664 }
4665
4666 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4667 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4668 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4669 })
4670 }
4671
4672 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4673 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4674 let line_mode = s.line_mode;
4675 s.move_with(|map, selection| {
4676 let cursor = if selection.is_empty() && !line_mode {
4677 movement::right(map, selection.end)
4678 } else {
4679 selection.end
4680 };
4681 selection.collapse_to(cursor, SelectionGoal::None)
4682 });
4683 })
4684 }
4685
4686 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4687 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4688 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4689 })
4690 }
4691
4692 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4693 if self.take_rename(true, cx).is_some() {
4694 return;
4695 }
4696
4697 if let Some(context_menu) = self.context_menu.as_mut() {
4698 if context_menu.select_prev(cx) {
4699 return;
4700 }
4701 }
4702
4703 if matches!(self.mode, EditorMode::SingleLine) {
4704 cx.propagate_action();
4705 return;
4706 }
4707
4708 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4709 let line_mode = s.line_mode;
4710 s.move_with(|map, selection| {
4711 if !selection.is_empty() && !line_mode {
4712 selection.goal = SelectionGoal::None;
4713 }
4714 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4715 selection.collapse_to(cursor, goal);
4716 });
4717 })
4718 }
4719
4720 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4721 if self.take_rename(true, cx).is_some() {
4722 return;
4723 }
4724
4725 if self
4726 .context_menu
4727 .as_mut()
4728 .map(|menu| menu.select_first(cx))
4729 .unwrap_or(false)
4730 {
4731 return;
4732 }
4733
4734 if matches!(self.mode, EditorMode::SingleLine) {
4735 cx.propagate_action();
4736 return;
4737 }
4738
4739 let row_count = if let Some(row_count) = self.visible_line_count() {
4740 row_count as u32 - 1
4741 } else {
4742 return;
4743 };
4744
4745 let autoscroll = if action.center_cursor {
4746 Autoscroll::center()
4747 } else {
4748 Autoscroll::fit()
4749 };
4750
4751 self.change_selections(Some(autoscroll), cx, |s| {
4752 let line_mode = s.line_mode;
4753 s.move_with(|map, selection| {
4754 if !selection.is_empty() && !line_mode {
4755 selection.goal = SelectionGoal::None;
4756 }
4757 let (cursor, goal) =
4758 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4759 selection.collapse_to(cursor, goal);
4760 });
4761 });
4762 }
4763
4764 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4765 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4766 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4767 })
4768 }
4769
4770 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4771 self.take_rename(true, cx);
4772
4773 if let Some(context_menu) = self.context_menu.as_mut() {
4774 if context_menu.select_next(cx) {
4775 return;
4776 }
4777 }
4778
4779 if self.mode == EditorMode::SingleLine {
4780 cx.propagate_action();
4781 return;
4782 }
4783
4784 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4785 let line_mode = s.line_mode;
4786 s.move_with(|map, selection| {
4787 if !selection.is_empty() && !line_mode {
4788 selection.goal = SelectionGoal::None;
4789 }
4790 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4791 selection.collapse_to(cursor, goal);
4792 });
4793 });
4794 }
4795
4796 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4797 if self.take_rename(true, cx).is_some() {
4798 return;
4799 }
4800
4801 if self
4802 .context_menu
4803 .as_mut()
4804 .map(|menu| menu.select_last(cx))
4805 .unwrap_or(false)
4806 {
4807 return;
4808 }
4809
4810 if matches!(self.mode, EditorMode::SingleLine) {
4811 cx.propagate_action();
4812 return;
4813 }
4814
4815 let row_count = if let Some(row_count) = self.visible_line_count() {
4816 row_count as u32 - 1
4817 } else {
4818 return;
4819 };
4820
4821 let autoscroll = if action.center_cursor {
4822 Autoscroll::center()
4823 } else {
4824 Autoscroll::fit()
4825 };
4826
4827 self.change_selections(Some(autoscroll), cx, |s| {
4828 let line_mode = s.line_mode;
4829 s.move_with(|map, selection| {
4830 if !selection.is_empty() && !line_mode {
4831 selection.goal = SelectionGoal::None;
4832 }
4833 let (cursor, goal) =
4834 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4835 selection.collapse_to(cursor, goal);
4836 });
4837 });
4838 }
4839
4840 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4841 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4842 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4843 });
4844 }
4845
4846 pub fn move_to_previous_word_start(
4847 &mut self,
4848 _: &MoveToPreviousWordStart,
4849 cx: &mut ViewContext<Self>,
4850 ) {
4851 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4852 s.move_cursors_with(|map, head, _| {
4853 (
4854 movement::previous_word_start(map, head),
4855 SelectionGoal::None,
4856 )
4857 });
4858 })
4859 }
4860
4861 pub fn move_to_previous_subword_start(
4862 &mut self,
4863 _: &MoveToPreviousSubwordStart,
4864 cx: &mut ViewContext<Self>,
4865 ) {
4866 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4867 s.move_cursors_with(|map, head, _| {
4868 (
4869 movement::previous_subword_start(map, head),
4870 SelectionGoal::None,
4871 )
4872 });
4873 })
4874 }
4875
4876 pub fn select_to_previous_word_start(
4877 &mut self,
4878 _: &SelectToPreviousWordStart,
4879 cx: &mut ViewContext<Self>,
4880 ) {
4881 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4882 s.move_heads_with(|map, head, _| {
4883 (
4884 movement::previous_word_start(map, head),
4885 SelectionGoal::None,
4886 )
4887 });
4888 })
4889 }
4890
4891 pub fn select_to_previous_subword_start(
4892 &mut self,
4893 _: &SelectToPreviousSubwordStart,
4894 cx: &mut ViewContext<Self>,
4895 ) {
4896 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4897 s.move_heads_with(|map, head, _| {
4898 (
4899 movement::previous_subword_start(map, head),
4900 SelectionGoal::None,
4901 )
4902 });
4903 })
4904 }
4905
4906 pub fn delete_to_previous_word_start(
4907 &mut self,
4908 _: &DeleteToPreviousWordStart,
4909 cx: &mut ViewContext<Self>,
4910 ) {
4911 self.transact(cx, |this, cx| {
4912 this.select_autoclose_pair(cx);
4913 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4914 let line_mode = s.line_mode;
4915 s.move_with(|map, selection| {
4916 if selection.is_empty() && !line_mode {
4917 let cursor = movement::previous_word_start(map, selection.head());
4918 selection.set_head(cursor, SelectionGoal::None);
4919 }
4920 });
4921 });
4922 this.insert("", cx);
4923 });
4924 }
4925
4926 pub fn delete_to_previous_subword_start(
4927 &mut self,
4928 _: &DeleteToPreviousSubwordStart,
4929 cx: &mut ViewContext<Self>,
4930 ) {
4931 self.transact(cx, |this, cx| {
4932 this.select_autoclose_pair(cx);
4933 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4934 let line_mode = s.line_mode;
4935 s.move_with(|map, selection| {
4936 if selection.is_empty() && !line_mode {
4937 let cursor = movement::previous_subword_start(map, selection.head());
4938 selection.set_head(cursor, SelectionGoal::None);
4939 }
4940 });
4941 });
4942 this.insert("", cx);
4943 });
4944 }
4945
4946 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4947 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4948 s.move_cursors_with(|map, head, _| {
4949 (movement::next_word_end(map, head), SelectionGoal::None)
4950 });
4951 })
4952 }
4953
4954 pub fn move_to_next_subword_end(
4955 &mut self,
4956 _: &MoveToNextSubwordEnd,
4957 cx: &mut ViewContext<Self>,
4958 ) {
4959 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4960 s.move_cursors_with(|map, head, _| {
4961 (movement::next_subword_end(map, head), SelectionGoal::None)
4962 });
4963 })
4964 }
4965
4966 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4967 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4968 s.move_heads_with(|map, head, _| {
4969 (movement::next_word_end(map, head), SelectionGoal::None)
4970 });
4971 })
4972 }
4973
4974 pub fn select_to_next_subword_end(
4975 &mut self,
4976 _: &SelectToNextSubwordEnd,
4977 cx: &mut ViewContext<Self>,
4978 ) {
4979 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4980 s.move_heads_with(|map, head, _| {
4981 (movement::next_subword_end(map, head), SelectionGoal::None)
4982 });
4983 })
4984 }
4985
4986 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4987 self.transact(cx, |this, cx| {
4988 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4989 let line_mode = s.line_mode;
4990 s.move_with(|map, selection| {
4991 if selection.is_empty() && !line_mode {
4992 let cursor = movement::next_word_end(map, selection.head());
4993 selection.set_head(cursor, SelectionGoal::None);
4994 }
4995 });
4996 });
4997 this.insert("", cx);
4998 });
4999 }
5000
5001 pub fn delete_to_next_subword_end(
5002 &mut self,
5003 _: &DeleteToNextSubwordEnd,
5004 cx: &mut ViewContext<Self>,
5005 ) {
5006 self.transact(cx, |this, cx| {
5007 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5008 s.move_with(|map, selection| {
5009 if selection.is_empty() {
5010 let cursor = movement::next_subword_end(map, selection.head());
5011 selection.set_head(cursor, SelectionGoal::None);
5012 }
5013 });
5014 });
5015 this.insert("", cx);
5016 });
5017 }
5018
5019 pub fn move_to_beginning_of_line(
5020 &mut self,
5021 _: &MoveToBeginningOfLine,
5022 cx: &mut ViewContext<Self>,
5023 ) {
5024 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5025 s.move_cursors_with(|map, head, _| {
5026 (
5027 movement::indented_line_beginning(map, head, true),
5028 SelectionGoal::None,
5029 )
5030 });
5031 })
5032 }
5033
5034 pub fn select_to_beginning_of_line(
5035 &mut self,
5036 action: &SelectToBeginningOfLine,
5037 cx: &mut ViewContext<Self>,
5038 ) {
5039 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5040 s.move_heads_with(|map, head, _| {
5041 (
5042 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5043 SelectionGoal::None,
5044 )
5045 });
5046 });
5047 }
5048
5049 pub fn delete_to_beginning_of_line(
5050 &mut self,
5051 _: &DeleteToBeginningOfLine,
5052 cx: &mut ViewContext<Self>,
5053 ) {
5054 self.transact(cx, |this, cx| {
5055 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5056 s.move_with(|_, selection| {
5057 selection.reversed = true;
5058 });
5059 });
5060
5061 this.select_to_beginning_of_line(
5062 &SelectToBeginningOfLine {
5063 stop_at_soft_wraps: false,
5064 },
5065 cx,
5066 );
5067 this.backspace(&Backspace, cx);
5068 });
5069 }
5070
5071 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5072 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5073 s.move_cursors_with(|map, head, _| {
5074 (movement::line_end(map, head, true), SelectionGoal::None)
5075 });
5076 })
5077 }
5078
5079 pub fn select_to_end_of_line(
5080 &mut self,
5081 action: &SelectToEndOfLine,
5082 cx: &mut ViewContext<Self>,
5083 ) {
5084 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5085 s.move_heads_with(|map, head, _| {
5086 (
5087 movement::line_end(map, head, action.stop_at_soft_wraps),
5088 SelectionGoal::None,
5089 )
5090 });
5091 })
5092 }
5093
5094 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5095 self.transact(cx, |this, cx| {
5096 this.select_to_end_of_line(
5097 &SelectToEndOfLine {
5098 stop_at_soft_wraps: false,
5099 },
5100 cx,
5101 );
5102 this.delete(&Delete, cx);
5103 });
5104 }
5105
5106 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5107 self.transact(cx, |this, cx| {
5108 this.select_to_end_of_line(
5109 &SelectToEndOfLine {
5110 stop_at_soft_wraps: false,
5111 },
5112 cx,
5113 );
5114 this.cut(&Cut, cx);
5115 });
5116 }
5117
5118 pub fn move_to_start_of_paragraph(
5119 &mut self,
5120 _: &MoveToStartOfParagraph,
5121 cx: &mut ViewContext<Self>,
5122 ) {
5123 if matches!(self.mode, EditorMode::SingleLine) {
5124 cx.propagate_action();
5125 return;
5126 }
5127
5128 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5129 s.move_with(|map, selection| {
5130 selection.collapse_to(
5131 movement::start_of_paragraph(map, selection.head(), 1),
5132 SelectionGoal::None,
5133 )
5134 });
5135 })
5136 }
5137
5138 pub fn move_to_end_of_paragraph(
5139 &mut self,
5140 _: &MoveToEndOfParagraph,
5141 cx: &mut ViewContext<Self>,
5142 ) {
5143 if matches!(self.mode, EditorMode::SingleLine) {
5144 cx.propagate_action();
5145 return;
5146 }
5147
5148 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5149 s.move_with(|map, selection| {
5150 selection.collapse_to(
5151 movement::end_of_paragraph(map, selection.head(), 1),
5152 SelectionGoal::None,
5153 )
5154 });
5155 })
5156 }
5157
5158 pub fn select_to_start_of_paragraph(
5159 &mut self,
5160 _: &SelectToStartOfParagraph,
5161 cx: &mut ViewContext<Self>,
5162 ) {
5163 if matches!(self.mode, EditorMode::SingleLine) {
5164 cx.propagate_action();
5165 return;
5166 }
5167
5168 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5169 s.move_heads_with(|map, head, _| {
5170 (
5171 movement::start_of_paragraph(map, head, 1),
5172 SelectionGoal::None,
5173 )
5174 });
5175 })
5176 }
5177
5178 pub fn select_to_end_of_paragraph(
5179 &mut self,
5180 _: &SelectToEndOfParagraph,
5181 cx: &mut ViewContext<Self>,
5182 ) {
5183 if matches!(self.mode, EditorMode::SingleLine) {
5184 cx.propagate_action();
5185 return;
5186 }
5187
5188 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5189 s.move_heads_with(|map, head, _| {
5190 (
5191 movement::end_of_paragraph(map, head, 1),
5192 SelectionGoal::None,
5193 )
5194 });
5195 })
5196 }
5197
5198 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5199 if matches!(self.mode, EditorMode::SingleLine) {
5200 cx.propagate_action();
5201 return;
5202 }
5203
5204 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5205 s.select_ranges(vec![0..0]);
5206 });
5207 }
5208
5209 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5210 let mut selection = self.selections.last::<Point>(cx);
5211 selection.set_head(Point::zero(), SelectionGoal::None);
5212
5213 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5214 s.select(vec![selection]);
5215 });
5216 }
5217
5218 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5219 if matches!(self.mode, EditorMode::SingleLine) {
5220 cx.propagate_action();
5221 return;
5222 }
5223
5224 let cursor = self.buffer.read(cx).read(cx).len();
5225 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5226 s.select_ranges(vec![cursor..cursor])
5227 });
5228 }
5229
5230 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5231 self.nav_history = nav_history;
5232 }
5233
5234 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5235 self.nav_history.as_ref()
5236 }
5237
5238 fn push_to_nav_history(
5239 &mut self,
5240 cursor_anchor: Anchor,
5241 new_position: Option<Point>,
5242 cx: &mut ViewContext<Self>,
5243 ) {
5244 if let Some(nav_history) = self.nav_history.as_mut() {
5245 let buffer = self.buffer.read(cx).read(cx);
5246 let cursor_position = cursor_anchor.to_point(&buffer);
5247 let scroll_state = self.scroll_manager.anchor();
5248 let scroll_top_row = scroll_state.top_row(&buffer);
5249 drop(buffer);
5250
5251 if let Some(new_position) = new_position {
5252 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5253 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5254 return;
5255 }
5256 }
5257
5258 nav_history.push(
5259 Some(NavigationData {
5260 cursor_anchor,
5261 cursor_position,
5262 scroll_anchor: scroll_state,
5263 scroll_top_row,
5264 }),
5265 cx,
5266 );
5267 }
5268 }
5269
5270 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5271 let buffer = self.buffer.read(cx).snapshot(cx);
5272 let mut selection = self.selections.first::<usize>(cx);
5273 selection.set_head(buffer.len(), SelectionGoal::None);
5274 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5275 s.select(vec![selection]);
5276 });
5277 }
5278
5279 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5280 let end = self.buffer.read(cx).read(cx).len();
5281 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5282 s.select_ranges(vec![0..end]);
5283 });
5284 }
5285
5286 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5287 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5288 let mut selections = self.selections.all::<Point>(cx);
5289 let max_point = display_map.buffer_snapshot.max_point();
5290 for selection in &mut selections {
5291 let rows = selection.spanned_rows(true, &display_map);
5292 selection.start = Point::new(rows.start, 0);
5293 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5294 selection.reversed = false;
5295 }
5296 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5297 s.select(selections);
5298 });
5299 }
5300
5301 pub fn split_selection_into_lines(
5302 &mut self,
5303 _: &SplitSelectionIntoLines,
5304 cx: &mut ViewContext<Self>,
5305 ) {
5306 let mut to_unfold = Vec::new();
5307 let mut new_selection_ranges = Vec::new();
5308 {
5309 let selections = self.selections.all::<Point>(cx);
5310 let buffer = self.buffer.read(cx).read(cx);
5311 for selection in selections {
5312 for row in selection.start.row..selection.end.row {
5313 let cursor = Point::new(row, buffer.line_len(row));
5314 new_selection_ranges.push(cursor..cursor);
5315 }
5316 new_selection_ranges.push(selection.end..selection.end);
5317 to_unfold.push(selection.start..selection.end);
5318 }
5319 }
5320 self.unfold_ranges(to_unfold, true, true, cx);
5321 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5322 s.select_ranges(new_selection_ranges);
5323 });
5324 }
5325
5326 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5327 self.add_selection(true, cx);
5328 }
5329
5330 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5331 self.add_selection(false, cx);
5332 }
5333
5334 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5335 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5336 let mut selections = self.selections.all::<Point>(cx);
5337 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5338 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5339 let range = oldest_selection.display_range(&display_map).sorted();
5340 let columns = cmp::min(range.start.column(), range.end.column())
5341 ..cmp::max(range.start.column(), range.end.column());
5342
5343 selections.clear();
5344 let mut stack = Vec::new();
5345 for row in range.start.row()..=range.end.row() {
5346 if let Some(selection) = self.selections.build_columnar_selection(
5347 &display_map,
5348 row,
5349 &columns,
5350 oldest_selection.reversed,
5351 ) {
5352 stack.push(selection.id);
5353 selections.push(selection);
5354 }
5355 }
5356
5357 if above {
5358 stack.reverse();
5359 }
5360
5361 AddSelectionsState { above, stack }
5362 });
5363
5364 let last_added_selection = *state.stack.last().unwrap();
5365 let mut new_selections = Vec::new();
5366 if above == state.above {
5367 let end_row = if above {
5368 0
5369 } else {
5370 display_map.max_point().row()
5371 };
5372
5373 'outer: for selection in selections {
5374 if selection.id == last_added_selection {
5375 let range = selection.display_range(&display_map).sorted();
5376 debug_assert_eq!(range.start.row(), range.end.row());
5377 let mut row = range.start.row();
5378 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5379 {
5380 start..end
5381 } else {
5382 cmp::min(range.start.column(), range.end.column())
5383 ..cmp::max(range.start.column(), range.end.column())
5384 };
5385
5386 while row != end_row {
5387 if above {
5388 row -= 1;
5389 } else {
5390 row += 1;
5391 }
5392
5393 if let Some(new_selection) = self.selections.build_columnar_selection(
5394 &display_map,
5395 row,
5396 &columns,
5397 selection.reversed,
5398 ) {
5399 state.stack.push(new_selection.id);
5400 if above {
5401 new_selections.push(new_selection);
5402 new_selections.push(selection);
5403 } else {
5404 new_selections.push(selection);
5405 new_selections.push(new_selection);
5406 }
5407
5408 continue 'outer;
5409 }
5410 }
5411 }
5412
5413 new_selections.push(selection);
5414 }
5415 } else {
5416 new_selections = selections;
5417 new_selections.retain(|s| s.id != last_added_selection);
5418 state.stack.pop();
5419 }
5420
5421 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5422 s.select(new_selections);
5423 });
5424 if state.stack.len() > 1 {
5425 self.add_selections_state = Some(state);
5426 }
5427 }
5428
5429 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5430 self.push_to_selection_history();
5431 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5432 let buffer = &display_map.buffer_snapshot;
5433 let mut selections = self.selections.all::<usize>(cx);
5434 if let Some(mut select_next_state) = self.select_next_state.take() {
5435 let query = &select_next_state.query;
5436 if !select_next_state.done {
5437 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5438 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5439 let mut next_selected_range = None;
5440
5441 let bytes_after_last_selection =
5442 buffer.bytes_in_range(last_selection.end..buffer.len());
5443 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5444 let query_matches = query
5445 .stream_find_iter(bytes_after_last_selection)
5446 .map(|result| (last_selection.end, result))
5447 .chain(
5448 query
5449 .stream_find_iter(bytes_before_first_selection)
5450 .map(|result| (0, result)),
5451 );
5452 for (start_offset, query_match) in query_matches {
5453 let query_match = query_match.unwrap(); // can only fail due to I/O
5454 let offset_range =
5455 start_offset + query_match.start()..start_offset + query_match.end();
5456 let display_range = offset_range.start.to_display_point(&display_map)
5457 ..offset_range.end.to_display_point(&display_map);
5458
5459 if !select_next_state.wordwise
5460 || (!movement::is_inside_word(&display_map, display_range.start)
5461 && !movement::is_inside_word(&display_map, display_range.end))
5462 {
5463 next_selected_range = Some(offset_range);
5464 break;
5465 }
5466 }
5467
5468 if let Some(next_selected_range) = next_selected_range {
5469 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5470 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5471 if action.replace_newest {
5472 s.delete(s.newest_anchor().id);
5473 }
5474 s.insert_range(next_selected_range);
5475 });
5476 } else {
5477 select_next_state.done = true;
5478 }
5479 }
5480
5481 self.select_next_state = Some(select_next_state);
5482 } else if selections.len() == 1 {
5483 let selection = selections.last_mut().unwrap();
5484 if selection.start == selection.end {
5485 let word_range = movement::surrounding_word(
5486 &display_map,
5487 selection.start.to_display_point(&display_map),
5488 );
5489 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5490 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5491 selection.goal = SelectionGoal::None;
5492 selection.reversed = false;
5493
5494 let query = buffer
5495 .text_for_range(selection.start..selection.end)
5496 .collect::<String>();
5497 let select_state = SelectNextState {
5498 query: AhoCorasick::new_auto_configured(&[query]),
5499 wordwise: true,
5500 done: false,
5501 };
5502 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5503 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5504 s.select(selections);
5505 });
5506 self.select_next_state = Some(select_state);
5507 } else {
5508 let query = buffer
5509 .text_for_range(selection.start..selection.end)
5510 .collect::<String>();
5511 self.select_next_state = Some(SelectNextState {
5512 query: AhoCorasick::new_auto_configured(&[query]),
5513 wordwise: false,
5514 done: false,
5515 });
5516 self.select_next(action, cx);
5517 }
5518 }
5519 }
5520
5521 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5522 self.push_to_selection_history();
5523 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5524 let buffer = &display_map.buffer_snapshot;
5525 let mut selections = self.selections.all::<usize>(cx);
5526 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5527 let query = &select_prev_state.query;
5528 if !select_prev_state.done {
5529 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5530 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5531 let mut next_selected_range = None;
5532 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5533 let bytes_before_last_selection =
5534 buffer.reversed_bytes_in_range(0..last_selection.start);
5535 let bytes_after_first_selection =
5536 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5537 let query_matches = query
5538 .stream_find_iter(bytes_before_last_selection)
5539 .map(|result| (last_selection.start, result))
5540 .chain(
5541 query
5542 .stream_find_iter(bytes_after_first_selection)
5543 .map(|result| (buffer.len(), result)),
5544 );
5545 for (end_offset, query_match) in query_matches {
5546 let query_match = query_match.unwrap(); // can only fail due to I/O
5547 let offset_range =
5548 end_offset - query_match.end()..end_offset - query_match.start();
5549 let display_range = offset_range.start.to_display_point(&display_map)
5550 ..offset_range.end.to_display_point(&display_map);
5551
5552 if !select_prev_state.wordwise
5553 || (!movement::is_inside_word(&display_map, display_range.start)
5554 && !movement::is_inside_word(&display_map, display_range.end))
5555 {
5556 next_selected_range = Some(offset_range);
5557 break;
5558 }
5559 }
5560
5561 if let Some(next_selected_range) = next_selected_range {
5562 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5563 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5564 if action.replace_newest {
5565 s.delete(s.newest_anchor().id);
5566 }
5567 s.insert_range(next_selected_range);
5568 });
5569 } else {
5570 select_prev_state.done = true;
5571 }
5572 }
5573
5574 self.select_prev_state = Some(select_prev_state);
5575 } else if selections.len() == 1 {
5576 let selection = selections.last_mut().unwrap();
5577 if selection.start == selection.end {
5578 let word_range = movement::surrounding_word(
5579 &display_map,
5580 selection.start.to_display_point(&display_map),
5581 );
5582 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5583 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5584 selection.goal = SelectionGoal::None;
5585 selection.reversed = false;
5586
5587 let query = buffer
5588 .text_for_range(selection.start..selection.end)
5589 .collect::<String>();
5590 let query = query.chars().rev().collect::<String>();
5591 let select_state = SelectNextState {
5592 query: AhoCorasick::new_auto_configured(&[query]),
5593 wordwise: true,
5594 done: false,
5595 };
5596 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5597 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5598 s.select(selections);
5599 });
5600 self.select_prev_state = Some(select_state);
5601 } else {
5602 let query = buffer
5603 .text_for_range(selection.start..selection.end)
5604 .collect::<String>();
5605 let query = query.chars().rev().collect::<String>();
5606 self.select_prev_state = Some(SelectNextState {
5607 query: AhoCorasick::new_auto_configured(&[query]),
5608 wordwise: false,
5609 done: false,
5610 });
5611 self.select_previous(action, cx);
5612 }
5613 }
5614 }
5615
5616 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5617 self.transact(cx, |this, cx| {
5618 let mut selections = this.selections.all::<Point>(cx);
5619 let mut edits = Vec::new();
5620 let mut selection_edit_ranges = Vec::new();
5621 let mut last_toggled_row = None;
5622 let snapshot = this.buffer.read(cx).read(cx);
5623 let empty_str: Arc<str> = "".into();
5624 let mut suffixes_inserted = Vec::new();
5625
5626 fn comment_prefix_range(
5627 snapshot: &MultiBufferSnapshot,
5628 row: u32,
5629 comment_prefix: &str,
5630 comment_prefix_whitespace: &str,
5631 ) -> Range<Point> {
5632 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5633
5634 let mut line_bytes = snapshot
5635 .bytes_in_range(start..snapshot.max_point())
5636 .flatten()
5637 .copied();
5638
5639 // If this line currently begins with the line comment prefix, then record
5640 // the range containing the prefix.
5641 if line_bytes
5642 .by_ref()
5643 .take(comment_prefix.len())
5644 .eq(comment_prefix.bytes())
5645 {
5646 // Include any whitespace that matches the comment prefix.
5647 let matching_whitespace_len = line_bytes
5648 .zip(comment_prefix_whitespace.bytes())
5649 .take_while(|(a, b)| a == b)
5650 .count() as u32;
5651 let end = Point::new(
5652 start.row,
5653 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5654 );
5655 start..end
5656 } else {
5657 start..start
5658 }
5659 }
5660
5661 fn comment_suffix_range(
5662 snapshot: &MultiBufferSnapshot,
5663 row: u32,
5664 comment_suffix: &str,
5665 comment_suffix_has_leading_space: bool,
5666 ) -> Range<Point> {
5667 let end = Point::new(row, snapshot.line_len(row));
5668 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5669
5670 let mut line_end_bytes = snapshot
5671 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5672 .flatten()
5673 .copied();
5674
5675 let leading_space_len = if suffix_start_column > 0
5676 && line_end_bytes.next() == Some(b' ')
5677 && comment_suffix_has_leading_space
5678 {
5679 1
5680 } else {
5681 0
5682 };
5683
5684 // If this line currently begins with the line comment prefix, then record
5685 // the range containing the prefix.
5686 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5687 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5688 start..end
5689 } else {
5690 end..end
5691 }
5692 }
5693
5694 // TODO: Handle selections that cross excerpts
5695 for selection in &mut selections {
5696 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5697 let language = if let Some(language) =
5698 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5699 {
5700 language
5701 } else {
5702 continue;
5703 };
5704
5705 selection_edit_ranges.clear();
5706
5707 // If multiple selections contain a given row, avoid processing that
5708 // row more than once.
5709 let mut start_row = selection.start.row;
5710 if last_toggled_row == Some(start_row) {
5711 start_row += 1;
5712 }
5713 let end_row =
5714 if selection.end.row > selection.start.row && selection.end.column == 0 {
5715 selection.end.row - 1
5716 } else {
5717 selection.end.row
5718 };
5719 last_toggled_row = Some(end_row);
5720
5721 if start_row > end_row {
5722 continue;
5723 }
5724
5725 // If the language has line comments, toggle those.
5726 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5727 // Split the comment prefix's trailing whitespace into a separate string,
5728 // as that portion won't be used for detecting if a line is a comment.
5729 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5730 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5731 let mut all_selection_lines_are_comments = true;
5732
5733 for row in start_row..=end_row {
5734 if snapshot.is_line_blank(row) && start_row < end_row {
5735 continue;
5736 }
5737
5738 let prefix_range = comment_prefix_range(
5739 snapshot.deref(),
5740 row,
5741 comment_prefix,
5742 comment_prefix_whitespace,
5743 );
5744 if prefix_range.is_empty() {
5745 all_selection_lines_are_comments = false;
5746 }
5747 selection_edit_ranges.push(prefix_range);
5748 }
5749
5750 if all_selection_lines_are_comments {
5751 edits.extend(
5752 selection_edit_ranges
5753 .iter()
5754 .cloned()
5755 .map(|range| (range, empty_str.clone())),
5756 );
5757 } else {
5758 let min_column = selection_edit_ranges
5759 .iter()
5760 .map(|r| r.start.column)
5761 .min()
5762 .unwrap_or(0);
5763 edits.extend(selection_edit_ranges.iter().map(|range| {
5764 let position = Point::new(range.start.row, min_column);
5765 (position..position, full_comment_prefix.clone())
5766 }));
5767 }
5768 } else if let Some((full_comment_prefix, comment_suffix)) =
5769 language.block_comment_delimiters()
5770 {
5771 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5772 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5773 let prefix_range = comment_prefix_range(
5774 snapshot.deref(),
5775 start_row,
5776 comment_prefix,
5777 comment_prefix_whitespace,
5778 );
5779 let suffix_range = comment_suffix_range(
5780 snapshot.deref(),
5781 end_row,
5782 comment_suffix.trim_start_matches(' '),
5783 comment_suffix.starts_with(' '),
5784 );
5785
5786 if prefix_range.is_empty() || suffix_range.is_empty() {
5787 edits.push((
5788 prefix_range.start..prefix_range.start,
5789 full_comment_prefix.clone(),
5790 ));
5791 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5792 suffixes_inserted.push((end_row, comment_suffix.len()));
5793 } else {
5794 edits.push((prefix_range, empty_str.clone()));
5795 edits.push((suffix_range, empty_str.clone()));
5796 }
5797 } else {
5798 continue;
5799 }
5800 }
5801
5802 drop(snapshot);
5803 this.buffer.update(cx, |buffer, cx| {
5804 buffer.edit(edits, None, cx);
5805 });
5806
5807 // Adjust selections so that they end before any comment suffixes that
5808 // were inserted.
5809 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5810 let mut selections = this.selections.all::<Point>(cx);
5811 let snapshot = this.buffer.read(cx).read(cx);
5812 for selection in &mut selections {
5813 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5814 match row.cmp(&selection.end.row) {
5815 Ordering::Less => {
5816 suffixes_inserted.next();
5817 continue;
5818 }
5819 Ordering::Greater => break,
5820 Ordering::Equal => {
5821 if selection.end.column == snapshot.line_len(row) {
5822 if selection.is_empty() {
5823 selection.start.column -= suffix_len as u32;
5824 }
5825 selection.end.column -= suffix_len as u32;
5826 }
5827 break;
5828 }
5829 }
5830 }
5831 }
5832
5833 drop(snapshot);
5834 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5835
5836 let selections = this.selections.all::<Point>(cx);
5837 let selections_on_single_row = selections.windows(2).all(|selections| {
5838 selections[0].start.row == selections[1].start.row
5839 && selections[0].end.row == selections[1].end.row
5840 && selections[0].start.row == selections[0].end.row
5841 });
5842 let selections_selecting = selections
5843 .iter()
5844 .any(|selection| selection.start != selection.end);
5845 let advance_downwards = action.advance_downwards
5846 && selections_on_single_row
5847 && !selections_selecting
5848 && this.mode != EditorMode::SingleLine;
5849
5850 if advance_downwards {
5851 let snapshot = this.buffer.read(cx).snapshot(cx);
5852
5853 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5854 s.move_cursors_with(|display_snapshot, display_point, _| {
5855 let mut point = display_point.to_point(display_snapshot);
5856 point.row += 1;
5857 point = snapshot.clip_point(point, Bias::Left);
5858 let display_point = point.to_display_point(display_snapshot);
5859 (display_point, SelectionGoal::Column(display_point.column()))
5860 })
5861 });
5862 }
5863 });
5864 }
5865
5866 pub fn select_larger_syntax_node(
5867 &mut self,
5868 _: &SelectLargerSyntaxNode,
5869 cx: &mut ViewContext<Self>,
5870 ) {
5871 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5872 let buffer = self.buffer.read(cx).snapshot(cx);
5873 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5874
5875 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5876 let mut selected_larger_node = false;
5877 let new_selections = old_selections
5878 .iter()
5879 .map(|selection| {
5880 let old_range = selection.start..selection.end;
5881 let mut new_range = old_range.clone();
5882 while let Some(containing_range) =
5883 buffer.range_for_syntax_ancestor(new_range.clone())
5884 {
5885 new_range = containing_range;
5886 if !display_map.intersects_fold(new_range.start)
5887 && !display_map.intersects_fold(new_range.end)
5888 {
5889 break;
5890 }
5891 }
5892
5893 selected_larger_node |= new_range != old_range;
5894 Selection {
5895 id: selection.id,
5896 start: new_range.start,
5897 end: new_range.end,
5898 goal: SelectionGoal::None,
5899 reversed: selection.reversed,
5900 }
5901 })
5902 .collect::<Vec<_>>();
5903
5904 if selected_larger_node {
5905 stack.push(old_selections);
5906 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5907 s.select(new_selections);
5908 });
5909 }
5910 self.select_larger_syntax_node_stack = stack;
5911 }
5912
5913 pub fn select_smaller_syntax_node(
5914 &mut self,
5915 _: &SelectSmallerSyntaxNode,
5916 cx: &mut ViewContext<Self>,
5917 ) {
5918 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5919 if let Some(selections) = stack.pop() {
5920 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5921 s.select(selections.to_vec());
5922 });
5923 }
5924 self.select_larger_syntax_node_stack = stack;
5925 }
5926
5927 pub fn move_to_enclosing_bracket(
5928 &mut self,
5929 _: &MoveToEnclosingBracket,
5930 cx: &mut ViewContext<Self>,
5931 ) {
5932 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5933 s.move_offsets_with(|snapshot, selection| {
5934 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5935 return;
5936 };
5937
5938 let mut best_length = usize::MAX;
5939 let mut best_inside = false;
5940 let mut best_in_bracket_range = false;
5941 let mut best_destination = None;
5942 for (open, close) in enclosing_bracket_ranges {
5943 let close = close.to_inclusive();
5944 let length = close.end() - open.start;
5945 let inside = selection.start >= open.end && selection.end <= *close.start();
5946 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5947
5948 // If best is next to a bracket and current isn't, skip
5949 if !in_bracket_range && best_in_bracket_range {
5950 continue;
5951 }
5952
5953 // Prefer smaller lengths unless best is inside and current isn't
5954 if length > best_length && (best_inside || !inside) {
5955 continue;
5956 }
5957
5958 best_length = length;
5959 best_inside = inside;
5960 best_in_bracket_range = in_bracket_range;
5961 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5962 if inside {
5963 open.end
5964 } else {
5965 open.start
5966 }
5967 } else {
5968 if inside {
5969 *close.start()
5970 } else {
5971 *close.end()
5972 }
5973 });
5974 }
5975
5976 if let Some(destination) = best_destination {
5977 selection.collapse_to(destination, SelectionGoal::None);
5978 }
5979 })
5980 });
5981 }
5982
5983 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5984 self.end_selection(cx);
5985 self.selection_history.mode = SelectionHistoryMode::Undoing;
5986 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5987 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5988 self.select_next_state = entry.select_next_state;
5989 self.select_prev_state = entry.select_prev_state;
5990 self.add_selections_state = entry.add_selections_state;
5991 self.request_autoscroll(Autoscroll::newest(), cx);
5992 }
5993 self.selection_history.mode = SelectionHistoryMode::Normal;
5994 }
5995
5996 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5997 self.end_selection(cx);
5998 self.selection_history.mode = SelectionHistoryMode::Redoing;
5999 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6000 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6001 self.select_next_state = entry.select_next_state;
6002 self.select_prev_state = entry.select_prev_state;
6003 self.add_selections_state = entry.add_selections_state;
6004 self.request_autoscroll(Autoscroll::newest(), cx);
6005 }
6006 self.selection_history.mode = SelectionHistoryMode::Normal;
6007 }
6008
6009 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6010 self.go_to_diagnostic_impl(Direction::Next, cx)
6011 }
6012
6013 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6014 self.go_to_diagnostic_impl(Direction::Prev, cx)
6015 }
6016
6017 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6018 let buffer = self.buffer.read(cx).snapshot(cx);
6019 let selection = self.selections.newest::<usize>(cx);
6020
6021 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6022 if direction == Direction::Next {
6023 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6024 let (group_id, jump_to) = popover.activation_info();
6025 if self.activate_diagnostics(group_id, cx) {
6026 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6027 let mut new_selection = s.newest_anchor().clone();
6028 new_selection.collapse_to(jump_to, SelectionGoal::None);
6029 s.select_anchors(vec![new_selection.clone()]);
6030 });
6031 }
6032 return;
6033 }
6034 }
6035
6036 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6037 active_diagnostics
6038 .primary_range
6039 .to_offset(&buffer)
6040 .to_inclusive()
6041 });
6042 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6043 if active_primary_range.contains(&selection.head()) {
6044 *active_primary_range.end()
6045 } else {
6046 selection.head()
6047 }
6048 } else {
6049 selection.head()
6050 };
6051
6052 loop {
6053 let mut diagnostics = if direction == Direction::Prev {
6054 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6055 } else {
6056 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6057 };
6058 let group = diagnostics.find_map(|entry| {
6059 if entry.diagnostic.is_primary
6060 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6061 && !entry.range.is_empty()
6062 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6063 {
6064 Some((entry.range, entry.diagnostic.group_id))
6065 } else {
6066 None
6067 }
6068 });
6069
6070 if let Some((primary_range, group_id)) = group {
6071 if self.activate_diagnostics(group_id, cx) {
6072 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6073 s.select(vec![Selection {
6074 id: selection.id,
6075 start: primary_range.start,
6076 end: primary_range.start,
6077 reversed: false,
6078 goal: SelectionGoal::None,
6079 }]);
6080 });
6081 }
6082 break;
6083 } else {
6084 // Cycle around to the start of the buffer, potentially moving back to the start of
6085 // the currently active diagnostic.
6086 active_primary_range.take();
6087 if direction == Direction::Prev {
6088 if search_start == buffer.len() {
6089 break;
6090 } else {
6091 search_start = buffer.len();
6092 }
6093 } else if search_start == 0 {
6094 break;
6095 } else {
6096 search_start = 0;
6097 }
6098 }
6099 }
6100 }
6101
6102 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6103 let snapshot = self
6104 .display_map
6105 .update(cx, |display_map, cx| display_map.snapshot(cx));
6106 let selection = self.selections.newest::<Point>(cx);
6107
6108 if !self.seek_in_direction(
6109 &snapshot,
6110 selection.head(),
6111 false,
6112 snapshot
6113 .buffer_snapshot
6114 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6115 cx,
6116 ) {
6117 let wrapped_point = Point::zero();
6118 self.seek_in_direction(
6119 &snapshot,
6120 wrapped_point,
6121 true,
6122 snapshot
6123 .buffer_snapshot
6124 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6125 cx,
6126 );
6127 }
6128 }
6129
6130 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6131 let snapshot = self
6132 .display_map
6133 .update(cx, |display_map, cx| display_map.snapshot(cx));
6134 let selection = self.selections.newest::<Point>(cx);
6135
6136 if !self.seek_in_direction(
6137 &snapshot,
6138 selection.head(),
6139 false,
6140 snapshot
6141 .buffer_snapshot
6142 .git_diff_hunks_in_range_rev(0..selection.head().row),
6143 cx,
6144 ) {
6145 let wrapped_point = snapshot.buffer_snapshot.max_point();
6146 self.seek_in_direction(
6147 &snapshot,
6148 wrapped_point,
6149 true,
6150 snapshot
6151 .buffer_snapshot
6152 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6153 cx,
6154 );
6155 }
6156 }
6157
6158 fn seek_in_direction(
6159 &mut self,
6160 snapshot: &DisplaySnapshot,
6161 initial_point: Point,
6162 is_wrapped: bool,
6163 hunks: impl Iterator<Item = DiffHunk<u32>>,
6164 cx: &mut ViewContext<Editor>,
6165 ) -> bool {
6166 let display_point = initial_point.to_display_point(snapshot);
6167 let mut hunks = hunks
6168 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6169 .skip_while(|hunk| {
6170 if is_wrapped {
6171 false
6172 } else {
6173 hunk.contains_display_row(display_point.row())
6174 }
6175 })
6176 .dedup();
6177
6178 if let Some(hunk) = hunks.next() {
6179 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6180 let row = hunk.start_display_row();
6181 let point = DisplayPoint::new(row, 0);
6182 s.select_display_ranges([point..point]);
6183 });
6184
6185 true
6186 } else {
6187 false
6188 }
6189 }
6190
6191 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6192 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6193 }
6194
6195 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6196 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6197 }
6198
6199 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6200 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6201 }
6202
6203 pub fn go_to_type_definition_split(
6204 &mut self,
6205 _: &GoToTypeDefinitionSplit,
6206 cx: &mut ViewContext<Self>,
6207 ) {
6208 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6209 }
6210
6211 fn go_to_definition_of_kind(
6212 &mut self,
6213 kind: GotoDefinitionKind,
6214 split: bool,
6215 cx: &mut ViewContext<Self>,
6216 ) {
6217 let Some(workspace) = self.workspace(cx) else { return };
6218 let buffer = self.buffer.read(cx);
6219 let head = self.selections.newest::<usize>(cx).head();
6220 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6221 text_anchor
6222 } else {
6223 return;
6224 };
6225
6226 let project = workspace.read(cx).project().clone();
6227 let definitions = project.update(cx, |project, cx| match kind {
6228 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6229 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6230 });
6231
6232 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6233 let definitions = definitions.await?;
6234 editor.update(&mut cx, |editor, cx| {
6235 editor.navigate_to_definitions(definitions, split, cx);
6236 })?;
6237 Ok::<(), anyhow::Error>(())
6238 })
6239 .detach_and_log_err(cx);
6240 }
6241
6242 pub fn navigate_to_definitions(
6243 &mut self,
6244 mut definitions: Vec<LocationLink>,
6245 split: bool,
6246 cx: &mut ViewContext<Editor>,
6247 ) {
6248 let Some(workspace) = self.workspace(cx) else { return };
6249 let pane = workspace.read(cx).active_pane().clone();
6250 // If there is one definition, just open it directly
6251 if definitions.len() == 1 {
6252 let definition = definitions.pop().unwrap();
6253 let range = definition
6254 .target
6255 .range
6256 .to_offset(definition.target.buffer.read(cx));
6257
6258 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6259 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6260 s.select_ranges([range]);
6261 });
6262 } else {
6263 cx.window_context().defer(move |cx| {
6264 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6265 if split {
6266 workspace.split_project_item(definition.target.buffer.clone(), cx)
6267 } else {
6268 workspace.open_project_item(definition.target.buffer.clone(), cx)
6269 }
6270 });
6271 target_editor.update(cx, |target_editor, cx| {
6272 // When selecting a definition in a different buffer, disable the nav history
6273 // to avoid creating a history entry at the previous cursor location.
6274 pane.update(cx, |pane, _| pane.disable_history());
6275 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6276 s.select_ranges([range]);
6277 });
6278 pane.update(cx, |pane, _| pane.enable_history());
6279 });
6280 });
6281 }
6282 } else if !definitions.is_empty() {
6283 let replica_id = self.replica_id(cx);
6284 cx.window_context().defer(move |cx| {
6285 let title = definitions
6286 .iter()
6287 .find(|definition| definition.origin.is_some())
6288 .and_then(|definition| {
6289 definition.origin.as_ref().map(|origin| {
6290 let buffer = origin.buffer.read(cx);
6291 format!(
6292 "Definitions for {}",
6293 buffer
6294 .text_for_range(origin.range.clone())
6295 .collect::<String>()
6296 )
6297 })
6298 })
6299 .unwrap_or("Definitions".to_owned());
6300 let locations = definitions
6301 .into_iter()
6302 .map(|definition| definition.target)
6303 .collect();
6304 workspace.update(cx, |workspace, cx| {
6305 Self::open_locations_in_multibuffer(
6306 workspace, locations, replica_id, title, split, cx,
6307 )
6308 });
6309 });
6310 }
6311 }
6312
6313 pub fn find_all_references(
6314 workspace: &mut Workspace,
6315 _: &FindAllReferences,
6316 cx: &mut ViewContext<Workspace>,
6317 ) -> Option<Task<Result<()>>> {
6318 let active_item = workspace.active_item(cx)?;
6319 let editor_handle = active_item.act_as::<Self>(cx)?;
6320
6321 let editor = editor_handle.read(cx);
6322 let buffer = editor.buffer.read(cx);
6323 let head = editor.selections.newest::<usize>(cx).head();
6324 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6325 let replica_id = editor.replica_id(cx);
6326
6327 let project = workspace.project().clone();
6328 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6329 Some(cx.spawn_labeled(
6330 "Finding All References...",
6331 |workspace, mut cx| async move {
6332 let locations = references.await?;
6333 if locations.is_empty() {
6334 return Ok(());
6335 }
6336
6337 workspace.update(&mut cx, |workspace, cx| {
6338 let title = locations
6339 .first()
6340 .as_ref()
6341 .map(|location| {
6342 let buffer = location.buffer.read(cx);
6343 format!(
6344 "References to `{}`",
6345 buffer
6346 .text_for_range(location.range.clone())
6347 .collect::<String>()
6348 )
6349 })
6350 .unwrap();
6351 Self::open_locations_in_multibuffer(
6352 workspace, locations, replica_id, title, false, cx,
6353 );
6354 })?;
6355
6356 Ok(())
6357 },
6358 ))
6359 }
6360
6361 /// Opens a multibuffer with the given project locations in it
6362 pub fn open_locations_in_multibuffer(
6363 workspace: &mut Workspace,
6364 mut locations: Vec<Location>,
6365 replica_id: ReplicaId,
6366 title: String,
6367 split: bool,
6368 cx: &mut ViewContext<Workspace>,
6369 ) {
6370 // If there are multiple definitions, open them in a multibuffer
6371 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6372 let mut locations = locations.into_iter().peekable();
6373 let mut ranges_to_highlight = Vec::new();
6374
6375 let excerpt_buffer = cx.add_model(|cx| {
6376 let mut multibuffer = MultiBuffer::new(replica_id);
6377 while let Some(location) = locations.next() {
6378 let buffer = location.buffer.read(cx);
6379 let mut ranges_for_buffer = Vec::new();
6380 let range = location.range.to_offset(buffer);
6381 ranges_for_buffer.push(range.clone());
6382
6383 while let Some(next_location) = locations.peek() {
6384 if next_location.buffer == location.buffer {
6385 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6386 locations.next();
6387 } else {
6388 break;
6389 }
6390 }
6391
6392 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6393 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6394 location.buffer.clone(),
6395 ranges_for_buffer,
6396 1,
6397 cx,
6398 ))
6399 }
6400
6401 multibuffer.with_title(title)
6402 });
6403
6404 let editor = cx.add_view(|cx| {
6405 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6406 });
6407 editor.update(cx, |editor, cx| {
6408 editor.highlight_background::<Self>(
6409 ranges_to_highlight,
6410 |theme| theme.editor.highlighted_line_background,
6411 cx,
6412 );
6413 });
6414 if split {
6415 workspace.split_item(Box::new(editor), cx);
6416 } else {
6417 workspace.add_item(Box::new(editor), cx);
6418 }
6419 }
6420
6421 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6422 use language::ToOffset as _;
6423
6424 let project = self.project.clone()?;
6425 let selection = self.selections.newest_anchor().clone();
6426 let (cursor_buffer, cursor_buffer_position) = self
6427 .buffer
6428 .read(cx)
6429 .text_anchor_for_position(selection.head(), cx)?;
6430 let (tail_buffer, _) = self
6431 .buffer
6432 .read(cx)
6433 .text_anchor_for_position(selection.tail(), cx)?;
6434 if tail_buffer != cursor_buffer {
6435 return None;
6436 }
6437
6438 let snapshot = cursor_buffer.read(cx).snapshot();
6439 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6440 let prepare_rename = project.update(cx, |project, cx| {
6441 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6442 });
6443
6444 Some(cx.spawn(|this, mut cx| async move {
6445 let rename_range = if let Some(range) = prepare_rename.await? {
6446 Some(range)
6447 } else {
6448 this.read_with(&cx, |this, cx| {
6449 let buffer = this.buffer.read(cx).snapshot(cx);
6450 let mut buffer_highlights = this
6451 .document_highlights_for_position(selection.head(), &buffer)
6452 .filter(|highlight| {
6453 highlight.start.excerpt_id() == selection.head().excerpt_id()
6454 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6455 });
6456 buffer_highlights
6457 .next()
6458 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6459 })?
6460 };
6461 if let Some(rename_range) = rename_range {
6462 let rename_buffer_range = rename_range.to_offset(&snapshot);
6463 let cursor_offset_in_rename_range =
6464 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6465
6466 this.update(&mut cx, |this, cx| {
6467 this.take_rename(false, cx);
6468 let style = this.style(cx);
6469 let buffer = this.buffer.read(cx).read(cx);
6470 let cursor_offset = selection.head().to_offset(&buffer);
6471 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6472 let rename_end = rename_start + rename_buffer_range.len();
6473 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6474 let mut old_highlight_id = None;
6475 let old_name: Arc<str> = buffer
6476 .chunks(rename_start..rename_end, true)
6477 .map(|chunk| {
6478 if old_highlight_id.is_none() {
6479 old_highlight_id = chunk.syntax_highlight_id;
6480 }
6481 chunk.text
6482 })
6483 .collect::<String>()
6484 .into();
6485
6486 drop(buffer);
6487
6488 // Position the selection in the rename editor so that it matches the current selection.
6489 this.show_local_selections = false;
6490 let rename_editor = cx.add_view(|cx| {
6491 let mut editor = Editor::single_line(None, cx);
6492 if let Some(old_highlight_id) = old_highlight_id {
6493 editor.override_text_style =
6494 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6495 }
6496 editor.buffer.update(cx, |buffer, cx| {
6497 buffer.edit([(0..0, old_name.clone())], None, cx)
6498 });
6499 editor.select_all(&SelectAll, cx);
6500 editor
6501 });
6502
6503 let ranges = this
6504 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6505 .into_iter()
6506 .flat_map(|(_, ranges)| ranges)
6507 .chain(
6508 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6509 .into_iter()
6510 .flat_map(|(_, ranges)| ranges),
6511 )
6512 .collect();
6513
6514 this.highlight_text::<Rename>(
6515 ranges,
6516 HighlightStyle {
6517 fade_out: Some(style.rename_fade),
6518 ..Default::default()
6519 },
6520 cx,
6521 );
6522 cx.focus(&rename_editor);
6523 let block_id = this.insert_blocks(
6524 [BlockProperties {
6525 style: BlockStyle::Flex,
6526 position: range.start.clone(),
6527 height: 1,
6528 render: Arc::new({
6529 let editor = rename_editor.clone();
6530 move |cx: &mut BlockContext| {
6531 ChildView::new(&editor, cx)
6532 .contained()
6533 .with_padding_left(cx.anchor_x)
6534 .into_any()
6535 }
6536 }),
6537 disposition: BlockDisposition::Below,
6538 }],
6539 Some(Autoscroll::fit()),
6540 cx,
6541 )[0];
6542 this.pending_rename = Some(RenameState {
6543 range,
6544 old_name,
6545 editor: rename_editor,
6546 block_id,
6547 });
6548 })?;
6549 }
6550
6551 Ok(())
6552 }))
6553 }
6554
6555 pub fn confirm_rename(
6556 workspace: &mut Workspace,
6557 _: &ConfirmRename,
6558 cx: &mut ViewContext<Workspace>,
6559 ) -> Option<Task<Result<()>>> {
6560 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6561
6562 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6563 let rename = editor.take_rename(false, cx)?;
6564 let buffer = editor.buffer.read(cx);
6565 let (start_buffer, start) =
6566 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6567 let (end_buffer, end) =
6568 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6569 if start_buffer == end_buffer {
6570 let new_name = rename.editor.read(cx).text(cx);
6571 Some((start_buffer, start..end, rename.old_name, new_name))
6572 } else {
6573 None
6574 }
6575 })?;
6576
6577 let rename = workspace.project().clone().update(cx, |project, cx| {
6578 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6579 });
6580
6581 let editor = editor.downgrade();
6582 Some(cx.spawn(|workspace, mut cx| async move {
6583 let project_transaction = rename.await?;
6584 Self::open_project_transaction(
6585 &editor,
6586 workspace,
6587 project_transaction,
6588 format!("Rename: {} → {}", old_name, new_name),
6589 cx.clone(),
6590 )
6591 .await?;
6592
6593 editor.update(&mut cx, |editor, cx| {
6594 editor.refresh_document_highlights(cx);
6595 })?;
6596 Ok(())
6597 }))
6598 }
6599
6600 fn take_rename(
6601 &mut self,
6602 moving_cursor: bool,
6603 cx: &mut ViewContext<Self>,
6604 ) -> Option<RenameState> {
6605 let rename = self.pending_rename.take()?;
6606 self.remove_blocks(
6607 [rename.block_id].into_iter().collect(),
6608 Some(Autoscroll::fit()),
6609 cx,
6610 );
6611 self.clear_text_highlights::<Rename>(cx);
6612 self.show_local_selections = true;
6613
6614 if moving_cursor {
6615 let rename_editor = rename.editor.read(cx);
6616 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6617
6618 // Update the selection to match the position of the selection inside
6619 // the rename editor.
6620 let snapshot = self.buffer.read(cx).read(cx);
6621 let rename_range = rename.range.to_offset(&snapshot);
6622 let cursor_in_editor = snapshot
6623 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6624 .min(rename_range.end);
6625 drop(snapshot);
6626
6627 self.change_selections(None, cx, |s| {
6628 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6629 });
6630 } else {
6631 self.refresh_document_highlights(cx);
6632 }
6633
6634 Some(rename)
6635 }
6636
6637 #[cfg(any(test, feature = "test-support"))]
6638 pub fn pending_rename(&self) -> Option<&RenameState> {
6639 self.pending_rename.as_ref()
6640 }
6641
6642 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6643 let project = match &self.project {
6644 Some(project) => project.clone(),
6645 None => return None,
6646 };
6647
6648 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6649 }
6650
6651 fn perform_format(
6652 &mut self,
6653 project: ModelHandle<Project>,
6654 trigger: FormatTrigger,
6655 cx: &mut ViewContext<Self>,
6656 ) -> Task<Result<()>> {
6657 let buffer = self.buffer().clone();
6658 let buffers = buffer.read(cx).all_buffers();
6659
6660 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6661 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6662
6663 cx.spawn(|_, mut cx| async move {
6664 let transaction = futures::select_biased! {
6665 _ = timeout => {
6666 log::warn!("timed out waiting for formatting");
6667 None
6668 }
6669 transaction = format.log_err().fuse() => transaction,
6670 };
6671
6672 buffer.update(&mut cx, |buffer, cx| {
6673 if let Some(transaction) = transaction {
6674 if !buffer.is_singleton() {
6675 buffer.push_transaction(&transaction.0, cx);
6676 }
6677 }
6678
6679 cx.notify();
6680 });
6681
6682 Ok(())
6683 })
6684 }
6685
6686 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6687 if let Some(project) = self.project.clone() {
6688 self.buffer.update(cx, |multi_buffer, cx| {
6689 project.update(cx, |project, cx| {
6690 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6691 });
6692 })
6693 }
6694 }
6695
6696 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6697 cx.show_character_palette();
6698 }
6699
6700 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6701 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6702 let buffer = self.buffer.read(cx).snapshot(cx);
6703 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6704 let is_valid = buffer
6705 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6706 .any(|entry| {
6707 entry.diagnostic.is_primary
6708 && !entry.range.is_empty()
6709 && entry.range.start == primary_range_start
6710 && entry.diagnostic.message == active_diagnostics.primary_message
6711 });
6712
6713 if is_valid != active_diagnostics.is_valid {
6714 active_diagnostics.is_valid = is_valid;
6715 let mut new_styles = HashMap::default();
6716 for (block_id, diagnostic) in &active_diagnostics.blocks {
6717 new_styles.insert(
6718 *block_id,
6719 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6720 );
6721 }
6722 self.display_map
6723 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6724 }
6725 }
6726 }
6727
6728 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6729 self.dismiss_diagnostics(cx);
6730 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6731 let buffer = self.buffer.read(cx).snapshot(cx);
6732
6733 let mut primary_range = None;
6734 let mut primary_message = None;
6735 let mut group_end = Point::zero();
6736 let diagnostic_group = buffer
6737 .diagnostic_group::<Point>(group_id)
6738 .map(|entry| {
6739 if entry.range.end > group_end {
6740 group_end = entry.range.end;
6741 }
6742 if entry.diagnostic.is_primary {
6743 primary_range = Some(entry.range.clone());
6744 primary_message = Some(entry.diagnostic.message.clone());
6745 }
6746 entry
6747 })
6748 .collect::<Vec<_>>();
6749 let primary_range = primary_range?;
6750 let primary_message = primary_message?;
6751 let primary_range =
6752 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6753
6754 let blocks = display_map
6755 .insert_blocks(
6756 diagnostic_group.iter().map(|entry| {
6757 let diagnostic = entry.diagnostic.clone();
6758 let message_height = diagnostic.message.lines().count() as u8;
6759 BlockProperties {
6760 style: BlockStyle::Fixed,
6761 position: buffer.anchor_after(entry.range.start),
6762 height: message_height,
6763 render: diagnostic_block_renderer(diagnostic, true),
6764 disposition: BlockDisposition::Below,
6765 }
6766 }),
6767 cx,
6768 )
6769 .into_iter()
6770 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6771 .collect();
6772
6773 Some(ActiveDiagnosticGroup {
6774 primary_range,
6775 primary_message,
6776 blocks,
6777 is_valid: true,
6778 })
6779 });
6780 self.active_diagnostics.is_some()
6781 }
6782
6783 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6784 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6785 self.display_map.update(cx, |display_map, cx| {
6786 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6787 });
6788 cx.notify();
6789 }
6790 }
6791
6792 pub fn set_selections_from_remote(
6793 &mut self,
6794 selections: Vec<Selection<Anchor>>,
6795 pending_selection: Option<Selection<Anchor>>,
6796 cx: &mut ViewContext<Self>,
6797 ) {
6798 let old_cursor_position = self.selections.newest_anchor().head();
6799 self.selections.change_with(cx, |s| {
6800 s.select_anchors(selections);
6801 if let Some(pending_selection) = pending_selection {
6802 s.set_pending(pending_selection, SelectMode::Character);
6803 } else {
6804 s.clear_pending();
6805 }
6806 });
6807 self.selections_did_change(false, &old_cursor_position, cx);
6808 }
6809
6810 fn push_to_selection_history(&mut self) {
6811 self.selection_history.push(SelectionHistoryEntry {
6812 selections: self.selections.disjoint_anchors(),
6813 select_next_state: self.select_next_state.clone(),
6814 select_prev_state: self.select_prev_state.clone(),
6815 add_selections_state: self.add_selections_state.clone(),
6816 });
6817 }
6818
6819 pub fn transact(
6820 &mut self,
6821 cx: &mut ViewContext<Self>,
6822 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6823 ) -> Option<TransactionId> {
6824 self.start_transaction_at(Instant::now(), cx);
6825 update(self, cx);
6826 self.end_transaction_at(Instant::now(), cx)
6827 }
6828
6829 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6830 self.end_selection(cx);
6831 if let Some(tx_id) = self
6832 .buffer
6833 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6834 {
6835 self.selection_history
6836 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6837 }
6838 }
6839
6840 fn end_transaction_at(
6841 &mut self,
6842 now: Instant,
6843 cx: &mut ViewContext<Self>,
6844 ) -> Option<TransactionId> {
6845 if let Some(tx_id) = self
6846 .buffer
6847 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6848 {
6849 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6850 *end_selections = Some(self.selections.disjoint_anchors());
6851 } else {
6852 error!("unexpectedly ended a transaction that wasn't started by this editor");
6853 }
6854
6855 cx.emit(Event::Edited);
6856 Some(tx_id)
6857 } else {
6858 None
6859 }
6860 }
6861
6862 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6863 let mut fold_ranges = Vec::new();
6864
6865 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6866
6867 let selections = self.selections.all::<Point>(cx);
6868 for selection in selections {
6869 let range = selection.range().sorted();
6870 let buffer_start_row = range.start.row;
6871
6872 for row in (0..=range.end.row).rev() {
6873 let fold_range = display_map.foldable_range(row);
6874
6875 if let Some(fold_range) = fold_range {
6876 if fold_range.end.row >= buffer_start_row {
6877 fold_ranges.push(fold_range);
6878 if row <= range.start.row {
6879 break;
6880 }
6881 }
6882 }
6883 }
6884 }
6885
6886 self.fold_ranges(fold_ranges, true, cx);
6887 }
6888
6889 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6890 let buffer_row = fold_at.buffer_row;
6891 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6892
6893 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6894 let autoscroll = self
6895 .selections
6896 .all::<Point>(cx)
6897 .iter()
6898 .any(|selection| fold_range.overlaps(&selection.range()));
6899
6900 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6901 }
6902 }
6903
6904 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6905 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6906 let buffer = &display_map.buffer_snapshot;
6907 let selections = self.selections.all::<Point>(cx);
6908 let ranges = selections
6909 .iter()
6910 .map(|s| {
6911 let range = s.display_range(&display_map).sorted();
6912 let mut start = range.start.to_point(&display_map);
6913 let mut end = range.end.to_point(&display_map);
6914 start.column = 0;
6915 end.column = buffer.line_len(end.row);
6916 start..end
6917 })
6918 .collect::<Vec<_>>();
6919
6920 self.unfold_ranges(ranges, true, true, cx);
6921 }
6922
6923 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6924 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6925
6926 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6927 ..Point::new(
6928 unfold_at.buffer_row,
6929 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6930 );
6931
6932 let autoscroll = self
6933 .selections
6934 .all::<Point>(cx)
6935 .iter()
6936 .any(|selection| selection.range().overlaps(&intersection_range));
6937
6938 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6939 }
6940
6941 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6942 let selections = self.selections.all::<Point>(cx);
6943 let ranges = selections.into_iter().map(|s| s.start..s.end);
6944 self.fold_ranges(ranges, true, cx);
6945 }
6946
6947 pub fn fold_ranges<T: ToOffset + Clone>(
6948 &mut self,
6949 ranges: impl IntoIterator<Item = Range<T>>,
6950 auto_scroll: bool,
6951 cx: &mut ViewContext<Self>,
6952 ) {
6953 let mut ranges = ranges.into_iter().peekable();
6954 if ranges.peek().is_some() {
6955 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6956
6957 if auto_scroll {
6958 self.request_autoscroll(Autoscroll::fit(), cx);
6959 }
6960
6961 cx.notify();
6962 }
6963 }
6964
6965 pub fn unfold_ranges<T: ToOffset + Clone>(
6966 &mut self,
6967 ranges: impl IntoIterator<Item = Range<T>>,
6968 inclusive: bool,
6969 auto_scroll: bool,
6970 cx: &mut ViewContext<Self>,
6971 ) {
6972 let mut ranges = ranges.into_iter().peekable();
6973 if ranges.peek().is_some() {
6974 self.display_map
6975 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6976 if auto_scroll {
6977 self.request_autoscroll(Autoscroll::fit(), cx);
6978 }
6979
6980 cx.notify();
6981 }
6982 }
6983
6984 pub fn gutter_hover(
6985 &mut self,
6986 GutterHover { hovered }: &GutterHover,
6987 cx: &mut ViewContext<Self>,
6988 ) {
6989 self.gutter_hovered = *hovered;
6990 cx.notify();
6991 }
6992
6993 pub fn insert_blocks(
6994 &mut self,
6995 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6996 autoscroll: Option<Autoscroll>,
6997 cx: &mut ViewContext<Self>,
6998 ) -> Vec<BlockId> {
6999 let blocks = self
7000 .display_map
7001 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7002 if let Some(autoscroll) = autoscroll {
7003 self.request_autoscroll(autoscroll, cx);
7004 }
7005 blocks
7006 }
7007
7008 pub fn replace_blocks(
7009 &mut self,
7010 blocks: HashMap<BlockId, RenderBlock>,
7011 autoscroll: Option<Autoscroll>,
7012 cx: &mut ViewContext<Self>,
7013 ) {
7014 self.display_map
7015 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7016 if let Some(autoscroll) = autoscroll {
7017 self.request_autoscroll(autoscroll, cx);
7018 }
7019 }
7020
7021 pub fn remove_blocks(
7022 &mut self,
7023 block_ids: HashSet<BlockId>,
7024 autoscroll: Option<Autoscroll>,
7025 cx: &mut ViewContext<Self>,
7026 ) {
7027 self.display_map.update(cx, |display_map, cx| {
7028 display_map.remove_blocks(block_ids, cx)
7029 });
7030 if let Some(autoscroll) = autoscroll {
7031 self.request_autoscroll(autoscroll, cx);
7032 }
7033 }
7034
7035 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7036 self.display_map
7037 .update(cx, |map, cx| map.snapshot(cx))
7038 .longest_row()
7039 }
7040
7041 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7042 self.display_map
7043 .update(cx, |map, cx| map.snapshot(cx))
7044 .max_point()
7045 }
7046
7047 pub fn text(&self, cx: &AppContext) -> String {
7048 self.buffer.read(cx).read(cx).text()
7049 }
7050
7051 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7052 self.transact(cx, |this, cx| {
7053 this.buffer
7054 .read(cx)
7055 .as_singleton()
7056 .expect("you can only call set_text on editors for singleton buffers")
7057 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7058 });
7059 }
7060
7061 pub fn display_text(&self, cx: &mut AppContext) -> String {
7062 self.display_map
7063 .update(cx, |map, cx| map.snapshot(cx))
7064 .text()
7065 }
7066
7067 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7068 let settings = self.buffer.read(cx).settings_at(0, cx);
7069 let mode = self
7070 .soft_wrap_mode_override
7071 .unwrap_or_else(|| settings.soft_wrap);
7072 match mode {
7073 language_settings::SoftWrap::None => SoftWrap::None,
7074 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7075 language_settings::SoftWrap::PreferredLineLength => {
7076 SoftWrap::Column(settings.preferred_line_length)
7077 }
7078 }
7079 }
7080
7081 pub fn set_soft_wrap_mode(
7082 &mut self,
7083 mode: language_settings::SoftWrap,
7084 cx: &mut ViewContext<Self>,
7085 ) {
7086 self.soft_wrap_mode_override = Some(mode);
7087 cx.notify();
7088 }
7089
7090 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7091 self.display_map
7092 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7093 }
7094
7095 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7096 if self.soft_wrap_mode_override.is_some() {
7097 self.soft_wrap_mode_override.take();
7098 } else {
7099 let soft_wrap = match self.soft_wrap_mode(cx) {
7100 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7101 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7102 };
7103 self.soft_wrap_mode_override = Some(soft_wrap);
7104 }
7105 cx.notify();
7106 }
7107
7108 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7109 self.show_gutter = show_gutter;
7110 cx.notify();
7111 }
7112
7113 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7114 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7115 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7116 cx.reveal_path(&file.abs_path(cx));
7117 }
7118 }
7119 }
7120
7121 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7122 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7123 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7124 if let Some(path) = file.abs_path(cx).to_str() {
7125 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7126 }
7127 }
7128 }
7129 }
7130
7131 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7132 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7133 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7134 if let Some(path) = file.path().to_str() {
7135 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7136 }
7137 }
7138 }
7139 }
7140
7141 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7142 self.highlighted_rows = rows;
7143 }
7144
7145 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7146 self.highlighted_rows.clone()
7147 }
7148
7149 pub fn highlight_background<T: 'static>(
7150 &mut self,
7151 ranges: Vec<Range<Anchor>>,
7152 color_fetcher: fn(&Theme) -> Color,
7153 cx: &mut ViewContext<Self>,
7154 ) {
7155 self.background_highlights
7156 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7157 cx.notify();
7158 }
7159
7160 #[allow(clippy::type_complexity)]
7161 pub fn clear_background_highlights<T: 'static>(
7162 &mut self,
7163 cx: &mut ViewContext<Self>,
7164 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
7165 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7166 if highlights.is_some() {
7167 cx.notify();
7168 }
7169 highlights
7170 }
7171
7172 #[cfg(feature = "test-support")]
7173 pub fn all_background_highlights(
7174 &mut self,
7175 cx: &mut ViewContext<Self>,
7176 ) -> Vec<(Range<DisplayPoint>, Color)> {
7177 let snapshot = self.snapshot(cx);
7178 let buffer = &snapshot.buffer_snapshot;
7179 let start = buffer.anchor_before(0);
7180 let end = buffer.anchor_after(buffer.len());
7181 let theme = theme::current(cx);
7182 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7183 }
7184
7185 fn document_highlights_for_position<'a>(
7186 &'a self,
7187 position: Anchor,
7188 buffer: &'a MultiBufferSnapshot,
7189 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7190 let read_highlights = self
7191 .background_highlights
7192 .get(&TypeId::of::<DocumentHighlightRead>())
7193 .map(|h| &h.1);
7194 let write_highlights = self
7195 .background_highlights
7196 .get(&TypeId::of::<DocumentHighlightWrite>())
7197 .map(|h| &h.1);
7198 let left_position = position.bias_left(buffer);
7199 let right_position = position.bias_right(buffer);
7200 read_highlights
7201 .into_iter()
7202 .chain(write_highlights)
7203 .flat_map(move |ranges| {
7204 let start_ix = match ranges.binary_search_by(|probe| {
7205 let cmp = probe.end.cmp(&left_position, buffer);
7206 if cmp.is_ge() {
7207 Ordering::Greater
7208 } else {
7209 Ordering::Less
7210 }
7211 }) {
7212 Ok(i) | Err(i) => i,
7213 };
7214
7215 let right_position = right_position.clone();
7216 ranges[start_ix..]
7217 .iter()
7218 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7219 })
7220 }
7221
7222 pub fn background_highlights_in_range(
7223 &self,
7224 search_range: Range<Anchor>,
7225 display_snapshot: &DisplaySnapshot,
7226 theme: &Theme,
7227 ) -> Vec<(Range<DisplayPoint>, Color)> {
7228 let mut results = Vec::new();
7229 let buffer = &display_snapshot.buffer_snapshot;
7230 for (color_fetcher, ranges) in self.background_highlights.values() {
7231 let color = color_fetcher(theme);
7232 let start_ix = match ranges.binary_search_by(|probe| {
7233 let cmp = probe.end.cmp(&search_range.start, buffer);
7234 if cmp.is_gt() {
7235 Ordering::Greater
7236 } else {
7237 Ordering::Less
7238 }
7239 }) {
7240 Ok(i) | Err(i) => i,
7241 };
7242 for range in &ranges[start_ix..] {
7243 if range.start.cmp(&search_range.end, buffer).is_ge() {
7244 break;
7245 }
7246 let start = range
7247 .start
7248 .to_point(buffer)
7249 .to_display_point(display_snapshot);
7250 let end = range
7251 .end
7252 .to_point(buffer)
7253 .to_display_point(display_snapshot);
7254 results.push((start..end, color))
7255 }
7256 }
7257 results
7258 }
7259 pub fn background_highlights_in_range_for<T: 'static>(
7260 &self,
7261 search_range: Range<Anchor>,
7262 display_snapshot: &DisplaySnapshot,
7263 theme: &Theme,
7264 ) -> Vec<(Range<DisplayPoint>, Color)> {
7265 let mut results = Vec::new();
7266 let buffer = &display_snapshot.buffer_snapshot;
7267 let Some((color_fetcher, ranges)) = self.background_highlights
7268 .get(&TypeId::of::<T>()) else {
7269 return vec![];
7270 };
7271
7272 let color = color_fetcher(theme);
7273 let start_ix = match ranges.binary_search_by(|probe| {
7274 let cmp = probe.end.cmp(&search_range.start, buffer);
7275 if cmp.is_gt() {
7276 Ordering::Greater
7277 } else {
7278 Ordering::Less
7279 }
7280 }) {
7281 Ok(i) | Err(i) => i,
7282 };
7283 for range in &ranges[start_ix..] {
7284 if range.start.cmp(&search_range.end, buffer).is_ge() {
7285 break;
7286 }
7287 let start = range
7288 .start
7289 .to_point(buffer)
7290 .to_display_point(display_snapshot);
7291 let end = range
7292 .end
7293 .to_point(buffer)
7294 .to_display_point(display_snapshot);
7295 results.push((start..end, color))
7296 }
7297
7298 results
7299 }
7300
7301 pub fn highlight_text<T: 'static>(
7302 &mut self,
7303 ranges: Vec<Range<Anchor>>,
7304 style: HighlightStyle,
7305 cx: &mut ViewContext<Self>,
7306 ) {
7307 self.display_map.update(cx, |map, _| {
7308 map.highlight_text(TypeId::of::<T>(), ranges, style)
7309 });
7310 cx.notify();
7311 }
7312
7313 pub fn text_highlights<'a, T: 'static>(
7314 &'a self,
7315 cx: &'a AppContext,
7316 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7317 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7318 }
7319
7320 pub fn clear_text_highlights<T: 'static>(
7321 &mut self,
7322 cx: &mut ViewContext<Self>,
7323 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7324 let highlights = self
7325 .display_map
7326 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7327 if highlights.is_some() {
7328 cx.notify();
7329 }
7330 highlights
7331 }
7332
7333 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7334 self.blink_manager.read(cx).visible() && self.focused
7335 }
7336
7337 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7338 cx.notify();
7339 }
7340
7341 fn on_buffer_event(
7342 &mut self,
7343 multibuffer: ModelHandle<MultiBuffer>,
7344 event: &multi_buffer::Event,
7345 cx: &mut ViewContext<Self>,
7346 ) {
7347 match event {
7348 multi_buffer::Event::Edited => {
7349 self.refresh_active_diagnostics(cx);
7350 self.refresh_code_actions(cx);
7351 if self.has_active_copilot_suggestion(cx) {
7352 self.update_visible_copilot_suggestion(cx);
7353 }
7354 cx.emit(Event::BufferEdited);
7355
7356 if let Some(project) = &self.project {
7357 let project = project.read(cx);
7358 let languages_affected = multibuffer
7359 .read(cx)
7360 .all_buffers()
7361 .into_iter()
7362 .filter_map(|buffer| {
7363 let buffer = buffer.read(cx);
7364 let language = buffer.language()?;
7365 if project.is_local()
7366 && project.language_servers_for_buffer(buffer, cx).count() == 0
7367 {
7368 None
7369 } else {
7370 Some(language)
7371 }
7372 })
7373 .cloned()
7374 .collect::<HashSet<_>>();
7375 if !languages_affected.is_empty() {
7376 self.refresh_inlays(
7377 InlayRefreshReason::BufferEdited(languages_affected),
7378 cx,
7379 );
7380 }
7381 }
7382 }
7383 multi_buffer::Event::ExcerptsAdded {
7384 buffer,
7385 predecessor,
7386 excerpts,
7387 } => cx.emit(Event::ExcerptsAdded {
7388 buffer: buffer.clone(),
7389 predecessor: *predecessor,
7390 excerpts: excerpts.clone(),
7391 }),
7392 multi_buffer::Event::ExcerptsRemoved { ids } => {
7393 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7394 }
7395 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7396 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7397 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7398 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7399 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7400 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7401 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7402 multi_buffer::Event::DiagnosticsUpdated => {
7403 self.refresh_active_diagnostics(cx);
7404 }
7405 _ => {}
7406 };
7407 }
7408
7409 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7410 cx.notify();
7411 }
7412
7413 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7414 self.refresh_copilot_suggestions(true, cx);
7415 self.refresh_inlays(
7416 InlayRefreshReason::SettingsChange(inlay_hint_settings(
7417 self.selections.newest_anchor().head(),
7418 &self.buffer.read(cx).snapshot(cx),
7419 cx,
7420 )),
7421 cx,
7422 );
7423 }
7424
7425 pub fn set_searchable(&mut self, searchable: bool) {
7426 self.searchable = searchable;
7427 }
7428
7429 pub fn searchable(&self) -> bool {
7430 self.searchable
7431 }
7432
7433 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7434 let active_item = workspace.active_item(cx);
7435 let editor_handle = if let Some(editor) = active_item
7436 .as_ref()
7437 .and_then(|item| item.act_as::<Self>(cx))
7438 {
7439 editor
7440 } else {
7441 cx.propagate_action();
7442 return;
7443 };
7444
7445 let editor = editor_handle.read(cx);
7446 let buffer = editor.buffer.read(cx);
7447 if buffer.is_singleton() {
7448 cx.propagate_action();
7449 return;
7450 }
7451
7452 let mut new_selections_by_buffer = HashMap::default();
7453 for selection in editor.selections.all::<usize>(cx) {
7454 for (buffer, mut range, _) in
7455 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7456 {
7457 if selection.reversed {
7458 mem::swap(&mut range.start, &mut range.end);
7459 }
7460 new_selections_by_buffer
7461 .entry(buffer)
7462 .or_insert(Vec::new())
7463 .push(range)
7464 }
7465 }
7466
7467 editor_handle.update(cx, |editor, cx| {
7468 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7469 });
7470 let pane = workspace.active_pane().clone();
7471 pane.update(cx, |pane, _| pane.disable_history());
7472
7473 // We defer the pane interaction because we ourselves are a workspace item
7474 // and activating a new item causes the pane to call a method on us reentrantly,
7475 // which panics if we're on the stack.
7476 cx.defer(move |workspace, cx| {
7477 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7478 let editor = workspace.open_project_item::<Self>(buffer, cx);
7479 editor.update(cx, |editor, cx| {
7480 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7481 s.select_ranges(ranges);
7482 });
7483 });
7484 }
7485
7486 pane.update(cx, |pane, _| pane.enable_history());
7487 });
7488 }
7489
7490 fn jump(
7491 workspace: &mut Workspace,
7492 path: ProjectPath,
7493 position: Point,
7494 anchor: language::Anchor,
7495 cx: &mut ViewContext<Workspace>,
7496 ) {
7497 let editor = workspace.open_path(path, None, true, cx);
7498 cx.spawn(|_, mut cx| async move {
7499 let editor = editor
7500 .await?
7501 .downcast::<Editor>()
7502 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7503 .downgrade();
7504 editor.update(&mut cx, |editor, cx| {
7505 let buffer = editor
7506 .buffer()
7507 .read(cx)
7508 .as_singleton()
7509 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7510 let buffer = buffer.read(cx);
7511 let cursor = if buffer.can_resolve(&anchor) {
7512 language::ToPoint::to_point(&anchor, buffer)
7513 } else {
7514 buffer.clip_point(position, Bias::Left)
7515 };
7516
7517 let nav_history = editor.nav_history.take();
7518 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7519 s.select_ranges([cursor..cursor]);
7520 });
7521 editor.nav_history = nav_history;
7522
7523 anyhow::Ok(())
7524 })??;
7525
7526 anyhow::Ok(())
7527 })
7528 .detach_and_log_err(cx);
7529 }
7530
7531 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7532 let snapshot = self.buffer.read(cx).read(cx);
7533 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7534 Some(
7535 ranges
7536 .iter()
7537 .map(move |range| {
7538 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7539 })
7540 .collect(),
7541 )
7542 }
7543
7544 fn selection_replacement_ranges(
7545 &self,
7546 range: Range<OffsetUtf16>,
7547 cx: &AppContext,
7548 ) -> Vec<Range<OffsetUtf16>> {
7549 let selections = self.selections.all::<OffsetUtf16>(cx);
7550 let newest_selection = selections
7551 .iter()
7552 .max_by_key(|selection| selection.id)
7553 .unwrap();
7554 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7555 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7556 let snapshot = self.buffer.read(cx).read(cx);
7557 selections
7558 .into_iter()
7559 .map(|mut selection| {
7560 selection.start.0 =
7561 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7562 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7563 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7564 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7565 })
7566 .collect()
7567 }
7568
7569 fn report_copilot_event(
7570 &self,
7571 suggestion_id: Option<String>,
7572 suggestion_accepted: bool,
7573 cx: &AppContext,
7574 ) {
7575 let Some(project) = &self.project else {
7576 return
7577 };
7578
7579 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7580 let file_extension = self
7581 .buffer
7582 .read(cx)
7583 .as_singleton()
7584 .and_then(|b| b.read(cx).file())
7585 .and_then(|file| Path::new(file.file_name(cx)).extension())
7586 .and_then(|e| e.to_str())
7587 .map(|a| a.to_string());
7588
7589 let telemetry = project.read(cx).client().telemetry().clone();
7590 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7591
7592 let event = ClickhouseEvent::Copilot {
7593 suggestion_id,
7594 suggestion_accepted,
7595 file_extension,
7596 };
7597 telemetry.report_clickhouse_event(event, telemetry_settings);
7598 }
7599
7600 fn report_editor_event(
7601 &self,
7602 operation: &'static str,
7603 file_extension: Option<String>,
7604 cx: &AppContext,
7605 ) {
7606 let Some(project) = &self.project else {
7607 return
7608 };
7609
7610 // If None, we are in a file without an extension
7611 let file = self
7612 .buffer
7613 .read(cx)
7614 .as_singleton()
7615 .and_then(|b| b.read(cx).file());
7616 let file_extension = file_extension.or(file
7617 .as_ref()
7618 .and_then(|file| Path::new(file.file_name(cx)).extension())
7619 .and_then(|e| e.to_str())
7620 .map(|a| a.to_string()));
7621
7622 let vim_mode = cx
7623 .global::<SettingsStore>()
7624 .raw_user_settings()
7625 .get("vim_mode")
7626 == Some(&serde_json::Value::Bool(true));
7627 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7628 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7629 let copilot_enabled_for_language = self
7630 .buffer
7631 .read(cx)
7632 .settings_at(0, cx)
7633 .show_copilot_suggestions;
7634
7635 let telemetry = project.read(cx).client().telemetry().clone();
7636 let event = ClickhouseEvent::Editor {
7637 file_extension,
7638 vim_mode,
7639 operation,
7640 copilot_enabled,
7641 copilot_enabled_for_language,
7642 };
7643 telemetry.report_clickhouse_event(event, telemetry_settings)
7644 }
7645
7646 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7647 /// with each line being an array of {text, highlight} objects.
7648 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7649 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7650 return;
7651 };
7652
7653 #[derive(Serialize)]
7654 struct Chunk<'a> {
7655 text: String,
7656 highlight: Option<&'a str>,
7657 }
7658
7659 let snapshot = buffer.read(cx).snapshot();
7660 let range = self
7661 .selected_text_range(cx)
7662 .and_then(|selected_range| {
7663 if selected_range.is_empty() {
7664 None
7665 } else {
7666 Some(selected_range)
7667 }
7668 })
7669 .unwrap_or_else(|| 0..snapshot.len());
7670
7671 let chunks = snapshot.chunks(range, true);
7672 let mut lines = Vec::new();
7673 let mut line: VecDeque<Chunk> = VecDeque::new();
7674
7675 let theme = &theme::current(cx).editor.syntax;
7676
7677 for chunk in chunks {
7678 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7679 let mut chunk_lines = chunk.text.split("\n").peekable();
7680 while let Some(text) = chunk_lines.next() {
7681 let mut merged_with_last_token = false;
7682 if let Some(last_token) = line.back_mut() {
7683 if last_token.highlight == highlight {
7684 last_token.text.push_str(text);
7685 merged_with_last_token = true;
7686 }
7687 }
7688
7689 if !merged_with_last_token {
7690 line.push_back(Chunk {
7691 text: text.into(),
7692 highlight,
7693 });
7694 }
7695
7696 if chunk_lines.peek().is_some() {
7697 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7698 line.pop_front();
7699 }
7700 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7701 line.pop_back();
7702 }
7703
7704 lines.push(mem::take(&mut line));
7705 }
7706 }
7707 }
7708
7709 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7710 cx.write_to_clipboard(ClipboardItem::new(lines));
7711 }
7712
7713 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
7714 &self.inlay_hint_cache
7715 }
7716}
7717
7718fn inlay_hint_settings(
7719 location: Anchor,
7720 snapshot: &MultiBufferSnapshot,
7721 cx: &mut ViewContext<'_, '_, Editor>,
7722) -> InlayHintSettings {
7723 let file = snapshot.file_at(location);
7724 let language = snapshot.language_at(location);
7725 let settings = all_language_settings(file, cx);
7726 settings
7727 .language(language.map(|l| l.name()).as_deref())
7728 .inlay_hints
7729}
7730
7731fn consume_contiguous_rows(
7732 contiguous_row_selections: &mut Vec<Selection<Point>>,
7733 selection: &Selection<Point>,
7734 display_map: &DisplaySnapshot,
7735 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7736) -> (u32, u32) {
7737 contiguous_row_selections.push(selection.clone());
7738 let start_row = selection.start.row;
7739 let mut end_row = ending_row(selection, display_map);
7740
7741 while let Some(next_selection) = selections.peek() {
7742 if next_selection.start.row <= end_row {
7743 end_row = ending_row(next_selection, display_map);
7744 contiguous_row_selections.push(selections.next().unwrap().clone());
7745 } else {
7746 break;
7747 }
7748 }
7749 (start_row, end_row)
7750}
7751
7752fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7753 if next_selection.end.column > 0 || next_selection.is_empty() {
7754 display_map.next_line_boundary(next_selection.end).0.row + 1
7755 } else {
7756 next_selection.end.row
7757 }
7758}
7759
7760impl EditorSnapshot {
7761 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7762 self.display_snapshot.buffer_snapshot.language_at(position)
7763 }
7764
7765 pub fn is_focused(&self) -> bool {
7766 self.is_focused
7767 }
7768
7769 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7770 self.placeholder_text.as_ref()
7771 }
7772
7773 pub fn scroll_position(&self) -> Vector2F {
7774 self.scroll_anchor.scroll_position(&self.display_snapshot)
7775 }
7776}
7777
7778impl Deref for EditorSnapshot {
7779 type Target = DisplaySnapshot;
7780
7781 fn deref(&self) -> &Self::Target {
7782 &self.display_snapshot
7783 }
7784}
7785
7786#[derive(Clone, Debug, PartialEq, Eq)]
7787pub enum Event {
7788 InputIgnored {
7789 text: Arc<str>,
7790 },
7791 ExcerptsAdded {
7792 buffer: ModelHandle<Buffer>,
7793 predecessor: ExcerptId,
7794 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7795 },
7796 ExcerptsRemoved {
7797 ids: Vec<ExcerptId>,
7798 },
7799 BufferEdited,
7800 Edited,
7801 Reparsed,
7802 Focused,
7803 Blurred,
7804 DirtyChanged,
7805 Saved,
7806 TitleChanged,
7807 DiffBaseChanged,
7808 SelectionsChanged {
7809 local: bool,
7810 },
7811 ScrollPositionChanged {
7812 local: bool,
7813 autoscroll: bool,
7814 },
7815 Closed,
7816}
7817
7818pub struct EditorFocused(pub ViewHandle<Editor>);
7819pub struct EditorBlurred(pub ViewHandle<Editor>);
7820pub struct EditorReleased(pub WeakViewHandle<Editor>);
7821
7822impl Entity for Editor {
7823 type Event = Event;
7824
7825 fn release(&mut self, cx: &mut AppContext) {
7826 cx.emit_global(EditorReleased(self.handle.clone()));
7827 }
7828}
7829
7830impl View for Editor {
7831 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7832 let style = self.style(cx);
7833 let font_changed = self.display_map.update(cx, |map, cx| {
7834 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7835 map.set_font(style.text.font_id, style.text.font_size, cx)
7836 });
7837
7838 if font_changed {
7839 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7840 hide_hover(editor, cx);
7841 hide_link_definition(editor, cx);
7842 });
7843 }
7844
7845 Stack::new()
7846 .with_child(EditorElement::new(style.clone()))
7847 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7848 .into_any()
7849 }
7850
7851 fn ui_name() -> &'static str {
7852 "Editor"
7853 }
7854
7855 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7856 if cx.is_self_focused() {
7857 let focused_event = EditorFocused(cx.handle());
7858 cx.emit(Event::Focused);
7859 cx.emit_global(focused_event);
7860 }
7861 if let Some(rename) = self.pending_rename.as_ref() {
7862 cx.focus(&rename.editor);
7863 } else {
7864 if !self.focused {
7865 self.blink_manager.update(cx, BlinkManager::enable);
7866 }
7867 self.focused = true;
7868 self.buffer.update(cx, |buffer, cx| {
7869 buffer.finalize_last_transaction(cx);
7870 if self.leader_replica_id.is_none() {
7871 buffer.set_active_selections(
7872 &self.selections.disjoint_anchors(),
7873 self.selections.line_mode,
7874 self.cursor_shape,
7875 cx,
7876 );
7877 }
7878 });
7879 }
7880 }
7881
7882 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7883 let blurred_event = EditorBlurred(cx.handle());
7884 cx.emit_global(blurred_event);
7885 self.focused = false;
7886 self.blink_manager.update(cx, BlinkManager::disable);
7887 self.buffer
7888 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7889 self.hide_context_menu(cx);
7890 hide_hover(self, cx);
7891 cx.emit(Event::Blurred);
7892 cx.notify();
7893 }
7894
7895 fn modifiers_changed(
7896 &mut self,
7897 event: &gpui::platform::ModifiersChangedEvent,
7898 cx: &mut ViewContext<Self>,
7899 ) -> bool {
7900 let pending_selection = self.has_pending_selection();
7901
7902 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7903 if event.cmd && !pending_selection {
7904 let snapshot = self.snapshot(cx);
7905 let kind = if event.shift {
7906 LinkDefinitionKind::Type
7907 } else {
7908 LinkDefinitionKind::Symbol
7909 };
7910
7911 show_link_definition(kind, self, point, snapshot, cx);
7912 return false;
7913 }
7914 }
7915
7916 {
7917 if self.link_go_to_definition_state.symbol_range.is_some()
7918 || !self.link_go_to_definition_state.definitions.is_empty()
7919 {
7920 self.link_go_to_definition_state.symbol_range.take();
7921 self.link_go_to_definition_state.definitions.clear();
7922 cx.notify();
7923 }
7924
7925 self.link_go_to_definition_state.task = None;
7926
7927 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7928 }
7929
7930 false
7931 }
7932
7933 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7934 Self::reset_to_default_keymap_context(keymap);
7935 let mode = match self.mode {
7936 EditorMode::SingleLine => "single_line",
7937 EditorMode::AutoHeight { .. } => "auto_height",
7938 EditorMode::Full => "full",
7939 };
7940 keymap.add_key("mode", mode);
7941 if self.pending_rename.is_some() {
7942 keymap.add_identifier("renaming");
7943 }
7944 match self.context_menu.as_ref() {
7945 Some(ContextMenu::Completions(_)) => {
7946 keymap.add_identifier("menu");
7947 keymap.add_identifier("showing_completions")
7948 }
7949 Some(ContextMenu::CodeActions(_)) => {
7950 keymap.add_identifier("menu");
7951 keymap.add_identifier("showing_code_actions")
7952 }
7953 None => {}
7954 }
7955 for layer in self.keymap_context_layers.values() {
7956 keymap.extend(layer);
7957 }
7958
7959 if let Some(extension) = self
7960 .buffer
7961 .read(cx)
7962 .as_singleton()
7963 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7964 {
7965 keymap.add_key("extension", extension.to_string());
7966 }
7967 }
7968
7969 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7970 Some(
7971 self.buffer
7972 .read(cx)
7973 .read(cx)
7974 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7975 .collect(),
7976 )
7977 }
7978
7979 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7980 // Prevent the IME menu from appearing when holding down an alphabetic key
7981 // while input is disabled.
7982 if !self.input_enabled {
7983 return None;
7984 }
7985
7986 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7987 Some(range.start.0..range.end.0)
7988 }
7989
7990 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7991 let snapshot = self.buffer.read(cx).read(cx);
7992 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7993 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7994 }
7995
7996 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7997 self.clear_text_highlights::<InputComposition>(cx);
7998 self.ime_transaction.take();
7999 }
8000
8001 fn replace_text_in_range(
8002 &mut self,
8003 range_utf16: Option<Range<usize>>,
8004 text: &str,
8005 cx: &mut ViewContext<Self>,
8006 ) {
8007 self.transact(cx, |this, cx| {
8008 if this.input_enabled {
8009 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8010 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8011 Some(this.selection_replacement_ranges(range_utf16, cx))
8012 } else {
8013 this.marked_text_ranges(cx)
8014 };
8015
8016 if let Some(new_selected_ranges) = new_selected_ranges {
8017 this.change_selections(None, cx, |selections| {
8018 selections.select_ranges(new_selected_ranges)
8019 });
8020 }
8021 }
8022
8023 this.handle_input(text, cx);
8024 });
8025
8026 if !self.input_enabled {
8027 return;
8028 }
8029
8030 if let Some(transaction) = self.ime_transaction {
8031 self.buffer.update(cx, |buffer, cx| {
8032 buffer.group_until_transaction(transaction, cx);
8033 });
8034 }
8035
8036 self.unmark_text(cx);
8037 }
8038
8039 fn replace_and_mark_text_in_range(
8040 &mut self,
8041 range_utf16: Option<Range<usize>>,
8042 text: &str,
8043 new_selected_range_utf16: Option<Range<usize>>,
8044 cx: &mut ViewContext<Self>,
8045 ) {
8046 if !self.input_enabled {
8047 return;
8048 }
8049
8050 let transaction = self.transact(cx, |this, cx| {
8051 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8052 let snapshot = this.buffer.read(cx).read(cx);
8053 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8054 for marked_range in &mut marked_ranges {
8055 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8056 marked_range.start.0 += relative_range_utf16.start;
8057 marked_range.start =
8058 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8059 marked_range.end =
8060 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8061 }
8062 }
8063 Some(marked_ranges)
8064 } else if let Some(range_utf16) = range_utf16 {
8065 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8066 Some(this.selection_replacement_ranges(range_utf16, cx))
8067 } else {
8068 None
8069 };
8070
8071 if let Some(ranges) = ranges_to_replace {
8072 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8073 }
8074
8075 let marked_ranges = {
8076 let snapshot = this.buffer.read(cx).read(cx);
8077 this.selections
8078 .disjoint_anchors()
8079 .iter()
8080 .map(|selection| {
8081 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8082 })
8083 .collect::<Vec<_>>()
8084 };
8085
8086 if text.is_empty() {
8087 this.unmark_text(cx);
8088 } else {
8089 this.highlight_text::<InputComposition>(
8090 marked_ranges.clone(),
8091 this.style(cx).composition_mark,
8092 cx,
8093 );
8094 }
8095
8096 this.handle_input(text, cx);
8097
8098 if let Some(new_selected_range) = new_selected_range_utf16 {
8099 let snapshot = this.buffer.read(cx).read(cx);
8100 let new_selected_ranges = marked_ranges
8101 .into_iter()
8102 .map(|marked_range| {
8103 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8104 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8105 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8106 snapshot.clip_offset_utf16(new_start, Bias::Left)
8107 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8108 })
8109 .collect::<Vec<_>>();
8110
8111 drop(snapshot);
8112 this.change_selections(None, cx, |selections| {
8113 selections.select_ranges(new_selected_ranges)
8114 });
8115 }
8116 });
8117
8118 self.ime_transaction = self.ime_transaction.or(transaction);
8119 if let Some(transaction) = self.ime_transaction {
8120 self.buffer.update(cx, |buffer, cx| {
8121 buffer.group_until_transaction(transaction, cx);
8122 });
8123 }
8124
8125 if self.text_highlights::<InputComposition>(cx).is_none() {
8126 self.ime_transaction.take();
8127 }
8128 }
8129}
8130
8131fn build_style(
8132 settings: &ThemeSettings,
8133 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8134 override_text_style: Option<&OverrideTextStyle>,
8135 cx: &AppContext,
8136) -> EditorStyle {
8137 let font_cache = cx.font_cache();
8138 let line_height_scalar = settings.line_height();
8139 let theme_id = settings.theme.meta.id;
8140 let mut theme = settings.theme.editor.clone();
8141 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8142 let field_editor_theme = get_field_editor_theme(&settings.theme);
8143 theme.text_color = field_editor_theme.text.color;
8144 theme.selection = field_editor_theme.selection;
8145 theme.background = field_editor_theme
8146 .container
8147 .background_color
8148 .unwrap_or_default();
8149 EditorStyle {
8150 text: field_editor_theme.text,
8151 placeholder_text: field_editor_theme.placeholder_text,
8152 line_height_scalar,
8153 theme,
8154 theme_id,
8155 }
8156 } else {
8157 let font_family_id = settings.buffer_font_family;
8158 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8159 let font_properties = Default::default();
8160 let font_id = font_cache
8161 .select_font(font_family_id, &font_properties)
8162 .unwrap();
8163 let font_size = settings.buffer_font_size(cx);
8164 EditorStyle {
8165 text: TextStyle {
8166 color: settings.theme.editor.text_color,
8167 font_family_name,
8168 font_family_id,
8169 font_id,
8170 font_size,
8171 font_properties,
8172 underline: Default::default(),
8173 },
8174 placeholder_text: None,
8175 line_height_scalar,
8176 theme,
8177 theme_id,
8178 }
8179 };
8180
8181 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8182 if let Some(highlighted) = style
8183 .text
8184 .clone()
8185 .highlight(highlight_style, font_cache)
8186 .log_err()
8187 {
8188 style.text = highlighted;
8189 }
8190 }
8191
8192 style
8193}
8194
8195trait SelectionExt {
8196 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8197 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8198 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8199 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8200 -> Range<u32>;
8201}
8202
8203impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8204 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8205 let start = self.start.to_point(buffer);
8206 let end = self.end.to_point(buffer);
8207 if self.reversed {
8208 end..start
8209 } else {
8210 start..end
8211 }
8212 }
8213
8214 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8215 let start = self.start.to_offset(buffer);
8216 let end = self.end.to_offset(buffer);
8217 if self.reversed {
8218 end..start
8219 } else {
8220 start..end
8221 }
8222 }
8223
8224 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8225 let start = self
8226 .start
8227 .to_point(&map.buffer_snapshot)
8228 .to_display_point(map);
8229 let end = self
8230 .end
8231 .to_point(&map.buffer_snapshot)
8232 .to_display_point(map);
8233 if self.reversed {
8234 end..start
8235 } else {
8236 start..end
8237 }
8238 }
8239
8240 fn spanned_rows(
8241 &self,
8242 include_end_if_at_line_start: bool,
8243 map: &DisplaySnapshot,
8244 ) -> Range<u32> {
8245 let start = self.start.to_point(&map.buffer_snapshot);
8246 let mut end = self.end.to_point(&map.buffer_snapshot);
8247 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8248 end.row -= 1;
8249 }
8250
8251 let buffer_start = map.prev_line_boundary(start).0;
8252 let buffer_end = map.next_line_boundary(end).0;
8253 buffer_start.row..buffer_end.row + 1
8254 }
8255}
8256
8257impl<T: InvalidationRegion> InvalidationStack<T> {
8258 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8259 where
8260 S: Clone + ToOffset,
8261 {
8262 while let Some(region) = self.last() {
8263 let all_selections_inside_invalidation_ranges =
8264 if selections.len() == region.ranges().len() {
8265 selections
8266 .iter()
8267 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8268 .all(|(selection, invalidation_range)| {
8269 let head = selection.head().to_offset(buffer);
8270 invalidation_range.start <= head && invalidation_range.end >= head
8271 })
8272 } else {
8273 false
8274 };
8275
8276 if all_selections_inside_invalidation_ranges {
8277 break;
8278 } else {
8279 self.pop();
8280 }
8281 }
8282 }
8283}
8284
8285impl<T> Default for InvalidationStack<T> {
8286 fn default() -> Self {
8287 Self(Default::default())
8288 }
8289}
8290
8291impl<T> Deref for InvalidationStack<T> {
8292 type Target = Vec<T>;
8293
8294 fn deref(&self) -> &Self::Target {
8295 &self.0
8296 }
8297}
8298
8299impl<T> DerefMut for InvalidationStack<T> {
8300 fn deref_mut(&mut self) -> &mut Self::Target {
8301 &mut self.0
8302 }
8303}
8304
8305impl InvalidationRegion for SnippetState {
8306 fn ranges(&self) -> &[Range<Anchor>] {
8307 &self.ranges[self.active_index]
8308 }
8309}
8310
8311impl Deref for EditorStyle {
8312 type Target = theme::Editor;
8313
8314 fn deref(&self) -> &Self::Target {
8315 &self.theme
8316 }
8317}
8318
8319pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8320 let mut highlighted_lines = Vec::new();
8321
8322 for (index, line) in diagnostic.message.lines().enumerate() {
8323 let line = match &diagnostic.source {
8324 Some(source) if index == 0 => {
8325 let source_highlight = Vec::from_iter(0..source.len());
8326 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8327 }
8328
8329 _ => highlight_diagnostic_message(Vec::new(), line),
8330 };
8331 highlighted_lines.push(line);
8332 }
8333 let message = diagnostic.message;
8334 Arc::new(move |cx: &mut BlockContext| {
8335 let message = message.clone();
8336 let settings = settings::get::<ThemeSettings>(cx);
8337 let tooltip_style = settings.theme.tooltip.clone();
8338 let theme = &settings.theme.editor;
8339 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8340 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8341 let anchor_x = cx.anchor_x;
8342 enum BlockContextToolip {}
8343 MouseEventHandler::<BlockContext, _>::new(cx.block_id, cx, |_, _| {
8344 Flex::column()
8345 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8346 Label::new(
8347 line.clone(),
8348 style.message.clone().with_font_size(font_size),
8349 )
8350 .with_highlights(highlights.clone())
8351 .contained()
8352 .with_margin_left(anchor_x)
8353 }))
8354 .aligned()
8355 .left()
8356 .into_any()
8357 })
8358 .with_cursor_style(CursorStyle::PointingHand)
8359 .on_click(MouseButton::Left, move |_, _, cx| {
8360 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8361 })
8362 // We really need to rethink this ID system...
8363 .with_tooltip::<BlockContextToolip>(
8364 cx.block_id,
8365 "Copy diagnostic message".to_string(),
8366 None,
8367 tooltip_style,
8368 cx,
8369 )
8370 .into_any()
8371 })
8372}
8373
8374pub fn highlight_diagnostic_message(
8375 initial_highlights: Vec<usize>,
8376 message: &str,
8377) -> (String, Vec<usize>) {
8378 let mut message_without_backticks = String::new();
8379 let mut prev_offset = 0;
8380 let mut inside_block = false;
8381 let mut highlights = initial_highlights;
8382 for (match_ix, (offset, _)) in message
8383 .match_indices('`')
8384 .chain([(message.len(), "")])
8385 .enumerate()
8386 {
8387 message_without_backticks.push_str(&message[prev_offset..offset]);
8388 if inside_block {
8389 highlights.extend(prev_offset - match_ix..offset - match_ix);
8390 }
8391
8392 inside_block = !inside_block;
8393 prev_offset = offset + 1;
8394 }
8395
8396 (message_without_backticks, highlights)
8397}
8398
8399pub fn diagnostic_style(
8400 severity: DiagnosticSeverity,
8401 valid: bool,
8402 theme: &theme::Editor,
8403) -> DiagnosticStyle {
8404 match (severity, valid) {
8405 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8406 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8407 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8408 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8409 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8410 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8411 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8412 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8413 _ => theme.invalid_hint_diagnostic.clone(),
8414 }
8415}
8416
8417pub fn combine_syntax_and_fuzzy_match_highlights(
8418 text: &str,
8419 default_style: HighlightStyle,
8420 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8421 match_indices: &[usize],
8422) -> Vec<(Range<usize>, HighlightStyle)> {
8423 let mut result = Vec::new();
8424 let mut match_indices = match_indices.iter().copied().peekable();
8425
8426 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8427 {
8428 syntax_highlight.weight = None;
8429
8430 // Add highlights for any fuzzy match characters before the next
8431 // syntax highlight range.
8432 while let Some(&match_index) = match_indices.peek() {
8433 if match_index >= range.start {
8434 break;
8435 }
8436 match_indices.next();
8437 let end_index = char_ix_after(match_index, text);
8438 let mut match_style = default_style;
8439 match_style.weight = Some(fonts::Weight::BOLD);
8440 result.push((match_index..end_index, match_style));
8441 }
8442
8443 if range.start == usize::MAX {
8444 break;
8445 }
8446
8447 // Add highlights for any fuzzy match characters within the
8448 // syntax highlight range.
8449 let mut offset = range.start;
8450 while let Some(&match_index) = match_indices.peek() {
8451 if match_index >= range.end {
8452 break;
8453 }
8454
8455 match_indices.next();
8456 if match_index > offset {
8457 result.push((offset..match_index, syntax_highlight));
8458 }
8459
8460 let mut end_index = char_ix_after(match_index, text);
8461 while let Some(&next_match_index) = match_indices.peek() {
8462 if next_match_index == end_index && next_match_index < range.end {
8463 end_index = char_ix_after(next_match_index, text);
8464 match_indices.next();
8465 } else {
8466 break;
8467 }
8468 }
8469
8470 let mut match_style = syntax_highlight;
8471 match_style.weight = Some(fonts::Weight::BOLD);
8472 result.push((match_index..end_index, match_style));
8473 offset = end_index;
8474 }
8475
8476 if offset < range.end {
8477 result.push((offset..range.end, syntax_highlight));
8478 }
8479 }
8480
8481 fn char_ix_after(ix: usize, text: &str) -> usize {
8482 ix + text[ix..].chars().next().unwrap().len_utf8()
8483 }
8484
8485 result
8486}
8487
8488pub fn styled_runs_for_code_label<'a>(
8489 label: &'a CodeLabel,
8490 syntax_theme: &'a theme::SyntaxTheme,
8491) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8492 let fade_out = HighlightStyle {
8493 fade_out: Some(0.35),
8494 ..Default::default()
8495 };
8496
8497 let mut prev_end = label.filter_range.end;
8498 label
8499 .runs
8500 .iter()
8501 .enumerate()
8502 .flat_map(move |(ix, (range, highlight_id))| {
8503 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8504 style
8505 } else {
8506 return Default::default();
8507 };
8508 let mut muted_style = style;
8509 muted_style.highlight(fade_out);
8510
8511 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8512 if range.start >= label.filter_range.end {
8513 if range.start > prev_end {
8514 runs.push((prev_end..range.start, fade_out));
8515 }
8516 runs.push((range.clone(), muted_style));
8517 } else if range.end <= label.filter_range.end {
8518 runs.push((range.clone(), style));
8519 } else {
8520 runs.push((range.start..label.filter_range.end, style));
8521 runs.push((label.filter_range.end..range.end, muted_style));
8522 }
8523 prev_end = cmp::max(prev_end, range.end);
8524
8525 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8526 runs.push((prev_end..label.text.len(), fade_out));
8527 }
8528
8529 runs
8530 })
8531}
8532
8533pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8534 let mut index = 0;
8535 let mut codepoints = text.char_indices().peekable();
8536
8537 std::iter::from_fn(move || {
8538 let start_index = index;
8539 while let Some((new_index, codepoint)) = codepoints.next() {
8540 index = new_index + codepoint.len_utf8();
8541 let current_upper = codepoint.is_uppercase();
8542 let next_upper = codepoints
8543 .peek()
8544 .map(|(_, c)| c.is_uppercase())
8545 .unwrap_or(false);
8546
8547 if !current_upper && next_upper {
8548 return Some(&text[start_index..index]);
8549 }
8550 }
8551
8552 index = text.len();
8553 if start_index < text.len() {
8554 return Some(&text[start_index..]);
8555 }
8556 None
8557 })
8558 .flat_map(|word| word.split_inclusive('_'))
8559}
8560
8561trait RangeToAnchorExt {
8562 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8563}
8564
8565impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8566 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8567 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8568 }
8569}