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