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