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