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