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