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