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