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