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