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