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