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