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 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 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6277 let range = self.range_for_match(&range);
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 let range = target_editor.range_for_match(&range);
6295 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6296 s.select_ranges([range]);
6297 });
6298 pane.update(cx, |pane, _| pane.enable_history());
6299 });
6300 });
6301 }
6302 } else if !definitions.is_empty() {
6303 let replica_id = self.replica_id(cx);
6304 cx.window_context().defer(move |cx| {
6305 let title = definitions
6306 .iter()
6307 .find(|definition| definition.origin.is_some())
6308 .and_then(|definition| {
6309 definition.origin.as_ref().map(|origin| {
6310 let buffer = origin.buffer.read(cx);
6311 format!(
6312 "Definitions for {}",
6313 buffer
6314 .text_for_range(origin.range.clone())
6315 .collect::<String>()
6316 )
6317 })
6318 })
6319 .unwrap_or("Definitions".to_owned());
6320 let locations = definitions
6321 .into_iter()
6322 .map(|definition| definition.target)
6323 .collect();
6324 workspace.update(cx, |workspace, cx| {
6325 Self::open_locations_in_multibuffer(
6326 workspace, locations, replica_id, title, split, cx,
6327 )
6328 });
6329 });
6330 }
6331 }
6332
6333 pub fn find_all_references(
6334 workspace: &mut Workspace,
6335 _: &FindAllReferences,
6336 cx: &mut ViewContext<Workspace>,
6337 ) -> Option<Task<Result<()>>> {
6338 let active_item = workspace.active_item(cx)?;
6339 let editor_handle = active_item.act_as::<Self>(cx)?;
6340
6341 let editor = editor_handle.read(cx);
6342 let buffer = editor.buffer.read(cx);
6343 let head = editor.selections.newest::<usize>(cx).head();
6344 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6345 let replica_id = editor.replica_id(cx);
6346
6347 let project = workspace.project().clone();
6348 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6349 Some(cx.spawn_labeled(
6350 "Finding All References...",
6351 |workspace, mut cx| async move {
6352 let locations = references.await?;
6353 if locations.is_empty() {
6354 return Ok(());
6355 }
6356
6357 workspace.update(&mut cx, |workspace, cx| {
6358 let title = locations
6359 .first()
6360 .as_ref()
6361 .map(|location| {
6362 let buffer = location.buffer.read(cx);
6363 format!(
6364 "References to `{}`",
6365 buffer
6366 .text_for_range(location.range.clone())
6367 .collect::<String>()
6368 )
6369 })
6370 .unwrap();
6371 Self::open_locations_in_multibuffer(
6372 workspace, locations, replica_id, title, false, cx,
6373 );
6374 })?;
6375
6376 Ok(())
6377 },
6378 ))
6379 }
6380
6381 /// Opens a multibuffer with the given project locations in it
6382 pub fn open_locations_in_multibuffer(
6383 workspace: &mut Workspace,
6384 mut locations: Vec<Location>,
6385 replica_id: ReplicaId,
6386 title: String,
6387 split: bool,
6388 cx: &mut ViewContext<Workspace>,
6389 ) {
6390 // If there are multiple definitions, open them in a multibuffer
6391 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6392 let mut locations = locations.into_iter().peekable();
6393 let mut ranges_to_highlight = Vec::new();
6394
6395 let excerpt_buffer = cx.add_model(|cx| {
6396 let mut multibuffer = MultiBuffer::new(replica_id);
6397 while let Some(location) = locations.next() {
6398 let buffer = location.buffer.read(cx);
6399 let mut ranges_for_buffer = Vec::new();
6400 let range = location.range.to_offset(buffer);
6401 ranges_for_buffer.push(range.clone());
6402
6403 while let Some(next_location) = locations.peek() {
6404 if next_location.buffer == location.buffer {
6405 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6406 locations.next();
6407 } else {
6408 break;
6409 }
6410 }
6411
6412 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6413 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6414 location.buffer.clone(),
6415 ranges_for_buffer,
6416 1,
6417 cx,
6418 ))
6419 }
6420
6421 multibuffer.with_title(title)
6422 });
6423
6424 let editor = cx.add_view(|cx| {
6425 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6426 });
6427 editor.update(cx, |editor, cx| {
6428 editor.highlight_background::<Self>(
6429 ranges_to_highlight,
6430 |theme| theme.editor.highlighted_line_background,
6431 cx,
6432 );
6433 });
6434 if split {
6435 workspace.split_item(Box::new(editor), cx);
6436 } else {
6437 workspace.add_item(Box::new(editor), cx);
6438 }
6439 }
6440
6441 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6442 use language::ToOffset as _;
6443
6444 let project = self.project.clone()?;
6445 let selection = self.selections.newest_anchor().clone();
6446 let (cursor_buffer, cursor_buffer_position) = self
6447 .buffer
6448 .read(cx)
6449 .text_anchor_for_position(selection.head(), cx)?;
6450 let (tail_buffer, _) = self
6451 .buffer
6452 .read(cx)
6453 .text_anchor_for_position(selection.tail(), cx)?;
6454 if tail_buffer != cursor_buffer {
6455 return None;
6456 }
6457
6458 let snapshot = cursor_buffer.read(cx).snapshot();
6459 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6460 let prepare_rename = project.update(cx, |project, cx| {
6461 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6462 });
6463
6464 Some(cx.spawn(|this, mut cx| async move {
6465 let rename_range = if let Some(range) = prepare_rename.await? {
6466 Some(range)
6467 } else {
6468 this.read_with(&cx, |this, cx| {
6469 let buffer = this.buffer.read(cx).snapshot(cx);
6470 let mut buffer_highlights = this
6471 .document_highlights_for_position(selection.head(), &buffer)
6472 .filter(|highlight| {
6473 highlight.start.excerpt_id() == selection.head().excerpt_id()
6474 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6475 });
6476 buffer_highlights
6477 .next()
6478 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6479 })?
6480 };
6481 if let Some(rename_range) = rename_range {
6482 let rename_buffer_range = rename_range.to_offset(&snapshot);
6483 let cursor_offset_in_rename_range =
6484 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6485
6486 this.update(&mut cx, |this, cx| {
6487 this.take_rename(false, cx);
6488 let style = this.style(cx);
6489 let buffer = this.buffer.read(cx).read(cx);
6490 let cursor_offset = selection.head().to_offset(&buffer);
6491 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6492 let rename_end = rename_start + rename_buffer_range.len();
6493 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6494 let mut old_highlight_id = None;
6495 let old_name: Arc<str> = buffer
6496 .chunks(rename_start..rename_end, true)
6497 .map(|chunk| {
6498 if old_highlight_id.is_none() {
6499 old_highlight_id = chunk.syntax_highlight_id;
6500 }
6501 chunk.text
6502 })
6503 .collect::<String>()
6504 .into();
6505
6506 drop(buffer);
6507
6508 // Position the selection in the rename editor so that it matches the current selection.
6509 this.show_local_selections = false;
6510 let rename_editor = cx.add_view(|cx| {
6511 let mut editor = Editor::single_line(None, cx);
6512 if let Some(old_highlight_id) = old_highlight_id {
6513 editor.override_text_style =
6514 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6515 }
6516 editor.buffer.update(cx, |buffer, cx| {
6517 buffer.edit([(0..0, old_name.clone())], None, cx)
6518 });
6519 editor.select_all(&SelectAll, cx);
6520 editor
6521 });
6522
6523 let ranges = this
6524 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6525 .into_iter()
6526 .flat_map(|(_, ranges)| ranges)
6527 .chain(
6528 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6529 .into_iter()
6530 .flat_map(|(_, ranges)| ranges),
6531 )
6532 .collect();
6533
6534 this.highlight_text::<Rename>(
6535 ranges,
6536 HighlightStyle {
6537 fade_out: Some(style.rename_fade),
6538 ..Default::default()
6539 },
6540 cx,
6541 );
6542 cx.focus(&rename_editor);
6543 let block_id = this.insert_blocks(
6544 [BlockProperties {
6545 style: BlockStyle::Flex,
6546 position: range.start.clone(),
6547 height: 1,
6548 render: Arc::new({
6549 let editor = rename_editor.clone();
6550 move |cx: &mut BlockContext| {
6551 ChildView::new(&editor, cx)
6552 .contained()
6553 .with_padding_left(cx.anchor_x)
6554 .into_any()
6555 }
6556 }),
6557 disposition: BlockDisposition::Below,
6558 }],
6559 Some(Autoscroll::fit()),
6560 cx,
6561 )[0];
6562 this.pending_rename = Some(RenameState {
6563 range,
6564 old_name,
6565 editor: rename_editor,
6566 block_id,
6567 });
6568 })?;
6569 }
6570
6571 Ok(())
6572 }))
6573 }
6574
6575 pub fn confirm_rename(
6576 workspace: &mut Workspace,
6577 _: &ConfirmRename,
6578 cx: &mut ViewContext<Workspace>,
6579 ) -> Option<Task<Result<()>>> {
6580 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6581
6582 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6583 let rename = editor.take_rename(false, cx)?;
6584 let buffer = editor.buffer.read(cx);
6585 let (start_buffer, start) =
6586 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6587 let (end_buffer, end) =
6588 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6589 if start_buffer == end_buffer {
6590 let new_name = rename.editor.read(cx).text(cx);
6591 Some((start_buffer, start..end, rename.old_name, new_name))
6592 } else {
6593 None
6594 }
6595 })?;
6596
6597 let rename = workspace.project().clone().update(cx, |project, cx| {
6598 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6599 });
6600
6601 let editor = editor.downgrade();
6602 Some(cx.spawn(|workspace, mut cx| async move {
6603 let project_transaction = rename.await?;
6604 Self::open_project_transaction(
6605 &editor,
6606 workspace,
6607 project_transaction,
6608 format!("Rename: {} → {}", old_name, new_name),
6609 cx.clone(),
6610 )
6611 .await?;
6612
6613 editor.update(&mut cx, |editor, cx| {
6614 editor.refresh_document_highlights(cx);
6615 })?;
6616 Ok(())
6617 }))
6618 }
6619
6620 fn take_rename(
6621 &mut self,
6622 moving_cursor: bool,
6623 cx: &mut ViewContext<Self>,
6624 ) -> Option<RenameState> {
6625 let rename = self.pending_rename.take()?;
6626 self.remove_blocks(
6627 [rename.block_id].into_iter().collect(),
6628 Some(Autoscroll::fit()),
6629 cx,
6630 );
6631 self.clear_text_highlights::<Rename>(cx);
6632 self.show_local_selections = true;
6633
6634 if moving_cursor {
6635 let rename_editor = rename.editor.read(cx);
6636 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6637
6638 // Update the selection to match the position of the selection inside
6639 // the rename editor.
6640 let snapshot = self.buffer.read(cx).read(cx);
6641 let rename_range = rename.range.to_offset(&snapshot);
6642 let cursor_in_editor = snapshot
6643 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6644 .min(rename_range.end);
6645 drop(snapshot);
6646
6647 self.change_selections(None, cx, |s| {
6648 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6649 });
6650 } else {
6651 self.refresh_document_highlights(cx);
6652 }
6653
6654 Some(rename)
6655 }
6656
6657 #[cfg(any(test, feature = "test-support"))]
6658 pub fn pending_rename(&self) -> Option<&RenameState> {
6659 self.pending_rename.as_ref()
6660 }
6661
6662 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6663 let project = match &self.project {
6664 Some(project) => project.clone(),
6665 None => return None,
6666 };
6667
6668 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6669 }
6670
6671 fn perform_format(
6672 &mut self,
6673 project: ModelHandle<Project>,
6674 trigger: FormatTrigger,
6675 cx: &mut ViewContext<Self>,
6676 ) -> Task<Result<()>> {
6677 let buffer = self.buffer().clone();
6678 let buffers = buffer.read(cx).all_buffers();
6679
6680 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6681 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6682
6683 cx.spawn(|_, mut cx| async move {
6684 let transaction = futures::select_biased! {
6685 _ = timeout => {
6686 log::warn!("timed out waiting for formatting");
6687 None
6688 }
6689 transaction = format.log_err().fuse() => transaction,
6690 };
6691
6692 buffer.update(&mut cx, |buffer, cx| {
6693 if let Some(transaction) = transaction {
6694 if !buffer.is_singleton() {
6695 buffer.push_transaction(&transaction.0, cx);
6696 }
6697 }
6698
6699 cx.notify();
6700 });
6701
6702 Ok(())
6703 })
6704 }
6705
6706 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6707 if let Some(project) = self.project.clone() {
6708 self.buffer.update(cx, |multi_buffer, cx| {
6709 project.update(cx, |project, cx| {
6710 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6711 });
6712 })
6713 }
6714 }
6715
6716 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6717 cx.show_character_palette();
6718 }
6719
6720 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6721 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6722 let buffer = self.buffer.read(cx).snapshot(cx);
6723 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6724 let is_valid = buffer
6725 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6726 .any(|entry| {
6727 entry.diagnostic.is_primary
6728 && !entry.range.is_empty()
6729 && entry.range.start == primary_range_start
6730 && entry.diagnostic.message == active_diagnostics.primary_message
6731 });
6732
6733 if is_valid != active_diagnostics.is_valid {
6734 active_diagnostics.is_valid = is_valid;
6735 let mut new_styles = HashMap::default();
6736 for (block_id, diagnostic) in &active_diagnostics.blocks {
6737 new_styles.insert(
6738 *block_id,
6739 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6740 );
6741 }
6742 self.display_map
6743 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6744 }
6745 }
6746 }
6747
6748 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6749 self.dismiss_diagnostics(cx);
6750 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6751 let buffer = self.buffer.read(cx).snapshot(cx);
6752
6753 let mut primary_range = None;
6754 let mut primary_message = None;
6755 let mut group_end = Point::zero();
6756 let diagnostic_group = buffer
6757 .diagnostic_group::<Point>(group_id)
6758 .map(|entry| {
6759 if entry.range.end > group_end {
6760 group_end = entry.range.end;
6761 }
6762 if entry.diagnostic.is_primary {
6763 primary_range = Some(entry.range.clone());
6764 primary_message = Some(entry.diagnostic.message.clone());
6765 }
6766 entry
6767 })
6768 .collect::<Vec<_>>();
6769 let primary_range = primary_range?;
6770 let primary_message = primary_message?;
6771 let primary_range =
6772 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6773
6774 let blocks = display_map
6775 .insert_blocks(
6776 diagnostic_group.iter().map(|entry| {
6777 let diagnostic = entry.diagnostic.clone();
6778 let message_height = diagnostic.message.lines().count() as u8;
6779 BlockProperties {
6780 style: BlockStyle::Fixed,
6781 position: buffer.anchor_after(entry.range.start),
6782 height: message_height,
6783 render: diagnostic_block_renderer(diagnostic, true),
6784 disposition: BlockDisposition::Below,
6785 }
6786 }),
6787 cx,
6788 )
6789 .into_iter()
6790 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6791 .collect();
6792
6793 Some(ActiveDiagnosticGroup {
6794 primary_range,
6795 primary_message,
6796 blocks,
6797 is_valid: true,
6798 })
6799 });
6800 self.active_diagnostics.is_some()
6801 }
6802
6803 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6804 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6805 self.display_map.update(cx, |display_map, cx| {
6806 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6807 });
6808 cx.notify();
6809 }
6810 }
6811
6812 pub fn set_selections_from_remote(
6813 &mut self,
6814 selections: Vec<Selection<Anchor>>,
6815 pending_selection: Option<Selection<Anchor>>,
6816 cx: &mut ViewContext<Self>,
6817 ) {
6818 let old_cursor_position = self.selections.newest_anchor().head();
6819 self.selections.change_with(cx, |s| {
6820 s.select_anchors(selections);
6821 if let Some(pending_selection) = pending_selection {
6822 s.set_pending(pending_selection, SelectMode::Character);
6823 } else {
6824 s.clear_pending();
6825 }
6826 });
6827 self.selections_did_change(false, &old_cursor_position, cx);
6828 }
6829
6830 fn push_to_selection_history(&mut self) {
6831 self.selection_history.push(SelectionHistoryEntry {
6832 selections: self.selections.disjoint_anchors(),
6833 select_next_state: self.select_next_state.clone(),
6834 select_prev_state: self.select_prev_state.clone(),
6835 add_selections_state: self.add_selections_state.clone(),
6836 });
6837 }
6838
6839 pub fn transact(
6840 &mut self,
6841 cx: &mut ViewContext<Self>,
6842 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6843 ) -> Option<TransactionId> {
6844 self.start_transaction_at(Instant::now(), cx);
6845 update(self, cx);
6846 self.end_transaction_at(Instant::now(), cx)
6847 }
6848
6849 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6850 self.end_selection(cx);
6851 if let Some(tx_id) = self
6852 .buffer
6853 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6854 {
6855 self.selection_history
6856 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6857 }
6858 }
6859
6860 fn end_transaction_at(
6861 &mut self,
6862 now: Instant,
6863 cx: &mut ViewContext<Self>,
6864 ) -> Option<TransactionId> {
6865 if let Some(tx_id) = self
6866 .buffer
6867 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6868 {
6869 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6870 *end_selections = Some(self.selections.disjoint_anchors());
6871 } else {
6872 error!("unexpectedly ended a transaction that wasn't started by this editor");
6873 }
6874
6875 cx.emit(Event::Edited);
6876 Some(tx_id)
6877 } else {
6878 None
6879 }
6880 }
6881
6882 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6883 let mut fold_ranges = Vec::new();
6884
6885 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6886
6887 let selections = self.selections.all::<Point>(cx);
6888 for selection in selections {
6889 let range = selection.range().sorted();
6890 let buffer_start_row = range.start.row;
6891
6892 for row in (0..=range.end.row).rev() {
6893 let fold_range = display_map.foldable_range(row);
6894
6895 if let Some(fold_range) = fold_range {
6896 if fold_range.end.row >= buffer_start_row {
6897 fold_ranges.push(fold_range);
6898 if row <= range.start.row {
6899 break;
6900 }
6901 }
6902 }
6903 }
6904 }
6905
6906 self.fold_ranges(fold_ranges, true, cx);
6907 }
6908
6909 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6910 let buffer_row = fold_at.buffer_row;
6911 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6912
6913 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6914 let autoscroll = self
6915 .selections
6916 .all::<Point>(cx)
6917 .iter()
6918 .any(|selection| fold_range.overlaps(&selection.range()));
6919
6920 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6921 }
6922 }
6923
6924 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6925 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6926 let buffer = &display_map.buffer_snapshot;
6927 let selections = self.selections.all::<Point>(cx);
6928 let ranges = selections
6929 .iter()
6930 .map(|s| {
6931 let range = s.display_range(&display_map).sorted();
6932 let mut start = range.start.to_point(&display_map);
6933 let mut end = range.end.to_point(&display_map);
6934 start.column = 0;
6935 end.column = buffer.line_len(end.row);
6936 start..end
6937 })
6938 .collect::<Vec<_>>();
6939
6940 self.unfold_ranges(ranges, true, true, cx);
6941 }
6942
6943 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6944 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6945
6946 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6947 ..Point::new(
6948 unfold_at.buffer_row,
6949 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6950 );
6951
6952 let autoscroll = self
6953 .selections
6954 .all::<Point>(cx)
6955 .iter()
6956 .any(|selection| selection.range().overlaps(&intersection_range));
6957
6958 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6959 }
6960
6961 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6962 let selections = self.selections.all::<Point>(cx);
6963 let ranges = selections.into_iter().map(|s| s.start..s.end);
6964 self.fold_ranges(ranges, true, cx);
6965 }
6966
6967 pub fn fold_ranges<T: ToOffset + Clone>(
6968 &mut self,
6969 ranges: impl IntoIterator<Item = Range<T>>,
6970 auto_scroll: bool,
6971 cx: &mut ViewContext<Self>,
6972 ) {
6973 let mut ranges = ranges.into_iter().peekable();
6974 if ranges.peek().is_some() {
6975 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6976
6977 if auto_scroll {
6978 self.request_autoscroll(Autoscroll::fit(), cx);
6979 }
6980
6981 cx.notify();
6982 }
6983 }
6984
6985 pub fn unfold_ranges<T: ToOffset + Clone>(
6986 &mut self,
6987 ranges: impl IntoIterator<Item = Range<T>>,
6988 inclusive: bool,
6989 auto_scroll: bool,
6990 cx: &mut ViewContext<Self>,
6991 ) {
6992 let mut ranges = ranges.into_iter().peekable();
6993 if ranges.peek().is_some() {
6994 self.display_map
6995 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6996 if auto_scroll {
6997 self.request_autoscroll(Autoscroll::fit(), cx);
6998 }
6999
7000 cx.notify();
7001 }
7002 }
7003
7004 pub fn gutter_hover(
7005 &mut self,
7006 GutterHover { hovered }: &GutterHover,
7007 cx: &mut ViewContext<Self>,
7008 ) {
7009 self.gutter_hovered = *hovered;
7010 cx.notify();
7011 }
7012
7013 pub fn insert_blocks(
7014 &mut self,
7015 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7016 autoscroll: Option<Autoscroll>,
7017 cx: &mut ViewContext<Self>,
7018 ) -> Vec<BlockId> {
7019 let blocks = self
7020 .display_map
7021 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7022 if let Some(autoscroll) = autoscroll {
7023 self.request_autoscroll(autoscroll, cx);
7024 }
7025 blocks
7026 }
7027
7028 pub fn replace_blocks(
7029 &mut self,
7030 blocks: HashMap<BlockId, RenderBlock>,
7031 autoscroll: Option<Autoscroll>,
7032 cx: &mut ViewContext<Self>,
7033 ) {
7034 self.display_map
7035 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7036 if let Some(autoscroll) = autoscroll {
7037 self.request_autoscroll(autoscroll, cx);
7038 }
7039 }
7040
7041 pub fn remove_blocks(
7042 &mut self,
7043 block_ids: HashSet<BlockId>,
7044 autoscroll: Option<Autoscroll>,
7045 cx: &mut ViewContext<Self>,
7046 ) {
7047 self.display_map.update(cx, |display_map, cx| {
7048 display_map.remove_blocks(block_ids, cx)
7049 });
7050 if let Some(autoscroll) = autoscroll {
7051 self.request_autoscroll(autoscroll, cx);
7052 }
7053 }
7054
7055 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7056 self.display_map
7057 .update(cx, |map, cx| map.snapshot(cx))
7058 .longest_row()
7059 }
7060
7061 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7062 self.display_map
7063 .update(cx, |map, cx| map.snapshot(cx))
7064 .max_point()
7065 }
7066
7067 pub fn text(&self, cx: &AppContext) -> String {
7068 self.buffer.read(cx).read(cx).text()
7069 }
7070
7071 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7072 self.transact(cx, |this, cx| {
7073 this.buffer
7074 .read(cx)
7075 .as_singleton()
7076 .expect("you can only call set_text on editors for singleton buffers")
7077 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7078 });
7079 }
7080
7081 pub fn display_text(&self, cx: &mut AppContext) -> String {
7082 self.display_map
7083 .update(cx, |map, cx| map.snapshot(cx))
7084 .text()
7085 }
7086
7087 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7088 let settings = self.buffer.read(cx).settings_at(0, cx);
7089 let mode = self
7090 .soft_wrap_mode_override
7091 .unwrap_or_else(|| settings.soft_wrap);
7092 match mode {
7093 language_settings::SoftWrap::None => SoftWrap::None,
7094 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7095 language_settings::SoftWrap::PreferredLineLength => {
7096 SoftWrap::Column(settings.preferred_line_length)
7097 }
7098 }
7099 }
7100
7101 pub fn set_soft_wrap_mode(
7102 &mut self,
7103 mode: language_settings::SoftWrap,
7104 cx: &mut ViewContext<Self>,
7105 ) {
7106 self.soft_wrap_mode_override = Some(mode);
7107 cx.notify();
7108 }
7109
7110 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7111 self.display_map
7112 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7113 }
7114
7115 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7116 if self.soft_wrap_mode_override.is_some() {
7117 self.soft_wrap_mode_override.take();
7118 } else {
7119 let soft_wrap = match self.soft_wrap_mode(cx) {
7120 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7121 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7122 };
7123 self.soft_wrap_mode_override = Some(soft_wrap);
7124 }
7125 cx.notify();
7126 }
7127
7128 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7129 self.show_gutter = show_gutter;
7130 cx.notify();
7131 }
7132
7133 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7134 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7135 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7136 cx.reveal_path(&file.abs_path(cx));
7137 }
7138 }
7139 }
7140
7141 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7142 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7143 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7144 if let Some(path) = file.abs_path(cx).to_str() {
7145 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7146 }
7147 }
7148 }
7149 }
7150
7151 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7152 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7153 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7154 if let Some(path) = file.path().to_str() {
7155 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7156 }
7157 }
7158 }
7159 }
7160
7161 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7162 self.highlighted_rows = rows;
7163 }
7164
7165 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7166 self.highlighted_rows.clone()
7167 }
7168
7169 pub fn highlight_background<T: 'static>(
7170 &mut self,
7171 ranges: Vec<Range<Anchor>>,
7172 color_fetcher: fn(&Theme) -> Color,
7173 cx: &mut ViewContext<Self>,
7174 ) {
7175 self.background_highlights
7176 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7177 cx.notify();
7178 }
7179
7180 #[allow(clippy::type_complexity)]
7181 pub fn clear_background_highlights<T: 'static>(
7182 &mut self,
7183 cx: &mut ViewContext<Self>,
7184 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
7185 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7186 if highlights.is_some() {
7187 cx.notify();
7188 }
7189 highlights
7190 }
7191
7192 #[cfg(feature = "test-support")]
7193 pub fn all_background_highlights(
7194 &mut self,
7195 cx: &mut ViewContext<Self>,
7196 ) -> Vec<(Range<DisplayPoint>, Color)> {
7197 let snapshot = self.snapshot(cx);
7198 let buffer = &snapshot.buffer_snapshot;
7199 let start = buffer.anchor_before(0);
7200 let end = buffer.anchor_after(buffer.len());
7201 let theme = theme::current(cx);
7202 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7203 }
7204
7205 fn document_highlights_for_position<'a>(
7206 &'a self,
7207 position: Anchor,
7208 buffer: &'a MultiBufferSnapshot,
7209 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7210 let read_highlights = self
7211 .background_highlights
7212 .get(&TypeId::of::<DocumentHighlightRead>())
7213 .map(|h| &h.1);
7214 let write_highlights = self
7215 .background_highlights
7216 .get(&TypeId::of::<DocumentHighlightWrite>())
7217 .map(|h| &h.1);
7218 let left_position = position.bias_left(buffer);
7219 let right_position = position.bias_right(buffer);
7220 read_highlights
7221 .into_iter()
7222 .chain(write_highlights)
7223 .flat_map(move |ranges| {
7224 let start_ix = match ranges.binary_search_by(|probe| {
7225 let cmp = probe.end.cmp(&left_position, buffer);
7226 if cmp.is_ge() {
7227 Ordering::Greater
7228 } else {
7229 Ordering::Less
7230 }
7231 }) {
7232 Ok(i) | Err(i) => i,
7233 };
7234
7235 let right_position = right_position.clone();
7236 ranges[start_ix..]
7237 .iter()
7238 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7239 })
7240 }
7241
7242 pub fn background_highlights_in_range(
7243 &self,
7244 search_range: Range<Anchor>,
7245 display_snapshot: &DisplaySnapshot,
7246 theme: &Theme,
7247 ) -> Vec<(Range<DisplayPoint>, Color)> {
7248 let mut results = Vec::new();
7249 let buffer = &display_snapshot.buffer_snapshot;
7250 for (color_fetcher, ranges) in self.background_highlights.values() {
7251 let color = color_fetcher(theme);
7252 let start_ix = match ranges.binary_search_by(|probe| {
7253 let cmp = probe.end.cmp(&search_range.start, buffer);
7254 if cmp.is_gt() {
7255 Ordering::Greater
7256 } else {
7257 Ordering::Less
7258 }
7259 }) {
7260 Ok(i) | Err(i) => i,
7261 };
7262 for range in &ranges[start_ix..] {
7263 if range.start.cmp(&search_range.end, buffer).is_ge() {
7264 break;
7265 }
7266 let start = range
7267 .start
7268 .to_point(buffer)
7269 .to_display_point(display_snapshot);
7270 let end = range
7271 .end
7272 .to_point(buffer)
7273 .to_display_point(display_snapshot);
7274 results.push((start..end, color))
7275 }
7276 }
7277 results
7278 }
7279 pub fn background_highlights_in_range_for<T: 'static>(
7280 &self,
7281 search_range: Range<Anchor>,
7282 display_snapshot: &DisplaySnapshot,
7283 theme: &Theme,
7284 ) -> Vec<(Range<DisplayPoint>, Color)> {
7285 let mut results = Vec::new();
7286 let buffer = &display_snapshot.buffer_snapshot;
7287 let Some((color_fetcher, ranges)) = self.background_highlights
7288 .get(&TypeId::of::<T>()) else {
7289 return vec![];
7290 };
7291
7292 let color = color_fetcher(theme);
7293 let start_ix = match ranges.binary_search_by(|probe| {
7294 let cmp = probe.end.cmp(&search_range.start, buffer);
7295 if cmp.is_gt() {
7296 Ordering::Greater
7297 } else {
7298 Ordering::Less
7299 }
7300 }) {
7301 Ok(i) | Err(i) => i,
7302 };
7303 for range in &ranges[start_ix..] {
7304 if range.start.cmp(&search_range.end, buffer).is_ge() {
7305 break;
7306 }
7307 let start = range
7308 .start
7309 .to_point(buffer)
7310 .to_display_point(display_snapshot);
7311 let end = range
7312 .end
7313 .to_point(buffer)
7314 .to_display_point(display_snapshot);
7315 results.push((start..end, color))
7316 }
7317
7318 results
7319 }
7320
7321 pub fn highlight_text<T: 'static>(
7322 &mut self,
7323 ranges: Vec<Range<Anchor>>,
7324 style: HighlightStyle,
7325 cx: &mut ViewContext<Self>,
7326 ) {
7327 self.display_map.update(cx, |map, _| {
7328 map.highlight_text(TypeId::of::<T>(), ranges, style)
7329 });
7330 cx.notify();
7331 }
7332
7333 pub fn text_highlights<'a, T: 'static>(
7334 &'a self,
7335 cx: &'a AppContext,
7336 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7337 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7338 }
7339
7340 pub fn clear_text_highlights<T: 'static>(
7341 &mut self,
7342 cx: &mut ViewContext<Self>,
7343 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7344 let highlights = self
7345 .display_map
7346 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7347 if highlights.is_some() {
7348 cx.notify();
7349 }
7350 highlights
7351 }
7352
7353 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7354 self.blink_manager.read(cx).visible() && self.focused
7355 }
7356
7357 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7358 cx.notify();
7359 }
7360
7361 fn on_buffer_event(
7362 &mut self,
7363 multibuffer: ModelHandle<MultiBuffer>,
7364 event: &multi_buffer::Event,
7365 cx: &mut ViewContext<Self>,
7366 ) {
7367 match event {
7368 multi_buffer::Event::Edited => {
7369 self.refresh_active_diagnostics(cx);
7370 self.refresh_code_actions(cx);
7371 if self.has_active_copilot_suggestion(cx) {
7372 self.update_visible_copilot_suggestion(cx);
7373 }
7374 cx.emit(Event::BufferEdited);
7375
7376 if let Some(project) = &self.project {
7377 let project = project.read(cx);
7378 let languages_affected = multibuffer
7379 .read(cx)
7380 .all_buffers()
7381 .into_iter()
7382 .filter_map(|buffer| {
7383 let buffer = buffer.read(cx);
7384 let language = buffer.language()?;
7385 if project.is_local()
7386 && project.language_servers_for_buffer(buffer, cx).count() == 0
7387 {
7388 None
7389 } else {
7390 Some(language)
7391 }
7392 })
7393 .cloned()
7394 .collect::<HashSet<_>>();
7395 if !languages_affected.is_empty() {
7396 self.refresh_inlays(
7397 InlayRefreshReason::BufferEdited(languages_affected),
7398 cx,
7399 );
7400 }
7401 }
7402 }
7403 multi_buffer::Event::ExcerptsAdded {
7404 buffer,
7405 predecessor,
7406 excerpts,
7407 } => cx.emit(Event::ExcerptsAdded {
7408 buffer: buffer.clone(),
7409 predecessor: *predecessor,
7410 excerpts: excerpts.clone(),
7411 }),
7412 multi_buffer::Event::ExcerptsRemoved { ids } => {
7413 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7414 }
7415 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7416 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7417 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7418 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7419 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7420 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7421 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7422 multi_buffer::Event::DiagnosticsUpdated => {
7423 self.refresh_active_diagnostics(cx);
7424 }
7425 _ => {}
7426 };
7427 }
7428
7429 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7430 cx.notify();
7431 }
7432
7433 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7434 self.refresh_copilot_suggestions(true, cx);
7435 self.refresh_inlays(
7436 InlayRefreshReason::SettingsChange(inlay_hint_settings(
7437 self.selections.newest_anchor().head(),
7438 &self.buffer.read(cx).snapshot(cx),
7439 cx,
7440 )),
7441 cx,
7442 );
7443 }
7444
7445 pub fn set_searchable(&mut self, searchable: bool) {
7446 self.searchable = searchable;
7447 }
7448
7449 pub fn searchable(&self) -> bool {
7450 self.searchable
7451 }
7452
7453 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7454 let active_item = workspace.active_item(cx);
7455 let editor_handle = if let Some(editor) = active_item
7456 .as_ref()
7457 .and_then(|item| item.act_as::<Self>(cx))
7458 {
7459 editor
7460 } else {
7461 cx.propagate_action();
7462 return;
7463 };
7464
7465 let editor = editor_handle.read(cx);
7466 let buffer = editor.buffer.read(cx);
7467 if buffer.is_singleton() {
7468 cx.propagate_action();
7469 return;
7470 }
7471
7472 let mut new_selections_by_buffer = HashMap::default();
7473 for selection in editor.selections.all::<usize>(cx) {
7474 for (buffer, mut range, _) in
7475 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7476 {
7477 if selection.reversed {
7478 mem::swap(&mut range.start, &mut range.end);
7479 }
7480 new_selections_by_buffer
7481 .entry(buffer)
7482 .or_insert(Vec::new())
7483 .push(range)
7484 }
7485 }
7486
7487 editor_handle.update(cx, |editor, cx| {
7488 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7489 });
7490 let pane = workspace.active_pane().clone();
7491 pane.update(cx, |pane, _| pane.disable_history());
7492
7493 // We defer the pane interaction because we ourselves are a workspace item
7494 // and activating a new item causes the pane to call a method on us reentrantly,
7495 // which panics if we're on the stack.
7496 cx.defer(move |workspace, cx| {
7497 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7498 let editor = workspace.open_project_item::<Self>(buffer, cx);
7499 editor.update(cx, |editor, cx| {
7500 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7501 s.select_ranges(ranges);
7502 });
7503 });
7504 }
7505
7506 pane.update(cx, |pane, _| pane.enable_history());
7507 });
7508 }
7509
7510 fn jump(
7511 workspace: &mut Workspace,
7512 path: ProjectPath,
7513 position: Point,
7514 anchor: language::Anchor,
7515 cx: &mut ViewContext<Workspace>,
7516 ) {
7517 let editor = workspace.open_path(path, None, true, cx);
7518 cx.spawn(|_, mut cx| async move {
7519 let editor = editor
7520 .await?
7521 .downcast::<Editor>()
7522 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7523 .downgrade();
7524 editor.update(&mut cx, |editor, cx| {
7525 let buffer = editor
7526 .buffer()
7527 .read(cx)
7528 .as_singleton()
7529 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7530 let buffer = buffer.read(cx);
7531 let cursor = if buffer.can_resolve(&anchor) {
7532 language::ToPoint::to_point(&anchor, buffer)
7533 } else {
7534 buffer.clip_point(position, Bias::Left)
7535 };
7536
7537 let nav_history = editor.nav_history.take();
7538 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7539 s.select_ranges([cursor..cursor]);
7540 });
7541 editor.nav_history = nav_history;
7542
7543 anyhow::Ok(())
7544 })??;
7545
7546 anyhow::Ok(())
7547 })
7548 .detach_and_log_err(cx);
7549 }
7550
7551 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7552 let snapshot = self.buffer.read(cx).read(cx);
7553 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7554 Some(
7555 ranges
7556 .iter()
7557 .map(move |range| {
7558 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7559 })
7560 .collect(),
7561 )
7562 }
7563
7564 fn selection_replacement_ranges(
7565 &self,
7566 range: Range<OffsetUtf16>,
7567 cx: &AppContext,
7568 ) -> Vec<Range<OffsetUtf16>> {
7569 let selections = self.selections.all::<OffsetUtf16>(cx);
7570 let newest_selection = selections
7571 .iter()
7572 .max_by_key(|selection| selection.id)
7573 .unwrap();
7574 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7575 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7576 let snapshot = self.buffer.read(cx).read(cx);
7577 selections
7578 .into_iter()
7579 .map(|mut selection| {
7580 selection.start.0 =
7581 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7582 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7583 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7584 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7585 })
7586 .collect()
7587 }
7588
7589 fn report_copilot_event(
7590 &self,
7591 suggestion_id: Option<String>,
7592 suggestion_accepted: bool,
7593 cx: &AppContext,
7594 ) {
7595 let Some(project) = &self.project else {
7596 return
7597 };
7598
7599 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7600 let file_extension = self
7601 .buffer
7602 .read(cx)
7603 .as_singleton()
7604 .and_then(|b| b.read(cx).file())
7605 .and_then(|file| Path::new(file.file_name(cx)).extension())
7606 .and_then(|e| e.to_str())
7607 .map(|a| a.to_string());
7608
7609 let telemetry = project.read(cx).client().telemetry().clone();
7610 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7611
7612 let event = ClickhouseEvent::Copilot {
7613 suggestion_id,
7614 suggestion_accepted,
7615 file_extension,
7616 };
7617 telemetry.report_clickhouse_event(event, telemetry_settings);
7618 }
7619
7620 fn report_editor_event(
7621 &self,
7622 operation: &'static str,
7623 file_extension: Option<String>,
7624 cx: &AppContext,
7625 ) {
7626 let Some(project) = &self.project else {
7627 return
7628 };
7629
7630 // If None, we are in a file without an extension
7631 let file = self
7632 .buffer
7633 .read(cx)
7634 .as_singleton()
7635 .and_then(|b| b.read(cx).file());
7636 let file_extension = file_extension.or(file
7637 .as_ref()
7638 .and_then(|file| Path::new(file.file_name(cx)).extension())
7639 .and_then(|e| e.to_str())
7640 .map(|a| a.to_string()));
7641
7642 let vim_mode = cx
7643 .global::<SettingsStore>()
7644 .raw_user_settings()
7645 .get("vim_mode")
7646 == Some(&serde_json::Value::Bool(true));
7647 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7648 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7649 let copilot_enabled_for_language = self
7650 .buffer
7651 .read(cx)
7652 .settings_at(0, cx)
7653 .show_copilot_suggestions;
7654
7655 let telemetry = project.read(cx).client().telemetry().clone();
7656 let event = ClickhouseEvent::Editor {
7657 file_extension,
7658 vim_mode,
7659 operation,
7660 copilot_enabled,
7661 copilot_enabled_for_language,
7662 };
7663 telemetry.report_clickhouse_event(event, telemetry_settings)
7664 }
7665
7666 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7667 /// with each line being an array of {text, highlight} objects.
7668 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7669 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7670 return;
7671 };
7672
7673 #[derive(Serialize)]
7674 struct Chunk<'a> {
7675 text: String,
7676 highlight: Option<&'a str>,
7677 }
7678
7679 let snapshot = buffer.read(cx).snapshot();
7680 let range = self
7681 .selected_text_range(cx)
7682 .and_then(|selected_range| {
7683 if selected_range.is_empty() {
7684 None
7685 } else {
7686 Some(selected_range)
7687 }
7688 })
7689 .unwrap_or_else(|| 0..snapshot.len());
7690
7691 let chunks = snapshot.chunks(range, true);
7692 let mut lines = Vec::new();
7693 let mut line: VecDeque<Chunk> = VecDeque::new();
7694
7695 let theme = &theme::current(cx).editor.syntax;
7696
7697 for chunk in chunks {
7698 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7699 let mut chunk_lines = chunk.text.split("\n").peekable();
7700 while let Some(text) = chunk_lines.next() {
7701 let mut merged_with_last_token = false;
7702 if let Some(last_token) = line.back_mut() {
7703 if last_token.highlight == highlight {
7704 last_token.text.push_str(text);
7705 merged_with_last_token = true;
7706 }
7707 }
7708
7709 if !merged_with_last_token {
7710 line.push_back(Chunk {
7711 text: text.into(),
7712 highlight,
7713 });
7714 }
7715
7716 if chunk_lines.peek().is_some() {
7717 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7718 line.pop_front();
7719 }
7720 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7721 line.pop_back();
7722 }
7723
7724 lines.push(mem::take(&mut line));
7725 }
7726 }
7727 }
7728
7729 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7730 cx.write_to_clipboard(ClipboardItem::new(lines));
7731 }
7732
7733 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
7734 &self.inlay_hint_cache
7735 }
7736}
7737
7738fn inlay_hint_settings(
7739 location: Anchor,
7740 snapshot: &MultiBufferSnapshot,
7741 cx: &mut ViewContext<'_, '_, Editor>,
7742) -> InlayHintSettings {
7743 let file = snapshot.file_at(location);
7744 let language = snapshot.language_at(location);
7745 let settings = all_language_settings(file, cx);
7746 settings
7747 .language(language.map(|l| l.name()).as_deref())
7748 .inlay_hints
7749}
7750
7751fn consume_contiguous_rows(
7752 contiguous_row_selections: &mut Vec<Selection<Point>>,
7753 selection: &Selection<Point>,
7754 display_map: &DisplaySnapshot,
7755 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7756) -> (u32, u32) {
7757 contiguous_row_selections.push(selection.clone());
7758 let start_row = selection.start.row;
7759 let mut end_row = ending_row(selection, display_map);
7760
7761 while let Some(next_selection) = selections.peek() {
7762 if next_selection.start.row <= end_row {
7763 end_row = ending_row(next_selection, display_map);
7764 contiguous_row_selections.push(selections.next().unwrap().clone());
7765 } else {
7766 break;
7767 }
7768 }
7769 (start_row, end_row)
7770}
7771
7772fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7773 if next_selection.end.column > 0 || next_selection.is_empty() {
7774 display_map.next_line_boundary(next_selection.end).0.row + 1
7775 } else {
7776 next_selection.end.row
7777 }
7778}
7779
7780impl EditorSnapshot {
7781 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7782 self.display_snapshot.buffer_snapshot.language_at(position)
7783 }
7784
7785 pub fn is_focused(&self) -> bool {
7786 self.is_focused
7787 }
7788
7789 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7790 self.placeholder_text.as_ref()
7791 }
7792
7793 pub fn scroll_position(&self) -> Vector2F {
7794 self.scroll_anchor.scroll_position(&self.display_snapshot)
7795 }
7796}
7797
7798impl Deref for EditorSnapshot {
7799 type Target = DisplaySnapshot;
7800
7801 fn deref(&self) -> &Self::Target {
7802 &self.display_snapshot
7803 }
7804}
7805
7806#[derive(Clone, Debug, PartialEq, Eq)]
7807pub enum Event {
7808 InputIgnored {
7809 text: Arc<str>,
7810 },
7811 ExcerptsAdded {
7812 buffer: ModelHandle<Buffer>,
7813 predecessor: ExcerptId,
7814 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7815 },
7816 ExcerptsRemoved {
7817 ids: Vec<ExcerptId>,
7818 },
7819 BufferEdited,
7820 Edited,
7821 Reparsed,
7822 Focused,
7823 Blurred,
7824 DirtyChanged,
7825 Saved,
7826 TitleChanged,
7827 DiffBaseChanged,
7828 SelectionsChanged {
7829 local: bool,
7830 },
7831 ScrollPositionChanged {
7832 local: bool,
7833 autoscroll: bool,
7834 },
7835 Closed,
7836}
7837
7838pub struct EditorFocused(pub ViewHandle<Editor>);
7839pub struct EditorBlurred(pub ViewHandle<Editor>);
7840pub struct EditorReleased(pub WeakViewHandle<Editor>);
7841
7842impl Entity for Editor {
7843 type Event = Event;
7844
7845 fn release(&mut self, cx: &mut AppContext) {
7846 cx.emit_global(EditorReleased(self.handle.clone()));
7847 }
7848}
7849
7850impl View for Editor {
7851 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7852 let style = self.style(cx);
7853 let font_changed = self.display_map.update(cx, |map, cx| {
7854 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7855 map.set_font(style.text.font_id, style.text.font_size, cx)
7856 });
7857
7858 if font_changed {
7859 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7860 hide_hover(editor, cx);
7861 hide_link_definition(editor, cx);
7862 });
7863 }
7864
7865 Stack::new()
7866 .with_child(EditorElement::new(style.clone()))
7867 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7868 .into_any()
7869 }
7870
7871 fn ui_name() -> &'static str {
7872 "Editor"
7873 }
7874
7875 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7876 if cx.is_self_focused() {
7877 let focused_event = EditorFocused(cx.handle());
7878 cx.emit(Event::Focused);
7879 cx.emit_global(focused_event);
7880 }
7881 if let Some(rename) = self.pending_rename.as_ref() {
7882 cx.focus(&rename.editor);
7883 } else {
7884 if !self.focused {
7885 self.blink_manager.update(cx, BlinkManager::enable);
7886 }
7887 self.focused = true;
7888 self.buffer.update(cx, |buffer, cx| {
7889 buffer.finalize_last_transaction(cx);
7890 if self.leader_replica_id.is_none() {
7891 buffer.set_active_selections(
7892 &self.selections.disjoint_anchors(),
7893 self.selections.line_mode,
7894 self.cursor_shape,
7895 cx,
7896 );
7897 }
7898 });
7899 }
7900 }
7901
7902 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7903 let blurred_event = EditorBlurred(cx.handle());
7904 cx.emit_global(blurred_event);
7905 self.focused = false;
7906 self.blink_manager.update(cx, BlinkManager::disable);
7907 self.buffer
7908 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7909 self.hide_context_menu(cx);
7910 hide_hover(self, cx);
7911 cx.emit(Event::Blurred);
7912 cx.notify();
7913 }
7914
7915 fn modifiers_changed(
7916 &mut self,
7917 event: &gpui::platform::ModifiersChangedEvent,
7918 cx: &mut ViewContext<Self>,
7919 ) -> bool {
7920 let pending_selection = self.has_pending_selection();
7921
7922 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7923 if event.cmd && !pending_selection {
7924 let snapshot = self.snapshot(cx);
7925 let kind = if event.shift {
7926 LinkDefinitionKind::Type
7927 } else {
7928 LinkDefinitionKind::Symbol
7929 };
7930
7931 show_link_definition(kind, self, point, snapshot, cx);
7932 return false;
7933 }
7934 }
7935
7936 {
7937 if self.link_go_to_definition_state.symbol_range.is_some()
7938 || !self.link_go_to_definition_state.definitions.is_empty()
7939 {
7940 self.link_go_to_definition_state.symbol_range.take();
7941 self.link_go_to_definition_state.definitions.clear();
7942 cx.notify();
7943 }
7944
7945 self.link_go_to_definition_state.task = None;
7946
7947 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7948 }
7949
7950 false
7951 }
7952
7953 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7954 Self::reset_to_default_keymap_context(keymap);
7955 let mode = match self.mode {
7956 EditorMode::SingleLine => "single_line",
7957 EditorMode::AutoHeight { .. } => "auto_height",
7958 EditorMode::Full => "full",
7959 };
7960 keymap.add_key("mode", mode);
7961 if self.pending_rename.is_some() {
7962 keymap.add_identifier("renaming");
7963 }
7964 match self.context_menu.as_ref() {
7965 Some(ContextMenu::Completions(_)) => {
7966 keymap.add_identifier("menu");
7967 keymap.add_identifier("showing_completions")
7968 }
7969 Some(ContextMenu::CodeActions(_)) => {
7970 keymap.add_identifier("menu");
7971 keymap.add_identifier("showing_code_actions")
7972 }
7973 None => {}
7974 }
7975 for layer in self.keymap_context_layers.values() {
7976 keymap.extend(layer);
7977 }
7978
7979 if let Some(extension) = self
7980 .buffer
7981 .read(cx)
7982 .as_singleton()
7983 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7984 {
7985 keymap.add_key("extension", extension.to_string());
7986 }
7987 }
7988
7989 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7990 Some(
7991 self.buffer
7992 .read(cx)
7993 .read(cx)
7994 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7995 .collect(),
7996 )
7997 }
7998
7999 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8000 // Prevent the IME menu from appearing when holding down an alphabetic key
8001 // while input is disabled.
8002 if !self.input_enabled {
8003 return None;
8004 }
8005
8006 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8007 Some(range.start.0..range.end.0)
8008 }
8009
8010 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8011 let snapshot = self.buffer.read(cx).read(cx);
8012 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8013 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8014 }
8015
8016 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8017 self.clear_text_highlights::<InputComposition>(cx);
8018 self.ime_transaction.take();
8019 }
8020
8021 fn replace_text_in_range(
8022 &mut self,
8023 range_utf16: Option<Range<usize>>,
8024 text: &str,
8025 cx: &mut ViewContext<Self>,
8026 ) {
8027 self.transact(cx, |this, cx| {
8028 if this.input_enabled {
8029 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8030 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8031 Some(this.selection_replacement_ranges(range_utf16, cx))
8032 } else {
8033 this.marked_text_ranges(cx)
8034 };
8035
8036 if let Some(new_selected_ranges) = new_selected_ranges {
8037 this.change_selections(None, cx, |selections| {
8038 selections.select_ranges(new_selected_ranges)
8039 });
8040 }
8041 }
8042
8043 this.handle_input(text, cx);
8044 });
8045
8046 if !self.input_enabled {
8047 return;
8048 }
8049
8050 if let Some(transaction) = self.ime_transaction {
8051 self.buffer.update(cx, |buffer, cx| {
8052 buffer.group_until_transaction(transaction, cx);
8053 });
8054 }
8055
8056 self.unmark_text(cx);
8057 }
8058
8059 fn replace_and_mark_text_in_range(
8060 &mut self,
8061 range_utf16: Option<Range<usize>>,
8062 text: &str,
8063 new_selected_range_utf16: Option<Range<usize>>,
8064 cx: &mut ViewContext<Self>,
8065 ) {
8066 if !self.input_enabled {
8067 return;
8068 }
8069
8070 let transaction = self.transact(cx, |this, cx| {
8071 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8072 let snapshot = this.buffer.read(cx).read(cx);
8073 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8074 for marked_range in &mut marked_ranges {
8075 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8076 marked_range.start.0 += relative_range_utf16.start;
8077 marked_range.start =
8078 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8079 marked_range.end =
8080 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8081 }
8082 }
8083 Some(marked_ranges)
8084 } else if let Some(range_utf16) = range_utf16 {
8085 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8086 Some(this.selection_replacement_ranges(range_utf16, cx))
8087 } else {
8088 None
8089 };
8090
8091 if let Some(ranges) = ranges_to_replace {
8092 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8093 }
8094
8095 let marked_ranges = {
8096 let snapshot = this.buffer.read(cx).read(cx);
8097 this.selections
8098 .disjoint_anchors()
8099 .iter()
8100 .map(|selection| {
8101 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8102 })
8103 .collect::<Vec<_>>()
8104 };
8105
8106 if text.is_empty() {
8107 this.unmark_text(cx);
8108 } else {
8109 this.highlight_text::<InputComposition>(
8110 marked_ranges.clone(),
8111 this.style(cx).composition_mark,
8112 cx,
8113 );
8114 }
8115
8116 this.handle_input(text, cx);
8117
8118 if let Some(new_selected_range) = new_selected_range_utf16 {
8119 let snapshot = this.buffer.read(cx).read(cx);
8120 let new_selected_ranges = marked_ranges
8121 .into_iter()
8122 .map(|marked_range| {
8123 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8124 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8125 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8126 snapshot.clip_offset_utf16(new_start, Bias::Left)
8127 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8128 })
8129 .collect::<Vec<_>>();
8130
8131 drop(snapshot);
8132 this.change_selections(None, cx, |selections| {
8133 selections.select_ranges(new_selected_ranges)
8134 });
8135 }
8136 });
8137
8138 self.ime_transaction = self.ime_transaction.or(transaction);
8139 if let Some(transaction) = self.ime_transaction {
8140 self.buffer.update(cx, |buffer, cx| {
8141 buffer.group_until_transaction(transaction, cx);
8142 });
8143 }
8144
8145 if self.text_highlights::<InputComposition>(cx).is_none() {
8146 self.ime_transaction.take();
8147 }
8148 }
8149}
8150
8151fn build_style(
8152 settings: &ThemeSettings,
8153 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8154 override_text_style: Option<&OverrideTextStyle>,
8155 cx: &AppContext,
8156) -> EditorStyle {
8157 let font_cache = cx.font_cache();
8158 let line_height_scalar = settings.line_height();
8159 let theme_id = settings.theme.meta.id;
8160 let mut theme = settings.theme.editor.clone();
8161 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8162 let field_editor_theme = get_field_editor_theme(&settings.theme);
8163 theme.text_color = field_editor_theme.text.color;
8164 theme.selection = field_editor_theme.selection;
8165 theme.background = field_editor_theme
8166 .container
8167 .background_color
8168 .unwrap_or_default();
8169 EditorStyle {
8170 text: field_editor_theme.text,
8171 placeholder_text: field_editor_theme.placeholder_text,
8172 line_height_scalar,
8173 theme,
8174 theme_id,
8175 }
8176 } else {
8177 let font_family_id = settings.buffer_font_family;
8178 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8179 let font_properties = Default::default();
8180 let font_id = font_cache
8181 .select_font(font_family_id, &font_properties)
8182 .unwrap();
8183 let font_size = settings.buffer_font_size(cx);
8184 EditorStyle {
8185 text: TextStyle {
8186 color: settings.theme.editor.text_color,
8187 font_family_name,
8188 font_family_id,
8189 font_id,
8190 font_size,
8191 font_properties,
8192 underline: Default::default(),
8193 },
8194 placeholder_text: None,
8195 line_height_scalar,
8196 theme,
8197 theme_id,
8198 }
8199 };
8200
8201 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8202 if let Some(highlighted) = style
8203 .text
8204 .clone()
8205 .highlight(highlight_style, font_cache)
8206 .log_err()
8207 {
8208 style.text = highlighted;
8209 }
8210 }
8211
8212 style
8213}
8214
8215trait SelectionExt {
8216 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8217 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8218 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8219 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8220 -> Range<u32>;
8221}
8222
8223impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8224 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8225 let start = self.start.to_point(buffer);
8226 let end = self.end.to_point(buffer);
8227 if self.reversed {
8228 end..start
8229 } else {
8230 start..end
8231 }
8232 }
8233
8234 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8235 let start = self.start.to_offset(buffer);
8236 let end = self.end.to_offset(buffer);
8237 if self.reversed {
8238 end..start
8239 } else {
8240 start..end
8241 }
8242 }
8243
8244 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8245 let start = self
8246 .start
8247 .to_point(&map.buffer_snapshot)
8248 .to_display_point(map);
8249 let end = self
8250 .end
8251 .to_point(&map.buffer_snapshot)
8252 .to_display_point(map);
8253 if self.reversed {
8254 end..start
8255 } else {
8256 start..end
8257 }
8258 }
8259
8260 fn spanned_rows(
8261 &self,
8262 include_end_if_at_line_start: bool,
8263 map: &DisplaySnapshot,
8264 ) -> Range<u32> {
8265 let start = self.start.to_point(&map.buffer_snapshot);
8266 let mut end = self.end.to_point(&map.buffer_snapshot);
8267 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8268 end.row -= 1;
8269 }
8270
8271 let buffer_start = map.prev_line_boundary(start).0;
8272 let buffer_end = map.next_line_boundary(end).0;
8273 buffer_start.row..buffer_end.row + 1
8274 }
8275}
8276
8277impl<T: InvalidationRegion> InvalidationStack<T> {
8278 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8279 where
8280 S: Clone + ToOffset,
8281 {
8282 while let Some(region) = self.last() {
8283 let all_selections_inside_invalidation_ranges =
8284 if selections.len() == region.ranges().len() {
8285 selections
8286 .iter()
8287 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8288 .all(|(selection, invalidation_range)| {
8289 let head = selection.head().to_offset(buffer);
8290 invalidation_range.start <= head && invalidation_range.end >= head
8291 })
8292 } else {
8293 false
8294 };
8295
8296 if all_selections_inside_invalidation_ranges {
8297 break;
8298 } else {
8299 self.pop();
8300 }
8301 }
8302 }
8303}
8304
8305impl<T> Default for InvalidationStack<T> {
8306 fn default() -> Self {
8307 Self(Default::default())
8308 }
8309}
8310
8311impl<T> Deref for InvalidationStack<T> {
8312 type Target = Vec<T>;
8313
8314 fn deref(&self) -> &Self::Target {
8315 &self.0
8316 }
8317}
8318
8319impl<T> DerefMut for InvalidationStack<T> {
8320 fn deref_mut(&mut self) -> &mut Self::Target {
8321 &mut self.0
8322 }
8323}
8324
8325impl InvalidationRegion for SnippetState {
8326 fn ranges(&self) -> &[Range<Anchor>] {
8327 &self.ranges[self.active_index]
8328 }
8329}
8330
8331impl Deref for EditorStyle {
8332 type Target = theme::Editor;
8333
8334 fn deref(&self) -> &Self::Target {
8335 &self.theme
8336 }
8337}
8338
8339pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8340 let mut highlighted_lines = Vec::new();
8341
8342 for (index, line) in diagnostic.message.lines().enumerate() {
8343 let line = match &diagnostic.source {
8344 Some(source) if index == 0 => {
8345 let source_highlight = Vec::from_iter(0..source.len());
8346 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8347 }
8348
8349 _ => highlight_diagnostic_message(Vec::new(), line),
8350 };
8351 highlighted_lines.push(line);
8352 }
8353 let message = diagnostic.message;
8354 Arc::new(move |cx: &mut BlockContext| {
8355 let message = message.clone();
8356 let settings = settings::get::<ThemeSettings>(cx);
8357 let tooltip_style = settings.theme.tooltip.clone();
8358 let theme = &settings.theme.editor;
8359 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8360 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8361 let anchor_x = cx.anchor_x;
8362 enum BlockContextToolip {}
8363 MouseEventHandler::<BlockContext, _>::new(cx.block_id, cx, |_, _| {
8364 Flex::column()
8365 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8366 Label::new(
8367 line.clone(),
8368 style.message.clone().with_font_size(font_size),
8369 )
8370 .with_highlights(highlights.clone())
8371 .contained()
8372 .with_margin_left(anchor_x)
8373 }))
8374 .aligned()
8375 .left()
8376 .into_any()
8377 })
8378 .with_cursor_style(CursorStyle::PointingHand)
8379 .on_click(MouseButton::Left, move |_, _, cx| {
8380 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8381 })
8382 // We really need to rethink this ID system...
8383 .with_tooltip::<BlockContextToolip>(
8384 cx.block_id,
8385 "Copy diagnostic message".to_string(),
8386 None,
8387 tooltip_style,
8388 cx,
8389 )
8390 .into_any()
8391 })
8392}
8393
8394pub fn highlight_diagnostic_message(
8395 initial_highlights: Vec<usize>,
8396 message: &str,
8397) -> (String, Vec<usize>) {
8398 let mut message_without_backticks = String::new();
8399 let mut prev_offset = 0;
8400 let mut inside_block = false;
8401 let mut highlights = initial_highlights;
8402 for (match_ix, (offset, _)) in message
8403 .match_indices('`')
8404 .chain([(message.len(), "")])
8405 .enumerate()
8406 {
8407 message_without_backticks.push_str(&message[prev_offset..offset]);
8408 if inside_block {
8409 highlights.extend(prev_offset - match_ix..offset - match_ix);
8410 }
8411
8412 inside_block = !inside_block;
8413 prev_offset = offset + 1;
8414 }
8415
8416 (message_without_backticks, highlights)
8417}
8418
8419pub fn diagnostic_style(
8420 severity: DiagnosticSeverity,
8421 valid: bool,
8422 theme: &theme::Editor,
8423) -> DiagnosticStyle {
8424 match (severity, valid) {
8425 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8426 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8427 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8428 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8429 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8430 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8431 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8432 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8433 _ => theme.invalid_hint_diagnostic.clone(),
8434 }
8435}
8436
8437pub fn combine_syntax_and_fuzzy_match_highlights(
8438 text: &str,
8439 default_style: HighlightStyle,
8440 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8441 match_indices: &[usize],
8442) -> Vec<(Range<usize>, HighlightStyle)> {
8443 let mut result = Vec::new();
8444 let mut match_indices = match_indices.iter().copied().peekable();
8445
8446 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8447 {
8448 syntax_highlight.weight = None;
8449
8450 // Add highlights for any fuzzy match characters before the next
8451 // syntax highlight range.
8452 while let Some(&match_index) = match_indices.peek() {
8453 if match_index >= range.start {
8454 break;
8455 }
8456 match_indices.next();
8457 let end_index = char_ix_after(match_index, text);
8458 let mut match_style = default_style;
8459 match_style.weight = Some(fonts::Weight::BOLD);
8460 result.push((match_index..end_index, match_style));
8461 }
8462
8463 if range.start == usize::MAX {
8464 break;
8465 }
8466
8467 // Add highlights for any fuzzy match characters within the
8468 // syntax highlight range.
8469 let mut offset = range.start;
8470 while let Some(&match_index) = match_indices.peek() {
8471 if match_index >= range.end {
8472 break;
8473 }
8474
8475 match_indices.next();
8476 if match_index > offset {
8477 result.push((offset..match_index, syntax_highlight));
8478 }
8479
8480 let mut end_index = char_ix_after(match_index, text);
8481 while let Some(&next_match_index) = match_indices.peek() {
8482 if next_match_index == end_index && next_match_index < range.end {
8483 end_index = char_ix_after(next_match_index, text);
8484 match_indices.next();
8485 } else {
8486 break;
8487 }
8488 }
8489
8490 let mut match_style = syntax_highlight;
8491 match_style.weight = Some(fonts::Weight::BOLD);
8492 result.push((match_index..end_index, match_style));
8493 offset = end_index;
8494 }
8495
8496 if offset < range.end {
8497 result.push((offset..range.end, syntax_highlight));
8498 }
8499 }
8500
8501 fn char_ix_after(ix: usize, text: &str) -> usize {
8502 ix + text[ix..].chars().next().unwrap().len_utf8()
8503 }
8504
8505 result
8506}
8507
8508pub fn styled_runs_for_code_label<'a>(
8509 label: &'a CodeLabel,
8510 syntax_theme: &'a theme::SyntaxTheme,
8511) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8512 let fade_out = HighlightStyle {
8513 fade_out: Some(0.35),
8514 ..Default::default()
8515 };
8516
8517 let mut prev_end = label.filter_range.end;
8518 label
8519 .runs
8520 .iter()
8521 .enumerate()
8522 .flat_map(move |(ix, (range, highlight_id))| {
8523 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8524 style
8525 } else {
8526 return Default::default();
8527 };
8528 let mut muted_style = style;
8529 muted_style.highlight(fade_out);
8530
8531 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8532 if range.start >= label.filter_range.end {
8533 if range.start > prev_end {
8534 runs.push((prev_end..range.start, fade_out));
8535 }
8536 runs.push((range.clone(), muted_style));
8537 } else if range.end <= label.filter_range.end {
8538 runs.push((range.clone(), style));
8539 } else {
8540 runs.push((range.start..label.filter_range.end, style));
8541 runs.push((label.filter_range.end..range.end, muted_style));
8542 }
8543 prev_end = cmp::max(prev_end, range.end);
8544
8545 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8546 runs.push((prev_end..label.text.len(), fade_out));
8547 }
8548
8549 runs
8550 })
8551}
8552
8553pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8554 let mut index = 0;
8555 let mut codepoints = text.char_indices().peekable();
8556
8557 std::iter::from_fn(move || {
8558 let start_index = index;
8559 while let Some((new_index, codepoint)) = codepoints.next() {
8560 index = new_index + codepoint.len_utf8();
8561 let current_upper = codepoint.is_uppercase();
8562 let next_upper = codepoints
8563 .peek()
8564 .map(|(_, c)| c.is_uppercase())
8565 .unwrap_or(false);
8566
8567 if !current_upper && next_upper {
8568 return Some(&text[start_index..index]);
8569 }
8570 }
8571
8572 index = text.len();
8573 if start_index < text.len() {
8574 return Some(&text[start_index..]);
8575 }
8576 None
8577 })
8578 .flat_map(|word| word.split_inclusive('_'))
8579}
8580
8581trait RangeToAnchorExt {
8582 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8583}
8584
8585impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8586 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8587 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8588 }
8589}