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