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