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