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