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