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