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