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