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