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