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