Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:31:28.331Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

editor.rs

12539 lines · 459.6 KB · rust
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//!
11//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
12//!
13//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
14pub mod actions;
15pub mod blink_manager;
16mod bracket_colorization;
17mod clangd_ext;
18pub mod code_context_menus;
19mod code_lens;
20pub mod display_map;
21mod document_colors;
22mod document_links;
23mod document_symbols;
24mod editor_settings;
25mod element;
26mod fold;
27mod folding_ranges;
28mod git;
29mod highlight_matching_bracket;
30pub mod hover_links;
31pub mod hover_popover;
32mod indent_guides;
33mod inlays;
34pub mod items;
35mod jsx_tag_auto_close;
36mod linked_editing_ranges;
37mod lsp_ext;
38mod mouse_context_menu;
39pub mod movement;
40mod persistence;
41mod runnables;
42mod rust_analyzer_ext;
43pub mod scroll;
44mod selections_collection;
45pub mod semantic_tokens;
46mod split;
47pub mod split_editor_view;
48
49mod bookmarks;
50#[cfg(test)]
51mod code_completion_tests;
52#[cfg(test)]
53mod edit_prediction_tests;
54#[cfg(test)]
55mod editor_block_comment_tests;
56#[cfg(test)]
57mod editor_tests;
58mod signature_help;
59#[cfg(any(test, feature = "test-support"))]
60pub mod test;
61
62mod clipboard;
63mod code_actions;
64mod completions;
65mod config;
66mod diagnostics;
67mod edit_prediction;
68mod input;
69mod markdown_actions;
70mod navigation;
71mod rewrap;
72mod selection;
73
74pub(crate) use actions::*;
75pub use clipboard::ClipboardSelection;
76pub use code_actions::CodeActionProvider;
77use collections::TypeIdHashMap;
78pub use completions::CompletionProvider;
79#[cfg(test)]
80pub(crate) use completions::snippet_candidate_suffixes;
81pub(crate) use completions::split_words;
82use diagnostics::{ActiveDiagnostic, GlobalDiagnosticRenderer, InlineDiagnostic};
83pub use diagnostics::{DiagnosticRenderer, set_diagnostic_renderer};
84pub use display_map::{
85    ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder, HighlightKey,
86    NavigationOverlayKey, SemanticTokenHighlight,
87};
88pub use edit_prediction::make_suggestion_styles;
89pub(crate) use edit_prediction::{
90    EditDisplayMode, EditPrediction, EditPredictionPreview, EditPredictionSettings,
91    EditPredictionState, MenuEditPredictionsPolicy, RegisteredEditPredictionDelegate,
92};
93#[cfg(test)]
94pub(crate) use edit_prediction::{
95    EditPredictionKeybindAction, EditPredictionKeybindSurface, edit_prediction_edit_text,
96};
97pub use edit_prediction_types::Direction;
98pub use edit_prediction_types::EditPredictionRequestTrigger;
99pub use editor_settings::{
100    CompletionDetailAlignment, CompletionMenuItemKind, CurrentLineHighlight, DiffViewStyle,
101    DocumentColorsRenderMode, EditorSettings, EditorSettingsScrollbarProxy, OpenResultsIn,
102    ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowMinimap,
103    ui_scrollbar_settings_from_raw,
104};
105pub use element::{
106    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
107    file_status_label_color, render_breadcrumb_text,
108};
109pub use git::blame::BlameRenderer;
110pub use git::{
111    DiffHunkDelegate, ResolvedDiffHunk, ResolvedDiffHunks, RestoreOnlyDiffHunkDelegate,
112    RestoreOnlyUnstagedDiffHunkDelegate, UncommittedDiffHunkDelegate, render_diff_hunk_controls,
113    set_blame_renderer,
114};
115pub(crate) use git::{DiffHunkKey, StoredReviewComment};
116use git::{
117    DiffReviewDragState, DiffReviewOverlay, InlineBlamePopover, update_uncommitted_diff_for_buffer,
118};
119pub(crate) use git::{DisplayDiffHunk, PhantomDiffReviewIndicator};
120pub use hover_popover::hover_markdown_style;
121pub use inlays::Inlay;
122pub use items::MAX_TAB_TITLE_LEN;
123pub use linked_editing_ranges::LinkedEdits;
124pub use lsp::CompletionContext;
125pub use lsp_ext::lsp_tasks;
126pub use multi_buffer::{
127    Anchor, AnchorRangeExt, BufferOffset, ExcerptRange, MBTextSummary, MultiBuffer,
128    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
129    ToPoint,
130};
131pub use split::{DiffStyleControls, SplittableEditor, ToggleSplitDiff};
132pub use split_editor_view::SplitEditorView;
133pub use text::Bias;
134
135use ::git::{Blame, status::FileStatus};
136use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
137use anyhow::{Context as _, Result, anyhow, bail};
138use blink_manager::BlinkManager;
139use client::{Collaborator, ParticipantIndex, parse_zed_link};
140use clock::ReplicaId;
141use code_context_menus::{
142    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
143    CompletionsMenu, ContextMenuOrigin,
144};
145use code_lens::CodeLensState;
146use collections::{BTreeMap, HashMap, HashSet, VecDeque};
147use convert_case::{Case, Casing};
148use dap::TelemetrySpawnLocation;
149use display_map::*;
150use document_colors::LspColorData;
151use document_links::LspDocumentLinks;
152use edit_prediction_types::{
153    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
154    EditPredictionGranularity, SuggestionDisplayType,
155};
156use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
157use element::{LineWithInvisibles, PositionMap, layout_line};
158use futures::{
159    FutureExt,
160    future::{self, Shared},
161};
162use fuzzy::{StringMatch, StringMatchCandidate};
163use git::blame::{GitBlame, GlobalBlameRenderer};
164use gpui::{
165    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
166    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
167    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
168    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla, IsZero,
169    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
170    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
171    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
172    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
173    pulsating_between, px, relative, size,
174};
175use hover_links::{HoverLink, HoveredLinkState, find_file};
176use hover_popover::{HoverState, hide_hover};
177use indent_guides::ActiveIndentGuidesState;
178use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
179use itertools::{Either, Itertools};
180use language::{
181    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
182    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
183    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
184    IndentSize, Language, LanguageAwareStyling, LanguageName, LanguageRegistry, LanguageScope,
185    LocalFile, OffsetRangeExt, OutlineItem, Point, Selection, SelectionGoal, TextObject,
186    TransactionId, TreeSitterOptions, WordsQuery,
187    language_settings::{
188        self, AllLanguageSettings, LanguageSettings, LspInsertMode, RewrapBehavior,
189        WordsCompletionMode, all_language_settings,
190    },
191    point_from_lsp, point_to_lsp, text_diff_with_options,
192};
193use linked_editing_ranges::refresh_linked_ranges;
194use lsp::{
195    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
196    LanguageServerId,
197};
198use markdown::Markdown;
199use mouse_context_menu::MouseContextMenu;
200use movement::TextLayoutDetails;
201use multi_buffer::{
202    ExcerptBoundaryInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint,
203    MultiBufferRow,
204};
205use parking_lot::Mutex;
206use persistence::EditorDb;
207use project::{
208    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
209    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
210    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
211    ProjectItem, ProjectPath, ProjectTransaction,
212    bookmark_store::BookmarkStore,
213    debugger::{
214        breakpoint_store::{
215            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
216            BreakpointStore, BreakpointStoreEvent,
217        },
218        session::{Session, SessionEvent},
219    },
220    git_store::GitStoreEvent,
221    lsp_store::{
222        BufferSemanticTokens, CacheInlayHints, CompletionDocumentation, FormatTrigger,
223        LspFormatTarget, OpenLspBufferHandle,
224    },
225    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
226};
227use rand::seq::SliceRandom;
228use regex::Regex;
229use rpc::{ErrorCode, ErrorExt, proto::PeerId};
230use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
231use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
232use serde::{Deserialize, Serialize};
233use settings::{
234    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
235    update_settings_file,
236};
237use smallvec::{SmallVec, smallvec};
238use snippet::Snippet;
239use std::{
240    any::{Any, TypeId},
241    borrow::Cow,
242    cell::{OnceCell, RefCell},
243    cmp::{self, Ordering, Reverse},
244    collections::hash_map,
245    iter::{self, Peekable},
246    mem,
247    num::NonZeroU32,
248    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
249    path::{Path, PathBuf},
250    rc::Rc,
251    sync::Arc,
252    time::{Duration, Instant},
253};
254use task::TaskVariables;
255use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
256use theme::{
257    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
258};
259use theme_settings::{ThemeSettings, observe_buffer_font_size_adjustment};
260use ui::{
261    Avatar, ContextMenu, Disclosure, IconButtonShape, Indicator, Key, KeyBinding, Tooltip,
262    prelude::*, scrollbars::ScrollbarAutoHide, tooltip_container, utils::WithRemSize,
263};
264use ui_input::ErasedEditor;
265use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
266use workspace::{
267    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
268    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
269    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
270    item::{ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
271    notifications::{DetachAndPromptErr, NotificationId, NotifyResultExt, NotifyTaskExt},
272    searchable::SearchEvent,
273};
274pub use zed_actions::editor::RevealInFileManager;
275use zed_actions::editor::{MoveDown, MoveUp};
276
277use crate::{
278    code_context_menus::CompletionsMenuSource,
279    editor_settings::MultiCursorModifier,
280    hover_links::{find_url, find_url_from_range},
281    inlays::{
282        InlineValueCache,
283        inlay_hints::{LspInlayHintData, inlay_hint_settings},
284    },
285    runnables::{ResolvedTasks, RunnableData, RunnableTaskStatus, RunnableTasks},
286    scroll::{ScrollOffset, ScrollPixelOffset},
287    selections_collection::resolve_selections_wrapping_blocks,
288    semantic_tokens::SemanticTokenState,
289    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
290};
291
292pub const FILE_HEADER_HEIGHT: u32 = 2;
293pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
294pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
295const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
296const MAX_LINE_LEN: usize = 1024;
297const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
298const MAX_SELECTION_HISTORY_LEN: usize = 1024;
299pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
300#[doc(hidden)]
301pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
302pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
303
304pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
305pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
306pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
307pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
308
309pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
310pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
311
312enum ReportEditorEvent {
313    Saved { auto_saved: bool },
314    EditorOpened,
315    Closed,
316}
317
318impl ReportEditorEvent {
319    pub fn event_type(&self) -> &'static str {
320        match self {
321            Self::Saved { .. } => "Editor Saved",
322            Self::EditorOpened => "Editor Opened",
323            Self::Closed => "Editor Closed",
324        }
325    }
326}
327
328pub enum ActiveDebugLine {}
329pub enum DebugStackFrameLine {}
330
331pub enum ConflictsOuter {}
332pub enum ConflictsOurs {}
333pub enum ConflictsTheirs {}
334pub enum ConflictsOursMarker {}
335pub enum ConflictsTheirsMarker {}
336
337pub struct HunkAddedColor;
338pub struct HunkRemovedColor;
339
340#[derive(Debug, Copy, Clone, PartialEq, Eq)]
341pub enum Navigated {
342    Yes,
343    No,
344}
345
346impl Navigated {
347    pub fn from_bool(yes: bool) -> Navigated {
348        if yes { Navigated::Yes } else { Navigated::No }
349    }
350}
351
352pub fn init(cx: &mut App) {
353    cx.set_global(GlobalBlameRenderer(Arc::new(())));
354    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
355
356    workspace::register_project_item::<Editor>(cx);
357    workspace::FollowableViewRegistry::register::<Editor>(cx);
358    workspace::register_serializable_item::<Editor>(cx);
359
360    cx.observe_new(
361        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
362            workspace.register_action(Editor::new_file);
363            workspace.register_action(Editor::new_file_split);
364            workspace.register_action(Editor::new_file_vertical);
365            workspace.register_action(Editor::new_file_horizontal);
366            workspace.register_action(Editor::cancel_language_server_work);
367            workspace.register_action(Editor::toggle_focus);
368            workspace.register_action(Editor::view_bookmarks);
369        },
370    )
371    .detach();
372
373    cx.on_action(move |_: &workspace::NewFile, cx| {
374        let app_state = workspace::AppState::global(cx);
375        workspace::open_new(
376            Default::default(),
377            app_state,
378            cx,
379            |workspace, window, cx| Editor::new_file(workspace, &Default::default(), window, cx),
380        )
381        .detach_and_log_err(cx);
382    })
383    .on_action(move |_: &workspace::NewWindow, cx| {
384        let app_state = workspace::AppState::global(cx);
385        workspace::open_new(
386            Default::default(),
387            app_state,
388            cx,
389            |workspace, window, cx| {
390                cx.activate(true);
391                Editor::new_file(workspace, &Default::default(), window, cx)
392            },
393        )
394        .detach_and_log_err(cx);
395    });
396    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
397        Arc::new(ErasedEditorImpl(
398            cx.new(|cx| Editor::single_line(window, cx)),
399        )) as Arc<dyn ErasedEditor>
400    });
401    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
402}
403
404pub struct SearchWithinRange;
405
406trait InvalidationRegion {
407    fn ranges(&self) -> &[Range<Anchor>];
408}
409
410#[derive(Clone, Debug, PartialEq)]
411pub enum SelectPhase {
412    Begin {
413        position: DisplayPoint,
414        add: bool,
415        click_count: usize,
416    },
417    BeginColumnar {
418        position: DisplayPoint,
419        reset: bool,
420        mode: ColumnarMode,
421        goal_column: u32,
422    },
423    Extend {
424        position: DisplayPoint,
425        click_count: usize,
426    },
427    Update {
428        position: DisplayPoint,
429        goal_column: u32,
430        scroll_delta: gpui::Point<f32>,
431    },
432    End,
433}
434
435#[derive(Clone, Debug, PartialEq)]
436pub enum ColumnarMode {
437    FromMouse,
438    FromSelection,
439}
440
441#[derive(Clone, Debug)]
442pub enum SelectMode {
443    Character,
444    Word(Range<Anchor>),
445    Line(Range<Anchor>),
446    All,
447}
448
449#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
450pub enum SizingBehavior {
451    /// The editor will layout itself using `size_full` and will include the vertical
452    /// scroll margin as requested by user settings.
453    #[default]
454    Default,
455    /// The editor will layout itself using `size_full`, but will not have any
456    /// vertical overscroll.
457    ExcludeOverscrollMargin,
458    /// The editor will request a vertical size according to its content and will be
459    /// layouted without a vertical scroll margin.
460    SizeByContent,
461}
462
463#[derive(Clone, PartialEq, Eq, Debug)]
464pub enum EditorMode {
465    SingleLine,
466    AutoHeight {
467        min_lines: usize,
468        max_lines: Option<usize>,
469    },
470    Full {
471        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
472        scale_ui_elements_with_buffer_font_size: bool,
473        /// When set to `true`, the editor will render a background for the active line.
474        show_active_line_background: bool,
475        /// Determines the sizing behavior for this editor
476        sizing_behavior: SizingBehavior,
477    },
478    Minimap {
479        parent: WeakEntity<Editor>,
480    },
481}
482
483impl EditorMode {
484    pub fn full() -> Self {
485        Self::Full {
486            scale_ui_elements_with_buffer_font_size: true,
487            show_active_line_background: true,
488            sizing_behavior: SizingBehavior::Default,
489        }
490    }
491
492    #[inline]
493    pub fn is_full(&self) -> bool {
494        matches!(self, Self::Full { .. })
495    }
496
497    #[inline]
498    pub fn is_single_line(&self) -> bool {
499        matches!(self, Self::SingleLine { .. })
500    }
501
502    #[inline]
503    fn is_minimap(&self) -> bool {
504        matches!(self, Self::Minimap { .. })
505    }
506}
507
508#[derive(Copy, Clone, Debug)]
509pub enum SoftWrap {
510    /// Prefer not to wrap at all.
511    ///
512    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
513    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
514    GitDiff,
515    /// Prefer a single line generally, unless an overly long line is encountered.
516    None,
517    /// Soft wrap lines that exceed the editor width.
518    EditorWidth,
519    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
520    Bounded(u32),
521}
522
523#[derive(Clone)]
524pub struct EditorStyle {
525    pub background: Hsla,
526    pub border: Hsla,
527    pub local_player: PlayerColor,
528    pub text: TextStyle,
529    pub scrollbar_width: Pixels,
530    pub syntax: Arc<SyntaxTheme>,
531    pub status: StatusColors,
532    pub inlay_hints_style: HighlightStyle,
533    pub edit_prediction_styles: EditPredictionStyles,
534    pub unnecessary_code_fade: f32,
535    pub show_underlines: bool,
536}
537
538impl Default for EditorStyle {
539    fn default() -> Self {
540        static NONE_SYNTAX: std::sync::LazyLock<Arc<SyntaxTheme>> =
541            std::sync::LazyLock::new(|| Arc::new(SyntaxTheme::default()));
542        Self {
543            background: Hsla::default(),
544            border: Hsla::default(),
545            local_player: PlayerColor::default(),
546            text: TextStyle::default(),
547            scrollbar_width: Pixels::default(),
548            syntax: NONE_SYNTAX.clone(),
549            // HACK: Status colors don't have a real default.
550            // We should look into removing the status colors from the editor
551            // style and retrieve them directly from the theme.
552            status: StatusColors::dark(),
553            inlay_hints_style: HighlightStyle::default(),
554            edit_prediction_styles: EditPredictionStyles {
555                insertion: HighlightStyle::default(),
556                whitespace: HighlightStyle::default(),
557            },
558            unnecessary_code_fade: Default::default(),
559            show_underlines: true,
560        }
561    }
562}
563
564pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
565    let show_background = AllLanguageSettings::get_global(cx)
566        .defaults
567        .inlay_hints
568        .show_background;
569
570    let mut style = cx
571        .theme()
572        .syntax()
573        .style_for_name("hint")
574        .unwrap_or_default();
575
576    if style.color.is_none() {
577        style.color = Some(cx.theme().status().hint);
578    }
579
580    if !show_background {
581        style.background_color = None;
582        return style;
583    }
584
585    if style.background_color.is_none() {
586        style.background_color = Some(cx.theme().status().hint_background);
587    }
588
589    style
590}
591
592type CompletionId = usize;
593
594pub struct ContextMenuOptions {
595    pub min_entries_visible: usize,
596    pub max_entries_visible: usize,
597    pub placement: Option<ContextMenuPlacement>,
598}
599
600#[derive(Debug, Clone, PartialEq, Eq)]
601pub enum ContextMenuPlacement {
602    Above,
603    Below,
604}
605
606#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
607struct EditorActionId(usize);
608
609impl EditorActionId {
610    pub fn post_inc(&mut self) -> Self {
611        let answer = self.0;
612
613        *self = Self(answer + 1);
614
615        Self(answer)
616    }
617}
618
619type BackgroundHighlight = (
620    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
621    Arc<[Range<Anchor>]>,
622);
623type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
624
625#[derive(Default)]
626struct ScrollbarMarkerState {
627    scrollbar_size: Size<Pixels>,
628    dirty: bool,
629    markers: Arc<[PaintQuad]>,
630    pending_refresh: Option<Task<Result<()>>>,
631}
632
633impl ScrollbarMarkerState {
634    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
635        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
636    }
637}
638
639#[derive(Clone, Copy, PartialEq, Eq)]
640pub enum MinimapVisibility {
641    Disabled,
642    Enabled {
643        /// The configuration currently present in the users settings.
644        setting_configuration: bool,
645        /// Whether to override the currently set visibility from the users setting.
646        toggle_override: bool,
647    },
648}
649
650impl MinimapVisibility {
651    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
652        if mode.is_full() {
653            Self::Enabled {
654                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
655                toggle_override: false,
656            }
657        } else {
658            Self::Disabled
659        }
660    }
661
662    fn hidden(&self) -> Self {
663        match *self {
664            Self::Enabled {
665                setting_configuration,
666                ..
667            } => Self::Enabled {
668                setting_configuration,
669                toggle_override: setting_configuration,
670            },
671            Self::Disabled => Self::Disabled,
672        }
673    }
674
675    fn disabled(&self) -> bool {
676        matches!(*self, Self::Disabled)
677    }
678
679    fn settings_visibility(&self) -> bool {
680        match *self {
681            Self::Enabled {
682                setting_configuration,
683                ..
684            } => setting_configuration,
685            _ => false,
686        }
687    }
688
689    fn visible(&self) -> bool {
690        match *self {
691            Self::Enabled {
692                setting_configuration,
693                toggle_override,
694            } => setting_configuration ^ toggle_override,
695            _ => false,
696        }
697    }
698
699    fn toggle_visibility(&self) -> Self {
700        match *self {
701            Self::Enabled {
702                toggle_override,
703                setting_configuration,
704            } => Self::Enabled {
705                setting_configuration,
706                toggle_override: !toggle_override,
707            },
708            Self::Disabled => Self::Disabled,
709        }
710    }
711}
712
713#[derive(Clone, Copy, PartialEq, Eq)]
714struct BreadcrumbsVisibility {
715    setting_configuration: bool,
716    toggle_override: bool,
717}
718
719impl BreadcrumbsVisibility {
720    fn from_settings(cx: &App) -> Self {
721        Self::new(EditorSettings::get_global(cx).toolbar.breadcrumbs)
722    }
723
724    fn new(setting_configuration: bool) -> Self {
725        Self {
726            setting_configuration,
727            toggle_override: false,
728        }
729    }
730
731    fn settings_visibility(&self) -> bool {
732        self.setting_configuration
733    }
734
735    fn visible(&self) -> bool {
736        self.setting_configuration ^ self.toggle_override
737    }
738
739    fn toggle_visibility(&self) -> Self {
740        Self {
741            setting_configuration: self.setting_configuration,
742            toggle_override: !self.toggle_override,
743        }
744    }
745}
746
747#[derive(Debug, Clone, Copy, PartialEq, Eq)]
748pub enum BufferSerialization {
749    All,
750    NonDirtyBuffers,
751}
752
753impl BufferSerialization {
754    fn new(restore_unsaved_buffers: bool) -> Self {
755        if restore_unsaved_buffers {
756            Self::All
757        } else {
758            Self::NonDirtyBuffers
759        }
760    }
761}
762
763/// Addons allow storing per-editor state in other crates (e.g. Vim)
764pub trait Addon: 'static {
765    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
766
767    fn render_buffer_header_controls(
768        &self,
769        _: &ExcerptBoundaryInfo,
770        _: &language::BufferSnapshot,
771        _: &Window,
772        _: &App,
773    ) -> Option<AnyElement> {
774        None
775    }
776
777    fn extend_buffer_header_context_menu(
778        &self,
779        menu: ui::ContextMenu,
780        _: &language::BufferSnapshot,
781        _: &mut Window,
782        _: &mut App,
783    ) -> ui::ContextMenu {
784        menu
785    }
786
787    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
788        None
789    }
790
791    fn to_any(&self) -> &dyn std::any::Any;
792
793    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
794        None
795    }
796}
797
798struct ChangeLocation {
799    current: Option<Vec<Anchor>>,
800    original: Vec<Anchor>,
801}
802impl ChangeLocation {
803    fn locations(&self) -> &[Anchor] {
804        self.current.as_ref().unwrap_or(&self.original)
805    }
806}
807
808/// A set of caret positions, registered when the editor was edited.
809pub struct ChangeList {
810    changes: Vec<ChangeLocation>,
811    /// Currently "selected" change.
812    position: Option<usize>,
813}
814
815impl ChangeList {
816    pub fn new() -> Self {
817        Self {
818            changes: Vec::new(),
819            position: None,
820        }
821    }
822
823    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
824    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
825    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
826        if self.changes.is_empty() {
827            return None;
828        }
829
830        let prev = self.position.unwrap_or(self.changes.len());
831        let next = if direction == Direction::Prev {
832            prev.saturating_sub(count)
833        } else {
834            (prev + count).min(self.changes.len() - 1)
835        };
836        self.position = Some(next);
837        self.changes.get(next).map(|change| change.locations())
838    }
839
840    /// Adds a new change to the list, resetting the change list position.
841    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
842        self.position.take();
843        if let Some(last) = self.changes.last_mut()
844            && group
845        {
846            last.current = Some(new_positions)
847        } else {
848            self.changes.push(ChangeLocation {
849                original: new_positions,
850                current: None,
851            });
852        }
853    }
854
855    pub fn last(&self) -> Option<&[Anchor]> {
856        self.changes.last().map(|change| change.locations())
857    }
858
859    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
860        self.changes.last().map(|change| change.original.as_slice())
861    }
862
863    pub fn invert_last_group(&mut self) {
864        if let Some(last) = self.changes.last_mut()
865            && let Some(current) = last.current.as_mut()
866        {
867            mem::swap(&mut last.original, current);
868        }
869    }
870}
871
872enum SelectionDragState {
873    /// State when no drag related activity is detected.
874    None,
875    /// State when the mouse is down on a selection that is about to be dragged.
876    ReadyToDrag {
877        selection: Selection<Anchor>,
878        click_position: gpui::Point<Pixels>,
879        mouse_down_time: Instant,
880    },
881    /// State when the mouse is dragging the selection in the editor.
882    Dragging {
883        selection: Selection<Anchor>,
884        drop_cursor: Selection<Anchor>,
885        hide_drop_cursor: bool,
886    },
887}
888
889enum ColumnarSelectionState {
890    FromMouse {
891        selection_tail: Anchor,
892        display_point: Option<DisplayPoint>,
893    },
894    FromSelection {
895        selection_tail: Anchor,
896    },
897}
898
899/// Represents a button that shows up when hovering over lines in the gutter that don't have
900/// any button on them already (like a bookmark, breakpoint or run indicator).
901#[derive(Clone, Copy, Debug, PartialEq, Eq)]
902struct GutterHoverButton {
903    display_row: DisplayRow,
904    /// There's a small debounce between hovering over the line and showing the indicator.
905    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
906    is_active: bool,
907}
908
909enum CodeActionsForSelection {
910    None,
911    Fetching(Shared<Task<Option<ActionFetchReady>>>),
912    Ready(ActionFetchReady),
913}
914
915#[derive(Clone)]
916struct ActionFetchReady {
917    location: Location,
918    actions: Rc<[AvailableCodeAction]>,
919}
920
921/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
922///
923/// See the [module level documentation](self) for more information.
924pub struct Editor {
925    focus_handle: FocusHandle,
926    last_focused_descendant: Option<WeakFocusHandle>,
927    /// The text buffer being edited
928    buffer: Entity<MultiBuffer>,
929    /// Map of how text in the buffer should be displayed.
930    /// Handles soft wraps, folds, fake inlay text insertions, etc.
931    pub display_map: Entity<DisplayMap>,
932    placeholder_display_map: Option<Entity<DisplayMap>>,
933    pub selections: SelectionsCollection,
934    /// Manages the scroll position for the given editor.
935    ///
936    /// Whenever you want to modify the scroll position of the editor, you should
937    /// usually use the existing available APIs as opposed to directly interacting
938    /// with the scroll manager.
939    pub scroll_manager: ScrollManager,
940    /// When inline assist editors are linked, they all render cursors because
941    /// typing enters text into each of them, even the ones that aren't focused.
942    pub(crate) show_cursor_when_unfocused: bool,
943    columnar_selection_state: Option<ColumnarSelectionState>,
944    add_selections_state: Option<AddSelectionsState>,
945    select_next_state: Option<SelectNextState>,
946    select_prev_state: Option<SelectNextState>,
947    selection_history: SelectionHistory,
948    defer_selection_effects: bool,
949    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
950    autoclose_regions: Vec<AutocloseRegion>,
951    snippet_stack: InvalidationStack<SnippetState>,
952    select_syntax_node_history: SelectSyntaxNodeHistory,
953    ime_transaction: Option<TransactionId>,
954    pub diagnostics_max_severity: DiagnosticSeverity,
955    active_diagnostics: ActiveDiagnostic,
956    show_inline_diagnostics: bool,
957    inline_diagnostics_update: Task<()>,
958    inline_diagnostics_enabled: bool,
959    diagnostics_enabled: bool,
960    word_completions_enabled: bool,
961    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
962    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
963    hard_wrap: Option<usize>,
964    project: Option<Entity<Project>>,
965    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
966    completion_provider: Option<Rc<dyn CompletionProvider>>,
967    collaboration_hub: Option<Box<dyn CollaborationHub>>,
968    blink_manager: Entity<BlinkManager>,
969    show_cursor_names: bool,
970    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
971    pub show_local_selections: bool,
972    mode: EditorMode,
973    breadcrumbs_visibility: BreadcrumbsVisibility,
974    show_gutter: bool,
975    show_scrollbars: ScrollbarAxes,
976    minimap_visibility: MinimapVisibility,
977    offset_content: bool,
978    disable_expand_excerpt_buttons: bool,
979    delegate_expand_excerpts: bool,
980    delegate_open_excerpts: bool,
981    enable_lsp_data: bool,
982    needs_initial_data_update: bool,
983    enable_runnables: bool,
984    enable_code_lens: bool,
985    enable_mouse_wheel_zoom: bool,
986    show_line_numbers: Option<bool>,
987    use_relative_line_numbers: Option<bool>,
988    show_git_diff_gutter: Option<bool>,
989    show_code_actions: Option<bool>,
990    show_runnables: Option<bool>,
991    show_bookmarks: Option<bool>,
992    show_breakpoints: Option<bool>,
993    show_diff_review_button: bool,
994    show_wrap_guides: Option<bool>,
995    show_indent_guides: Option<bool>,
996    buffers_with_disabled_indent_guides: HashSet<BufferId>,
997    highlight_order: usize,
998    highlighted_rows: TypeIdHashMap<Vec<RowHighlight>>,
999    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
1000    navigation_overlays: HashMap<NavigationOverlayKey, Arc<[NavigationTargetOverlay]>>,
1001    gutter_highlights: TypeIdHashMap<GutterHighlight>,
1002    allow_git_diff_scrollbar_markers: bool,
1003    scrollbar_marker_state: ScrollbarMarkerState,
1004    active_indent_guides_state: ActiveIndentGuidesState,
1005    nav_history: Option<ItemNavHistory>,
1006    context_menu: RefCell<Option<CodeContextMenu>>,
1007    context_menu_options: Option<ContextMenuOptions>,
1008    mouse_context_menu: Option<MouseContextMenu>,
1009    completion_tasks: Vec<(CompletionId, Task<()>)>,
1010    inline_blame_popover: Option<InlineBlamePopover>,
1011    inline_blame_popover_show_task: Option<Task<()>>,
1012    signature_help_state: SignatureHelpState,
1013    auto_signature_help: Option<bool>,
1014    find_all_references_task_sources: Vec<Anchor>,
1015    next_completion_id: CompletionId,
1016    code_actions_for_selection: CodeActionsForSelection,
1017    runnables_for_selection_toggle: Task<()>,
1018    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
1019    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
1020    debounced_selection_highlight_complete: bool,
1021    last_selection_from_search: bool,
1022    document_highlights_task: Option<Task<()>>,
1023    linked_editing_range_task: Option<Task<Option<()>>>,
1024    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
1025    pending_rename: Option<RenameState>,
1026    searchable: bool,
1027    cursor_shape: CursorShape,
1028    /// Whether the cursor is offset one character to the left when something is
1029    /// selected (needed for vim visual mode)
1030    cursor_offset_on_selection: bool,
1031    current_line_highlight: Option<CurrentLineHighlight>,
1032    /// Whether to collapse search match ranges to just their start position.
1033    /// When true, navigating to a match positions the cursor at the match
1034    /// without selecting the matched text.
1035    collapse_matches: bool,
1036    autoindent_mode: Option<AutoindentMode>,
1037    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
1038    input_enabled: bool,
1039    expects_character_input: bool,
1040    use_modal_editing: bool,
1041    read_only: bool,
1042    leader_id: Option<CollaboratorId>,
1043    remote_id: Option<ViewId>,
1044    pub hover_state: HoverState,
1045    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
1046    prev_pressure_stage: Option<PressureStage>,
1047    gutter_hovered: bool,
1048    hovered_link_state: Option<HoveredLinkState>,
1049    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
1050    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
1051    active_edit_prediction: Option<EditPredictionState>,
1052    /// Used to prevent flickering as the user types while the menu is open
1053    stale_edit_prediction_in_menu: Option<EditPredictionState>,
1054    edit_prediction_settings: EditPredictionSettings,
1055    edit_predictions_hidden_for_vim_mode: bool,
1056    show_edit_predictions_override: Option<bool>,
1057    show_completions_on_input_override: Option<bool>,
1058    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
1059    edit_prediction_preview: EditPredictionPreview,
1060    in_leading_whitespace: bool,
1061    next_inlay_id: usize,
1062    next_color_inlay_id: usize,
1063    _subscriptions: Vec<Subscription>,
1064    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
1065    gutter_dimensions: GutterDimensions,
1066    style: Option<EditorStyle>,
1067    text_style_refinement: Option<TextStyleRefinement>,
1068    next_editor_action_id: EditorActionId,
1069    editor_actions: Rc<
1070        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
1071    >,
1072    use_autoclose: bool,
1073    use_auto_surround: bool,
1074    use_selection_highlight: bool,
1075    auto_replace_emoji_shortcode: bool,
1076    jsx_tag_auto_close_enabled_in_any_buffer: bool,
1077    show_git_blame_gutter: bool,
1078    show_git_blame_inline: bool,
1079    show_git_blame_inline_delay_task: Option<Task<()>>,
1080    git_blame_inline_enabled: bool,
1081    buffer_serialization: Option<BufferSerialization>,
1082    show_selection_menu: Option<bool>,
1083    blame: Option<Entity<GitBlame>>,
1084    blame_subscription: Option<Subscription>,
1085    pending_blame_hover_observation: Option<Subscription>,
1086    custom_context_menu: Option<
1087        Box<
1088            dyn 'static
1089                + Fn(
1090                    &mut Self,
1091                    DisplayPoint,
1092                    &mut Window,
1093                    &mut Context<Self>,
1094                ) -> Option<Entity<ui::ContextMenu>>,
1095        >,
1096    >,
1097    last_bounds: Option<Bounds<Pixels>>,
1098    last_position_map: Option<Rc<PositionMap>>,
1099    /// The right margin (vertical scrollbar + minimap width) the editor was
1100    /// last laid out with, updated on every prepaint.
1101    /// Used later in the frame by `SplitBufferHeadersElement` to shrink the
1102    /// width available to buffer headers.
1103    last_right_margin: Pixels,
1104    /// Whether the horizontal scrollbar was laid out as visible during the last
1105    /// prepaint.
1106    /// Used by `SplitBufferHeadersElement` to clip buffer headers so they don't
1107    /// paint over the scrollbar.
1108    last_horizontal_scrollbar_visible: bool,
1109    expect_bounds_change: Option<Bounds<Pixels>>,
1110    runnables: RunnableData,
1111    bookmark_store: Option<Entity<BookmarkStore>>,
1112    breakpoint_store: Option<Entity<BreakpointStore>>,
1113    gutter_hover_button: (Option<GutterHoverButton>, Option<Task<()>>),
1114    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
1115    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
1116    /// Active diff review overlays. Multiple overlays can be open simultaneously
1117    /// when hunks have comments stored.
1118    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
1119    /// Stored review comments grouped by hunk.
1120    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
1121    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
1122    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
1123    /// Counter for generating unique comment IDs.
1124    next_review_comment_id: usize,
1125    hovered_diff_hunk_row: Option<DisplayRow>,
1126    pull_diagnostics_task: Task<()>,
1127    in_project_search: bool,
1128    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
1129    breadcrumb_header: Option<String>,
1130    focused_block: Option<FocusedBlock>,
1131    next_scroll_position: NextScrollCursorCenterTopBottom,
1132    addons: TypeIdHashMap<Box<dyn Addon>>,
1133    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
1134    load_diff_task: Option<Shared<Task<()>>>,
1135    diff_hunk_delegate: Option<Arc<dyn DiffHunkDelegate>>,
1136    selection_mark_mode: bool,
1137    toggle_fold_multiple_buffers: Task<()>,
1138    _scroll_cursor_center_top_bottom_task: Task<()>,
1139    serialize_selections: Task<()>,
1140    serialize_folds: Task<()>,
1141    minimap: Option<Entity<Self>>,
1142    pub change_list: ChangeList,
1143    inline_value_cache: InlineValueCache,
1144    number_deleted_lines: bool,
1145
1146    selection_drag_state: SelectionDragState,
1147    colors: Option<LspColorData>,
1148    code_lens: Option<CodeLensState>,
1149    post_scroll_update: Task<()>,
1150    refresh_colors_task: Task<()>,
1151    refresh_code_lens_task: Task<()>,
1152    use_document_folding_ranges: bool,
1153    refresh_folding_ranges_task: Task<()>,
1154    inlay_hints: Option<LspInlayHintData>,
1155    folding_newlines: Task<()>,
1156    select_next_is_case_sensitive: Option<bool>,
1157    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
1158    on_local_selections_changed:
1159        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
1160    suppress_selection_callback: bool,
1161    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
1162    accent_data: Option<AccentData>,
1163    bracket_fetched_tree_sitter_chunks: HashMap<Range<text::Anchor>, HashSet<Range<BufferRow>>>,
1164    semantic_token_state: SemanticTokenState,
1165    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
1166    refresh_document_symbols_task: Shared<Task<()>>,
1167    lsp_document_links: LspDocumentLinks,
1168    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
1169    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
1170    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
1171    sticky_headers_task: Task<()>,
1172    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
1173    pub(crate) colorize_brackets_task: Task<()>,
1174}
1175
1176#[derive(Debug, PartialEq)]
1177struct AccentData {
1178    colors: AccentColors,
1179    overrides: Vec<SharedString>,
1180}
1181
1182fn debounce_value(debounce_ms: u64) -> Option<Duration> {
1183    if debounce_ms > 0 {
1184        Some(Duration::from_millis(debounce_ms))
1185    } else {
1186        None
1187    }
1188}
1189
1190#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
1191enum NextScrollCursorCenterTopBottom {
1192    #[default]
1193    Center,
1194    Top,
1195    Bottom,
1196}
1197
1198impl NextScrollCursorCenterTopBottom {
1199    fn next(&self) -> Self {
1200        match self {
1201            Self::Center => Self::Top,
1202            Self::Top => Self::Bottom,
1203            Self::Bottom => Self::Center,
1204        }
1205    }
1206}
1207
1208#[derive(Clone)]
1209pub struct EditorSnapshot {
1210    pub mode: EditorMode,
1211    show_gutter: bool,
1212    offset_content: bool,
1213    show_line_numbers: Option<bool>,
1214    number_deleted_lines: bool,
1215    show_git_diff_gutter: Option<bool>,
1216    show_code_actions: Option<bool>,
1217    show_runnables: Option<bool>,
1218    show_breakpoints: Option<bool>,
1219    show_bookmarks: Option<bool>,
1220    git_blame_gutter_max_author_length: Option<usize>,
1221    pub display_snapshot: DisplaySnapshot,
1222    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
1223    is_focused: bool,
1224    scroll_anchor: SharedScrollAnchor,
1225    ongoing_scroll: OngoingScroll,
1226    current_line_highlight: CurrentLineHighlight,
1227    gutter_hovered: bool,
1228    semantic_tokens_enabled: bool,
1229}
1230
1231#[derive(Clone, Debug, PartialEq)]
1232pub struct NavigationTargetOverlay {
1233    pub target_range: Range<Anchor>,
1234    pub label: NavigationOverlayLabel,
1235    pub covered_text_range: Option<Range<Anchor>>,
1236}
1237
1238#[derive(Clone, Debug, PartialEq)]
1239pub struct NavigationOverlayLabel {
1240    pub text: SharedString,
1241    pub text_color: Hsla,
1242    pub x_offset: Pixels,
1243    pub scale_factor: f32,
1244}
1245
1246#[derive(Default, Debug, Clone, Copy)]
1247pub struct GutterDimensions {
1248    pub left_padding: Pixels,
1249    pub right_padding: Pixels,
1250    pub width: Pixels,
1251    pub margin: Pixels,
1252    pub git_blame_entries_width: Option<Pixels>,
1253}
1254
1255impl GutterDimensions {
1256    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
1257        Self {
1258            margin: Self::default_gutter_margin(font_id, font_size, cx),
1259            ..Default::default()
1260        }
1261    }
1262
1263    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
1264        -cx.text_system().descent(font_id, font_size)
1265    }
1266    /// The full width of the space taken up by the gutter.
1267    pub fn full_width(&self) -> Pixels {
1268        self.margin + self.width
1269    }
1270}
1271
1272struct CharacterDimensions {
1273    em_width: Pixels,
1274    em_advance: Pixels,
1275    line_height: Pixels,
1276}
1277
1278#[derive(Debug)]
1279pub struct RemoteSelection {
1280    pub replica_id: ReplicaId,
1281    pub selection: Selection<Anchor>,
1282    pub cursor_shape: CursorShape,
1283    pub collaborator_id: CollaboratorId,
1284    pub line_mode: bool,
1285    pub user_name: Option<SharedString>,
1286    pub color: PlayerColor,
1287}
1288
1289#[derive(Clone, Debug)]
1290struct SelectionHistoryEntry {
1291    selections: Arc<[Selection<Anchor>]>,
1292    select_next_state: Option<SelectNextState>,
1293    select_prev_state: Option<SelectNextState>,
1294    add_selections_state: Option<AddSelectionsState>,
1295}
1296
1297#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
1298enum SelectionHistoryMode {
1299    #[default]
1300    Normal,
1301    Undoing,
1302    Redoing,
1303    Skipping,
1304}
1305
1306#[derive(Clone, PartialEq, Eq, Hash)]
1307struct HoveredCursor {
1308    replica_id: ReplicaId,
1309    selection_id: usize,
1310}
1311
1312#[derive(Debug)]
1313/// SelectionEffects controls the side-effects of updating the selection.
1314///
1315/// The default behaviour does "what you mostly want":
1316/// - it pushes to the nav history if the cursor moved by >10 lines
1317/// - it re-triggers completion requests
1318/// - it scrolls to fit
1319///
1320/// You might want to modify these behaviours. For example when doing a "jump"
1321/// like go to definition, we always want to add to nav history; but when scrolling
1322/// in vim mode we never do.
1323///
1324/// Similarly, you might want to disable scrolling if you don't want the viewport to
1325/// move.
1326#[derive(Clone)]
1327pub struct SelectionEffects {
1328    nav_history: Option<bool>,
1329    completions: bool,
1330    scroll: Option<Autoscroll>,
1331    from_search: bool,
1332}
1333
1334impl Default for SelectionEffects {
1335    fn default() -> Self {
1336        Self {
1337            nav_history: None,
1338            completions: true,
1339            scroll: Some(Autoscroll::fit()),
1340            from_search: false,
1341        }
1342    }
1343}
1344impl SelectionEffects {
1345    pub fn scroll(scroll: Autoscroll) -> Self {
1346        Self {
1347            scroll: Some(scroll),
1348            ..Default::default()
1349        }
1350    }
1351
1352    pub fn no_scroll() -> Self {
1353        Self {
1354            scroll: None,
1355            ..Default::default()
1356        }
1357    }
1358
1359    pub fn completions(self, completions: bool) -> Self {
1360        Self {
1361            completions,
1362            ..self
1363        }
1364    }
1365
1366    pub fn nav_history(self, nav_history: bool) -> Self {
1367        Self {
1368            nav_history: Some(nav_history),
1369            ..self
1370        }
1371    }
1372
1373    pub fn from_search(self, from_search: bool) -> Self {
1374        Self {
1375            from_search,
1376            ..self
1377        }
1378    }
1379}
1380
1381struct DeferredSelectionEffectsState {
1382    changed: bool,
1383    effects: SelectionEffects,
1384    old_cursor_position: Anchor,
1385    history_entry: SelectionHistoryEntry,
1386}
1387
1388#[derive(Clone, Debug)]
1389pub struct TransactionSelections {
1390    pub undo: Arc<[Selection<Anchor>]>,
1391    pub redo: Option<Arc<[Selection<Anchor>]>>,
1392}
1393
1394#[derive(Default)]
1395struct SelectionHistory {
1396    selections_by_transaction: HashMap<TransactionId, TransactionSelections>,
1397    mode: SelectionHistoryMode,
1398    undo_stack: VecDeque<SelectionHistoryEntry>,
1399    redo_stack: VecDeque<SelectionHistoryEntry>,
1400}
1401
1402impl SelectionHistory {
1403    #[track_caller]
1404    fn insert_transaction(
1405        &mut self,
1406        transaction_id: TransactionId,
1407        selections: Arc<[Selection<Anchor>]>,
1408    ) {
1409        if selections.is_empty() {
1410            log::error!(
1411                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
1412                std::panic::Location::caller()
1413            );
1414            return;
1415        }
1416        self.selections_by_transaction.insert(
1417            transaction_id,
1418            TransactionSelections {
1419                undo: selections,
1420                redo: None,
1421            },
1422        );
1423    }
1424
1425    fn transaction(&self, transaction_id: TransactionId) -> Option<&TransactionSelections> {
1426        self.selections_by_transaction.get(&transaction_id)
1427    }
1428
1429    fn transaction_mut(
1430        &mut self,
1431        transaction_id: TransactionId,
1432    ) -> Option<&mut TransactionSelections> {
1433        self.selections_by_transaction.get_mut(&transaction_id)
1434    }
1435
1436    fn push(&mut self, entry: SelectionHistoryEntry) {
1437        if !entry.selections.is_empty() {
1438            match self.mode {
1439                SelectionHistoryMode::Normal => {
1440                    self.push_undo(entry);
1441                    self.redo_stack.clear();
1442                }
1443                SelectionHistoryMode::Undoing => self.push_redo(entry),
1444                SelectionHistoryMode::Redoing => self.push_undo(entry),
1445                SelectionHistoryMode::Skipping => {}
1446            }
1447        }
1448    }
1449
1450    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
1451        if self
1452            .undo_stack
1453            .back()
1454            .is_none_or(|e| e.selections != entry.selections)
1455        {
1456            self.undo_stack.push_back(entry);
1457            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
1458                self.undo_stack.pop_front();
1459            }
1460        }
1461    }
1462
1463    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
1464        if self
1465            .redo_stack
1466            .back()
1467            .is_none_or(|e| e.selections != entry.selections)
1468        {
1469            self.redo_stack.push_back(entry);
1470            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
1471                self.redo_stack.pop_front();
1472            }
1473        }
1474    }
1475}
1476
1477#[derive(Clone, Copy)]
1478pub struct RowHighlightOptions {
1479    pub autoscroll: bool,
1480    pub include_gutter: bool,
1481}
1482
1483impl Default for RowHighlightOptions {
1484    fn default() -> Self {
1485        Self {
1486            autoscroll: Default::default(),
1487            include_gutter: true,
1488        }
1489    }
1490}
1491
1492struct RowHighlight {
1493    index: usize,
1494    range: Range<Anchor>,
1495    color: fn(&App) -> Hsla,
1496    options: RowHighlightOptions,
1497    type_id: TypeId,
1498}
1499
1500#[derive(Clone, Debug)]
1501struct AddSelectionsState {
1502    groups: Vec<AddSelectionsGroup>,
1503}
1504
1505#[derive(Clone, Debug)]
1506struct AddSelectionsGroup {
1507    above: bool,
1508    stack: Vec<usize>,
1509}
1510
1511#[derive(Clone)]
1512struct SelectNextState {
1513    query: AhoCorasick,
1514    wordwise: bool,
1515    done: bool,
1516}
1517
1518impl std::fmt::Debug for SelectNextState {
1519    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1520        f.debug_struct(std::any::type_name::<Self>())
1521            .field("wordwise", &self.wordwise)
1522            .field("done", &self.done)
1523            .finish()
1524    }
1525}
1526
1527#[derive(Debug)]
1528struct AutocloseRegion {
1529    selection_id: usize,
1530    range: Range<Anchor>,
1531    pair: BracketPair,
1532}
1533
1534#[derive(Debug)]
1535struct SnippetState {
1536    ranges: Vec<Vec<Range<Anchor>>>,
1537    active_index: usize,
1538    choices: Vec<Option<Vec<String>>>,
1539}
1540
1541#[doc(hidden)]
1542pub struct RenameState {
1543    pub range: Range<Anchor>,
1544    pub old_name: Arc<str>,
1545    pub editor: Entity<Editor>,
1546    block_id: CustomBlockId,
1547}
1548
1549struct InvalidationStack<T>(Vec<T>);
1550
1551// selections, scroll behavior, was newest selection reversed
1552type SelectSyntaxNodeHistoryState = (
1553    Box<[Selection<Anchor>]>,
1554    SelectSyntaxNodeScrollBehavior,
1555    bool,
1556);
1557
1558#[derive(Default)]
1559struct SelectSyntaxNodeHistory {
1560    stack: Vec<SelectSyntaxNodeHistoryState>,
1561    // disable temporarily to allow changing selections without losing the stack
1562    pub disable_clearing: bool,
1563}
1564
1565impl SelectSyntaxNodeHistory {
1566    pub fn try_clear(&mut self) {
1567        if !self.disable_clearing {
1568            self.stack.clear();
1569        }
1570    }
1571
1572    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
1573        self.stack.push(selection);
1574    }
1575
1576    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
1577        self.stack.pop()
1578    }
1579}
1580
1581enum SelectSyntaxNodeScrollBehavior {
1582    CursorTop,
1583    FitSelection,
1584    CursorBottom,
1585}
1586
1587#[derive(Debug, Clone, Copy)]
1588pub(crate) struct NavigationData {
1589    cursor_anchor: Anchor,
1590    cursor_position: Point,
1591    scroll_anchor: ScrollAnchor,
1592    scroll_top_row: u32,
1593}
1594
1595#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1596pub enum GotoDefinitionKind {
1597    Symbol,
1598    Declaration,
1599    Type,
1600    Implementation,
1601}
1602
1603pub enum FormatTarget {
1604    Buffers(HashSet<Entity<Buffer>>),
1605    Ranges(Vec<Range<MultiBufferPoint>>),
1606}
1607
1608pub(crate) struct FocusedBlock {
1609    id: BlockId,
1610    focus_handle: WeakFocusHandle,
1611}
1612
1613#[derive(Clone, Debug)]
1614pub enum JumpData {
1615    MultiBufferRow {
1616        row: MultiBufferRow,
1617        line_offset_from_top: u32,
1618    },
1619    MultiBufferPoint {
1620        anchor: language::Anchor,
1621        position: Point,
1622        line_offset_from_top: u32,
1623    },
1624}
1625
1626pub enum MultibufferSelectionMode {
1627    First,
1628    All,
1629}
1630
1631#[derive(Clone, Copy, Debug, Default)]
1632pub struct RewrapOptions {
1633    pub override_language_settings: bool,
1634    pub preserve_existing_whitespace: bool,
1635    pub line_length: Option<usize>,
1636}
1637
1638#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1639enum GutterButtonIntent {
1640    SetBookmark,
1641    SetBreakpoint,
1642}
1643
1644impl GutterButtonIntent {
1645    fn as_str(&self) -> &'static str {
1646        match self {
1647            Self::SetBookmark => "Set Bookmark",
1648            Self::SetBreakpoint => "Set Breakpoint",
1649        }
1650    }
1651
1652    fn icon(&self) -> ui::IconName {
1653        match self {
1654            Self::SetBookmark => ui::IconName::Bookmark,
1655            Self::SetBreakpoint => ui::IconName::DebugBreakpoint,
1656        }
1657    }
1658
1659    fn color(&self) -> Color {
1660        match self {
1661            Self::SetBookmark => Color::Info,
1662            Self::SetBreakpoint => Color::Hint,
1663        }
1664    }
1665
1666    fn action(&self) -> &'static dyn Action {
1667        match self {
1668            Self::SetBookmark => &ToggleBookmark,
1669            Self::SetBreakpoint => &ToggleBreakpoint,
1670        }
1671    }
1672}
1673
1674struct GutterButtonTooltip {
1675    primary: GutterButtonIntent,
1676    secondary: GutterButtonIntent,
1677    focus_handle: FocusHandle,
1678}
1679
1680impl GutterButtonTooltip {
1681    fn active_intent(&self, modifiers: Modifiers) -> GutterButtonIntent {
1682        if modifiers.secondary() {
1683            self.secondary
1684        } else {
1685            self.primary
1686        }
1687    }
1688
1689    fn meta_text(&self, intent: GutterButtonIntent) -> String {
1690        const RIGHT_CLICK_HINT: &str = "right-click for more options";
1691
1692        if self.primary == self.secondary {
1693            return RIGHT_CLICK_HINT.to_string();
1694        }
1695        let modifier_as_text = gpui::Keystroke {
1696            modifiers: Modifiers::secondary_key(),
1697            ..Default::default()
1698        };
1699        let other = match intent {
1700            GutterButtonIntent::SetBookmark => "breakpoint",
1701            GutterButtonIntent::SetBreakpoint => "bookmark",
1702        };
1703        format!("{modifier_as_text}-click to add a {other}\n{RIGHT_CLICK_HINT}")
1704    }
1705}
1706
1707impl Render for GutterButtonTooltip {
1708    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
1709        let intent = self.active_intent(window.modifiers());
1710        let key_binding = KeyBinding::for_action_in(intent.action(), &self.focus_handle, cx);
1711        let meta_text = self.meta_text(intent);
1712
1713        tooltip_container(cx, move |this, _| {
1714            this.child(
1715                h_flex()
1716                    .justify_between()
1717                    .child(intent.as_str())
1718                    .child(key_binding),
1719            )
1720            .child(
1721                Label::new(meta_text)
1722                    .size(LabelSize::Small)
1723                    .color(Color::Muted),
1724            )
1725        })
1726    }
1727}
1728
1729impl Editor {
1730    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1731        let buffer = cx.new(|cx| Buffer::local("", cx));
1732        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1733        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
1734    }
1735
1736    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1737        let buffer = cx.new(|cx| Buffer::local("", cx));
1738        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1739        Self::new(EditorMode::full(), buffer, None, window, cx)
1740    }
1741
1742    pub fn auto_height(
1743        min_lines: usize,
1744        max_lines: usize,
1745        window: &mut Window,
1746        cx: &mut Context<Self>,
1747    ) -> Self {
1748        let buffer = cx.new(|cx| Buffer::local("", cx));
1749        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1750        Self::new(
1751            EditorMode::AutoHeight {
1752                min_lines,
1753                max_lines: Some(max_lines),
1754            },
1755            buffer,
1756            None,
1757            window,
1758            cx,
1759        )
1760    }
1761
1762    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
1763    /// The editor grows as tall as needed to fit its content.
1764    pub fn auto_height_unbounded(
1765        min_lines: usize,
1766        window: &mut Window,
1767        cx: &mut Context<Self>,
1768    ) -> Self {
1769        let buffer = cx.new(|cx| Buffer::local("", cx));
1770        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1771        Self::new(
1772            EditorMode::AutoHeight {
1773                min_lines,
1774                max_lines: None,
1775            },
1776            buffer,
1777            None,
1778            window,
1779            cx,
1780        )
1781    }
1782
1783    pub fn for_buffer(
1784        buffer: Entity<Buffer>,
1785        project: Option<Entity<Project>>,
1786        window: &mut Window,
1787        cx: &mut Context<Self>,
1788    ) -> Self {
1789        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1790        Self::new(EditorMode::full(), buffer, project, window, cx)
1791    }
1792
1793    pub fn for_multibuffer(
1794        buffer: Entity<MultiBuffer>,
1795        project: Option<Entity<Project>>,
1796        window: &mut Window,
1797        cx: &mut Context<Self>,
1798    ) -> Self {
1799        Self::new(EditorMode::full(), buffer, project, window, cx)
1800    }
1801
1802    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1803        let mut clone = Self::new(
1804            self.mode.clone(),
1805            self.buffer.clone(),
1806            self.project.clone(),
1807            window,
1808            cx,
1809        );
1810        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
1811            let snapshot = display_map.snapshot(cx);
1812            clone.display_map.update(cx, |display_map, cx| {
1813                display_map.set_state(&snapshot, cx);
1814            });
1815            snapshot
1816        });
1817        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
1818        clone.folds_did_change(cx);
1819        clone.selections.clone_state(&self.selections);
1820        clone
1821            .scroll_manager
1822            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
1823        clone.searchable = self.searchable;
1824        clone.read_only = self.read_only;
1825        clone.buffers_with_disabled_indent_guides =
1826            self.buffers_with_disabled_indent_guides.clone();
1827        clone.enable_mouse_wheel_zoom = self.enable_mouse_wheel_zoom;
1828        clone.enable_lsp_data = self.enable_lsp_data;
1829        clone.needs_initial_data_update = self.enable_lsp_data;
1830        clone.enable_runnables = self.enable_runnables;
1831        clone.enable_code_lens = self.enable_code_lens;
1832        clone
1833    }
1834
1835    pub fn new(
1836        mode: EditorMode,
1837        buffer: Entity<MultiBuffer>,
1838        project: Option<Entity<Project>>,
1839        window: &mut Window,
1840        cx: &mut Context<Self>,
1841    ) -> Self {
1842        Editor::new_internal(mode, buffer, project, None, window, cx)
1843    }
1844
1845    pub fn refresh_sticky_headers(
1846        &mut self,
1847        display_snapshot: &DisplaySnapshot,
1848        cx: &mut Context<Editor>,
1849    ) {
1850        if !self.mode.is_full() {
1851            return;
1852        }
1853        let multi_buffer = display_snapshot.buffer_snapshot().clone();
1854        let scroll_anchor = self
1855            .scroll_manager
1856            .native_anchor(display_snapshot, cx)
1857            .anchor;
1858        let Some(buffer_snapshot) = multi_buffer.as_singleton() else {
1859            return;
1860        };
1861
1862        let buffer = buffer_snapshot.clone();
1863        let Some((buffer_visible_start, _)) = multi_buffer.anchor_to_buffer_anchor(scroll_anchor)
1864        else {
1865            return;
1866        };
1867        let buffer_visible_start = buffer_visible_start.to_point(&buffer);
1868        let max_row = buffer.max_point().row;
1869        let start_row = buffer_visible_start.row.min(max_row);
1870        let end_row = (buffer_visible_start.row + 10).min(max_row);
1871
1872        let syntax = self.style(cx).syntax.clone();
1873        let background_task = cx.background_spawn(async move {
1874            buffer
1875                .outline_items_containing(
1876                    Point::new(start_row, 0)..Point::new(end_row, 0),
1877                    true,
1878                    Some(syntax.as_ref()),
1879                )
1880                .into_iter()
1881                .filter_map(|outline_item| {
1882                    Some(OutlineItem {
1883                        depth: outline_item.depth,
1884                        range: multi_buffer
1885                            .buffer_anchor_range_to_anchor_range(outline_item.range)?,
1886                        selection_range: multi_buffer
1887                            .buffer_anchor_range_to_anchor_range(outline_item.selection_range)?,
1888                        source_range_for_text: multi_buffer.buffer_anchor_range_to_anchor_range(
1889                            outline_item.source_range_for_text,
1890                        )?,
1891                        text: outline_item.text,
1892                        highlight_ranges: outline_item.highlight_ranges,
1893                        name_ranges: outline_item.name_ranges,
1894                        body_range: outline_item.body_range.and_then(|range| {
1895                            multi_buffer.buffer_anchor_range_to_anchor_range(range)
1896                        }),
1897                        annotation_range: outline_item.annotation_range.and_then(|range| {
1898                            multi_buffer.buffer_anchor_range_to_anchor_range(range)
1899                        }),
1900                    })
1901                })
1902                .collect()
1903        });
1904        self.sticky_headers_task = cx.spawn(async move |this, cx| {
1905            let sticky_headers = background_task.await;
1906            this.update(cx, |this, cx| {
1907                if this.sticky_headers.as_ref() != Some(&sticky_headers) {
1908                    this.sticky_headers = Some(sticky_headers);
1909                    cx.notify();
1910                }
1911            })
1912            .ok();
1913        });
1914    }
1915
1916    fn new_internal(
1917        mode: EditorMode,
1918        multi_buffer: Entity<MultiBuffer>,
1919        project: Option<Entity<Project>>,
1920        display_map: Option<Entity<DisplayMap>>,
1921        window: &mut Window,
1922        cx: &mut Context<Self>,
1923    ) -> Self {
1924        debug_assert!(
1925            display_map.is_none() || mode.is_minimap(),
1926            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
1927        );
1928
1929        let full_mode = mode.is_full();
1930        let is_minimap = mode.is_minimap();
1931        let diagnostics_max_severity = if full_mode {
1932            EditorSettings::get_global(cx)
1933                .diagnostics_max_severity
1934                .unwrap_or(DiagnosticSeverity::Hint)
1935        } else {
1936            DiagnosticSeverity::Off
1937        };
1938        let style = window.text_style();
1939        let font_size = style.font_size.to_pixels(window.rem_size());
1940        let editor = cx.entity().downgrade();
1941        let fold_placeholder = FoldPlaceholder {
1942            constrain_width: false,
1943            render: Arc::new(move |fold_id, fold_range, cx| {
1944                let editor = editor.clone();
1945                FoldPlaceholder::fold_element(fold_id, cx)
1946                    .cursor_pointer()
1947                    .child("⋯")
1948                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1949                    .on_click(move |_, _window, cx| {
1950                        editor
1951                            .update(cx, |editor, cx| {
1952                                editor.unfold_ranges(
1953                                    &[fold_range.start..fold_range.end],
1954                                    true,
1955                                    false,
1956                                    cx,
1957                                );
1958                                cx.stop_propagation();
1959                            })
1960                            .ok();
1961                    })
1962                    .into_any()
1963            }),
1964            merge_adjacent: true,
1965            ..FoldPlaceholder::default()
1966        };
1967        let display_map = display_map.unwrap_or_else(|| {
1968            cx.new(|cx| {
1969                DisplayMap::new(
1970                    multi_buffer.clone(),
1971                    style.font(),
1972                    font_size,
1973                    None,
1974                    FILE_HEADER_HEIGHT,
1975                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1976                    fold_placeholder,
1977                    diagnostics_max_severity,
1978                    cx,
1979                )
1980            })
1981        });
1982
1983        let selections = SelectionsCollection::new();
1984
1985        let blink_manager = cx.new(|cx| {
1986            let mut blink_manager = BlinkManager::new(
1987                CURSOR_BLINK_INTERVAL,
1988                |cx| EditorSettings::get_global(cx).cursor_blink,
1989                cx,
1990            );
1991            if is_minimap {
1992                blink_manager.disable(cx);
1993            }
1994            blink_manager
1995        });
1996
1997        let soft_wrap_mode_override =
1998            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1999
2000        let mut project_subscriptions = Vec::new();
2001        if full_mode && let Some(project) = project.as_ref() {
2002            project_subscriptions.push(cx.subscribe_in(
2003                project,
2004                window,
2005                |editor, _, event, window, cx| match event {
2006                    project::Event::RefreshCodeLens { .. } => {
2007                        editor.refresh_code_lenses(None, window, cx);
2008                    }
2009                    project::Event::RefreshDocumentColors { .. } => {
2010                        editor.refresh_document_colors(None, window, cx);
2011                    }
2012                    project::Event::RefreshDocumentLinks { .. } => {
2013                        editor.refresh_document_links(None, cx);
2014                    }
2015                    project::Event::RefreshFoldingRanges { .. } => {
2016                        editor.refresh_folding_ranges(None, window, cx);
2017                    }
2018                    project::Event::RefreshDocumentSymbols { .. } => {
2019                        editor.refresh_document_symbols(None, cx);
2020                    }
2021                    project::Event::RefreshInlayHints { server_id } => {
2022                        editor.refresh_inlay_hints(
2023                            InlayHintRefreshReason::RefreshRequested {
2024                                server_id: *server_id,
2025                            },
2026                            cx,
2027                        );
2028                    }
2029                    project::Event::RefreshSemanticTokens { .. } => {
2030                        editor.refresh_semantic_tokens(None, true, cx);
2031                    }
2032                    project::Event::LanguageServerRemoved(_) => {
2033                        editor.registered_buffers.clear();
2034                        editor.register_visible_buffers(cx);
2035                        editor.invalidate_semantic_tokens(None);
2036                        editor.refresh_runnables(None, window, cx);
2037                        editor.update_lsp_data(None, window, cx);
2038                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
2039                    }
2040                    project::Event::SnippetEdit(id, snippet_edits) => {
2041                        // todo(lw): Non singletons
2042                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
2043                            let snapshot = buffer.read(cx).snapshot();
2044                            let focus_handle = editor.focus_handle(cx);
2045                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
2046                                for (range, snippet) in snippet_edits {
2047                                    let buffer_range =
2048                                        language::range_from_lsp(*range).to_offset(&snapshot);
2049                                    editor
2050                                        .insert_snippet(
2051                                            &[MultiBufferOffset(buffer_range.start)
2052                                                ..MultiBufferOffset(buffer_range.end)],
2053                                            snippet.clone(),
2054                                            window,
2055                                            cx,
2056                                        )
2057                                        .ok();
2058                                }
2059                            }
2060                        }
2061                    }
2062                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
2063                        let buffer_id = *buffer_id;
2064                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
2065                            editor.register_buffer(buffer_id, cx);
2066                            editor.refresh_runnables(Some(buffer_id), window, cx);
2067                            editor.invalidate_semantic_tokens(Some(buffer_id));
2068                            editor.update_lsp_data(Some(buffer_id), window, cx);
2069                            editor.refresh_inlay_hints(
2070                                InlayHintRefreshReason::LanguageServerRegistered,
2071                                cx,
2072                            );
2073                            refresh_linked_ranges(editor, window, cx);
2074                            editor.refresh_code_actions_for_selection(window, cx);
2075                            editor.refresh_document_highlights(cx);
2076                        }
2077                    }
2078
2079                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
2080                        let Some(workspace) = editor.workspace() else {
2081                            return;
2082                        };
2083                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
2084                        else {
2085                            return;
2086                        };
2087
2088                        if active_editor.entity_id() == cx.entity_id() {
2089                            let entity_id = cx.entity_id();
2090                            workspace.update(cx, |this, cx| {
2091                                this.panes_mut()
2092                                    .iter_mut()
2093                                    .filter(|pane| pane.entity_id() != entity_id)
2094                                    .for_each(|p| {
2095                                        p.update(cx, |pane, _| {
2096                                            pane.nav_history_mut().rename_item(
2097                                                entity_id,
2098                                                project_path.clone(),
2099                                                abs_path.clone().into(),
2100                                            );
2101                                        })
2102                                    });
2103                            });
2104
2105                            Self::open_transaction_for_hidden_buffers(
2106                                workspace,
2107                                transaction.clone(),
2108                                "Rename".to_string(),
2109                                window,
2110                                cx,
2111                            );
2112                        }
2113                    }
2114
2115                    project::Event::WorkspaceEditApplied(transaction) => {
2116                        let Some(workspace) = editor.workspace() else {
2117                            return;
2118                        };
2119                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
2120                        else {
2121                            return;
2122                        };
2123
2124                        if active_editor.entity_id() == cx.entity_id() {
2125                            Self::open_transaction_for_hidden_buffers(
2126                                workspace,
2127                                transaction.clone(),
2128                                "LSP Edit".to_string(),
2129                                window,
2130                                cx,
2131                            );
2132                        }
2133                    }
2134
2135                    _ => {}
2136                },
2137            ));
2138            if let Some(task_inventory) = project
2139                .read(cx)
2140                .task_store()
2141                .read(cx)
2142                .task_inventory()
2143                .cloned()
2144            {
2145                project_subscriptions.push(cx.observe_in(
2146                    &task_inventory,
2147                    window,
2148                    |editor, _, window, cx| {
2149                        editor.refresh_runnables(None, window, cx);
2150                    },
2151                ));
2152            };
2153
2154            project_subscriptions.push(cx.subscribe_in(
2155                &project.read(cx).breakpoint_store(),
2156                window,
2157                |editor, _, event, window, cx| match event {
2158                    BreakpointStoreEvent::ClearDebugLines => {
2159                        editor.clear_row_highlights::<ActiveDebugLine>();
2160                        editor.refresh_inline_values(cx);
2161                    }
2162                    BreakpointStoreEvent::SetDebugLine => {
2163                        if editor.go_to_active_debug_line(window, cx) {
2164                            cx.stop_propagation();
2165                        }
2166
2167                        editor.refresh_inline_values(cx);
2168                    }
2169                    _ => {}
2170                },
2171            ));
2172            let git_store = project.read(cx).git_store().clone();
2173            let project = project.clone();
2174            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
2175                if let GitStoreEvent::RepositoryAdded = event {
2176                    this.load_diff_task = Some(
2177                        update_uncommitted_diff_for_buffer(
2178                            cx.entity(),
2179                            &project,
2180                            this.buffer.read(cx).all_buffers(),
2181                            this.buffer.clone(),
2182                            cx,
2183                        )
2184                        .shared(),
2185                    );
2186                }
2187            }));
2188        }
2189
2190        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2191
2192        let inlay_hint_settings =
2193            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
2194        let focus_handle = cx.focus_handle();
2195        if !is_minimap {
2196            cx.on_focus(&focus_handle, window, Self::handle_focus)
2197                .detach();
2198            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
2199                .detach();
2200            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
2201                .detach();
2202            cx.on_blur(&focus_handle, window, Self::handle_blur)
2203                .detach();
2204            cx.observe_pending_input(window, Self::observe_pending_input)
2205                .detach();
2206        }
2207
2208        let show_indent_guides =
2209            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
2210                Some(false)
2211            } else {
2212                None
2213            };
2214
2215        let bookmark_store = match (&mode, project.as_ref()) {
2216            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).bookmark_store()),
2217            _ => None,
2218        };
2219
2220        let breakpoint_store = match (&mode, project.as_ref()) {
2221            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
2222            _ => None,
2223        };
2224
2225        let mut code_action_providers = Vec::new();
2226        let mut load_uncommitted_diff = None;
2227        if let Some(project) = project.clone() {
2228            load_uncommitted_diff = Some(
2229                update_uncommitted_diff_for_buffer(
2230                    cx.entity(),
2231                    &project,
2232                    multi_buffer.read(cx).all_buffers(),
2233                    multi_buffer.clone(),
2234                    cx,
2235                )
2236                .shared(),
2237            );
2238            code_action_providers.push(Rc::new(project) as Rc<_>);
2239        }
2240
2241        let mut editor = Self {
2242            focus_handle,
2243            show_cursor_when_unfocused: false,
2244            last_focused_descendant: None,
2245            buffer: multi_buffer.clone(),
2246            display_map: display_map.clone(),
2247            placeholder_display_map: None,
2248            selections,
2249            scroll_manager: ScrollManager::new(cx),
2250            columnar_selection_state: None,
2251            add_selections_state: None,
2252            select_next_state: None,
2253            select_prev_state: None,
2254            selection_history: SelectionHistory::default(),
2255            defer_selection_effects: false,
2256            deferred_selection_effects_state: None,
2257            autoclose_regions: Vec::new(),
2258            snippet_stack: InvalidationStack::default(),
2259            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
2260            ime_transaction: None,
2261            active_diagnostics: ActiveDiagnostic::None,
2262            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
2263            inline_diagnostics_update: Task::ready(()),
2264            inline_diagnostics: Vec::new(),
2265            soft_wrap_mode_override,
2266            diagnostics_max_severity,
2267            hard_wrap: None,
2268            completion_provider: project.clone().map(|project| Rc::new(project) as _),
2269            semantics_provider: project
2270                .as_ref()
2271                .map(|project| Rc::new(project.downgrade()) as _),
2272            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
2273            project,
2274            blink_manager: blink_manager.clone(),
2275            show_local_selections: true,
2276            show_scrollbars: ScrollbarAxes {
2277                horizontal: full_mode,
2278                vertical: full_mode,
2279            },
2280            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
2281            offset_content: !matches!(mode, EditorMode::SingleLine),
2282            breadcrumbs_visibility: BreadcrumbsVisibility::from_settings(cx),
2283            show_gutter: full_mode,
2284            show_line_numbers: (!full_mode).then_some(false),
2285            use_relative_line_numbers: None,
2286            disable_expand_excerpt_buttons: !full_mode,
2287            delegate_expand_excerpts: false,
2288            delegate_open_excerpts: false,
2289            enable_lsp_data: full_mode,
2290            needs_initial_data_update: full_mode,
2291            enable_runnables: full_mode,
2292            enable_code_lens: full_mode,
2293            enable_mouse_wheel_zoom: full_mode,
2294            show_git_diff_gutter: None,
2295            show_code_actions: None,
2296            show_runnables: None,
2297            show_bookmarks: None,
2298            show_breakpoints: None,
2299            show_diff_review_button: false,
2300            show_wrap_guides: None,
2301            show_indent_guides,
2302            buffers_with_disabled_indent_guides: HashSet::default(),
2303            highlight_order: 0,
2304            highlighted_rows: Default::default(),
2305            background_highlights: HashMap::default(),
2306            navigation_overlays: HashMap::default(),
2307            gutter_highlights: Default::default(),
2308            allow_git_diff_scrollbar_markers: false,
2309            scrollbar_marker_state: ScrollbarMarkerState::default(),
2310            active_indent_guides_state: ActiveIndentGuidesState::default(),
2311            nav_history: None,
2312            context_menu: RefCell::new(None),
2313            context_menu_options: None,
2314            mouse_context_menu: None,
2315            completion_tasks: Vec::new(),
2316            inline_blame_popover: None,
2317            inline_blame_popover_show_task: None,
2318            signature_help_state: SignatureHelpState::default(),
2319            auto_signature_help: None,
2320            find_all_references_task_sources: Vec::new(),
2321            next_completion_id: 0,
2322            next_inlay_id: 0,
2323            code_action_providers,
2324            code_actions_for_selection: CodeActionsForSelection::None,
2325            runnables_for_selection_toggle: Task::ready(()),
2326            quick_selection_highlight_task: None,
2327            debounced_selection_highlight_task: None,
2328            debounced_selection_highlight_complete: false,
2329            last_selection_from_search: false,
2330            document_highlights_task: None,
2331            linked_editing_range_task: None,
2332            pending_rename: None,
2333            searchable: !is_minimap,
2334            cursor_shape: EditorSettings::get_global(cx)
2335                .cursor_shape
2336                .unwrap_or_default(),
2337            cursor_offset_on_selection: false,
2338            current_line_highlight: None,
2339            autoindent_mode: Some(AutoindentMode::EachLine),
2340            collapse_matches: false,
2341            workspace: None,
2342            input_enabled: !is_minimap,
2343            expects_character_input: !is_minimap,
2344            use_modal_editing: full_mode,
2345            read_only: is_minimap,
2346            use_autoclose: true,
2347            use_auto_surround: true,
2348            use_selection_highlight: true,
2349            auto_replace_emoji_shortcode: false,
2350            jsx_tag_auto_close_enabled_in_any_buffer: false,
2351            leader_id: None,
2352            remote_id: None,
2353            hover_state: HoverState::default(),
2354            pending_mouse_down: None,
2355            prev_pressure_stage: None,
2356            hovered_link_state: None,
2357            edit_prediction_provider: None,
2358            active_edit_prediction: None,
2359            stale_edit_prediction_in_menu: None,
2360            edit_prediction_preview: EditPredictionPreview::Inactive {
2361                released_too_fast: false,
2362            },
2363            inline_diagnostics_enabled: full_mode,
2364            diagnostics_enabled: full_mode,
2365            word_completions_enabled: full_mode,
2366            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
2367            gutter_hovered: false,
2368            pixel_position_of_newest_cursor: None,
2369            last_bounds: None,
2370            last_position_map: None,
2371            last_right_margin: Pixels::ZERO,
2372            last_horizontal_scrollbar_visible: false,
2373            expect_bounds_change: None,
2374            gutter_dimensions: GutterDimensions::default(),
2375            style: None,
2376            show_cursor_names: false,
2377            hovered_cursors: HashMap::default(),
2378            next_editor_action_id: EditorActionId::default(),
2379            editor_actions: Rc::default(),
2380            edit_predictions_hidden_for_vim_mode: false,
2381            show_edit_predictions_override: None,
2382            show_completions_on_input_override: None,
2383            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
2384            edit_prediction_settings: EditPredictionSettings::Disabled,
2385            in_leading_whitespace: false,
2386            custom_context_menu: None,
2387            show_git_blame_gutter: false,
2388            show_git_blame_inline: false,
2389            show_selection_menu: None,
2390            show_git_blame_inline_delay_task: None,
2391            git_blame_inline_enabled: full_mode
2392                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
2393            buffer_serialization: is_minimap.not().then(|| {
2394                BufferSerialization::new(
2395                    ProjectSettings::get_global(cx)
2396                        .session
2397                        .restore_unsaved_buffers,
2398                )
2399            }),
2400            blame: None,
2401            blame_subscription: None,
2402            pending_blame_hover_observation: None,
2403
2404            bookmark_store,
2405            breakpoint_store,
2406            gutter_hover_button: (None, None),
2407            gutter_diff_review_indicator: (None, None),
2408            diff_review_drag_state: None,
2409            diff_review_overlays: Vec::new(),
2410            stored_review_comments: Vec::new(),
2411            next_review_comment_id: 0,
2412            hovered_diff_hunk_row: None,
2413            _subscriptions: (!is_minimap)
2414                .then(|| {
2415                    vec![
2416                        cx.observe(&multi_buffer, Self::on_buffer_changed),
2417                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
2418                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
2419                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
2420                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
2421                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
2422                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
2423                    ]
2424                })
2425                .unwrap_or_default(),
2426            runnables: RunnableData::new(),
2427            pull_diagnostics_task: Task::ready(()),
2428            colors: None,
2429            code_lens: None,
2430            refresh_colors_task: Task::ready(()),
2431            refresh_code_lens_task: Task::ready(()),
2432            use_document_folding_ranges: false,
2433            refresh_folding_ranges_task: Task::ready(()),
2434            inlay_hints: None,
2435            next_color_inlay_id: 0,
2436            post_scroll_update: Task::ready(()),
2437            linked_edit_ranges: Default::default(),
2438            in_project_search: false,
2439            previous_search_ranges: None,
2440            breadcrumb_header: None,
2441            focused_block: None,
2442            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
2443            addons: Default::default(),
2444            registered_buffers: HashMap::default(),
2445            _scroll_cursor_center_top_bottom_task: Task::ready(()),
2446            selection_mark_mode: false,
2447            toggle_fold_multiple_buffers: Task::ready(()),
2448            serialize_selections: Task::ready(()),
2449            serialize_folds: Task::ready(()),
2450            text_style_refinement: None,
2451            load_diff_task: load_uncommitted_diff,
2452            diff_hunk_delegate: None,
2453            minimap: None,
2454            change_list: ChangeList::new(),
2455            mode,
2456            selection_drag_state: SelectionDragState::None,
2457            folding_newlines: Task::ready(()),
2458            lookup_key: None,
2459            select_next_is_case_sensitive: None,
2460            on_local_selections_changed: None,
2461            suppress_selection_callback: false,
2462            applicable_language_settings: HashMap::default(),
2463            semantic_token_state: SemanticTokenState::new(cx, full_mode),
2464            accent_data: None,
2465            bracket_fetched_tree_sitter_chunks: HashMap::default(),
2466            number_deleted_lines: false,
2467            refresh_matching_bracket_highlights_task: Task::ready(()),
2468            refresh_document_symbols_task: Task::ready(()).shared(),
2469            lsp_document_links: LspDocumentLinks::new(cx),
2470            lsp_document_symbols: HashMap::default(),
2471            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
2472            outline_symbols_at_cursor: None,
2473            sticky_headers_task: Task::ready(()),
2474            sticky_headers: None,
2475            colorize_brackets_task: Task::ready(()),
2476        };
2477
2478        if is_minimap {
2479            return editor;
2480        }
2481
2482        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
2483        editor.accent_data = editor.fetch_accent_data(cx);
2484
2485        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
2486            editor
2487                ._subscriptions
2488                .push(cx.observe(breakpoints, |_, _, cx| {
2489                    cx.notify();
2490                }));
2491        }
2492        editor._subscriptions.extend(project_subscriptions);
2493
2494        editor._subscriptions.push(cx.subscribe_in(
2495            &cx.entity(),
2496            window,
2497            |editor, _, e: &EditorEvent, window, cx| match e {
2498                EditorEvent::ScrollPositionChanged { local, .. } => {
2499                    if *local {
2500                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
2501                        editor.hide_blame_popover(true, cx);
2502                        let snapshot = editor.snapshot(window, cx);
2503                        let new_anchor = editor
2504                            .scroll_manager
2505                            .native_anchor(&snapshot.display_snapshot, cx);
2506                        editor.update_restoration_data(cx, move |data| {
2507                            data.scroll_position = (
2508                                new_anchor.top_row(snapshot.buffer_snapshot()),
2509                                new_anchor.offset,
2510                            );
2511                        });
2512
2513                        editor.update_data_on_scroll(true, window, cx);
2514                    }
2515                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
2516                }
2517                EditorEvent::Edited { .. } => {
2518                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
2519                        .map(|vim_mode| vim_mode.0)
2520                        .unwrap_or(false);
2521                    if !vim_mode {
2522                        let display_map = editor.display_snapshot(cx);
2523                        let selections = editor.selections.all_adjusted_display(&display_map);
2524                        let pop_state = editor
2525                            .change_list
2526                            .last()
2527                            .map(|previous| {
2528                                previous.len() == selections.len()
2529                                    && previous.iter().enumerate().all(|(ix, p)| {
2530                                        p.to_display_point(&display_map).row()
2531                                            == selections[ix].head().row()
2532                                    })
2533                            })
2534                            .unwrap_or(false);
2535                        let new_positions = selections
2536                            .into_iter()
2537                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
2538                            .collect();
2539                        editor
2540                            .change_list
2541                            .push_to_change_list(pop_state, new_positions);
2542                    }
2543                }
2544                _ => (),
2545            },
2546        ));
2547
2548        if let Some(dap_store) = editor
2549            .project
2550            .as_ref()
2551            .map(|project| project.read(cx).dap_store())
2552        {
2553            let weak_editor = cx.weak_entity();
2554
2555            editor
2556                ._subscriptions
2557                .push(
2558                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
2559                        let session_entity = cx.entity();
2560                        weak_editor
2561                            .update(cx, |editor, cx| {
2562                                editor._subscriptions.push(
2563                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
2564                                );
2565                            })
2566                            .ok();
2567                    }),
2568                );
2569
2570            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
2571                editor
2572                    ._subscriptions
2573                    .push(cx.subscribe(&session, Self::on_debug_session_event));
2574            }
2575        }
2576
2577        // skip adding the initial selection to selection history
2578        editor.selection_history.mode = SelectionHistoryMode::Skipping;
2579        editor.end_selection(window, cx);
2580        editor.selection_history.mode = SelectionHistoryMode::Normal;
2581
2582        editor.scroll_manager.show_scrollbars(window, cx);
2583        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
2584
2585        if full_mode {
2586            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
2587            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
2588
2589            if editor.git_blame_inline_enabled {
2590                editor.start_git_blame_inline(false, window, cx);
2591            }
2592
2593            editor.go_to_active_debug_line(window, cx);
2594
2595            editor.minimap =
2596                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
2597            editor.colors = Some(LspColorData::new(cx));
2598            editor.use_document_folding_ranges = true;
2599            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
2600            if editor.enable_code_lens && EditorSettings::get_global(cx).code_lens.inline() {
2601                editor.code_lens = Some(CodeLensState::default());
2602            }
2603
2604            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
2605                editor.register_buffer(buffer.read(cx).remote_id(), cx);
2606            }
2607            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
2608        }
2609
2610        editor
2611    }
2612
2613    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
2614        self.display_map.update(cx, |map, cx| map.snapshot(cx))
2615    }
2616
2617    pub fn deploy_mouse_context_menu(
2618        &mut self,
2619        position: gpui::Point<Pixels>,
2620        context_menu: Entity<ContextMenu>,
2621        window: &mut Window,
2622        cx: &mut Context<Self>,
2623    ) {
2624        self.mouse_context_menu = Some(MouseContextMenu::new(
2625            self,
2626            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
2627            context_menu,
2628            window,
2629            cx,
2630        ));
2631    }
2632
2633    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
2634        self.mouse_context_menu
2635            .as_ref()
2636            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
2637    }
2638
2639    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
2640        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
2641    }
2642
2643    fn key_context_internal(
2644        &self,
2645        has_active_edit_prediction: bool,
2646        window: &mut Window,
2647        cx: &mut App,
2648    ) -> KeyContext {
2649        let mut key_context = KeyContext::new_with_defaults();
2650        key_context.add("Editor");
2651        let mode = match self.mode {
2652            EditorMode::SingleLine => "single_line",
2653            EditorMode::AutoHeight { .. } => "auto_height",
2654            EditorMode::Minimap { .. } => "minimap",
2655            EditorMode::Full { .. } => "full",
2656        };
2657
2658        if EditorSettings::jupyter_enabled(cx) {
2659            key_context.add("jupyter");
2660        }
2661
2662        key_context.set("mode", mode);
2663        if self.pending_rename.is_some() {
2664            key_context.add("renaming");
2665        }
2666
2667        if let Some(snippet_stack) = self.snippet_stack.last() {
2668            key_context.add("in_snippet");
2669
2670            if snippet_stack.active_index > 0 {
2671                key_context.add("has_previous_tabstop");
2672            }
2673
2674            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
2675                key_context.add("has_next_tabstop");
2676            }
2677        }
2678
2679        match self.context_menu.borrow().as_ref() {
2680            Some(CodeContextMenu::Completions(menu)) => {
2681                if menu.visible() {
2682                    key_context.add("menu");
2683                    key_context.add("showing_completions");
2684                }
2685            }
2686            Some(CodeContextMenu::CodeActions(menu)) => {
2687                if menu.visible() {
2688                    key_context.add("menu");
2689                    key_context.add("showing_code_actions")
2690                }
2691            }
2692            None => {}
2693        }
2694
2695        if self.signature_help_state.has_multiple_signatures() {
2696            key_context.add("showing_signature_help");
2697        }
2698
2699        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2700        if !self.focus_handle(cx).contains_focused(window, cx)
2701            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
2702        {
2703            for addon in self.addons.values() {
2704                addon.extend_key_context(&mut key_context, cx)
2705            }
2706        }
2707
2708        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
2709            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
2710                Some(
2711                    file.full_path(cx)
2712                        .extension()?
2713                        .to_string_lossy()
2714                        .to_lowercase(),
2715                )
2716            }) {
2717                key_context.set("extension", extension);
2718            }
2719        } else {
2720            key_context.add("multibuffer");
2721        }
2722
2723        if has_active_edit_prediction {
2724            key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
2725            key_context.add("copilot_suggestion");
2726        }
2727
2728        if self.in_leading_whitespace {
2729            key_context.add("in_leading_whitespace");
2730        }
2731        if self.edit_prediction_requires_modifier() {
2732            key_context.set("edit_prediction_mode", "subtle")
2733        } else {
2734            key_context.set("edit_prediction_mode", "eager");
2735        }
2736
2737        if self.selection_mark_mode {
2738            key_context.add("selection_mode");
2739        }
2740
2741        let disjoint = self.selections.disjoint_anchors();
2742        if matches!(
2743            &self.mode,
2744            EditorMode::SingleLine | EditorMode::AutoHeight { .. }
2745        ) && let [selection] = disjoint
2746            && selection.start == selection.end
2747        {
2748            let snapshot = self.snapshot(window, cx);
2749            let snapshot = snapshot.buffer_snapshot();
2750            let caret_offset = selection.end.to_offset(snapshot);
2751
2752            if caret_offset == MultiBufferOffset(0) {
2753                key_context.add("start_of_input");
2754            }
2755
2756            if caret_offset == snapshot.len() {
2757                key_context.add("end_of_input");
2758            }
2759        }
2760
2761        if self.has_any_expanded_diff_hunks(cx) {
2762            key_context.add("diffs_expanded");
2763        }
2764
2765        key_context
2766    }
2767
2768    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
2769        self.last_bounds.as_ref()
2770    }
2771
2772    pub(crate) fn last_right_margin(&self) -> Pixels {
2773        self.last_right_margin
2774    }
2775
2776    pub(crate) fn last_horizontal_scrollbar_visible(&self) -> bool {
2777        self.last_horizontal_scrollbar_visible
2778    }
2779
2780    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
2781        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
2782            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
2783                && let Some(dir) = file.abs_path(cx).parent()
2784            {
2785                return Some(dir.to_owned());
2786            }
2787        }
2788
2789        None
2790    }
2791
2792    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
2793        self.active_buffer(cx).and_then(|buffer| {
2794            let buffer = buffer.read(cx);
2795            if let Some(project_path) = buffer.project_path(cx) {
2796                let project = self.project()?.read(cx);
2797                project.absolute_path(&project_path, cx)
2798            } else {
2799                buffer
2800                    .file()
2801                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
2802            }
2803        })
2804    }
2805
2806    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
2807        self.show_selection_menu
2808            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
2809    }
2810
2811    pub fn toggle_selection_menu(
2812        &mut self,
2813        _: &ToggleSelectionMenu,
2814        _: &mut Window,
2815        cx: &mut Context<Self>,
2816    ) {
2817        self.show_selection_menu = self
2818            .show_selection_menu
2819            .map(|show_selections_menu| !show_selections_menu)
2820            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
2821
2822        cx.notify();
2823    }
2824
2825    pub fn new_file(
2826        workspace: &mut Workspace,
2827        _: &workspace::NewFile,
2828        window: &mut Window,
2829        cx: &mut Context<Workspace>,
2830    ) {
2831        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
2832            "Failed to create buffer",
2833            window,
2834            cx,
2835            |e, _, _| match e.error_code() {
2836                ErrorCode::RemoteUpgradeRequired => Some(format!(
2837                "The remote instance of Omega does not support this yet. It must be upgraded to {}",
2838                e.error_tag("required").unwrap_or("the latest version")
2839            )),
2840                _ => None,
2841            },
2842        );
2843    }
2844
2845    pub fn new_in_workspace(
2846        workspace: &mut Workspace,
2847        window: &mut Window,
2848        cx: &mut Context<Workspace>,
2849    ) -> Task<Result<Entity<Editor>>> {
2850        let project = workspace.project().clone();
2851        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
2852
2853        cx.spawn_in(window, async move |workspace, cx| {
2854            let buffer = create.await?;
2855            workspace.update_in(cx, |workspace, window, cx| {
2856                let editor =
2857                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
2858                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
2859                editor
2860            })
2861        })
2862    }
2863
2864    fn new_file_vertical(
2865        workspace: &mut Workspace,
2866        _: &workspace::NewFileSplitVertical,
2867        window: &mut Window,
2868        cx: &mut Context<Workspace>,
2869    ) {
2870        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
2871    }
2872
2873    fn new_file_horizontal(
2874        workspace: &mut Workspace,
2875        _: &workspace::NewFileSplitHorizontal,
2876        window: &mut Window,
2877        cx: &mut Context<Workspace>,
2878    ) {
2879        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
2880    }
2881
2882    fn new_file_split(
2883        workspace: &mut Workspace,
2884        action: &workspace::NewFileSplit,
2885        window: &mut Window,
2886        cx: &mut Context<Workspace>,
2887    ) {
2888        Self::new_file_in_direction(workspace, action.0, window, cx)
2889    }
2890
2891    fn new_file_in_direction(
2892        workspace: &mut Workspace,
2893        direction: SplitDirection,
2894        window: &mut Window,
2895        cx: &mut Context<Workspace>,
2896    ) {
2897        let project = workspace.project().clone();
2898        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
2899
2900        cx.spawn_in(window, async move |workspace, cx| {
2901            let buffer = create.await?;
2902            workspace.update_in(cx, move |workspace, window, cx| {
2903                workspace.split_item(
2904                    direction,
2905                    Box::new(
2906                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
2907                    ),
2908                    window,
2909                    cx,
2910                )
2911            })?;
2912            anyhow::Ok(())
2913        })
2914        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
2915            match e.error_code() {
2916                ErrorCode::RemoteUpgradeRequired => Some(format!(
2917                "The remote instance of Omega does not support this yet. It must be upgraded to {}",
2918                e.error_tag("required").unwrap_or("the latest version")
2919            )),
2920                _ => None,
2921            }
2922        });
2923    }
2924
2925    pub fn leader_id(&self) -> Option<CollaboratorId> {
2926        self.leader_id
2927    }
2928
2929    pub fn buffer(&self) -> &Entity<MultiBuffer> {
2930        &self.buffer
2931    }
2932
2933    pub fn project(&self) -> Option<&Entity<Project>> {
2934        self.project.as_ref()
2935    }
2936
2937    pub fn workspace(&self) -> Option<Entity<Workspace>> {
2938        self.workspace.as_ref()?.0.upgrade()
2939    }
2940
2941    /// Detaches a task and shows an error notification in the workspace if available,
2942    /// otherwise just logs the error.
2943    pub fn detach_and_notify_err<R, E>(
2944        &self,
2945        task: Task<Result<R, E>>,
2946        window: &mut Window,
2947        cx: &mut App,
2948    ) where
2949        E: std::fmt::Debug + std::fmt::Display + 'static,
2950        R: 'static,
2951    {
2952        if let Some(workspace) = self.workspace() {
2953            task.detach_and_notify_err(workspace.downgrade(), window, cx);
2954        } else {
2955            task.detach_and_log_err(cx);
2956        }
2957    }
2958
2959    /// Returns the workspace serialization ID if this editor should be serialized.
2960    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
2961        self.workspace
2962            .as_ref()
2963            .filter(|_| self.should_serialize_buffer())
2964            .and_then(|workspace| workspace.1)
2965    }
2966
2967    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
2968        self.buffer().read(cx).title(cx)
2969    }
2970
2971    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
2972        let git_blame_gutter_max_author_length = self
2973            .render_git_blame_gutter(cx)
2974            .then(|| {
2975                if let Some(blame) = self.blame.as_ref() {
2976                    let max_author_length =
2977                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
2978                    Some(max_author_length)
2979                } else {
2980                    None
2981                }
2982            })
2983            .flatten();
2984
2985        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2986
2987        EditorSnapshot {
2988            mode: self.mode.clone(),
2989            show_gutter: self.show_gutter,
2990            offset_content: self.offset_content,
2991            show_line_numbers: self.show_line_numbers,
2992            number_deleted_lines: self.number_deleted_lines,
2993            show_git_diff_gutter: self.show_git_diff_gutter,
2994            semantic_tokens_enabled: self.semantic_token_state.enabled(),
2995            show_code_actions: self.show_code_actions,
2996            show_runnables: self.show_runnables,
2997            show_bookmarks: self.show_bookmarks,
2998            show_breakpoints: self.show_breakpoints,
2999            git_blame_gutter_max_author_length,
3000            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
3001            display_snapshot,
3002            placeholder_display_snapshot: self
3003                .placeholder_display_map
3004                .as_ref()
3005                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
3006            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
3007            is_focused: self.focus_handle.is_focused(window),
3008            current_line_highlight: self
3009                .current_line_highlight
3010                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
3011            gutter_hovered: self.gutter_hovered,
3012        }
3013    }
3014
3015    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
3016        self.buffer.read(cx).language_at(point, cx)
3017    }
3018
3019    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
3020        self.buffer.read(cx).read(cx).file_at(point).cloned()
3021    }
3022
3023    pub fn active_buffer(&self, cx: &App) -> Option<Entity<Buffer>> {
3024        let multibuffer = self.buffer.read(cx);
3025        let snapshot = multibuffer.snapshot(cx);
3026        let (anchor, _) =
3027            snapshot.anchor_to_buffer_anchor(self.selections.newest_anchor().head())?;
3028        multibuffer.buffer(anchor.buffer_id)
3029    }
3030
3031    pub fn mode(&self) -> &EditorMode {
3032        &self.mode
3033    }
3034
3035    pub fn set_mode(&mut self, mode: EditorMode) {
3036        self.mode = mode;
3037    }
3038
3039    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
3040        self.collaboration_hub.as_deref()
3041    }
3042
3043    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
3044        self.collaboration_hub = Some(hub);
3045    }
3046
3047    pub fn set_in_project_search(&mut self, in_project_search: bool) {
3048        self.in_project_search = in_project_search;
3049    }
3050
3051    pub fn set_custom_context_menu(
3052        &mut self,
3053        f: impl 'static
3054        + Fn(
3055            &mut Self,
3056            DisplayPoint,
3057            &mut Window,
3058            &mut Context<Self>,
3059        ) -> Option<Entity<ui::ContextMenu>>,
3060    ) {
3061        self.custom_context_menu = Some(Box::new(f))
3062    }
3063
3064    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
3065        self.semantics_provider.clone()
3066    }
3067
3068    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
3069        self.semantics_provider = provider;
3070    }
3071
3072    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
3073        self.placeholder_display_map
3074            .as_ref()
3075            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
3076    }
3077
3078    pub fn set_placeholder_text(
3079        &mut self,
3080        placeholder_text: &str,
3081        window: &mut Window,
3082        cx: &mut Context<Self>,
3083    ) {
3084        let multibuffer = cx
3085            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
3086
3087        let style = window.text_style();
3088
3089        self.placeholder_display_map = Some(cx.new(|cx| {
3090            DisplayMap::new(
3091                multibuffer,
3092                style.font(),
3093                style.font_size.to_pixels(window.rem_size()),
3094                None,
3095                FILE_HEADER_HEIGHT,
3096                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
3097                Default::default(),
3098                DiagnosticSeverity::Off,
3099                cx,
3100            )
3101        }));
3102        cx.notify();
3103    }
3104
3105    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
3106        self.cursor_shape = cursor_shape;
3107
3108        // Disrupt blink for immediate user feedback that the cursor shape has changed
3109        self.blink_manager.update(cx, BlinkManager::show_cursor);
3110
3111        cx.notify();
3112    }
3113
3114    pub fn show_cursor(&mut self, cx: &mut Context<Self>) {
3115        self.blink_manager.update(cx, BlinkManager::show_cursor);
3116    }
3117
3118    pub fn cursor_shape(&self) -> CursorShape {
3119        self.cursor_shape
3120    }
3121
3122    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
3123        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
3124    }
3125
3126    /// Returns the anchor to use as the rename target for a selection.
3127    ///
3128    /// In selection-based modes, like vim's visual mode and helix, the rendered
3129    /// block cursor sits one position to the left of the selection's head,
3130    /// since the head is the exclusive end of a forward selection. Using the
3131    /// head
3132    /// directly would place the rename one character past the symbol, for
3133    /// example, trailing whitespace, so for a non-empty forward selection we
3134    /// shift one point left to land back on the symbol under the cursor.
3135    fn rename_target_anchor(&self, selection: &Selection<Anchor>, cx: &mut App) -> Anchor {
3136        let head = selection.head();
3137
3138        if self.cursor_offset_on_selection
3139            && !selection.reversed
3140            && selection.start != selection.end
3141        {
3142            let display_map = self.display_snapshot(cx);
3143            let display_head = head.to_display_point(&display_map);
3144
3145            if display_head.column() > 0 {
3146                return display_map.display_point_to_anchor(
3147                    movement::left(&display_map, display_head),
3148                    Bias::Left,
3149                );
3150            }
3151        }
3152        head
3153    }
3154
3155    pub fn set_current_line_highlight(
3156        &mut self,
3157        current_line_highlight: Option<CurrentLineHighlight>,
3158    ) {
3159        self.current_line_highlight = current_line_highlight;
3160    }
3161
3162    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
3163        self.collapse_matches = collapse_matches;
3164    }
3165
3166    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
3167        if self.collapse_matches {
3168            return range.start..range.start;
3169        }
3170        range.clone()
3171    }
3172
3173    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
3174        self.display_map.read(cx).clip_at_line_ends
3175    }
3176
3177    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
3178        if self.display_map.read(cx).clip_at_line_ends != clip {
3179            self.display_map
3180                .update(cx, |map, _| map.clip_at_line_ends = clip);
3181        }
3182    }
3183
3184    pub fn capability(&self, cx: &App) -> Capability {
3185        if self.read_only {
3186            Capability::ReadOnly
3187        } else {
3188            self.buffer.read(cx).capability()
3189        }
3190    }
3191
3192    pub fn read_only(&self, cx: &App) -> bool {
3193        self.read_only || self.buffer.read(cx).read_only()
3194    }
3195
3196    pub fn set_read_only(&mut self, read_only: bool) {
3197        self.read_only = read_only;
3198    }
3199
3200    pub fn set_use_selection_highlight(&mut self, highlight: bool) {
3201        self.use_selection_highlight = highlight;
3202    }
3203
3204    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
3205        self.buffer_serialization = should_serialize.then(|| {
3206            BufferSerialization::new(
3207                ProjectSettings::get_global(cx)
3208                    .session
3209                    .restore_unsaved_buffers,
3210            )
3211        })
3212    }
3213
3214    fn should_serialize_buffer(&self) -> bool {
3215        self.buffer_serialization.is_some()
3216    }
3217
3218    pub fn set_use_modal_editing(&mut self, to: bool) {
3219        self.use_modal_editing = to;
3220    }
3221
3222    pub fn use_modal_editing(&self) -> bool {
3223        self.use_modal_editing
3224    }
3225
3226    /// Inserted text is normalized to LF line endings before being applied.
3227    /// Normalize before measuring inserted text for post-edit offsets.
3228    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
3229    where
3230        I: IntoIterator<Item = (Range<S>, T)>,
3231        S: ToOffset,
3232        T: Into<Arc<str>>,
3233    {
3234        if self.read_only(cx) {
3235            return;
3236        }
3237
3238        self.buffer
3239            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3240    }
3241
3242    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
3243    where
3244        I: IntoIterator<Item = (Range<S>, T)>,
3245        S: ToOffset,
3246        T: Into<Arc<str>>,
3247    {
3248        if self.read_only(cx) {
3249            return;
3250        }
3251
3252        self.buffer.update(cx, |buffer, cx| {
3253            buffer.edit(edits, self.autoindent_mode.clone(), cx)
3254        });
3255    }
3256
3257    pub fn edit_with_block_indent<I, S, T>(
3258        &mut self,
3259        edits: I,
3260        original_indent_columns: Vec<Option<u32>>,
3261        cx: &mut Context<Self>,
3262    ) where
3263        I: IntoIterator<Item = (Range<S>, T)>,
3264        S: ToOffset,
3265        T: Into<Arc<str>>,
3266    {
3267        if self.read_only(cx) {
3268            return;
3269        }
3270
3271        self.buffer.update(cx, |buffer, cx| {
3272            buffer.edit(
3273                edits,
3274                Some(AutoindentMode::Block {
3275                    original_indent_columns,
3276                }),
3277                cx,
3278            )
3279        });
3280    }
3281
3282    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
3283        self.selection_mark_mode = false;
3284        self.selection_drag_state = SelectionDragState::None;
3285
3286        if self.dismiss_menus_and_popups(true, window, cx) {
3287            cx.notify();
3288            return;
3289        }
3290        if self.clear_expanded_diff_hunks(cx) {
3291            cx.notify();
3292            return;
3293        }
3294        if self.show_git_blame_gutter {
3295            self.show_git_blame_gutter = false;
3296            cx.notify();
3297            return;
3298        }
3299
3300        if self.mode.is_full()
3301            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
3302        {
3303            cx.notify();
3304            return;
3305        }
3306
3307        cx.propagate();
3308    }
3309
3310    pub fn dismiss_menus_and_popups(
3311        &mut self,
3312        is_user_requested: bool,
3313        window: &mut Window,
3314        cx: &mut Context<Self>,
3315    ) -> bool {
3316        let mut dismissed = false;
3317
3318        dismissed |= self.take_rename(false, window, cx).is_some();
3319        dismissed |= self.hide_blame_popover(true, cx);
3320        dismissed |= hide_hover(self, cx);
3321        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
3322        dismissed |= self.hide_context_menu(window, cx).is_some();
3323        dismissed |= self.mouse_context_menu.take().is_some();
3324        dismissed |= is_user_requested
3325            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
3326        dismissed |= self.snippet_stack.pop().is_some();
3327        if self.diff_review_drag_state.is_some() {
3328            self.cancel_diff_review_drag(cx);
3329            dismissed = true;
3330        }
3331        if !self.diff_review_overlays.is_empty() {
3332            self.dismiss_all_diff_review_overlays(cx);
3333            dismissed = true;
3334        }
3335
3336        if self.mode.is_full() && self.has_active_diagnostic_group() {
3337            self.dismiss_diagnostics(cx);
3338            dismissed = true;
3339        }
3340
3341        dismissed
3342    }
3343
3344    fn open_transaction_for_hidden_buffers(
3345        workspace: Entity<Workspace>,
3346        transaction: ProjectTransaction,
3347        title: String,
3348        window: &mut Window,
3349        cx: &mut Context<Self>,
3350    ) {
3351        if transaction.0.is_empty() {
3352            return;
3353        }
3354
3355        let edited_buffers_already_open = {
3356            let other_editors: Vec<Entity<Editor>> = workspace
3357                .read(cx)
3358                .panes()
3359                .iter()
3360                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
3361                .filter(|editor| editor.entity_id() != cx.entity_id())
3362                .collect();
3363
3364            transaction.0.keys().all(|buffer| {
3365                other_editors.iter().any(|editor| {
3366                    let multi_buffer = editor.read(cx).buffer();
3367                    multi_buffer.read(cx).is_singleton()
3368                        && multi_buffer
3369                            .read(cx)
3370                            .as_singleton()
3371                            .map_or(false, |singleton| {
3372                                singleton.entity_id() == buffer.entity_id()
3373                            })
3374                })
3375            })
3376        };
3377        if !edited_buffers_already_open {
3378            let workspace = workspace.downgrade();
3379            cx.defer_in(window, move |_, window, cx| {
3380                cx.spawn_in(window, async move |editor, cx| {
3381                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
3382                        .await
3383                        .ok()
3384                })
3385                .detach();
3386            });
3387        }
3388    }
3389
3390    pub async fn open_project_transaction(
3391        editor: &WeakEntity<Editor>,
3392        workspace: WeakEntity<Workspace>,
3393        transaction: ProjectTransaction,
3394        title: String,
3395        cx: &mut AsyncWindowContext,
3396    ) -> Result<()> {
3397        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3398        cx.update(|_, cx| {
3399            entries.sort_unstable_by_key(|(buffer, _)| {
3400                buffer.read(cx).file().map(|f| f.path().clone())
3401            });
3402        })?;
3403        if entries.is_empty() {
3404            return Ok(());
3405        }
3406
3407        // If the project transaction's edits are all contained within this editor, then
3408        // avoid opening a new editor to display them.
3409
3410        if let [(buffer, transaction)] = &*entries {
3411            let cursor_excerpt = editor.update(cx, |editor, cx| {
3412                let snapshot = editor.buffer().read(cx).snapshot(cx);
3413                let head = editor.selections.newest_anchor().head();
3414                let (buffer_snapshot, excerpt_range) = snapshot.excerpt_containing(head..head)?;
3415                if buffer_snapshot.remote_id() != buffer.read(cx).remote_id() {
3416                    return None;
3417                }
3418                Some(excerpt_range)
3419            })?;
3420
3421            if let Some(excerpt_range) = cursor_excerpt {
3422                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
3423                    let excerpt_range = excerpt_range.context.to_offset(buffer);
3424                    buffer
3425                        .edited_ranges_for_transaction::<usize>(transaction)
3426                        .all(|range| {
3427                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
3428                        })
3429                });
3430
3431                if all_edits_within_excerpt {
3432                    return Ok(());
3433                }
3434            }
3435        }
3436
3437        let mut ranges_to_highlight = Vec::new();
3438        let excerpt_buffer = cx.new(|cx| {
3439            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
3440            for (buffer_handle, transaction) in &entries {
3441                let edited_ranges = buffer_handle
3442                    .read(cx)
3443                    .edited_ranges_for_transaction::<Point>(transaction)
3444                    .collect::<Vec<_>>();
3445                multibuffer.set_excerpts_for_path(
3446                    PathKey::for_buffer(buffer_handle, cx),
3447                    buffer_handle.clone(),
3448                    edited_ranges.clone(),
3449                    multibuffer_context_lines(cx),
3450                    cx,
3451                );
3452                let snapshot = multibuffer.snapshot(cx);
3453                let buffer_snapshot = buffer_handle.read(cx).snapshot();
3454                ranges_to_highlight.extend(edited_ranges.into_iter().filter_map(|range| {
3455                    let text_range = buffer_snapshot.anchor_range_inside(range);
3456                    let start = snapshot.anchor_in_buffer(text_range.start)?;
3457                    let end = snapshot.anchor_in_buffer(text_range.end)?;
3458                    Some(start..end)
3459                }));
3460            }
3461            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3462            multibuffer
3463        });
3464
3465        workspace.update_in(cx, |workspace, window, cx| {
3466            let project = workspace.project().clone();
3467            let editor =
3468                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
3469            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
3470            editor.update(cx, |editor, cx| {
3471                editor.highlight_background(
3472                    HighlightKey::Editor,
3473                    &ranges_to_highlight,
3474                    |_, theme| theme.colors().editor_highlighted_line_background,
3475                    cx,
3476                );
3477            });
3478        })?;
3479
3480        Ok(())
3481    }
3482
3483    pub fn has_mouse_context_menu(&self) -> bool {
3484        self.mouse_context_menu.is_some()
3485    }
3486
3487    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
3488        if self.pending_rename.is_some() {
3489            return None;
3490        }
3491
3492        let provider = self.semantics_provider.clone()?;
3493        let buffer = self.buffer.read(cx);
3494        let newest_selection = self.selections.newest_anchor().clone();
3495        let cursor_position = newest_selection.head();
3496        let (cursor_buffer, cursor_buffer_position) =
3497            buffer.text_anchor_for_position(cursor_position, cx)?;
3498        let (tail_buffer, tail_buffer_position) =
3499            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3500        if cursor_buffer != tail_buffer {
3501            return None;
3502        }
3503
3504        let snapshot = cursor_buffer.read(cx).snapshot();
3505        let word_ranges = cx.background_spawn(async move {
3506            // this might look odd to put on the background thread, but
3507            // `surrounding_word` can be quite expensive as it calls into
3508            // tree-sitter language scopes
3509            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
3510            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
3511            (start_word_range, end_word_range)
3512        });
3513
3514        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
3515        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
3516            let (start_word_range, end_word_range) = word_ranges.await;
3517            if start_word_range != end_word_range {
3518                this.update(cx, |this, cx| {
3519                    this.document_highlights_task.take();
3520                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
3521                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
3522                })
3523                .ok();
3524                return;
3525            }
3526            cx.background_executor()
3527                .timer(Duration::from_millis(debounce))
3528                .await;
3529
3530            let highlights = if let Some(highlights) = cx.update(|cx| {
3531                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3532            }) {
3533                highlights.await.log_err()
3534            } else {
3535                None
3536            };
3537
3538            if let Some(highlights) = highlights {
3539                this.update(cx, |this, cx| {
3540                    if this.pending_rename.is_some() {
3541                        return;
3542                    }
3543
3544                    let buffer = this.buffer.read(cx);
3545                    if buffer
3546                        .text_anchor_for_position(cursor_position, cx)
3547                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
3548                    {
3549                        return;
3550                    }
3551
3552                    let mut write_ranges = Vec::new();
3553                    let mut read_ranges = Vec::new();
3554                    let multibuffer_snapshot = buffer.snapshot(cx);
3555                    for highlight in highlights {
3556                        for range in
3557                            multibuffer_snapshot.buffer_range_to_excerpt_ranges(highlight.range)
3558                        {
3559                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3560                                write_ranges.push(range);
3561                            } else {
3562                                read_ranges.push(range);
3563                            }
3564                        }
3565                    }
3566
3567                    this.highlight_background(
3568                        HighlightKey::DocumentHighlightRead,
3569                        &read_ranges,
3570                        |_, theme| theme.colors().editor_document_highlight_read_background,
3571                        cx,
3572                    );
3573                    this.highlight_background(
3574                        HighlightKey::DocumentHighlightWrite,
3575                        &write_ranges,
3576                        |_, theme| theme.colors().editor_document_highlight_write_background,
3577                        cx,
3578                    );
3579                    cx.notify();
3580                })
3581                .log_err();
3582            }
3583        }));
3584        None
3585    }
3586
3587    fn prepare_highlight_query_from_selection(
3588        &mut self,
3589        snapshot: &DisplaySnapshot,
3590        cx: &mut Context<Editor>,
3591    ) -> Option<(String, Range<Anchor>)> {
3592        if matches!(self.mode, EditorMode::SingleLine) {
3593            return None;
3594        }
3595        if !self.use_selection_highlight || !EditorSettings::get_global(cx).selection_highlight {
3596            return None;
3597        }
3598        // When the current selection was set by search navigation, suppress selection
3599        // occurrence highlights to avoid confusing non-matching occurrences with actual
3600        // search results (e.g. `^something` matches 3 line-start occurrences, but a
3601        // literal highlight would also mark a mid-line "something" that never matched
3602        // the regex). A manual selection made by the user clears this flag, restoring
3603        // the normal occurrence-highlight behavior.
3604        if self.last_selection_from_search
3605            && self.has_background_highlights(HighlightKey::BufferSearchHighlights)
3606        {
3607            return None;
3608        }
3609        if self.selections.count() != 1 || self.selections.line_mode() {
3610            return None;
3611        }
3612        let selection = self.selections.newest::<Point>(&snapshot);
3613        // If the selection spans multiple rows OR it is empty
3614        if selection.start.row != selection.end.row
3615            || selection.start.column == selection.end.column
3616        {
3617            return None;
3618        }
3619        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
3620        let query = snapshot
3621            .buffer_snapshot()
3622            .text_for_range(selection_anchor_range.clone())
3623            .collect::<String>();
3624        if query.trim().is_empty() {
3625            return None;
3626        }
3627        Some((query, selection_anchor_range))
3628    }
3629
3630    #[ztracing::instrument(skip_all)]
3631    fn update_selection_occurrence_highlights(
3632        &mut self,
3633        multi_buffer_snapshot: MultiBufferSnapshot,
3634        query_text: String,
3635        query_range: Range<Anchor>,
3636        multi_buffer_range_to_query: Range<Point>,
3637        use_debounce: bool,
3638        window: &mut Window,
3639        cx: &mut Context<Editor>,
3640    ) -> Task<()> {
3641        cx.spawn_in(window, async move |editor, cx| {
3642            if use_debounce {
3643                cx.background_executor()
3644                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
3645                    .await;
3646            }
3647            let match_task = cx.background_spawn(async move {
3648                let buffer_ranges = multi_buffer_snapshot
3649                    .range_to_buffer_ranges(
3650                        multi_buffer_range_to_query.start..multi_buffer_range_to_query.end,
3651                    )
3652                    .into_iter()
3653                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
3654                let mut match_ranges = Vec::new();
3655                let Ok(regex) = project::search::SearchQuery::text(
3656                    query_text,
3657                    false,
3658                    false,
3659                    false,
3660                    Default::default(),
3661                    Default::default(),
3662                    false,
3663                    None,
3664                ) else {
3665                    return Vec::default();
3666                };
3667                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
3668                for (buffer_snapshot, search_range, _) in buffer_ranges {
3669                    match_ranges.extend(
3670                        regex
3671                            .search(
3672                                &buffer_snapshot,
3673                                Some(search_range.start.0..search_range.end.0),
3674                            )
3675                            .await
3676                            .into_iter()
3677                            .filter_map(|match_range| {
3678                                let match_start = buffer_snapshot
3679                                    .anchor_after(search_range.start + match_range.start);
3680                                let match_end = buffer_snapshot
3681                                    .anchor_before(search_range.start + match_range.end);
3682                                {
3683                                    let range = multi_buffer_snapshot
3684                                        .anchor_in_buffer(match_start)?
3685                                        ..multi_buffer_snapshot.anchor_in_buffer(match_end)?;
3686                                    Some(range).filter(|match_anchor_range| {
3687                                        match_anchor_range != &query_range
3688                                    })
3689                                }
3690                            }),
3691                    );
3692                }
3693                match_ranges
3694            });
3695            let match_ranges = match_task.await;
3696            editor
3697                .update_in(cx, |editor, _, cx| {
3698                    if use_debounce {
3699                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
3700                        editor.debounced_selection_highlight_complete = true;
3701                    } else if editor.debounced_selection_highlight_complete {
3702                        return;
3703                    }
3704                    if !match_ranges.is_empty() {
3705                        editor.highlight_background(
3706                            HighlightKey::SelectedTextHighlight,
3707                            &match_ranges,
3708                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
3709                            cx,
3710                        )
3711                    }
3712                })
3713                .log_err();
3714        })
3715    }
3716
3717    #[ztracing::instrument(skip_all)]
3718    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
3719        if !self.lsp_data_enabled() {
3720            return;
3721        }
3722        let cursor = self.selections.newest_anchor().head();
3723        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
3724
3725        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
3726            self.outline_symbols_at_cursor =
3727                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
3728            cx.emit(EditorEvent::OutlineSymbolsChanged);
3729            cx.notify();
3730        } else {
3731            let syntax = cx.theme().syntax().clone();
3732            let background_task = cx.background_spawn(async move {
3733                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
3734            });
3735            self.refresh_outline_symbols_at_cursor_at_cursor_task =
3736                cx.spawn(async move |this, cx| {
3737                    let symbols = background_task.await;
3738                    this.update(cx, |this, cx| {
3739                        this.outline_symbols_at_cursor = symbols;
3740                        cx.emit(EditorEvent::OutlineSymbolsChanged);
3741                        cx.notify();
3742                    })
3743                    .ok();
3744                });
3745        }
3746    }
3747
3748    #[ztracing::instrument(skip_all)]
3749    fn refresh_selected_text_highlights(
3750        &mut self,
3751        snapshot: &DisplaySnapshot,
3752        on_buffer_edit: bool,
3753        window: &mut Window,
3754        cx: &mut Context<Editor>,
3755    ) {
3756        let Some((query_text, query_range)) =
3757            self.prepare_highlight_query_from_selection(snapshot, cx)
3758        else {
3759            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
3760            self.quick_selection_highlight_task.take();
3761            self.debounced_selection_highlight_task.take();
3762            self.debounced_selection_highlight_complete = false;
3763            return;
3764        };
3765        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3766        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
3767        let query_changed = self
3768            .quick_selection_highlight_task
3769            .as_ref()
3770            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
3771        if query_changed {
3772            self.debounced_selection_highlight_complete = false;
3773        }
3774        if on_buffer_edit || query_changed {
3775            self.quick_selection_highlight_task = Some((
3776                query_range.clone(),
3777                self.update_selection_occurrence_highlights(
3778                    snapshot.buffer.clone(),
3779                    query_text.clone(),
3780                    query_range.clone(),
3781                    self.multi_buffer_visible_range(&display_snapshot, cx),
3782                    false,
3783                    window,
3784                    cx,
3785                ),
3786            ));
3787        }
3788        if on_buffer_edit
3789            || self
3790                .debounced_selection_highlight_task
3791                .as_ref()
3792                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
3793        {
3794            let multi_buffer_start = multi_buffer_snapshot
3795                .anchor_before(MultiBufferOffset(0))
3796                .to_point(&multi_buffer_snapshot);
3797            let multi_buffer_end = multi_buffer_snapshot
3798                .anchor_after(multi_buffer_snapshot.len())
3799                .to_point(&multi_buffer_snapshot);
3800            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
3801            self.debounced_selection_highlight_task = Some((
3802                query_range.clone(),
3803                self.update_selection_occurrence_highlights(
3804                    snapshot.buffer.clone(),
3805                    query_text,
3806                    query_range,
3807                    multi_buffer_full_range,
3808                    true,
3809                    window,
3810                    cx,
3811                ),
3812            ));
3813        }
3814    }
3815
3816    pub fn multi_buffer_visible_range(
3817        &self,
3818        display_snapshot: &DisplaySnapshot,
3819        cx: &App,
3820    ) -> Range<Point> {
3821        let visible_start = self
3822            .scroll_manager
3823            .native_anchor(display_snapshot, cx)
3824            .anchor
3825            .to_point(display_snapshot.buffer_snapshot())
3826            .to_display_point(display_snapshot);
3827
3828        let mut target_end = visible_start;
3829        *target_end.row_mut() += self.visible_line_count().unwrap_or(0.).ceil() as u32;
3830
3831        visible_start.to_point(display_snapshot)
3832            ..display_snapshot
3833                .clip_point(target_end, Bias::Right)
3834                .to_point(display_snapshot)
3835    }
3836
3837    pub fn display_cursor_names(
3838        &mut self,
3839        _: &DisplayCursorNames,
3840        window: &mut Window,
3841        cx: &mut Context<Self>,
3842    ) {
3843        self.show_cursor_names(window, cx);
3844    }
3845
3846    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3847        self.show_cursor_names = true;
3848        cx.notify();
3849        cx.spawn_in(window, async move |this, cx| {
3850            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
3851            this.update(cx, |this, cx| {
3852                this.show_cursor_names = false;
3853                cx.notify()
3854            })
3855            .ok()
3856        })
3857        .detach();
3858    }
3859
3860    fn handle_modifiers_changed(
3861        &mut self,
3862        modifiers: Modifiers,
3863        position_map: &PositionMap,
3864        window: &mut Window,
3865        cx: &mut Context<Self>,
3866    ) {
3867        self.update_edit_prediction_settings(cx);
3868
3869        // Ensure that the edit prediction preview is updated, even when not
3870        // enabled, if there's an active edit prediction preview.
3871        if self.show_edit_predictions_in_menu()
3872            || self.edit_prediction_requires_modifier()
3873            || matches!(
3874                self.edit_prediction_preview,
3875                EditPredictionPreview::Active { .. }
3876            )
3877        {
3878            self.update_edit_prediction_preview(&modifiers, window, cx);
3879        }
3880
3881        self.update_selection_mode(&modifiers, position_map, window, cx);
3882
3883        let mouse_position = window.mouse_position();
3884        if !position_map.text_hitbox.is_hovered(window) {
3885            if self.gutter_hover_button.0.is_some() {
3886                cx.notify();
3887            }
3888            return;
3889        }
3890
3891        self.update_hovered_link(
3892            position_map.point_for_position(mouse_position),
3893            Some(mouse_position),
3894            &position_map.snapshot,
3895            modifiers,
3896            window,
3897            cx,
3898        )
3899    }
3900
3901    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
3902        match EditorSettings::get_global(cx).multi_cursor_modifier {
3903            MultiCursorModifier::Alt => modifiers.secondary(),
3904            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
3905        }
3906    }
3907
3908    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
3909        match EditorSettings::get_global(cx).multi_cursor_modifier {
3910            MultiCursorModifier::Alt => modifiers.alt,
3911            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
3912        }
3913    }
3914
3915    fn columnar_selection_mode(
3916        modifiers: &Modifiers,
3917        cx: &mut Context<Self>,
3918    ) -> Option<ColumnarMode> {
3919        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
3920            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
3921                Some(ColumnarMode::FromMouse)
3922            } else if Self::is_alt_pressed(modifiers, cx) {
3923                Some(ColumnarMode::FromSelection)
3924            } else {
3925                None
3926            }
3927        } else {
3928            None
3929        }
3930    }
3931
3932    fn update_selection_mode(
3933        &mut self,
3934        modifiers: &Modifiers,
3935        position_map: &PositionMap,
3936        window: &mut Window,
3937        cx: &mut Context<Self>,
3938    ) {
3939        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
3940            return;
3941        };
3942        if self.selections.pending_anchor().is_none() {
3943            return;
3944        }
3945
3946        let mouse_position = window.mouse_position();
3947        let point_for_position = position_map.point_for_position(mouse_position);
3948        let position = point_for_position.previous_valid;
3949
3950        self.select(
3951            SelectPhase::BeginColumnar {
3952                position,
3953                reset: false,
3954                mode,
3955                goal_column: point_for_position.exact_unclipped.column(),
3956            },
3957            window,
3958            cx,
3959        );
3960    }
3961
3962    fn active_run_indicators(
3963        &mut self,
3964        range: Range<DisplayRow>,
3965        window: &mut Window,
3966        cx: &mut Context<Self>,
3967    ) -> HashSet<DisplayRow> {
3968        let snapshot = self.snapshot(window, cx);
3969
3970        let offset_range_start =
3971            snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left);
3972
3973        let offset_range_end =
3974            snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
3975
3976        self.runnables
3977            .all_runnables()
3978            .filter_map(|tasks| {
3979                let multibuffer_point = tasks.offset.to_point(&snapshot.buffer_snapshot());
3980                if multibuffer_point < offset_range_start || multibuffer_point > offset_range_end {
3981                    return None;
3982                }
3983                let multibuffer_row = MultiBufferRow(multibuffer_point.row);
3984                let buffer_folded = snapshot
3985                    .buffer_snapshot()
3986                    .buffer_line_for_row(multibuffer_row)
3987                    .map(|(buffer_snapshot, _)| buffer_snapshot.remote_id())
3988                    .map(|buffer_id| self.is_buffer_folded(buffer_id, cx))
3989                    .unwrap_or(false);
3990                if buffer_folded {
3991                    return None;
3992                }
3993
3994                if snapshot.is_line_folded(multibuffer_row) {
3995                    // Skip folded indicators, unless it's the starting line of a fold.
3996                    if multibuffer_row
3997                        .0
3998                        .checked_sub(1)
3999                        .is_some_and(|previous_row| {
4000                            snapshot.is_line_folded(MultiBufferRow(previous_row))
4001                        })
4002                    {
4003                        return None;
4004                    }
4005                }
4006
4007                let display_row = multibuffer_point.to_display_point(&snapshot).row();
4008                Some(display_row)
4009            })
4010            .collect()
4011    }
4012
4013    fn active_bookmarks(
4014        &self,
4015        range: Range<DisplayRow>,
4016        window: &mut Window,
4017        cx: &mut Context<Self>,
4018    ) -> HashSet<DisplayRow> {
4019        let mut bookmark_display_points = HashSet::default();
4020
4021        let Some(bookmark_store) = self.bookmark_store.clone() else {
4022            return bookmark_display_points;
4023        };
4024
4025        let snapshot = self.snapshot(window, cx);
4026
4027        let multi_buffer_snapshot = snapshot.buffer_snapshot();
4028        let Some(project) = self.project() else {
4029            return bookmark_display_points;
4030        };
4031
4032        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
4033            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
4034
4035        for (buffer_snapshot, range, _excerpt_range) in
4036            multi_buffer_snapshot.range_to_buffer_ranges(range.start..range.end)
4037        {
4038            let Some(buffer) = project
4039                .read(cx)
4040                .buffer_for_id(buffer_snapshot.remote_id(), cx)
4041            else {
4042                continue;
4043            };
4044            let bookmarks = bookmark_store.update(cx, |store, cx| {
4045                store.bookmarks_for_buffer(
4046                    buffer,
4047                    buffer_snapshot.anchor_before(range.start)
4048                        ..buffer_snapshot.anchor_after(range.end),
4049                    &buffer_snapshot,
4050                    cx,
4051                )
4052            });
4053            for bookmark in bookmarks {
4054                let Some(multi_buffer_anchor) =
4055                    multi_buffer_snapshot.anchor_in_buffer(bookmark.anchor)
4056                else {
4057                    continue;
4058                };
4059                let position = multi_buffer_anchor
4060                    .to_point(&multi_buffer_snapshot)
4061                    .to_display_point(&snapshot);
4062
4063                bookmark_display_points.insert(position.row());
4064            }
4065        }
4066
4067        bookmark_display_points
4068    }
4069
4070    fn render_bookmark(&self, row: DisplayRow, cx: &mut Context<Self>) -> IconButton {
4071        let focus_handle = self.focus_handle.clone();
4072        IconButton::new(("bookmark indicator", row.0 as usize), IconName::Bookmark)
4073            .icon_size(IconSize::XSmall)
4074            .size(ui::ButtonSize::None)
4075            .icon_color(Color::Info)
4076            .style(ButtonStyle::Transparent)
4077            .on_click(cx.listener(move |editor, _, _window, cx| {
4078                editor.toggle_bookmark_at_row(row, cx);
4079            }))
4080            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
4081                editor.set_gutter_context_menu(row, None, event.position(), window, cx);
4082            }))
4083            .tooltip(move |_window, cx| {
4084                Tooltip::with_meta_in(
4085                    "Remove Bookmark",
4086                    Some(&ToggleBookmark),
4087                    SharedString::from("Right-click for more options"),
4088                    &focus_handle,
4089                    cx,
4090                )
4091            })
4092    }
4093
4094    /// Get all display points of breakpoints that will be rendered within editor
4095    ///
4096    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
4097    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
4098    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
4099    fn active_breakpoints(
4100        &self,
4101        range: Range<DisplayRow>,
4102        window: &mut Window,
4103        cx: &mut Context<Self>,
4104    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
4105        let mut breakpoint_display_points = HashMap::default();
4106
4107        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
4108            return breakpoint_display_points;
4109        };
4110
4111        let snapshot = self.snapshot(window, cx);
4112
4113        let multi_buffer_snapshot = snapshot.buffer_snapshot();
4114
4115        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
4116            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
4117
4118        for (buffer_snapshot, range, _) in
4119            multi_buffer_snapshot.range_to_buffer_ranges(range.start..range.end)
4120        {
4121            let Some(buffer) = self.buffer().read(cx).buffer(buffer_snapshot.remote_id()) else {
4122                continue;
4123            };
4124            let breakpoints = breakpoint_store.read(cx).breakpoints(
4125                &buffer,
4126                Some(
4127                    buffer_snapshot.anchor_before(range.start)
4128                        ..buffer_snapshot.anchor_after(range.end),
4129                ),
4130                &buffer_snapshot,
4131                cx,
4132            );
4133            for (breakpoint, state) in breakpoints {
4134                let Some(multi_buffer_anchor) =
4135                    multi_buffer_snapshot.anchor_in_excerpt(breakpoint.position)
4136                else {
4137                    continue;
4138                };
4139                let position = multi_buffer_anchor
4140                    .to_point(&multi_buffer_snapshot)
4141                    .to_display_point(&snapshot);
4142
4143                breakpoint_display_points.insert(
4144                    position.row(),
4145                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
4146                );
4147            }
4148        }
4149
4150        breakpoint_display_points
4151    }
4152
4153    fn gutter_context_menu(
4154        &self,
4155        anchor: Anchor,
4156        display_row: DisplayRow,
4157        window: &mut Window,
4158        cx: &mut Context<Self>,
4159    ) -> Entity<ContextMenu> {
4160        let weak_editor = cx.weak_entity();
4161        let focus_handle = self.focus_handle(cx);
4162
4163        let row = self
4164            .buffer
4165            .read(cx)
4166            .snapshot(cx)
4167            .summary_for_anchor::<Point>(&anchor)
4168            .row;
4169
4170        let breakpoint = self
4171            .breakpoint_at_row(row, window, cx)
4172            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
4173
4174        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
4175            "Edit Log Breakpoint"
4176        } else {
4177            "Set Log Breakpoint"
4178        };
4179
4180        let condition_breakpoint_msg = if breakpoint
4181            .as_ref()
4182            .is_some_and(|bp| bp.1.condition.is_some())
4183        {
4184            "Edit Condition Breakpoint"
4185        } else {
4186            "Set Condition Breakpoint"
4187        };
4188
4189        let hit_condition_breakpoint_msg = if breakpoint
4190            .as_ref()
4191            .is_some_and(|bp| bp.1.hit_condition.is_some())
4192        {
4193            "Edit Hit Condition Breakpoint"
4194        } else {
4195            "Set Hit Condition Breakpoint"
4196        };
4197
4198        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
4199            "Unset Breakpoint"
4200        } else {
4201            "Set Breakpoint"
4202        };
4203
4204        let git_blame_msg = if self.show_git_blame_gutter {
4205            "Close Git Blame"
4206        } else {
4207            "Open Git Blame"
4208        };
4209
4210        let bookmark = self.bookmark_at_row(row, window, cx);
4211
4212        let set_bookmark_msg = if bookmark.as_ref().is_some() {
4213            "Remove Bookmark"
4214        } else {
4215            "Add Bookmark"
4216        };
4217        let has_bookmark = bookmark.as_ref().is_some();
4218
4219        let clear_runnable_task_status = self
4220            .runnable_task_key_for_display_row(display_row, window, cx)
4221            .filter(|(buffer_id, buffer_row)| {
4222                matches!(
4223                    self.runnable_task_status(*buffer_id, *buffer_row),
4224                    Some(RunnableTaskStatus::Passed | RunnableTaskStatus::Failed)
4225                )
4226            });
4227
4228        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
4229
4230        let toggle_state_entry: Option<(&str, Box<dyn Action>)> =
4231            breakpoint.as_ref().map(|bp| match bp.1.state {
4232                BreakpointState::Enabled => {
4233                    ("Disable", crate::actions::DisableBreakpoint.boxed_clone())
4234                }
4235                BreakpointState::Disabled => {
4236                    ("Enable", crate::actions::EnableBreakpoint.boxed_clone())
4237                }
4238            });
4239
4240        let (anchor, breakpoint) =
4241            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
4242
4243        ContextMenu::build(window, cx, |menu, _, _cx| {
4244            menu.on_blur_subscription(Subscription::new(|| {}))
4245                .context(focus_handle)
4246                .when_some(
4247                    clear_runnable_task_status,
4248                    |this, (buffer_id, buffer_row)| {
4249                        this.entry("Clear Run Status", None, {
4250                            let weak_editor = weak_editor.clone();
4251                            move |_window, cx| {
4252                                weak_editor
4253                                    .update(cx, |this, cx| {
4254                                        this.clear_runnable_task_status(buffer_id, buffer_row, cx);
4255                                    })
4256                                    .log_err();
4257                            }
4258                        })
4259                        .separator()
4260                    },
4261                )
4262                .when(run_to_cursor, |this| {
4263                    let weak_editor = weak_editor.clone();
4264                    this.entry(
4265                        "Run to Cursor",
4266                        Some(RunToCursor.boxed_clone()),
4267                        move |window, cx| {
4268                            weak_editor
4269                                .update(cx, |editor, cx| {
4270                                    editor.change_selections(
4271                                        SelectionEffects::no_scroll(),
4272                                        window,
4273                                        cx,
4274                                        |s| {
4275                                            s.select_ranges(
4276                                                [Point::new(row, 0)..Point::new(row, 0)],
4277                                            )
4278                                        },
4279                                    );
4280                                })
4281                                .ok();
4282
4283                            window.dispatch_action(Box::new(RunToCursor), cx);
4284                        },
4285                    )
4286                    .separator()
4287                })
4288                .when_some(toggle_state_entry, |this, (msg, action)| {
4289                    this.entry(msg, Some(action), {
4290                        let weak_editor = weak_editor.clone();
4291                        let breakpoint = breakpoint.clone();
4292                        move |_window, cx| {
4293                            weak_editor
4294                                .update(cx, |this, cx| {
4295                                    this.edit_breakpoint_at_anchor(
4296                                        anchor,
4297                                        breakpoint.as_ref().clone(),
4298                                        BreakpointEditAction::InvertState,
4299                                        cx,
4300                                    );
4301                                })
4302                                .log_err();
4303                        }
4304                    })
4305                })
4306                .entry(
4307                    set_breakpoint_msg,
4308                    Some(crate::actions::ToggleBreakpoint.boxed_clone()),
4309                    {
4310                        let weak_editor = weak_editor.clone();
4311                        let breakpoint = breakpoint.clone();
4312                        move |_window, cx| {
4313                            weak_editor
4314                                .update(cx, |this, cx| {
4315                                    this.edit_breakpoint_at_anchor(
4316                                        anchor,
4317                                        breakpoint.as_ref().clone(),
4318                                        BreakpointEditAction::Toggle,
4319                                        cx,
4320                                    );
4321                                })
4322                                .log_err();
4323                        }
4324                    },
4325                )
4326                .entry(
4327                    log_breakpoint_msg,
4328                    Some(crate::actions::EditLogBreakpoint.boxed_clone()),
4329                    {
4330                        let breakpoint = breakpoint.clone();
4331                        let weak_editor = weak_editor.clone();
4332                        move |window, cx| {
4333                            weak_editor
4334                                .update(cx, |this, cx| {
4335                                    this.add_edit_breakpoint_block(
4336                                        anchor,
4337                                        breakpoint.as_ref(),
4338                                        BreakpointPromptEditAction::Log,
4339                                        window,
4340                                        cx,
4341                                    );
4342                                })
4343                                .log_err();
4344                        }
4345                    },
4346                )
4347                .entry(condition_breakpoint_msg, None, {
4348                    let breakpoint = breakpoint.clone();
4349                    let weak_editor = weak_editor.clone();
4350                    move |window, cx| {
4351                        weak_editor
4352                            .update(cx, |this, cx| {
4353                                this.add_edit_breakpoint_block(
4354                                    anchor,
4355                                    breakpoint.as_ref(),
4356                                    BreakpointPromptEditAction::Condition,
4357                                    window,
4358                                    cx,
4359                                );
4360                            })
4361                            .log_err();
4362                    }
4363                })
4364                .entry(hit_condition_breakpoint_msg, None, {
4365                    let breakpoint = breakpoint.clone();
4366                    let weak_editor = weak_editor.clone();
4367                    move |window, cx| {
4368                        weak_editor
4369                            .update(cx, |this, cx| {
4370                                this.add_edit_breakpoint_block(
4371                                    anchor,
4372                                    breakpoint.as_ref(),
4373                                    BreakpointPromptEditAction::HitCondition,
4374                                    window,
4375                                    cx,
4376                                );
4377                            })
4378                            .log_err();
4379                    }
4380                })
4381                .separator()
4382                .entry(git_blame_msg, Some(Blame.boxed_clone()), {
4383                    let weak_editor = weak_editor.clone();
4384                    move |window, cx| {
4385                        weak_editor
4386                            .update(cx, |this, cx| {
4387                                this.toggle_git_blame(&Blame, window, cx);
4388                            })
4389                            .log_err();
4390                    }
4391                })
4392                .separator()
4393                .entry(set_bookmark_msg, Some(ToggleBookmark.boxed_clone()), {
4394                    let weak_editor = weak_editor.clone();
4395                    move |_window, cx| {
4396                        weak_editor
4397                            .update(cx, |this, cx| {
4398                                this.toggle_bookmark_at_anchor(anchor, cx);
4399                            })
4400                            .log_err();
4401                    }
4402                })
4403                .when(has_bookmark, |this| {
4404                    this.entry(
4405                        "Edit Bookmark",
4406                        Some(EditBookmark.boxed_clone()),
4407                        move |window, cx| {
4408                            weak_editor
4409                                .update(cx, |this, cx| {
4410                                    this.edit_bookmark_at_anchor(anchor, window, cx);
4411                                })
4412                                .log_err();
4413                        },
4414                    )
4415                })
4416        })
4417    }
4418
4419    fn render_breakpoint(
4420        &self,
4421        position: Anchor,
4422        row: DisplayRow,
4423        breakpoint: &Breakpoint,
4424        state: Option<BreakpointSessionState>,
4425        cx: &mut Context<Self>,
4426    ) -> IconButton {
4427        let is_rejected = state.is_some_and(|s| !s.verified);
4428
4429        let (color, icon) = {
4430            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
4431                (false, false) => ui::IconName::DebugBreakpoint,
4432                (true, false) => ui::IconName::DebugLogBreakpoint,
4433                (false, true) => ui::IconName::DebugDisabledBreakpoint,
4434                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
4435            };
4436
4437            let color = if is_rejected {
4438                Color::Disabled
4439            } else {
4440                Color::Debugger
4441            };
4442
4443            (color, icon)
4444        };
4445
4446        let breakpoint = Arc::from(breakpoint.clone());
4447
4448        let alt_as_text = gpui::Keystroke {
4449            modifiers: Modifiers::secondary_key(),
4450            ..Default::default()
4451        };
4452        let primary_action_text = "Unset breakpoint";
4453        let focus_handle = self.focus_handle.clone();
4454        let has_context_menu = self.has_mouse_context_menu();
4455
4456        let meta = if is_rejected {
4457            SharedString::from("No executable code is associated with this line.")
4458        } else if !breakpoint.is_disabled() {
4459            SharedString::from(format!(
4460                "{alt_as_text}-click to disable\nright-click for more options"
4461            ))
4462        } else {
4463            SharedString::from("Right-click for more options")
4464        };
4465        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
4466            .icon_size(IconSize::XSmall)
4467            .size(ui::ButtonSize::None)
4468            .when(is_rejected, |this| {
4469                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
4470            })
4471            .icon_color(color)
4472            .style(ButtonStyle::Transparent)
4473            .on_click(cx.listener({
4474                move |editor, event: &ClickEvent, window, cx| {
4475                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
4476                        BreakpointEditAction::InvertState
4477                    } else {
4478                        BreakpointEditAction::Toggle
4479                    };
4480
4481                    window.focus(&editor.focus_handle(cx), cx);
4482                    editor.edit_breakpoint_at_anchor(
4483                        position,
4484                        breakpoint.as_ref().clone(),
4485                        edit_action,
4486                        cx,
4487                    );
4488                }
4489            }))
4490            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
4491                editor.set_gutter_context_menu(row, Some(position), event.position(), window, cx);
4492            }))
4493            .when(!has_context_menu, |button| {
4494                button.tooltip(move |_window, cx| {
4495                    Tooltip::with_meta_in(
4496                        primary_action_text,
4497                        Some(&ToggleBreakpoint),
4498                        meta.clone(),
4499                        &focus_handle,
4500                        cx,
4501                    )
4502                })
4503            })
4504    }
4505
4506    fn render_gutter_hover_button(
4507        &self,
4508        position: Anchor,
4509        row: DisplayRow,
4510        window: &mut Window,
4511        cx: &mut Context<Self>,
4512    ) -> IconButton {
4513        let gutter_settings = EditorSettings::get_global(cx).gutter;
4514        let show_bookmarks = self.show_bookmarks.unwrap_or(gutter_settings.bookmarks);
4515        let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
4516
4517        let [primary, secondary] = match [show_breakpoints, show_bookmarks] {
4518            [true, true] => [
4519                GutterButtonIntent::SetBreakpoint,
4520                GutterButtonIntent::SetBookmark,
4521            ],
4522            [true, false] => [GutterButtonIntent::SetBreakpoint; 2],
4523            [false, true] => [GutterButtonIntent::SetBookmark; 2],
4524            [false, false] => {
4525                log::error!("Trying to place gutter_hover without anything enabled!!");
4526                [GutterButtonIntent::SetBookmark; 2]
4527            }
4528        };
4529
4530        let intent = if window.modifiers().secondary() {
4531            secondary
4532        } else {
4533            primary
4534        };
4535
4536        let focus_handle = self.focus_handle.clone();
4537        let has_context_menu = self.has_mouse_context_menu();
4538        IconButton::new(("add_breakpoint_button", row.0 as usize), intent.icon())
4539            .icon_size(IconSize::XSmall)
4540            .size(ui::ButtonSize::None)
4541            .icon_color(intent.color())
4542            .style(ButtonStyle::Transparent)
4543            .on_click(cx.listener({
4544                move |editor, _: &ClickEvent, window, cx| {
4545                    window.focus(&editor.focus_handle(cx), cx);
4546                    let intent = if window.modifiers().secondary() {
4547                        secondary
4548                    } else {
4549                        primary
4550                    };
4551
4552                    match intent {
4553                        GutterButtonIntent::SetBookmark => editor.toggle_bookmark_at_row(row, cx),
4554                        GutterButtonIntent::SetBreakpoint => editor.edit_breakpoint_at_anchor(
4555                            position,
4556                            Breakpoint::new_standard(),
4557                            BreakpointEditAction::Toggle,
4558                            cx,
4559                        ),
4560                    }
4561                }
4562            }))
4563            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
4564                editor.set_gutter_context_menu(row, Some(position), event.position(), window, cx);
4565            }))
4566            .when(!has_context_menu, |button| {
4567                button.tooltip(move |_window, cx| {
4568                    cx.new(|_| GutterButtonTooltip {
4569                        primary,
4570                        secondary,
4571                        focus_handle: focus_handle.clone(),
4572                    })
4573                    .into()
4574                })
4575            })
4576    }
4577
4578    fn build_tasks_context(
4579        project: &Entity<Project>,
4580        buffer: &Entity<Buffer>,
4581        buffer_row: u32,
4582        tasks: &Arc<RunnableTasks>,
4583        cx: &mut Context<Self>,
4584    ) -> Task<Result<Option<task::TaskContext>>> {
4585        let position = Point::new(buffer_row, tasks.column);
4586        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4587        let location = Location {
4588            buffer: buffer.clone(),
4589            range: range_start..range_start,
4590        };
4591        // Fill in the environmental variables from the tree-sitter captures
4592        let mut captured_task_variables = TaskVariables::default();
4593        for (capture_name, value) in tasks.extra_variables.clone() {
4594            captured_task_variables.insert(
4595                task::VariableName::Custom(capture_name.into()),
4596                value.clone(),
4597            );
4598        }
4599        project.update(cx, |project, cx| {
4600            project.task_store().update(cx, |task_store, cx| {
4601                task_store.task_context_for_location(captured_task_variables, location, cx)
4602            })
4603        })
4604    }
4605
4606    pub fn context_menu_visible(&self) -> bool {
4607        !self.edit_prediction_preview_is_active()
4608            && self
4609                .context_menu
4610                .borrow()
4611                .as_ref()
4612                .is_some_and(|menu| menu.visible())
4613    }
4614
4615    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
4616        self.context_menu
4617            .borrow()
4618            .as_ref()
4619            .map(|menu| menu.origin())
4620    }
4621
4622    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
4623        self.context_menu_options = Some(options);
4624    }
4625
4626    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
4627        if self.read_only(cx) {
4628            cx.theme().players().read_only()
4629        } else {
4630            self.style.as_ref().unwrap().local_player
4631        }
4632    }
4633
4634    pub fn render_context_menu(
4635        &mut self,
4636        max_height_in_lines: u32,
4637        window: &mut Window,
4638        cx: &mut Context<Editor>,
4639    ) -> Option<AnyElement> {
4640        let menu = self.context_menu.borrow();
4641        let menu = menu.as_ref()?;
4642        if !menu.visible() {
4643            return None;
4644        };
4645        self.style
4646            .as_ref()
4647            .map(|style| menu.render(style, max_height_in_lines, window, cx))
4648    }
4649
4650    fn render_context_menu_aside(
4651        &mut self,
4652        max_size: Size<Pixels>,
4653        window: &mut Window,
4654        cx: &mut Context<Editor>,
4655    ) -> Option<AnyElement> {
4656        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
4657            if menu.visible() {
4658                menu.render_aside(max_size, window, cx)
4659            } else {
4660                None
4661            }
4662        })
4663    }
4664
4665    fn hide_context_menu(
4666        &mut self,
4667        window: &mut Window,
4668        cx: &mut Context<Self>,
4669    ) -> Option<CodeContextMenu> {
4670        cx.notify();
4671        self.completion_tasks.clear();
4672        let context_menu = self.context_menu.borrow_mut().take();
4673        self.stale_edit_prediction_in_menu.take();
4674        self.update_visible_edit_prediction(window, cx);
4675        if let Some(CodeContextMenu::Completions(_)) = &context_menu
4676            && let Some(completion_provider) = &self.completion_provider
4677        {
4678            completion_provider.selection_changed(None, window, cx);
4679        }
4680        context_menu
4681    }
4682
4683    fn show_snippet_choices(
4684        &mut self,
4685        choices: &Vec<String>,
4686        selection: Range<Anchor>,
4687        cx: &mut Context<Self>,
4688    ) {
4689        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
4690        let Some((buffer_snapshot, range)) =
4691            buffer_snapshot.anchor_range_to_buffer_anchor_range(selection.clone())
4692        else {
4693            return;
4694        };
4695        let Some(buffer) = self.buffer.read(cx).buffer(buffer_snapshot.remote_id()) else {
4696            return;
4697        };
4698
4699        let id = post_inc(&mut self.next_completion_id);
4700        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
4701        let mut context_menu = self.context_menu.borrow_mut();
4702        let old_menu = context_menu.take();
4703        *context_menu = Some(CodeContextMenu::Completions(
4704            CompletionsMenu::new_snippet_choices(
4705                id,
4706                true,
4707                choices,
4708                selection.start,
4709                range,
4710                buffer,
4711                old_menu.map(|menu| menu.primary_scroll_handle()),
4712                snippet_sort_order,
4713            ),
4714        ));
4715    }
4716
4717    pub fn insert_snippet(
4718        &mut self,
4719        insertion_ranges: &[Range<MultiBufferOffset>],
4720        snippet: Snippet,
4721        window: &mut Window,
4722        cx: &mut Context<Self>,
4723    ) -> Result<()> {
4724        struct Tabstop<T> {
4725            is_end_tabstop: bool,
4726            ranges: Vec<Range<T>>,
4727            choices: Option<Vec<String>>,
4728        }
4729
4730        let tabstops = self.buffer.update(cx, |buffer, cx| {
4731            let snippet_text: Arc<str> = snippet.text.clone().into();
4732            let edits = insertion_ranges
4733                .iter()
4734                .cloned()
4735                .map(|range| (range, snippet_text.clone()));
4736            let autoindent_mode = AutoindentMode::Block {
4737                original_indent_columns: Vec::new(),
4738            };
4739            buffer.edit(edits, Some(autoindent_mode), cx);
4740
4741            let snapshot = &*buffer.read(cx);
4742            let snippet = &snippet;
4743            snippet
4744                .tabstops
4745                .iter()
4746                .map(|tabstop| {
4747                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
4748                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
4749                    });
4750                    let mut tabstop_ranges = tabstop
4751                        .ranges
4752                        .iter()
4753                        .flat_map(|tabstop_range| {
4754                            let mut delta = 0_isize;
4755                            insertion_ranges.iter().map(move |insertion_range| {
4756                                let insertion_start = insertion_range.start + delta;
4757                                delta += snippet.text.len() as isize
4758                                    - (insertion_range.end - insertion_range.start) as isize;
4759
4760                                let start =
4761                                    (insertion_start + tabstop_range.start).min(snapshot.len());
4762                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
4763                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
4764                            })
4765                        })
4766                        .collect::<Vec<_>>();
4767                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4768
4769                    Tabstop {
4770                        is_end_tabstop,
4771                        ranges: tabstop_ranges,
4772                        choices: tabstop.choices.clone(),
4773                    }
4774                })
4775                .collect::<Vec<_>>()
4776        });
4777        if let Some(tabstop) = tabstops.first() {
4778            self.change_selections(Default::default(), window, cx, |s| {
4779                // Reverse order so that the first range is the newest created selection.
4780                // Completions will use it and autoscroll will prioritize it.
4781                s.select_ranges(tabstop.ranges.iter().rev().cloned());
4782            });
4783
4784            if let Some(choices) = &tabstop.choices
4785                && let Some(selection) = tabstop.ranges.first()
4786            {
4787                self.show_snippet_choices(choices, selection.clone(), cx)
4788            }
4789
4790            // If we're already at the last tabstop and it's at the end of the snippet,
4791            // we're done, we don't need to keep the state around.
4792            if !tabstop.is_end_tabstop {
4793                let choices = tabstops
4794                    .iter()
4795                    .map(|tabstop| tabstop.choices.clone())
4796                    .collect();
4797
4798                let ranges = tabstops
4799                    .into_iter()
4800                    .map(|tabstop| tabstop.ranges)
4801                    .collect::<Vec<_>>();
4802
4803                self.snippet_stack.push(SnippetState {
4804                    active_index: 0,
4805                    ranges,
4806                    choices,
4807                });
4808            }
4809
4810            // Check whether the just-entered snippet ends with an auto-closable bracket.
4811            if self.autoclose_regions.is_empty() {
4812                let snapshot = self.buffer.read(cx).snapshot(cx);
4813                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
4814                    let selection_head = selection.head();
4815                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
4816                        continue;
4817                    };
4818
4819                    let mut bracket_pair = None;
4820                    let max_lookup_length = scope
4821                        .brackets()
4822                        .map(|(pair, _)| {
4823                            pair.start
4824                                .as_str()
4825                                .chars()
4826                                .count()
4827                                .max(pair.end.as_str().chars().count())
4828                        })
4829                        .max();
4830                    if let Some(max_lookup_length) = max_lookup_length {
4831                        let next_text = snapshot
4832                            .chars_at(selection_head)
4833                            .take(max_lookup_length)
4834                            .collect::<String>();
4835                        let prev_text = snapshot
4836                            .reversed_chars_at(selection_head)
4837                            .take(max_lookup_length)
4838                            .collect::<String>();
4839
4840                        for (pair, enabled) in scope.brackets() {
4841                            if enabled
4842                                && pair.close
4843                                && prev_text.starts_with(pair.start.as_str())
4844                                && next_text.starts_with(pair.end.as_str())
4845                            {
4846                                bracket_pair = Some(pair.clone());
4847                                break;
4848                            }
4849                        }
4850                    }
4851
4852                    if let Some(pair) = bracket_pair {
4853                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
4854                        let autoclose_enabled =
4855                            self.use_autoclose && snapshot_settings.use_autoclose;
4856                        if autoclose_enabled {
4857                            let start = snapshot.anchor_after(selection_head);
4858                            let end = snapshot.anchor_after(selection_head);
4859                            self.autoclose_regions.push(AutocloseRegion {
4860                                selection_id: selection.id,
4861                                range: start..end,
4862                                pair,
4863                            });
4864                        }
4865                    }
4866                }
4867            }
4868        }
4869        Ok(())
4870    }
4871
4872    pub fn move_to_next_snippet_tabstop(
4873        &mut self,
4874        window: &mut Window,
4875        cx: &mut Context<Self>,
4876    ) -> bool {
4877        self.move_to_snippet_tabstop(Bias::Right, window, cx)
4878    }
4879
4880    pub fn move_to_prev_snippet_tabstop(
4881        &mut self,
4882        window: &mut Window,
4883        cx: &mut Context<Self>,
4884    ) -> bool {
4885        self.move_to_snippet_tabstop(Bias::Left, window, cx)
4886    }
4887
4888    pub fn move_to_snippet_tabstop(
4889        &mut self,
4890        bias: Bias,
4891        window: &mut Window,
4892        cx: &mut Context<Self>,
4893    ) -> bool {
4894        if let Some(mut snippet) = self.snippet_stack.pop() {
4895            match bias {
4896                Bias::Left => {
4897                    if snippet.active_index > 0 {
4898                        snippet.active_index -= 1;
4899                    } else {
4900                        self.snippet_stack.push(snippet);
4901                        return false;
4902                    }
4903                }
4904                Bias::Right => {
4905                    if snippet.active_index + 1 < snippet.ranges.len() {
4906                        snippet.active_index += 1;
4907                    } else {
4908                        self.snippet_stack.push(snippet);
4909                        return false;
4910                    }
4911                }
4912            }
4913            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4914                self.change_selections(Default::default(), window, cx, |s| {
4915                    // Reverse order so that the first range is the newest created selection.
4916                    // Completions will use it and autoscroll will prioritize it.
4917                    s.select_ranges(current_ranges.iter().rev().cloned())
4918                });
4919
4920                if let Some(choices) = &snippet.choices[snippet.active_index]
4921                    && let Some(selection) = current_ranges.first()
4922                {
4923                    self.show_snippet_choices(choices, selection.clone(), cx);
4924                }
4925
4926                // If snippet state is not at the last tabstop, push it back on the stack
4927                if snippet.active_index + 1 < snippet.ranges.len() {
4928                    self.snippet_stack.push(snippet);
4929                }
4930                return true;
4931            }
4932        }
4933
4934        false
4935    }
4936
4937    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4938        self.transact(window, cx, |this, window, cx| {
4939            this.select_all(&SelectAll, window, cx);
4940            this.insert("", window, cx);
4941        });
4942    }
4943
4944    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
4945        if self.read_only(cx) {
4946            return;
4947        }
4948        self.transact(window, cx, |this, window, cx| {
4949            this.select_autoclose_pair(window, cx);
4950
4951            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
4952
4953            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4954            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
4955            for selection in &mut selections {
4956                if selection.is_empty() {
4957                    let old_head = selection.head();
4958                    let mut new_head =
4959                        movement::left(&display_map, old_head.to_display_point(&display_map))
4960                            .to_point(&display_map);
4961                    if let Some((buffer, line_buffer_range)) = display_map
4962                        .buffer_snapshot()
4963                        .buffer_line_for_row(MultiBufferRow(old_head.row))
4964                    {
4965                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
4966                        let indent_len = match indent_size.kind {
4967                            IndentKind::Space => {
4968                                buffer.settings_at(line_buffer_range.start, cx).tab_size
4969                            }
4970                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4971                        };
4972                        if old_head.column <= indent_size.len && old_head.column > 0 {
4973                            let indent_len = indent_len.get();
4974                            new_head = cmp::min(
4975                                new_head,
4976                                MultiBufferPoint::new(
4977                                    old_head.row,
4978                                    ((old_head.column - 1) / indent_len) * indent_len,
4979                                ),
4980                            );
4981                        }
4982                    }
4983
4984                    selection.set_head(new_head, SelectionGoal::None);
4985                }
4986            }
4987
4988            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
4989            this.insert("", window, cx);
4990            linked_edits.apply_with_left_expansion(cx);
4991            this.refresh_edit_prediction(
4992                true,
4993                false,
4994                EditPredictionRequestTrigger::BufferEdit,
4995                window,
4996                cx,
4997            );
4998            refresh_linked_ranges(this, window, cx);
4999        });
5000    }
5001
5002    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
5003        if self.read_only(cx) {
5004            return;
5005        }
5006        self.transact(window, cx, |this, window, cx| {
5007            this.change_selections(Default::default(), window, cx, |s| {
5008                s.move_with(&mut |map, selection| {
5009                    if selection.is_empty() {
5010                        let cursor = movement::right(map, selection.head());
5011                        selection.end = cursor;
5012                        selection.reversed = true;
5013                        selection.goal = SelectionGoal::None;
5014                    }
5015                })
5016            });
5017            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
5018            this.insert("", window, cx);
5019            linked_edits.apply(cx);
5020            this.refresh_edit_prediction(
5021                true,
5022                false,
5023                EditPredictionRequestTrigger::BufferEdit,
5024                window,
5025                cx,
5026            );
5027            refresh_linked_ranges(this, window, cx);
5028        });
5029    }
5030
5031    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
5032        if self.mode.is_single_line() {
5033            cx.propagate();
5034            return;
5035        }
5036
5037        if self.move_to_prev_snippet_tabstop(window, cx) {
5038            return;
5039        }
5040        self.outdent(&Outdent, window, cx);
5041    }
5042
5043    pub fn next_snippet_tabstop(
5044        &mut self,
5045        _: &NextSnippetTabstop,
5046        window: &mut Window,
5047        cx: &mut Context<Self>,
5048    ) {
5049        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
5050            cx.propagate();
5051            return;
5052        }
5053
5054        if self.move_to_next_snippet_tabstop(window, cx) {
5055            return;
5056        }
5057        cx.propagate();
5058    }
5059
5060    pub fn previous_snippet_tabstop(
5061        &mut self,
5062        _: &PreviousSnippetTabstop,
5063        window: &mut Window,
5064        cx: &mut Context<Self>,
5065    ) {
5066        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
5067            cx.propagate();
5068            return;
5069        }
5070
5071        if self.move_to_prev_snippet_tabstop(window, cx) {
5072            return;
5073        }
5074        cx.propagate();
5075    }
5076
5077    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
5078        if self.mode.is_single_line() {
5079            cx.propagate();
5080            return;
5081        }
5082
5083        if self.move_to_next_snippet_tabstop(window, cx) {
5084            return;
5085        }
5086        if self.read_only(cx) {
5087            return;
5088        }
5089        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
5090        let buffer = self.buffer.read(cx);
5091        let snapshot = buffer.snapshot(cx);
5092        let rows_iter = selections.iter().map(|s| s.head().row);
5093        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5094
5095        let has_some_cursor_in_whitespace = selections
5096            .iter()
5097            .filter(|selection| selection.is_empty())
5098            .any(|selection| {
5099                let cursor = selection.head();
5100                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5101                cursor.column < current_indent.len
5102            });
5103
5104        let mut edits = Vec::new();
5105        let mut prev_edited_row = 0;
5106        let mut row_delta = 0;
5107        for selection in &mut selections {
5108            if selection.start.row != prev_edited_row {
5109                row_delta = 0;
5110            }
5111            prev_edited_row = selection.end.row;
5112
5113            // If cursor is after a list prefix, make selection non-empty to trigger line indent
5114            if selection.is_empty() {
5115                let cursor = selection.head();
5116                let settings = buffer.language_settings_at(cursor, cx);
5117                if settings.indent_list_on_tab {
5118                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
5119                        if input::is_list_prefix_row(
5120                            MultiBufferRow(cursor.row),
5121                            &snapshot,
5122                            &language,
5123                        ) {
5124                            row_delta = Self::indent_selection(
5125                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
5126                            );
5127                            continue;
5128                        }
5129                    }
5130                }
5131            }
5132
5133            // If the selection is non-empty, then increase the indentation of the selected lines.
5134            if !selection.is_empty() {
5135                row_delta =
5136                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5137                continue;
5138            }
5139
5140            let cursor = selection.head();
5141            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5142            if let Some(suggested_indent) =
5143                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5144            {
5145                // Don't do anything if already at suggested indent
5146                // and there is any other cursor which is not
5147                if has_some_cursor_in_whitespace
5148                    && cursor.column == current_indent.len
5149                    && current_indent.len == suggested_indent.len
5150                {
5151                    continue;
5152                }
5153
5154                // Adjust line and move cursor to suggested indent
5155                // if cursor is not at suggested indent
5156                if cursor.column < suggested_indent.len
5157                    && cursor.column <= current_indent.len
5158                    && current_indent.len <= suggested_indent.len
5159                {
5160                    selection.start = Point::new(cursor.row, suggested_indent.len);
5161                    selection.end = selection.start;
5162                    if row_delta == 0 {
5163                        edits.extend(Buffer::edit_for_indent_size_adjustment(
5164                            cursor.row,
5165                            current_indent,
5166                            suggested_indent,
5167                        ));
5168                        row_delta = suggested_indent.len - current_indent.len;
5169                    }
5170                    continue;
5171                }
5172
5173                // If current indent is more than suggested indent
5174                // only move cursor to current indent and skip indent
5175                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
5176                    selection.start = Point::new(cursor.row, current_indent.len);
5177                    selection.end = selection.start;
5178                    continue;
5179                }
5180            }
5181
5182            // Otherwise, insert a hard or soft tab.
5183            let settings = buffer.language_settings_at(cursor, cx);
5184            let tab_size = if settings.hard_tabs {
5185                IndentSize::tab()
5186            } else {
5187                let tab_size = settings.tab_size.get();
5188                let indent_remainder = snapshot
5189                    .text_for_range(Point::new(cursor.row, 0)..cursor)
5190                    .flat_map(str::chars)
5191                    .fold(row_delta % tab_size, |counter: u32, c| {
5192                        if c == '\t' {
5193                            0
5194                        } else {
5195                            (counter + 1) % tab_size
5196                        }
5197                    });
5198
5199                let chars_to_next_tab_stop = tab_size - indent_remainder;
5200                IndentSize::spaces(chars_to_next_tab_stop)
5201            };
5202            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5203            selection.end = selection.start;
5204            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5205            row_delta += tab_size.len;
5206        }
5207
5208        self.transact(window, cx, |this, window, cx| {
5209            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5210            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
5211            this.refresh_edit_prediction(
5212                true,
5213                false,
5214                EditPredictionRequestTrigger::BufferEdit,
5215                window,
5216                cx,
5217            );
5218        });
5219    }
5220
5221    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
5222        if self.read_only(cx) {
5223            return;
5224        }
5225        if self.mode.is_single_line() {
5226            cx.propagate();
5227            return;
5228        }
5229
5230        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
5231        let mut prev_edited_row = 0;
5232        let mut row_delta = 0;
5233        let mut edits = Vec::new();
5234        let buffer = self.buffer.read(cx);
5235        let snapshot = buffer.snapshot(cx);
5236        for selection in &mut selections {
5237            if selection.start.row != prev_edited_row {
5238                row_delta = 0;
5239            }
5240            prev_edited_row = selection.end.row;
5241
5242            row_delta =
5243                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5244        }
5245
5246        self.transact(window, cx, |this, window, cx| {
5247            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5248            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
5249        });
5250    }
5251
5252    fn indent_selection(
5253        buffer: &MultiBuffer,
5254        snapshot: &MultiBufferSnapshot,
5255        selection: &mut Selection<Point>,
5256        edits: &mut Vec<(Range<Point>, String)>,
5257        delta_for_start_row: u32,
5258        cx: &App,
5259    ) -> u32 {
5260        let settings = buffer.language_settings_at(selection.start, cx);
5261        let tab_size = settings.tab_size.get();
5262        let indent_kind = if settings.hard_tabs {
5263            IndentKind::Tab
5264        } else {
5265            IndentKind::Space
5266        };
5267        let mut start_row = selection.start.row;
5268        let mut end_row = selection.end.row + 1;
5269
5270        // If a selection ends at the beginning of a line, don't indent
5271        // that last line.
5272        if selection.end.column == 0 && selection.end.row > selection.start.row {
5273            end_row -= 1;
5274        }
5275
5276        // Avoid re-indenting a row that has already been indented by a
5277        // previous selection, but still update this selection's column
5278        // to reflect that indentation.
5279        if delta_for_start_row > 0 {
5280            start_row += 1;
5281            selection.start.column += delta_for_start_row;
5282            if selection.end.row == selection.start.row {
5283                selection.end.column += delta_for_start_row;
5284            }
5285        }
5286
5287        let mut delta_for_end_row = 0;
5288        let has_multiple_rows = start_row + 1 != end_row;
5289        for row in start_row..end_row {
5290            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5291            let indent_delta = match (current_indent.kind, indent_kind) {
5292                (IndentKind::Space, IndentKind::Space) => {
5293                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5294                    IndentSize::spaces(columns_to_next_tab_stop)
5295                }
5296                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5297                (_, IndentKind::Tab) => IndentSize::tab(),
5298            };
5299
5300            let start = if has_multiple_rows || current_indent.len < selection.start.column {
5301                0
5302            } else {
5303                selection.start.column
5304            };
5305            let row_start = Point::new(row, start);
5306            edits.push((
5307                row_start..row_start,
5308                indent_delta.chars().collect::<String>(),
5309            ));
5310
5311            // Update this selection's endpoints to reflect the indentation.
5312            if row == selection.start.row {
5313                selection.start.column += indent_delta.len;
5314            }
5315            if row == selection.end.row {
5316                selection.end.column += indent_delta.len;
5317                delta_for_end_row = indent_delta.len;
5318            }
5319        }
5320
5321        if selection.start.row == selection.end.row {
5322            delta_for_start_row + delta_for_end_row
5323        } else {
5324            delta_for_end_row
5325        }
5326    }
5327
5328    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
5329        if self.read_only(cx) {
5330            return;
5331        }
5332        if self.mode.is_single_line() {
5333            cx.propagate();
5334            return;
5335        }
5336
5337        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5338        let selections = self.selections.all::<Point>(&display_map);
5339        let mut deletion_ranges = Vec::new();
5340        let mut last_outdent = None;
5341        {
5342            let buffer = self.buffer.read(cx);
5343            let snapshot = buffer.snapshot(cx);
5344            for selection in &selections {
5345                let settings = buffer.language_settings_at(selection.start, cx);
5346                let tab_size = settings.tab_size;
5347                let mut rows = selection.spanned_rows(false, &display_map);
5348
5349                // Avoid re-outdenting a row that has already been outdented by a
5350                // previous selection.
5351                if let Some(last_row) = last_outdent
5352                    && last_row == rows.start
5353                {
5354                    rows.start = rows.start.next_row();
5355                }
5356                let has_multiple_rows = rows.len() > 1;
5357                for row in rows.iter_rows() {
5358                    let indent_size = snapshot.indent_size_for_line(row);
5359                    if indent_size.len > 0 {
5360                        let deletion_len = indent_size.outdent_len(tab_size);
5361                        let start = if has_multiple_rows
5362                            || deletion_len > selection.start.column
5363                            || indent_size.len < selection.start.column
5364                        {
5365                            0
5366                        } else {
5367                            selection.start.column - deletion_len
5368                        };
5369                        deletion_ranges.push(
5370                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5371                        );
5372                        last_outdent = Some(row);
5373                    }
5374                }
5375            }
5376        }
5377
5378        self.transact(window, cx, |this, window, cx| {
5379            this.buffer.update(cx, |buffer, cx| {
5380                let empty_str: Arc<str> = Arc::default();
5381                buffer.edit(
5382                    deletion_ranges
5383                        .into_iter()
5384                        .map(|range| (range, empty_str.clone())),
5385                    None,
5386                    cx,
5387                );
5388            });
5389            let selections = this
5390                .selections
5391                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
5392            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
5393        });
5394    }
5395
5396    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
5397        if self.read_only(cx) {
5398            return;
5399        }
5400        if self.mode.is_single_line() {
5401            cx.propagate();
5402            return;
5403        }
5404
5405        let selections = self
5406            .selections
5407            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
5408            .into_iter()
5409            .map(|s| s.range());
5410
5411        self.transact(window, cx, |this, window, cx| {
5412            this.buffer.update(cx, |buffer, cx| {
5413                buffer.autoindent_ranges(selections, cx);
5414            });
5415            let selections = this
5416                .selections
5417                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
5418            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
5419        });
5420    }
5421
5422    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
5423        if self.read_only(cx) {
5424            return;
5425        }
5426        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5427        let selections = self.selections.all::<Point>(&display_map);
5428
5429        let mut new_cursors = Vec::new();
5430        let mut edit_ranges = Vec::new();
5431        let mut selections = selections.iter().peekable();
5432        while let Some(selection) = selections.next() {
5433            let mut rows = selection.spanned_rows(false, &display_map);
5434
5435            // Accumulate contiguous regions of rows that we want to delete.
5436            while let Some(next_selection) = selections.peek() {
5437                let next_rows = next_selection.spanned_rows(false, &display_map);
5438                if next_rows.start <= rows.end {
5439                    rows.end = next_rows.end;
5440                    selections.next().unwrap();
5441                } else {
5442                    break;
5443                }
5444            }
5445
5446            let buffer = display_map.buffer_snapshot();
5447            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
5448            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
5449                // If there's a line after the range, delete the \n from the end of the row range
5450                (
5451                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
5452                    rows.end,
5453                )
5454            } else {
5455                // If there isn't a line after the range, delete the \n from the line before the
5456                // start of the row range
5457                edit_start = edit_start.saturating_sub_usize(1);
5458                (buffer.len(), rows.start.previous_row())
5459            };
5460
5461            let text_layout_details = self.text_layout_details(window, cx);
5462            let x = display_map.x_for_display_point(
5463                selection.head().to_display_point(&display_map),
5464                &text_layout_details,
5465            );
5466            let row = Point::new(target_row.0, 0)
5467                .to_display_point(&display_map)
5468                .row();
5469            let column = display_map.display_column_for_x(row, x, &text_layout_details);
5470
5471            new_cursors.push((
5472                selection.id,
5473                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
5474                SelectionGoal::None,
5475            ));
5476            edit_ranges.push(edit_start..edit_end);
5477        }
5478
5479        self.transact(window, cx, |this, window, cx| {
5480            let buffer = this.buffer.update(cx, |buffer, cx| {
5481                let empty_str: Arc<str> = Arc::default();
5482                buffer.edit(
5483                    edit_ranges
5484                        .into_iter()
5485                        .map(|range| (range, empty_str.clone())),
5486                    None,
5487                    cx,
5488                );
5489                buffer.snapshot(cx)
5490            });
5491            let new_selections = new_cursors
5492                .into_iter()
5493                .map(|(id, cursor, goal)| {
5494                    let cursor = cursor.to_point(&buffer);
5495                    Selection {
5496                        id,
5497                        start: cursor,
5498                        end: cursor,
5499                        reversed: false,
5500                        goal,
5501                    }
5502                })
5503                .collect();
5504
5505            this.change_selections(Default::default(), window, cx, |s| {
5506                s.select(new_selections);
5507            });
5508        });
5509    }
5510
5511    pub fn join_lines_impl(
5512        &mut self,
5513        insert_whitespace: bool,
5514        window: &mut Window,
5515        cx: &mut Context<Self>,
5516    ) {
5517        if self.read_only(cx) {
5518            return;
5519        }
5520        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5521        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
5522            let start = MultiBufferRow(selection.start.row);
5523            // Treat single line selections as if they include the next line. Otherwise this action
5524            // would do nothing for single line selections individual cursors.
5525            let end = if selection.start.row == selection.end.row {
5526                MultiBufferRow(selection.start.row + 1)
5527            } else if selection.end.column == 0 {
5528                // If the selection ends at the start of a line, it's logically at the end of the
5529                // previous line (plus its newline).
5530                // Don't include the end line unless there's only one line selected.
5531                if selection.start.row + 1 == selection.end.row {
5532                    MultiBufferRow(selection.end.row)
5533                } else {
5534                    MultiBufferRow(selection.end.row - 1)
5535                }
5536            } else {
5537                MultiBufferRow(selection.end.row)
5538            };
5539
5540            if let Some(last_row_range) = row_ranges.last_mut()
5541                && start <= last_row_range.end
5542            {
5543                last_row_range.end = end;
5544                continue;
5545            }
5546            row_ranges.push(start..end);
5547        }
5548
5549        let snapshot = self.buffer.read(cx).snapshot(cx);
5550        let mut cursor_positions = Vec::new();
5551        for row_range in &row_ranges {
5552            let anchor = snapshot.anchor_before(Point::new(
5553                row_range.end.previous_row().0,
5554                snapshot.line_len(row_range.end.previous_row()),
5555            ));
5556            cursor_positions.push(anchor..anchor);
5557        }
5558
5559        self.transact(window, cx, |this, window, cx| {
5560            for row_range in row_ranges.into_iter().rev() {
5561                for row in row_range.iter_rows().rev() {
5562                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
5563                    let next_line_row = row.next_row();
5564                    let indent = snapshot.indent_size_for_line(next_line_row);
5565                    let mut join_start_column = indent.len;
5566
5567                    if let Some(language_scope) =
5568                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
5569                    {
5570                        let line_end =
5571                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
5572                        let line_text_after_indent = snapshot
5573                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
5574                            .collect::<String>();
5575
5576                        if !line_text_after_indent.is_empty() {
5577                            let block_prefix = language_scope
5578                                .block_comment()
5579                                .map(|c| c.prefix.as_ref())
5580                                .filter(|p| !p.is_empty());
5581                            let doc_prefix = language_scope
5582                                .documentation_comment()
5583                                .map(|c| c.prefix.as_ref())
5584                                .filter(|p| !p.is_empty());
5585                            let comment_prefixes = language_scope
5586                                .line_comment_prefixes()
5587                                .iter()
5588                                .map(|p| p.as_ref())
5589                                .chain(block_prefix)
5590                                .chain(doc_prefix)
5591                                .map(|prefix| (prefix, false));
5592                            let all_prefixes = comment_prefixes.chain(
5593                                language_scope
5594                                    .unordered_list()
5595                                    .iter()
5596                                    .map(|prefix| (prefix.as_ref(), true)),
5597                            );
5598
5599                            let mut longest_prefix_len = None;
5600                            for (prefix, is_unordered_list) in all_prefixes {
5601                                let trimmed = prefix.trim_end();
5602                                let matches_full_prefix =
5603                                    line_text_after_indent.starts_with(prefix);
5604                                let nextline_is_bare_prefix = line_text_after_indent == trimmed;
5605                                if matches_full_prefix
5606                                    || (!is_unordered_list
5607                                        && line_text_after_indent.starts_with(trimmed))
5608                                    || nextline_is_bare_prefix
5609                                {
5610                                    let candidate_len = if matches_full_prefix {
5611                                        prefix.len()
5612                                    } else {
5613                                        trimmed.len()
5614                                    };
5615                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
5616                                        longest_prefix_len = Some(candidate_len);
5617                                    }
5618                                }
5619                            }
5620
5621                            if let Some(prefix_len) = longest_prefix_len {
5622                                join_start_column =
5623                                    join_start_column.saturating_add(prefix_len as u32);
5624                            }
5625                        }
5626                    }
5627
5628                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
5629
5630                    let replace = if snapshot.line_len(next_line_row) > join_start_column
5631                        && insert_whitespace
5632                    {
5633                        " "
5634                    } else {
5635                        ""
5636                    };
5637
5638                    this.buffer.update(cx, |buffer, cx| {
5639                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5640                    });
5641                }
5642            }
5643
5644            this.change_selections(Default::default(), window, cx, |s| {
5645                s.select_anchor_ranges(cursor_positions)
5646            });
5647        });
5648    }
5649
5650    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
5651        self.join_lines_impl(true, window, cx);
5652    }
5653
5654    pub fn sort_lines_case_sensitive(
5655        &mut self,
5656        _: &SortLinesCaseSensitive,
5657        window: &mut Window,
5658        cx: &mut Context<Self>,
5659    ) {
5660        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
5661    }
5662
5663    pub fn sort_lines_by_length(
5664        &mut self,
5665        _: &SortLinesByLength,
5666        window: &mut Window,
5667        cx: &mut Context<Self>,
5668    ) {
5669        self.manipulate_immutable_lines(window, cx, |lines| {
5670            lines.sort_by_key(|&line| line.chars().count())
5671        })
5672    }
5673
5674    pub fn sort_lines_case_insensitive(
5675        &mut self,
5676        _: &SortLinesCaseInsensitive,
5677        window: &mut Window,
5678        cx: &mut Context<Self>,
5679    ) {
5680        self.manipulate_immutable_lines(window, cx, |lines| {
5681            lines.sort_by_key(|line| line.to_lowercase())
5682        })
5683    }
5684
5685    pub fn unique_lines_case_insensitive(
5686        &mut self,
5687        _: &UniqueLinesCaseInsensitive,
5688        window: &mut Window,
5689        cx: &mut Context<Self>,
5690    ) {
5691        self.manipulate_immutable_lines(window, cx, |lines| {
5692            let mut seen = HashSet::default();
5693            lines.retain(|line| seen.insert(line.to_lowercase()));
5694        })
5695    }
5696
5697    pub fn unique_lines_case_sensitive(
5698        &mut self,
5699        _: &UniqueLinesCaseSensitive,
5700        window: &mut Window,
5701        cx: &mut Context<Self>,
5702    ) {
5703        self.manipulate_immutable_lines(window, cx, |lines| {
5704            let mut seen = HashSet::default();
5705            lines.retain(|line| seen.insert(*line));
5706        })
5707    }
5708
5709    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
5710        let snapshot = self.buffer.read(cx).snapshot(cx);
5711        for selection in self.selections.disjoint_anchors_arc().iter() {
5712            if snapshot
5713                .language_at(selection.start)
5714                .and_then(|lang| lang.config().wrap_characters.as_ref())
5715                .is_some()
5716            {
5717                return true;
5718            }
5719        }
5720        false
5721    }
5722
5723    fn wrap_selections_in_tag(
5724        &mut self,
5725        _: &WrapSelectionsInTag,
5726        window: &mut Window,
5727        cx: &mut Context<Self>,
5728    ) {
5729        if self.read_only(cx) {
5730            return;
5731        }
5732
5733        let snapshot = self.buffer.read(cx).snapshot(cx);
5734
5735        let mut edits = Vec::new();
5736        let mut boundaries = Vec::new();
5737
5738        for selection in self
5739            .selections
5740            .all_adjusted(&self.display_snapshot(cx))
5741            .iter()
5742        {
5743            let Some(wrap_config) = snapshot
5744                .language_at(selection.start)
5745                .and_then(|lang| lang.config().wrap_characters.clone())
5746            else {
5747                continue;
5748            };
5749
5750            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
5751            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
5752
5753            let start_before = snapshot.anchor_before(selection.start);
5754            let end_after = snapshot.anchor_after(selection.end);
5755
5756            edits.push((start_before..start_before, open_tag));
5757            edits.push((end_after..end_after, close_tag));
5758
5759            boundaries.push((
5760                start_before,
5761                end_after,
5762                wrap_config.start_prefix.len(),
5763                wrap_config.end_suffix.len(),
5764            ));
5765        }
5766
5767        if edits.is_empty() {
5768            return;
5769        }
5770
5771        self.transact(window, cx, |this, window, cx| {
5772            let buffer = this.buffer.update(cx, |buffer, cx| {
5773                buffer.edit(edits, None, cx);
5774                buffer.snapshot(cx)
5775            });
5776
5777            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
5778            for (start_before, end_after, start_prefix_len, end_suffix_len) in
5779                boundaries.into_iter()
5780            {
5781                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
5782                let close_offset = end_after
5783                    .to_offset(&buffer)
5784                    .saturating_sub_usize(end_suffix_len);
5785                new_selections.push(open_offset..open_offset);
5786                new_selections.push(close_offset..close_offset);
5787            }
5788
5789            this.change_selections(Default::default(), window, cx, |s| {
5790                s.select_ranges(new_selections);
5791            });
5792
5793            this.request_autoscroll(Autoscroll::fit(), cx);
5794        });
5795    }
5796
5797    pub fn toggle_read_only(
5798        &mut self,
5799        _: &workspace::ToggleReadOnlyFile,
5800        _: &mut Window,
5801        cx: &mut Context<Self>,
5802    ) {
5803        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
5804            buffer.update(cx, |buffer, cx| {
5805                buffer.set_capability(
5806                    match buffer.capability() {
5807                        Capability::ReadWrite => Capability::Read,
5808                        Capability::Read => Capability::ReadWrite,
5809                        Capability::ReadOnly => Capability::ReadOnly,
5810                    },
5811                    cx,
5812                );
5813            })
5814        }
5815    }
5816
5817    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
5818        let Some(project) = self.project.clone() else {
5819            return;
5820        };
5821        let task = self.reload(project, window, cx);
5822        self.detach_and_notify_err(task, window, cx);
5823    }
5824
5825    pub fn open_active_item_in_terminal(
5826        &mut self,
5827        _: &OpenInTerminal,
5828        window: &mut Window,
5829        cx: &mut Context<Self>,
5830    ) {
5831        if let Some(working_directory) = self.active_buffer(cx).and_then(|buffer| {
5832            let project_path = buffer.read(cx).project_path(cx)?;
5833            let project = self.project()?.read(cx);
5834            let entry = project.entry_for_path(&project_path, cx)?;
5835            let parent = match &entry.canonical_path {
5836                Some(canonical_path) => canonical_path.to_path_buf(),
5837                None => project.absolute_path(&project_path, cx)?,
5838            }
5839            .parent()?
5840            .to_path_buf();
5841            Some(parent)
5842        }) {
5843            window.dispatch_action(
5844                OpenTerminal {
5845                    working_directory,
5846                    local: false,
5847                }
5848                .boxed_clone(),
5849                cx,
5850            );
5851        }
5852    }
5853
5854    fn set_gutter_context_menu(
5855        &mut self,
5856        display_row: DisplayRow,
5857        position: Option<Anchor>,
5858        clicked_point: gpui::Point<Pixels>,
5859        window: &mut Window,
5860        cx: &mut Context<Self>,
5861    ) {
5862        let display_snapshot = self.display_snapshot(cx);
5863        let display_point = display_row.as_display_point();
5864        let source = display_snapshot.display_point_to_anchor(display_point, Bias::Left);
5865        let anchor = position.unwrap_or(source);
5866
5867        // Every entry in this menu either requires a worktree-file-backed buffer
5868        // (breakpoints, bookmarks, run to cursor) or is meaningless without one
5869        // (git blame), so don't open it for e.g. untitled buffers.
5870        if !display_snapshot
5871            .buffer_snapshot()
5872            .anchor_to_buffer_anchor(anchor)
5873            .is_some_and(|(_, buffer_snapshot)| {
5874                project::File::from_dyn(buffer_snapshot.file()).is_some()
5875            })
5876        {
5877            return;
5878        }
5879
5880        let context_menu = self.gutter_context_menu(anchor, display_row, window, cx);
5881
5882        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
5883            self,
5884            source,
5885            clicked_point,
5886            context_menu,
5887            window,
5888            cx,
5889        );
5890    }
5891
5892    fn add_edit_block(
5893        &mut self,
5894        anchor: Anchor,
5895        base_text: &str,
5896        placeholder_text: &str,
5897        confirm: Option<PromptEditorCallback>,
5898        cancel: Option<PromptEditorCallback>,
5899        window: &mut Window,
5900        cx: &mut Context<Self>,
5901    ) {
5902        let weak_editor = cx.weak_entity();
5903        let bp_prompt = cx.new(|cx| {
5904            let mut prompt_editor =
5905                PromptEditor::new(weak_editor, placeholder_text, base_text, window, cx);
5906
5907            if let Some(callback) = confirm {
5908                prompt_editor = prompt_editor.on_confirm(callback);
5909            }
5910            if let Some(callback) = cancel {
5911                prompt_editor = prompt_editor.on_cancel(callback);
5912            }
5913
5914            prompt_editor
5915        });
5916
5917        let height = bp_prompt.update(cx, |this, cx| {
5918            this.prompt
5919                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
5920        });
5921        let cloned_prompt = bp_prompt.clone();
5922        let blocks = vec![BlockProperties {
5923            style: BlockStyle::Sticky,
5924            placement: BlockPlacement::Above(anchor),
5925            height: Some(height),
5926            render: Arc::new(move |cx| {
5927                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
5928                cloned_prompt.clone().into_any_element()
5929            }),
5930            priority: 0,
5931        }];
5932
5933        let focus_handle = bp_prompt.focus_handle(cx);
5934        window.focus(&focus_handle, cx);
5935
5936        let block_ids = self.insert_blocks(blocks, None, cx);
5937        bp_prompt.update(cx, |prompt, _| {
5938            prompt.add_block_ids(block_ids);
5939        });
5940    }
5941
5942    fn add_edit_breakpoint_block(
5943        &mut self,
5944        anchor: Anchor,
5945        breakpoint: &Breakpoint,
5946        edit_action: BreakpointPromptEditAction,
5947        window: &mut Window,
5948        cx: &mut Context<Self>,
5949    ) {
5950        let base_text: &str = match edit_action {
5951            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
5952            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
5953            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
5954        }
5955        .map(|msg| msg.as_ref())
5956        .unwrap_or_default();
5957
5958        let placeholder_text = match edit_action {
5959            BreakpointPromptEditAction::Log => {
5960                "Message to log when a breakpoint is hit. Expressions within {} are interpolated."
5961            }
5962            BreakpointPromptEditAction::Condition => {
5963                "Condition when a breakpoint is hit. Expressions within {} are interpolated."
5964            }
5965            BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
5966        };
5967
5968        let breakpoint = breakpoint.clone();
5969        self.add_edit_block(
5970            anchor,
5971            base_text,
5972            placeholder_text,
5973            Some(Box::new(move |message: String, editor: &mut Self, cx| {
5974                editor.edit_breakpoint_at_anchor(
5975                    anchor,
5976                    breakpoint,
5977                    match edit_action {
5978                        BreakpointPromptEditAction::Log => {
5979                            BreakpointEditAction::EditLogMessage(message.into())
5980                        }
5981                        BreakpointPromptEditAction::Condition => {
5982                            BreakpointEditAction::EditCondition(message.into())
5983                        }
5984                        BreakpointPromptEditAction::HitCondition => {
5985                            BreakpointEditAction::EditHitCondition(message.into())
5986                        }
5987                    },
5988                    cx,
5989                );
5990            })),
5991            None,
5992            window,
5993            cx,
5994        );
5995    }
5996
5997    pub(crate) fn breakpoint_at_row(
5998        &self,
5999        row: u32,
6000        window: &mut Window,
6001        cx: &mut Context<Self>,
6002    ) -> Option<(Anchor, Breakpoint)> {
6003        let snapshot = self.snapshot(window, cx);
6004        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
6005
6006        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
6007    }
6008
6009    pub(crate) fn breakpoint_at_anchor(
6010        &self,
6011        breakpoint_position: Anchor,
6012        snapshot: &EditorSnapshot,
6013        cx: &mut Context<Self>,
6014    ) -> Option<(Anchor, Breakpoint)> {
6015        let (breakpoint_position, _) = snapshot
6016            .buffer_snapshot()
6017            .anchor_to_buffer_anchor(breakpoint_position)?;
6018        let buffer = self.buffer.read(cx).buffer(breakpoint_position.buffer_id)?;
6019
6020        let buffer_snapshot = buffer.read(cx).snapshot();
6021
6022        let row = buffer_snapshot
6023            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position)
6024            .row;
6025
6026        let line_len = buffer_snapshot.line_len(row);
6027        let anchor_end = buffer_snapshot.anchor_after(Point::new(row, line_len));
6028
6029        self.breakpoint_store
6030            .as_ref()?
6031            .read_with(cx, |breakpoint_store, cx| {
6032                breakpoint_store
6033                    .breakpoints(
6034                        &buffer,
6035                        Some(breakpoint_position..anchor_end),
6036                        &buffer_snapshot,
6037                        cx,
6038                    )
6039                    .next()
6040                    .and_then(|(bp, _)| {
6041                        let breakpoint_row = buffer_snapshot
6042                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
6043                            .row;
6044
6045                        if breakpoint_row == row {
6046                            snapshot
6047                                .buffer_snapshot()
6048                                .anchor_in_excerpt(bp.position)
6049                                .map(|position| (position, bp.bp.clone()))
6050                        } else {
6051                            None
6052                        }
6053                    })
6054            })
6055    }
6056
6057    pub(crate) fn bookmark_at_row(
6058        &self,
6059        row: u32,
6060        window: &mut Window,
6061        cx: &mut Context<Self>,
6062    ) -> Option<Anchor> {
6063        let snapshot = self.snapshot(window, cx);
6064        let bookmark_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
6065
6066        self.bookmark_at_anchor(bookmark_position, &snapshot, cx)
6067    }
6068
6069    pub(crate) fn bookmark_at_anchor(
6070        &self,
6071        bookmark_position: Anchor,
6072        snapshot: &EditorSnapshot,
6073        cx: &mut Context<Self>,
6074    ) -> Option<Anchor> {
6075        let (bookmark_position, _) = snapshot
6076            .buffer_snapshot()
6077            .anchor_to_buffer_anchor(bookmark_position)?;
6078        let buffer = self.buffer.read(cx).buffer(bookmark_position.buffer_id)?;
6079
6080        let buffer_snapshot = buffer.read(cx).snapshot();
6081
6082        let row = buffer_snapshot
6083            .summary_for_anchor::<text::PointUtf16>(&bookmark_position)
6084            .row;
6085
6086        let line_len = buffer_snapshot.line_len(row);
6087        let anchor_end = buffer_snapshot.anchor_after(Point::new(row, line_len));
6088
6089        self.bookmark_store
6090            .as_ref()?
6091            .update(cx, |bookmark_store, cx| {
6092                bookmark_store
6093                    .bookmarks_for_buffer(
6094                        buffer,
6095                        bookmark_position..anchor_end,
6096                        &buffer_snapshot,
6097                        cx,
6098                    )
6099                    .first()
6100                    .and_then(|bookmark| {
6101                        let bookmark_row = buffer_snapshot
6102                            .summary_for_anchor::<text::PointUtf16>(&bookmark.anchor)
6103                            .row;
6104
6105                        if bookmark_row == row {
6106                            snapshot
6107                                .buffer_snapshot()
6108                                .anchor_in_excerpt(bookmark.anchor)
6109                        } else {
6110                            None
6111                        }
6112                    })
6113            })
6114    }
6115
6116    pub fn edit_log_breakpoint(
6117        &mut self,
6118        _: &EditLogBreakpoint,
6119        window: &mut Window,
6120        cx: &mut Context<Self>,
6121    ) {
6122        if self.breakpoint_store.is_none() {
6123            return;
6124        }
6125
6126        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
6127            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
6128                message: None,
6129                state: BreakpointState::Enabled,
6130                condition: None,
6131                hit_condition: None,
6132            });
6133
6134            self.add_edit_breakpoint_block(
6135                anchor,
6136                &breakpoint,
6137                BreakpointPromptEditAction::Log,
6138                window,
6139                cx,
6140            );
6141        }
6142    }
6143
6144    fn breakpoints_at_cursors(
6145        &self,
6146        window: &mut Window,
6147        cx: &mut Context<Self>,
6148    ) -> Vec<(Anchor, Option<Breakpoint>)> {
6149        let snapshot = self.snapshot(window, cx);
6150        let cursors = self
6151            .selections
6152            .disjoint_anchors_arc()
6153            .iter()
6154            .map(|selection| {
6155                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
6156
6157                let breakpoint_position = self
6158                    .breakpoint_at_row(cursor_position.row, window, cx)
6159                    .map(|bp| bp.0)
6160                    .unwrap_or_else(|| {
6161                        snapshot
6162                            .display_snapshot
6163                            .buffer_snapshot()
6164                            .anchor_after(Point::new(cursor_position.row, 0))
6165                    });
6166
6167                let breakpoint = self
6168                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
6169                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
6170
6171                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
6172            })
6173            // There might be multiple cursors on the same line; all of them should have the same anchors though as their breakpoints positions, which makes it possible to sort and dedup the list.
6174            .collect::<HashMap<Anchor, _>>();
6175
6176        cursors.into_iter().collect()
6177    }
6178
6179    pub fn enable_breakpoint(
6180        &mut self,
6181        _: &crate::actions::EnableBreakpoint,
6182        window: &mut Window,
6183        cx: &mut Context<Self>,
6184    ) {
6185        if self.breakpoint_store.is_none() {
6186            return;
6187        }
6188
6189        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
6190            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
6191                continue;
6192            };
6193            self.edit_breakpoint_at_anchor(
6194                anchor,
6195                breakpoint,
6196                BreakpointEditAction::InvertState,
6197                cx,
6198            );
6199        }
6200    }
6201
6202    pub fn align_selections(
6203        &mut self,
6204        _: &crate::actions::AlignSelections,
6205        window: &mut Window,
6206        cx: &mut Context<Self>,
6207    ) {
6208        if self.read_only(cx) {
6209            return;
6210        }
6211
6212        let display_snapshot = self.display_snapshot(cx);
6213
6214        struct CursorData {
6215            anchor: Anchor,
6216            point: Point,
6217        }
6218        let cursor_data: Vec<CursorData> = self
6219            .selections
6220            .disjoint_anchors()
6221            .iter()
6222            .map(|selection| {
6223                let anchor = if selection.reversed {
6224                    selection.head()
6225                } else {
6226                    selection.tail()
6227                };
6228                CursorData {
6229                    anchor: anchor,
6230                    point: anchor.to_point(&display_snapshot.buffer_snapshot()),
6231                }
6232            })
6233            .collect();
6234
6235        let rows_anchors_count: Vec<usize> = cursor_data
6236            .iter()
6237            .map(|cursor| cursor.point.row)
6238            .chunk_by(|&row| row)
6239            .into_iter()
6240            .map(|(_, group)| group.count())
6241            .collect();
6242        let max_columns = rows_anchors_count.iter().max().copied().unwrap_or(0);
6243        let mut rows_column_offset = vec![0; rows_anchors_count.len()];
6244        let mut edits = Vec::new();
6245
6246        for column_idx in 0..max_columns {
6247            let mut cursor_index = 0;
6248
6249            // Calculate target_column => position that the selections will go
6250            let mut target_column = 0;
6251            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
6252                // Skip rows that don't have this column
6253                if column_idx >= *cursor_count {
6254                    cursor_index += cursor_count;
6255                    continue;
6256                }
6257
6258                let point = &cursor_data[cursor_index + column_idx].point;
6259                let adjusted_column = point.column + rows_column_offset[row_idx];
6260                if adjusted_column > target_column {
6261                    target_column = adjusted_column;
6262                }
6263                cursor_index += cursor_count;
6264            }
6265
6266            // Collect edits for this column
6267            cursor_index = 0;
6268            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
6269                // Skip rows that don't have this column
6270                if column_idx >= *cursor_count {
6271                    cursor_index += *cursor_count;
6272                    continue;
6273                }
6274
6275                let point = &cursor_data[cursor_index + column_idx].point;
6276                let spaces_needed = target_column - point.column - rows_column_offset[row_idx];
6277                if spaces_needed > 0 {
6278                    let anchor = cursor_data[cursor_index + column_idx]
6279                        .anchor
6280                        .bias_left(&display_snapshot);
6281                    edits.push((anchor..anchor, " ".repeat(spaces_needed as usize)));
6282                }
6283                rows_column_offset[row_idx] += spaces_needed;
6284
6285                cursor_index += *cursor_count;
6286            }
6287        }
6288
6289        if !edits.is_empty() {
6290            self.transact(window, cx, |editor, _window, cx| {
6291                editor.edit(edits, cx);
6292            });
6293        }
6294    }
6295
6296    pub fn disable_breakpoint(
6297        &mut self,
6298        _: &crate::actions::DisableBreakpoint,
6299        window: &mut Window,
6300        cx: &mut Context<Self>,
6301    ) {
6302        if self.breakpoint_store.is_none() {
6303            return;
6304        }
6305
6306        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
6307            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
6308                continue;
6309            };
6310            self.edit_breakpoint_at_anchor(
6311                anchor,
6312                breakpoint,
6313                BreakpointEditAction::InvertState,
6314                cx,
6315            );
6316        }
6317    }
6318
6319    pub fn toggle_breakpoint(
6320        &mut self,
6321        _: &crate::actions::ToggleBreakpoint,
6322        window: &mut Window,
6323        cx: &mut Context<Self>,
6324    ) {
6325        if self.breakpoint_store.is_none() {
6326            return;
6327        }
6328
6329        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
6330            if let Some(breakpoint) = breakpoint {
6331                self.edit_breakpoint_at_anchor(
6332                    anchor,
6333                    breakpoint,
6334                    BreakpointEditAction::Toggle,
6335                    cx,
6336                );
6337            } else {
6338                self.edit_breakpoint_at_anchor(
6339                    anchor,
6340                    Breakpoint::new_standard(),
6341                    BreakpointEditAction::Toggle,
6342                    cx,
6343                );
6344            }
6345        }
6346    }
6347
6348    pub fn edit_breakpoint_at_anchor(
6349        &mut self,
6350        breakpoint_position: Anchor,
6351        breakpoint: Breakpoint,
6352        edit_action: BreakpointEditAction,
6353        cx: &mut Context<Self>,
6354    ) {
6355        let Some(breakpoint_store) = &self.breakpoint_store else {
6356            return;
6357        };
6358        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6359        let Some((position, _)) = buffer_snapshot.anchor_to_buffer_anchor(breakpoint_position)
6360        else {
6361            return;
6362        };
6363        let Some(buffer) = self.buffer.read(cx).buffer(position.buffer_id) else {
6364            return;
6365        };
6366
6367        breakpoint_store.update(cx, |breakpoint_store, cx| {
6368            breakpoint_store.toggle_breakpoint(
6369                buffer,
6370                BreakpointWithPosition {
6371                    position,
6372                    bp: breakpoint,
6373                },
6374                edit_action,
6375                cx,
6376            );
6377        });
6378
6379        cx.notify();
6380    }
6381
6382    #[cfg(any(test, feature = "test-support"))]
6383    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
6384        self.breakpoint_store.clone()
6385    }
6386
6387    fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
6388        maybe!({
6389            let breakpoint_store = self.breakpoint_store.as_ref()?;
6390
6391            let (active_stack_frame, debug_line_pane_id) = {
6392                let store = breakpoint_store.read(cx);
6393                let active_stack_frame = store.active_position().cloned();
6394                let debug_line_pane_id = store.active_debug_line_pane_id();
6395                (active_stack_frame, debug_line_pane_id)
6396            };
6397
6398            let Some(active_stack_frame) = active_stack_frame else {
6399                self.clear_row_highlights::<ActiveDebugLine>();
6400                return None;
6401            };
6402
6403            if let Some(debug_line_pane_id) = debug_line_pane_id {
6404                if let Some(workspace) = self
6405                    .workspace
6406                    .as_ref()
6407                    .and_then(|(workspace, _)| workspace.upgrade())
6408                {
6409                    let editor_pane_id = workspace
6410                        .read(cx)
6411                        .pane_for_item_id(cx.entity_id())
6412                        .map(|pane| pane.entity_id());
6413
6414                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
6415                        self.clear_row_highlights::<ActiveDebugLine>();
6416                        return None;
6417                    }
6418                }
6419            }
6420
6421            let position = active_stack_frame.position;
6422
6423            let snapshot = self.buffer.read(cx).snapshot(cx);
6424            let multibuffer_anchor = snapshot.anchor_in_excerpt(position)?;
6425
6426            self.clear_row_highlights::<ActiveDebugLine>();
6427
6428            self.go_to_line::<ActiveDebugLine>(
6429                multibuffer_anchor,
6430                |cx| cx.theme().colors().editor_debugger_active_line_background,
6431                window,
6432                cx,
6433            );
6434
6435            cx.notify();
6436
6437            Some(())
6438        })
6439        .is_some()
6440    }
6441
6442    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
6443        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
6444    }
6445
6446    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
6447        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
6448    }
6449
6450    pub fn rotate_selections_forward(
6451        &mut self,
6452        _: &RotateSelectionsForward,
6453        window: &mut Window,
6454        cx: &mut Context<Self>,
6455    ) {
6456        self.rotate_selections(window, cx, false)
6457    }
6458
6459    pub fn rotate_selections_backward(
6460        &mut self,
6461        _: &RotateSelectionsBackward,
6462        window: &mut Window,
6463        cx: &mut Context<Self>,
6464    ) {
6465        self.rotate_selections(window, cx, true)
6466    }
6467
6468    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
6469        if self.read_only(cx) {
6470            return;
6471        }
6472        let display_snapshot = self.display_snapshot(cx);
6473        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
6474
6475        if selections.len() < 2 {
6476            return;
6477        }
6478
6479        let (edits, new_selections) = {
6480            let buffer = self.buffer.read(cx).read(cx);
6481            let has_selections = selections.iter().any(|s| !s.is_empty());
6482            if has_selections {
6483                let mut selected_texts: Vec<String> = selections
6484                    .iter()
6485                    .map(|selection| {
6486                        buffer
6487                            .text_for_range(selection.start..selection.end)
6488                            .collect()
6489                    })
6490                    .collect();
6491
6492                if reverse {
6493                    selected_texts.rotate_left(1);
6494                } else {
6495                    selected_texts.rotate_right(1);
6496                }
6497
6498                let mut offset_delta: i64 = 0;
6499                let mut new_selections = Vec::new();
6500                let edits: Vec<_> = selections
6501                    .iter()
6502                    .zip(selected_texts.iter())
6503                    .map(|(selection, new_text)| {
6504                        let old_len = (selection.end.0 - selection.start.0) as i64;
6505                        let new_len = new_text.len() as i64;
6506                        let adjusted_start =
6507                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
6508                        let adjusted_end =
6509                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
6510
6511                        new_selections.push(Selection {
6512                            id: selection.id,
6513                            start: adjusted_start,
6514                            end: adjusted_end,
6515                            reversed: selection.reversed,
6516                            goal: selection.goal,
6517                        });
6518
6519                        offset_delta += new_len - old_len;
6520                        (selection.start..selection.end, new_text.clone())
6521                    })
6522                    .collect();
6523                (edits, new_selections)
6524            } else {
6525                let mut all_rows: Vec<u32> = selections
6526                    .iter()
6527                    .map(|selection| buffer.offset_to_point(selection.start).row)
6528                    .collect();
6529                all_rows.sort_unstable();
6530                all_rows.dedup();
6531
6532                if all_rows.len() < 2 {
6533                    return;
6534                }
6535
6536                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
6537                    .iter()
6538                    .map(|&row| {
6539                        let start = Point::new(row, 0);
6540                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
6541                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
6542                    })
6543                    .collect();
6544
6545                let mut line_texts: Vec<String> = line_ranges
6546                    .iter()
6547                    .map(|range| buffer.text_for_range(range.clone()).collect())
6548                    .collect();
6549
6550                if reverse {
6551                    line_texts.rotate_left(1);
6552                } else {
6553                    line_texts.rotate_right(1);
6554                }
6555
6556                let edits = line_ranges
6557                    .iter()
6558                    .zip(line_texts.iter())
6559                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
6560                    .collect();
6561
6562                let num_rows = all_rows.len();
6563                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
6564                    .iter()
6565                    .enumerate()
6566                    .map(|(i, &row)| (row, i))
6567                    .collect();
6568
6569                // Compute new line start offsets after rotation (handles CRLF)
6570                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
6571                let first_line_start = line_ranges[0].start.0;
6572                let mut new_line_starts: Vec<usize> = vec![first_line_start];
6573                for text in line_texts.iter().take(num_rows - 1) {
6574                    let prev_start = *new_line_starts.last().unwrap();
6575                    new_line_starts.push(prev_start + text.len() + newline_len);
6576                }
6577
6578                let new_selections = selections
6579                    .iter()
6580                    .map(|selection| {
6581                        let point = buffer.offset_to_point(selection.start);
6582                        let old_index = row_to_index[&point.row];
6583                        let new_index = if reverse {
6584                            (old_index + num_rows - 1) % num_rows
6585                        } else {
6586                            (old_index + 1) % num_rows
6587                        };
6588                        let new_offset =
6589                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
6590                        Selection {
6591                            id: selection.id,
6592                            start: new_offset,
6593                            end: new_offset,
6594                            reversed: selection.reversed,
6595                            goal: selection.goal,
6596                        }
6597                    })
6598                    .collect();
6599
6600                (edits, new_selections)
6601            }
6602        };
6603
6604        self.transact(window, cx, |this, window, cx| {
6605            this.buffer.update(cx, |buffer, cx| {
6606                buffer.edit(edits, None, cx);
6607            });
6608            this.change_selections(Default::default(), window, cx, |s| {
6609                s.select(new_selections);
6610            });
6611        });
6612    }
6613
6614    fn manipulate_lines<M>(
6615        &mut self,
6616        window: &mut Window,
6617        cx: &mut Context<Self>,
6618        mut manipulate: M,
6619    ) where
6620        M: FnMut(&str) -> LineManipulationResult,
6621    {
6622        if self.read_only(cx) {
6623            return;
6624        }
6625
6626        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6627        let buffer = self.buffer.read(cx).snapshot(cx);
6628
6629        let mut edits = Vec::new();
6630
6631        let selections = self.selections.all::<Point>(&display_map);
6632        let mut selections = selections.iter().peekable();
6633        let mut contiguous_row_selections = Vec::new();
6634        let mut new_selections = Vec::new();
6635        let mut added_lines = 0;
6636        let mut removed_lines = 0;
6637
6638        while let Some(selection) = selections.next() {
6639            let (start_row, end_row) = consume_contiguous_rows(
6640                &mut contiguous_row_selections,
6641                selection,
6642                &display_map,
6643                &mut selections,
6644            );
6645
6646            let start_point = Point::new(start_row.0, 0);
6647            let end_point = Point::new(
6648                end_row.previous_row().0,
6649                buffer.line_len(end_row.previous_row()),
6650            );
6651            let text = buffer
6652                .text_for_range(start_point..end_point)
6653                .collect::<String>();
6654
6655            let LineManipulationResult {
6656                new_text,
6657                line_count_before,
6658                line_count_after,
6659            } = manipulate(&text);
6660
6661            edits.push((start_point..end_point, new_text));
6662
6663            // Selections must change based on added and removed line count
6664            let start_row =
6665                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6666            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
6667            new_selections.push(Selection {
6668                id: selection.id,
6669                start: start_row,
6670                end: end_row,
6671                goal: SelectionGoal::None,
6672                reversed: selection.reversed,
6673            });
6674
6675            if line_count_after > line_count_before {
6676                added_lines += line_count_after - line_count_before;
6677            } else if line_count_before > line_count_after {
6678                removed_lines += line_count_before - line_count_after;
6679            }
6680        }
6681
6682        self.transact(window, cx, |this, window, cx| {
6683            let buffer = this.buffer.update(cx, |buffer, cx| {
6684                buffer.edit(edits, None, cx);
6685                buffer.snapshot(cx)
6686            });
6687
6688            // Recalculate offsets on newly edited buffer
6689            let new_selections = new_selections
6690                .iter()
6691                .map(|s| {
6692                    let start_point = Point::new(s.start.0, 0);
6693                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6694                    Selection {
6695                        id: s.id,
6696                        start: buffer.point_to_offset(start_point),
6697                        end: buffer.point_to_offset(end_point),
6698                        goal: s.goal,
6699                        reversed: s.reversed,
6700                    }
6701                })
6702                .collect();
6703
6704            this.change_selections(Default::default(), window, cx, |s| {
6705                s.select(new_selections);
6706            });
6707
6708            this.request_autoscroll(Autoscroll::fit(), cx);
6709        });
6710    }
6711
6712    fn manipulate_immutable_lines<Fn>(
6713        &mut self,
6714        window: &mut Window,
6715        cx: &mut Context<Self>,
6716        mut callback: Fn,
6717    ) where
6718        Fn: FnMut(&mut Vec<&str>),
6719    {
6720        self.manipulate_lines(window, cx, |text| {
6721            let mut lines: Vec<&str> = text.split('\n').collect();
6722            let line_count_before = lines.len();
6723
6724            callback(&mut lines);
6725
6726            LineManipulationResult {
6727                new_text: lines.join("\n"),
6728                line_count_before,
6729                line_count_after: lines.len(),
6730            }
6731        });
6732    }
6733
6734    fn manipulate_mutable_lines<Fn>(
6735        &mut self,
6736        window: &mut Window,
6737        cx: &mut Context<Self>,
6738        mut callback: Fn,
6739    ) where
6740        Fn: FnMut(&mut Vec<Cow<'_, str>>),
6741    {
6742        self.manipulate_lines(window, cx, |text| {
6743            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
6744            let line_count_before = lines.len();
6745
6746            callback(&mut lines);
6747
6748            LineManipulationResult {
6749                new_text: lines.join("\n"),
6750                line_count_before,
6751                line_count_after: lines.len(),
6752            }
6753        });
6754    }
6755
6756    pub fn convert_indentation_to_spaces(
6757        &mut self,
6758        _: &ConvertIndentationToSpaces,
6759        window: &mut Window,
6760        cx: &mut Context<Self>,
6761    ) {
6762        let settings = self.buffer.read(cx).language_settings(cx);
6763        let tab_size = settings.tab_size.get() as usize;
6764
6765        self.manipulate_mutable_lines(window, cx, |lines| {
6766            // Allocates a reasonably sized scratch buffer once for the whole loop
6767            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
6768            // Avoids recomputing spaces that could be inserted many times
6769            let space_cache: Vec<Vec<char>> = (1..=tab_size)
6770                .map(|n| IndentSize::spaces(n as u32).chars().collect())
6771                .collect();
6772
6773            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
6774                let mut chars = line.as_ref().chars();
6775                let mut col = 0;
6776                let mut changed = false;
6777
6778                for ch in chars.by_ref() {
6779                    match ch {
6780                        ' ' => {
6781                            reindented_line.push(' ');
6782                            col += 1;
6783                        }
6784                        '\t' => {
6785                            // \t are converted to spaces depending on the current column
6786                            let spaces_len = tab_size - (col % tab_size);
6787                            reindented_line.extend(&space_cache[spaces_len - 1]);
6788                            col += spaces_len;
6789                            changed = true;
6790                        }
6791                        _ => {
6792                            // If we dont append before break, the character is consumed
6793                            reindented_line.push(ch);
6794                            break;
6795                        }
6796                    }
6797                }
6798
6799                if !changed {
6800                    reindented_line.clear();
6801                    continue;
6802                }
6803                // Append the rest of the line and replace old reference with new one
6804                reindented_line.extend(chars);
6805                *line = Cow::Owned(reindented_line.clone());
6806                reindented_line.clear();
6807            }
6808        });
6809    }
6810
6811    pub fn convert_indentation_to_tabs(
6812        &mut self,
6813        _: &ConvertIndentationToTabs,
6814        window: &mut Window,
6815        cx: &mut Context<Self>,
6816    ) {
6817        let settings = self.buffer.read(cx).language_settings(cx);
6818        let tab_size = settings.tab_size.get() as usize;
6819
6820        self.manipulate_mutable_lines(window, cx, |lines| {
6821            // Allocates a reasonably sized buffer once for the whole loop
6822            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
6823            // Avoids recomputing spaces that could be inserted many times
6824            let space_cache: Vec<Vec<char>> = (1..=tab_size)
6825                .map(|n| IndentSize::spaces(n as u32).chars().collect())
6826                .collect();
6827
6828            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
6829                let mut chars = line.chars();
6830                let mut spaces_count = 0;
6831                let mut first_non_indent_char = None;
6832                let mut changed = false;
6833
6834                for ch in chars.by_ref() {
6835                    match ch {
6836                        ' ' => {
6837                            // Keep track of spaces. Append \t when we reach tab_size
6838                            spaces_count += 1;
6839                            changed = true;
6840                            if spaces_count == tab_size {
6841                                reindented_line.push('\t');
6842                                spaces_count = 0;
6843                            }
6844                        }
6845                        '\t' => {
6846                            reindented_line.push('\t');
6847                            spaces_count = 0;
6848                        }
6849                        _ => {
6850                            // Dont append it yet, we might have remaining spaces
6851                            first_non_indent_char = Some(ch);
6852                            break;
6853                        }
6854                    }
6855                }
6856
6857                if !changed {
6858                    reindented_line.clear();
6859                    continue;
6860                }
6861                // Remaining spaces that didn't make a full tab stop
6862                if spaces_count > 0 {
6863                    reindented_line.extend(&space_cache[spaces_count - 1]);
6864                }
6865                // If we consume an extra character that was not indentation, add it back
6866                if let Some(extra_char) = first_non_indent_char {
6867                    reindented_line.push(extra_char);
6868                }
6869                // Append the rest of the line and replace old reference with new one
6870                reindented_line.extend(chars);
6871                *line = Cow::Owned(reindented_line.clone());
6872                reindented_line.clear();
6873            }
6874        });
6875    }
6876
6877    pub fn convert_to_upper_case(
6878        &mut self,
6879        _: &ConvertToUpperCase,
6880        window: &mut Window,
6881        cx: &mut Context<Self>,
6882    ) {
6883        self.manipulate_text(window, cx, |text| text.to_uppercase())
6884    }
6885
6886    pub fn convert_to_lower_case(
6887        &mut self,
6888        _: &ConvertToLowerCase,
6889        window: &mut Window,
6890        cx: &mut Context<Self>,
6891    ) {
6892        self.manipulate_text(window, cx, |text| text.to_lowercase())
6893    }
6894
6895    pub fn convert_to_title_case(
6896        &mut self,
6897        _: &ConvertToTitleCase,
6898        window: &mut Window,
6899        cx: &mut Context<Self>,
6900    ) {
6901        self.manipulate_text(window, cx, |text| {
6902            Self::convert_text_case(text, Case::Title)
6903        })
6904    }
6905
6906    pub fn convert_to_snake_case(
6907        &mut self,
6908        _: &ConvertToSnakeCase,
6909        window: &mut Window,
6910        cx: &mut Context<Self>,
6911    ) {
6912        self.manipulate_text(window, cx, |text| {
6913            Self::convert_text_case(text, Case::Snake)
6914        })
6915    }
6916
6917    pub fn convert_to_kebab_case(
6918        &mut self,
6919        _: &ConvertToKebabCase,
6920        window: &mut Window,
6921        cx: &mut Context<Self>,
6922    ) {
6923        self.manipulate_text(window, cx, |text| {
6924            Self::convert_text_case(text, Case::Kebab)
6925        })
6926    }
6927
6928    pub fn convert_to_upper_camel_case(
6929        &mut self,
6930        _: &ConvertToUpperCamelCase,
6931        window: &mut Window,
6932        cx: &mut Context<Self>,
6933    ) {
6934        self.manipulate_text(window, cx, |text| {
6935            Self::convert_text_case(text, Case::UpperCamel)
6936        })
6937    }
6938
6939    pub fn convert_to_lower_camel_case(
6940        &mut self,
6941        _: &ConvertToLowerCamelCase,
6942        window: &mut Window,
6943        cx: &mut Context<Self>,
6944    ) {
6945        self.manipulate_text(window, cx, |text| {
6946            Self::convert_text_case(text, Case::Camel)
6947        })
6948    }
6949
6950    pub fn convert_to_opposite_case(
6951        &mut self,
6952        _: &ConvertToOppositeCase,
6953        window: &mut Window,
6954        cx: &mut Context<Self>,
6955    ) {
6956        self.manipulate_text(window, cx, |text| {
6957            text.chars()
6958                .fold(String::with_capacity(text.len()), |mut t, c| {
6959                    if c.is_uppercase() {
6960                        t.extend(c.to_lowercase());
6961                    } else {
6962                        t.extend(c.to_uppercase());
6963                    }
6964                    t
6965                })
6966        })
6967    }
6968
6969    pub fn convert_to_sentence_case(
6970        &mut self,
6971        _: &ConvertToSentenceCase,
6972        window: &mut Window,
6973        cx: &mut Context<Self>,
6974    ) {
6975        self.manipulate_text(window, cx, |text| {
6976            Self::convert_text_case(text, Case::Sentence)
6977        })
6978    }
6979
6980    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
6981        self.manipulate_text(window, cx, |text| {
6982            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
6983            if has_upper_case_characters {
6984                text.to_lowercase()
6985            } else {
6986                text.to_uppercase()
6987            }
6988        })
6989    }
6990
6991    pub fn convert_to_rot13(
6992        &mut self,
6993        _: &ConvertToRot13,
6994        window: &mut Window,
6995        cx: &mut Context<Self>,
6996    ) {
6997        self.manipulate_text(window, cx, |text| {
6998            text.chars()
6999                .map(|c| match c {
7000                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
7001                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
7002                    _ => c,
7003                })
7004                .collect()
7005        })
7006    }
7007
7008    fn convert_text_case(text: &str, case: Case) -> String {
7009        text.lines()
7010            .map(|line| {
7011                let trimmed_start = line.trim_start();
7012                let leading = &line[..line.len() - trimmed_start.len()];
7013                let trimmed = trimmed_start.trim_end();
7014                let trailing = &trimmed_start[trimmed.len()..];
7015                format!("{}{}{}", leading, trimmed.to_case(case), trailing)
7016            })
7017            .join("\n")
7018    }
7019
7020    pub fn convert_to_rot47(
7021        &mut self,
7022        _: &ConvertToRot47,
7023        window: &mut Window,
7024        cx: &mut Context<Self>,
7025    ) {
7026        self.manipulate_text(window, cx, |text| {
7027            text.chars()
7028                .map(|c| {
7029                    let code_point = c as u32;
7030                    if code_point >= 33 && code_point <= 126 {
7031                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
7032                    }
7033                    c
7034                })
7035                .collect()
7036        })
7037    }
7038
7039    pub fn convert_to_base64(
7040        &mut self,
7041        _: &ConvertToBase64,
7042        window: &mut Window,
7043        cx: &mut Context<Self>,
7044    ) {
7045        use base64::Engine as _;
7046        self.manipulate_text(window, cx, |text| {
7047            base64::engine::general_purpose::STANDARD.encode(text)
7048        })
7049    }
7050
7051    pub fn convert_from_base64(
7052        &mut self,
7053        _: &ConvertFromBase64,
7054        window: &mut Window,
7055        cx: &mut Context<Self>,
7056    ) {
7057        use base64::Engine as _;
7058        self.manipulate_text(
7059            window,
7060            cx,
7061            |text| match base64::engine::general_purpose::STANDARD.decode(text) {
7062                Ok(bytes) => String::from_utf8(bytes).unwrap_or_else(|_| text.to_string()),
7063                Err(_) => text.to_string(),
7064            },
7065        )
7066    }
7067
7068    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
7069    where
7070        Fn: FnMut(&str) -> String,
7071    {
7072        if self.read_only(cx) {
7073            return;
7074        }
7075        let buffer = self.buffer.read(cx).snapshot(cx);
7076
7077        let mut new_selections = Vec::new();
7078        let mut edits = Vec::new();
7079
7080        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
7081            let selection_is_empty = selection.is_empty();
7082
7083            let (start, end) = if selection_is_empty {
7084                let (word_range, _) = buffer.surrounding_word(selection.start, None);
7085                (word_range.start, word_range.end)
7086            } else {
7087                (
7088                    buffer.point_to_offset(selection.start),
7089                    buffer.point_to_offset(selection.end),
7090                )
7091            };
7092
7093            let old_text = buffer.text_for_range(start..end).collect::<String>();
7094            let new_text = callback(&old_text);
7095
7096            new_selections.push(Selection {
7097                start: buffer.anchor_before(start),
7098                end: buffer.anchor_after(end),
7099                goal: SelectionGoal::None,
7100                id: selection.id,
7101                reversed: selection.reversed,
7102            });
7103
7104            if new_text != old_text {
7105                edits.push((start..end, new_text));
7106            }
7107        }
7108
7109        if edits.is_empty() {
7110            return;
7111        }
7112
7113        self.transact(window, cx, |this, window, cx| {
7114            this.buffer.update(cx, |buffer, cx| {
7115                buffer.edit(edits, None, cx);
7116            });
7117
7118            this.change_selections(Default::default(), window, cx, |s| {
7119                s.select(new_selections);
7120            });
7121
7122            this.request_autoscroll(Autoscroll::fit(), cx);
7123        });
7124    }
7125
7126    pub fn move_selection_on_drop(
7127        &mut self,
7128        selection: &Selection<Anchor>,
7129        target: DisplayPoint,
7130        is_cut: bool,
7131        window: &mut Window,
7132        cx: &mut Context<Self>,
7133    ) {
7134        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7135        let buffer = display_map.buffer_snapshot();
7136        let mut edits = Vec::new();
7137        let insert_point = display_map
7138            .clip_point(target, Bias::Left)
7139            .to_point(&display_map);
7140        let text = buffer
7141            .text_for_range(selection.start..selection.end)
7142            .collect::<String>();
7143        if is_cut {
7144            edits.push(((selection.start..selection.end), String::new()));
7145        }
7146        let insert_anchor = buffer.anchor_before(insert_point);
7147        edits.push(((insert_anchor..insert_anchor), text));
7148        let last_edit_start = insert_anchor.bias_left(buffer);
7149        let last_edit_end = insert_anchor.bias_right(buffer);
7150        self.transact(window, cx, |this, window, cx| {
7151            this.buffer.update(cx, |buffer, cx| {
7152                buffer.edit(edits, None, cx);
7153            });
7154            this.change_selections(Default::default(), window, cx, |s| {
7155                s.select_anchor_ranges([last_edit_start..last_edit_end]);
7156            });
7157        });
7158    }
7159
7160    pub fn clear_selection_drag_state(&mut self) {
7161        self.selection_drag_state = SelectionDragState::None;
7162    }
7163
7164    pub fn duplicate(
7165        &mut self,
7166        upwards: bool,
7167        whole_lines: bool,
7168        window: &mut Window,
7169        cx: &mut Context<Self>,
7170    ) {
7171        if self.read_only(cx) {
7172            return;
7173        }
7174
7175        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7176        let buffer = display_map.buffer_snapshot();
7177        let selections = self.selections.all::<Point>(&display_map);
7178
7179        let mut edits = Vec::new();
7180        let mut selections_iter = selections.iter().peekable();
7181        while let Some(selection) = selections_iter.next() {
7182            let mut rows = selection.spanned_rows(false, &display_map);
7183            // duplicate line-wise
7184            if whole_lines || selection.start == selection.end {
7185                // Avoid duplicating the same lines twice.
7186                while let Some(next_selection) = selections_iter.peek() {
7187                    let next_rows = next_selection.spanned_rows(false, &display_map);
7188                    if next_rows.start < rows.end {
7189                        rows.end = next_rows.end;
7190                        selections_iter.next().unwrap();
7191                    } else {
7192                        break;
7193                    }
7194                }
7195
7196                // Copy the text from the selected row region and splice it either at the start
7197                // or end of the region.
7198                let start = Point::new(rows.start.0, 0);
7199                let end = Point::new(
7200                    rows.end.previous_row().0,
7201                    buffer.line_len(rows.end.previous_row()),
7202                );
7203
7204                let mut text = buffer.text_for_range(start..end).collect::<String>();
7205
7206                let insert_location = if upwards {
7207                    // When duplicating upward, we need to insert before the current line.
7208                    // If we're on the last line and it doesn't end with a newline,
7209                    // we need to add a newline before the duplicated content.
7210                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
7211                        && buffer.max_point().column > 0
7212                        && !text.ends_with('\n');
7213
7214                    if needs_leading_newline {
7215                        text.insert(0, '\n');
7216                        end
7217                    } else {
7218                        text.push('\n');
7219                        Point::new(rows.start.0, 0)
7220                    }
7221                } else {
7222                    text.push('\n');
7223                    start
7224                };
7225                edits.push((insert_location..insert_location, text));
7226            } else {
7227                // duplicate character-wise
7228                let start = selection.start;
7229                let end = selection.end;
7230                let text = buffer.text_for_range(start..end).collect::<String>();
7231                edits.push((selection.end..selection.end, text));
7232            }
7233        }
7234
7235        self.transact(window, cx, |this, window, cx| {
7236            this.buffer.update(cx, |buffer, cx| {
7237                buffer.edit(edits, None, cx);
7238            });
7239
7240            // When duplicating upward with whole lines, move the cursor to the duplicated line
7241            if upwards && whole_lines {
7242                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7243
7244                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
7245                    let mut new_ranges = Vec::new();
7246                    let selections = s.all::<Point>(&display_map);
7247                    let mut selections_iter = selections.iter().peekable();
7248
7249                    while let Some(first_selection) = selections_iter.next() {
7250                        // Group contiguous selections together to find the total row span
7251                        let mut group_selections = vec![first_selection];
7252                        let mut rows = first_selection.spanned_rows(false, &display_map);
7253
7254                        while let Some(next_selection) = selections_iter.peek() {
7255                            let next_rows = next_selection.spanned_rows(false, &display_map);
7256                            if next_rows.start < rows.end {
7257                                rows.end = next_rows.end;
7258                                group_selections.push(selections_iter.next().unwrap());
7259                            } else {
7260                                break;
7261                            }
7262                        }
7263
7264                        let row_count = rows.end.0 - rows.start.0;
7265
7266                        // Move all selections in this group up by the total number of duplicated rows
7267                        for selection in group_selections {
7268                            let new_start = Point::new(
7269                                selection.start.row.saturating_sub(row_count),
7270                                selection.start.column,
7271                            );
7272
7273                            let new_end = Point::new(
7274                                selection.end.row.saturating_sub(row_count),
7275                                selection.end.column,
7276                            );
7277
7278                            new_ranges.push(new_start..new_end);
7279                        }
7280                    }
7281
7282                    s.select_ranges(new_ranges);
7283                });
7284            }
7285
7286            this.request_autoscroll(Autoscroll::fit(), cx);
7287        });
7288    }
7289
7290    pub fn duplicate_line_up(
7291        &mut self,
7292        _: &DuplicateLineUp,
7293        window: &mut Window,
7294        cx: &mut Context<Self>,
7295    ) {
7296        self.duplicate(true, true, window, cx);
7297    }
7298
7299    pub fn duplicate_line_down(
7300        &mut self,
7301        _: &DuplicateLineDown,
7302        window: &mut Window,
7303        cx: &mut Context<Self>,
7304    ) {
7305        self.duplicate(false, true, window, cx);
7306    }
7307
7308    pub fn duplicate_selection(
7309        &mut self,
7310        _: &DuplicateSelection,
7311        window: &mut Window,
7312        cx: &mut Context<Self>,
7313    ) {
7314        self.duplicate(false, false, window, cx);
7315    }
7316
7317    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
7318        if self.read_only(cx) {
7319            return;
7320        }
7321        if self.mode.is_single_line() {
7322            cx.propagate();
7323            return;
7324        }
7325
7326        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7327        let buffer = self.buffer.read(cx).snapshot(cx);
7328
7329        let mut edits = Vec::new();
7330        let mut unfold_ranges = Vec::new();
7331        let mut refold_creases = Vec::new();
7332
7333        let selections = self.selections.all::<Point>(&display_map);
7334        let mut selections = selections.iter().peekable();
7335        let mut contiguous_row_selections = Vec::new();
7336        let mut new_selections = Vec::new();
7337
7338        while let Some(selection) = selections.next() {
7339            // Find all the selections that span a contiguous row range
7340            let (start_row, end_row) = consume_contiguous_rows(
7341                &mut contiguous_row_selections,
7342                selection,
7343                &display_map,
7344                &mut selections,
7345            );
7346
7347            // Move the text spanned by the row range to be before the line preceding the row range
7348            if start_row.0 > 0 {
7349                let range_to_move = Point::new(
7350                    start_row.previous_row().0,
7351                    buffer.line_len(start_row.previous_row()),
7352                )
7353                    ..Point::new(
7354                        end_row.previous_row().0,
7355                        buffer.line_len(end_row.previous_row()),
7356                    );
7357                let insertion_point = display_map
7358                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
7359                    .0;
7360
7361                // Don't move lines across excerpts
7362                if buffer
7363                    .excerpt_containing(insertion_point..range_to_move.end)
7364                    .is_some()
7365                {
7366                    let text = buffer
7367                        .text_for_range(range_to_move.clone())
7368                        .flat_map(|s| s.chars())
7369                        .skip(1)
7370                        .chain(['\n'])
7371                        .collect::<String>();
7372
7373                    edits.push((
7374                        buffer.anchor_after(range_to_move.start)
7375                            ..buffer.anchor_before(range_to_move.end),
7376                        String::new(),
7377                    ));
7378                    let insertion_anchor = buffer.anchor_after(insertion_point);
7379                    edits.push((insertion_anchor..insertion_anchor, text));
7380
7381                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
7382
7383                    // Move selections up
7384                    new_selections.extend(contiguous_row_selections.drain(..).map(
7385                        |mut selection| {
7386                            selection.start.row -= row_delta;
7387                            selection.end.row -= row_delta;
7388                            selection
7389                        },
7390                    ));
7391
7392                    // Move folds up
7393                    unfold_ranges.push(range_to_move.clone());
7394                    for fold in display_map.folds_in_range(
7395                        buffer.anchor_before(range_to_move.start)
7396                            ..buffer.anchor_after(range_to_move.end),
7397                    ) {
7398                        let mut start = fold.range.start.to_point(&buffer);
7399                        let mut end = fold.range.end.to_point(&buffer);
7400                        start.row -= row_delta;
7401                        end.row -= row_delta;
7402                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7403                    }
7404                }
7405            }
7406
7407            // If we didn't move line(s), preserve the existing selections
7408            new_selections.append(&mut contiguous_row_selections);
7409        }
7410
7411        self.transact(window, cx, |this, window, cx| {
7412            this.unfold_ranges(&unfold_ranges, true, true, cx);
7413            this.buffer.update(cx, |buffer, cx| {
7414                for (range, text) in edits {
7415                    buffer.edit([(range, text)], None, cx);
7416                }
7417            });
7418            this.fold_creases(refold_creases, true, window, cx);
7419            this.change_selections(Default::default(), window, cx, |s| {
7420                s.select(new_selections);
7421            })
7422        });
7423    }
7424
7425    pub fn move_line_down(
7426        &mut self,
7427        _: &MoveLineDown,
7428        window: &mut Window,
7429        cx: &mut Context<Self>,
7430    ) {
7431        if self.read_only(cx) {
7432            return;
7433        }
7434        if self.mode.is_single_line() {
7435            cx.propagate();
7436            return;
7437        }
7438
7439        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7440        let buffer = self.buffer.read(cx).snapshot(cx);
7441
7442        let mut edits = Vec::new();
7443        let mut unfold_ranges = Vec::new();
7444        let mut refold_creases = Vec::new();
7445
7446        let selections = self.selections.all::<Point>(&display_map);
7447        let mut selections = selections.iter().peekable();
7448        let mut contiguous_row_selections = Vec::new();
7449        let mut new_selections = Vec::new();
7450
7451        while let Some(selection) = selections.next() {
7452            // Find all the selections that span a contiguous row range
7453            let (start_row, end_row) = consume_contiguous_rows(
7454                &mut contiguous_row_selections,
7455                selection,
7456                &display_map,
7457                &mut selections,
7458            );
7459
7460            // Move the text spanned by the row range to be after the last line of the row range
7461            if end_row.0 <= buffer.max_point().row {
7462                let range_to_move =
7463                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7464                let insertion_point = display_map
7465                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7466                    .0;
7467
7468                // Don't move lines across excerpt boundaries
7469                if buffer
7470                    .excerpt_containing(range_to_move.start..insertion_point)
7471                    .is_some()
7472                {
7473                    let mut text = String::from("\n");
7474                    text.extend(buffer.text_for_range(range_to_move.clone()));
7475                    text.pop(); // Drop trailing newline
7476                    edits.push((
7477                        buffer.anchor_after(range_to_move.start)
7478                            ..buffer.anchor_before(range_to_move.end),
7479                        String::new(),
7480                    ));
7481                    let insertion_anchor = buffer.anchor_after(insertion_point);
7482                    edits.push((insertion_anchor..insertion_anchor, text));
7483
7484                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
7485
7486                    // Move selections down
7487                    new_selections.extend(contiguous_row_selections.drain(..).map(
7488                        |mut selection| {
7489                            selection.start.row += row_delta;
7490                            selection.end.row += row_delta;
7491                            selection
7492                        },
7493                    ));
7494
7495                    // Move folds down
7496                    unfold_ranges.push(range_to_move.clone());
7497                    for fold in display_map.folds_in_range(
7498                        buffer.anchor_before(range_to_move.start)
7499                            ..buffer.anchor_after(range_to_move.end),
7500                    ) {
7501                        let mut start = fold.range.start.to_point(&buffer);
7502                        let mut end = fold.range.end.to_point(&buffer);
7503                        start.row += row_delta;
7504                        end.row += row_delta;
7505                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7506                    }
7507                }
7508            }
7509
7510            // If we didn't move line(s), preserve the existing selections
7511            new_selections.append(&mut contiguous_row_selections);
7512        }
7513
7514        self.transact(window, cx, |this, window, cx| {
7515            this.unfold_ranges(&unfold_ranges, true, true, cx);
7516            this.buffer.update(cx, |buffer, cx| {
7517                for (range, text) in edits {
7518                    buffer.edit([(range, text)], None, cx);
7519                }
7520            });
7521            this.fold_creases(refold_creases, true, window, cx);
7522            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
7523        });
7524    }
7525
7526    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
7527        if self.read_only(cx) {
7528            return;
7529        }
7530        let text_layout_details = &self.text_layout_details(window, cx);
7531        self.transact(window, cx, |this, window, cx| {
7532            let edits = this.change_selections(Default::default(), window, cx, |s| {
7533                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
7534                s.move_with(&mut |display_map, selection| {
7535                    if !selection.is_empty() {
7536                        return;
7537                    }
7538
7539                    let mut head = selection.head();
7540                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7541                    if head.column() == display_map.line_len(head.row()) {
7542                        transpose_offset = display_map
7543                            .buffer_snapshot()
7544                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
7545                    }
7546
7547                    if transpose_offset == MultiBufferOffset(0) {
7548                        return;
7549                    }
7550
7551                    *head.column_mut() += 1;
7552                    head = display_map.clip_point(head, Bias::Right);
7553                    let goal = SelectionGoal::HorizontalPosition(
7554                        display_map
7555                            .x_for_display_point(head, text_layout_details)
7556                            .into(),
7557                    );
7558                    selection.collapse_to(head, goal);
7559
7560                    let transpose_start = display_map
7561                        .buffer_snapshot()
7562                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
7563                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
7564                        let transpose_end = display_map
7565                            .buffer_snapshot()
7566                            .clip_offset(transpose_offset + 1usize, Bias::Right);
7567                        if let Some(ch) = display_map
7568                            .buffer_snapshot()
7569                            .chars_at(transpose_start)
7570                            .next()
7571                        {
7572                            edits.push((transpose_start..transpose_offset, String::new()));
7573                            edits.push((transpose_end..transpose_end, ch.to_string()));
7574                        }
7575                    }
7576                });
7577                edits
7578            });
7579            this.buffer
7580                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7581            let selections = this
7582                .selections
7583                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
7584            this.change_selections(Default::default(), window, cx, |s| {
7585                s.select(selections);
7586            });
7587        });
7588    }
7589
7590    fn restore_selections(
7591        &mut self,
7592        selections: Option<Arc<[Selection<Anchor>]>>,
7593        window: &mut Window,
7594        cx: &mut Context<Self>,
7595    ) {
7596        if let Some(selections) = selections.filter(|selections| !selections.is_empty()) {
7597            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
7598                s.select_anchors(selections.to_vec());
7599            });
7600        }
7601    }
7602
7603    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
7604        if self.read_only(cx) {
7605            return;
7606        }
7607
7608        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7609            let transaction = self.selection_history.transaction(transaction_id);
7610            if transaction.is_none() {
7611                log::error!("No selection history for undone transaction; selection unchanged");
7612            }
7613            let selections = transaction.map(|transaction| transaction.undo.clone());
7614            self.restore_selections(selections, window, cx);
7615            self.request_autoscroll(Autoscroll::fit(), cx);
7616            self.unmark_text(window, cx);
7617            self.refresh_edit_prediction(
7618                true,
7619                false,
7620                EditPredictionRequestTrigger::BufferEdit,
7621                window,
7622                cx,
7623            );
7624            cx.emit(EditorEvent::Edited { transaction_id });
7625            cx.emit(EditorEvent::TransactionUndone { transaction_id });
7626        }
7627    }
7628
7629    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
7630        if self.read_only(cx) {
7631            return;
7632        }
7633
7634        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7635            let selections = self
7636                .selection_history
7637                .transaction(transaction_id)
7638                .and_then(|transaction| transaction.redo.clone());
7639            self.restore_selections(selections, window, cx);
7640            self.request_autoscroll(Autoscroll::fit(), cx);
7641            self.unmark_text(window, cx);
7642            self.refresh_edit_prediction(
7643                true,
7644                false,
7645                EditPredictionRequestTrigger::BufferEdit,
7646                window,
7647                cx,
7648            );
7649            cx.emit(EditorEvent::Edited { transaction_id });
7650        }
7651    }
7652
7653    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
7654        self.buffer
7655            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7656    }
7657
7658    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
7659        self.buffer
7660            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7661    }
7662
7663    pub fn context_menu_first(
7664        &mut self,
7665        _: &ContextMenuFirst,
7666        window: &mut Window,
7667        cx: &mut Context<Self>,
7668    ) {
7669        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7670            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
7671        }
7672    }
7673
7674    pub fn context_menu_prev(
7675        &mut self,
7676        _: &ContextMenuPrevious,
7677        window: &mut Window,
7678        cx: &mut Context<Self>,
7679    ) {
7680        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7681            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
7682        }
7683    }
7684
7685    pub fn context_menu_next(
7686        &mut self,
7687        _: &ContextMenuNext,
7688        window: &mut Window,
7689        cx: &mut Context<Self>,
7690    ) {
7691        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7692            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
7693        }
7694    }
7695
7696    pub fn context_menu_last(
7697        &mut self,
7698        _: &ContextMenuLast,
7699        window: &mut Window,
7700        cx: &mut Context<Self>,
7701    ) {
7702        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7703            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
7704        }
7705    }
7706
7707    pub fn signature_help_prev(
7708        &mut self,
7709        _: &SignatureHelpPrevious,
7710        _: &mut Window,
7711        cx: &mut Context<Self>,
7712    ) {
7713        if let Some(popover) = self.signature_help_state.popover_mut() {
7714            if popover.current_signature == 0 {
7715                popover.current_signature = popover.signatures.len() - 1;
7716            } else {
7717                popover.current_signature -= 1;
7718            }
7719            cx.notify();
7720        }
7721    }
7722
7723    pub fn signature_help_next(
7724        &mut self,
7725        _: &SignatureHelpNext,
7726        _: &mut Window,
7727        cx: &mut Context<Self>,
7728    ) {
7729        if let Some(popover) = self.signature_help_state.popover_mut() {
7730            if popover.current_signature + 1 == popover.signatures.len() {
7731                popover.current_signature = 0;
7732            } else {
7733                popover.current_signature += 1;
7734            }
7735            cx.notify();
7736        }
7737    }
7738
7739    pub fn rename(
7740        &mut self,
7741        _: &Rename,
7742        window: &mut Window,
7743        cx: &mut Context<Self>,
7744    ) -> Option<Task<Result<()>>> {
7745        use language::ToOffset as _;
7746
7747        if self.read_only(cx) {
7748            return None;
7749        }
7750        let provider = self.semantics_provider.clone()?;
7751        let selection = self.selections.newest_anchor().clone();
7752        let cursor = self.rename_target_anchor(&selection, cx);
7753        let (cursor_buffer, cursor_buffer_position) =
7754            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
7755        let (head_buffer, head_buffer_position) = self
7756            .buffer
7757            .read(cx)
7758            .text_anchor_for_position(selection.head(), cx)?;
7759        let (tail_buffer, cursor_buffer_position_end) = self
7760            .buffer
7761            .read(cx)
7762            .text_anchor_for_position(selection.tail(), cx)?;
7763        if tail_buffer != cursor_buffer || head_buffer != cursor_buffer {
7764            return None;
7765        }
7766
7767        let snapshot = cursor_buffer.read(cx).snapshot();
7768        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7769        let head_buffer_offset = head_buffer_position.to_offset(&snapshot);
7770        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
7771        let prepare_rename = provider.range_for_rename(&cursor_buffer, cursor_buffer_position, cx);
7772        drop(snapshot);
7773
7774        Some(cx.spawn_in(window, async move |this, cx| {
7775            let rename_range = prepare_rename.await?;
7776            if let Some(rename_range) = rename_range {
7777                this.update_in(cx, |this, window, cx| {
7778                    let snapshot = cursor_buffer.read(cx).snapshot();
7779                    let rename_buffer_range = rename_range.to_offset(&snapshot);
7780                    let cursor_offset_in_rename_range =
7781                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7782                    let head_offset_in_rename_range =
7783                        head_buffer_offset.saturating_sub(rename_buffer_range.start);
7784                    let tail_offset_in_rename_range =
7785                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
7786
7787                    this.take_rename(false, window, cx);
7788                    let buffer = this.buffer.read(cx).read(cx);
7789                    let cursor_offset = cursor.to_offset(&buffer);
7790                    let rename_start =
7791                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
7792                    let rename_end = rename_start + rename_buffer_range.len();
7793                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7794                    let mut old_highlight_id = None;
7795                    let old_name: Arc<str> = buffer
7796                        .chunks(
7797                            rename_start..rename_end,
7798                            LanguageAwareStyling {
7799                                tree_sitter: true,
7800                                diagnostics: true,
7801                            },
7802                        )
7803                        .map(|chunk| {
7804                            if old_highlight_id.is_none() {
7805                                old_highlight_id = chunk.syntax_highlight_id;
7806                            }
7807                            chunk.text
7808                        })
7809                        .collect::<String>()
7810                        .into();
7811
7812                    drop(buffer);
7813
7814                    // Position the selection in the rename editor so that it matches the current selection.
7815                    this.show_local_selections = false;
7816                    let rename_editor = cx.new(|cx| {
7817                        let mut editor = Editor::single_line(window, cx);
7818                        editor.buffer.update(cx, |buffer, cx| {
7819                            buffer.edit(
7820                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
7821                                None,
7822                                cx,
7823                            )
7824                        });
7825                        let head_offset_in_rename_range =
7826                            MultiBufferOffset(head_offset_in_rename_range);
7827                        let tail_offset_in_rename_range =
7828                            MultiBufferOffset(tail_offset_in_rename_range);
7829                        let rename_selection_range =
7830                            match head_offset_in_rename_range.cmp(&tail_offset_in_rename_range) {
7831                                Ordering::Equal => {
7832                                    editor.select_all(&SelectAll, window, cx);
7833                                    return editor;
7834                                }
7835                                Ordering::Less => {
7836                                    head_offset_in_rename_range..tail_offset_in_rename_range
7837                                }
7838                                Ordering::Greater => {
7839                                    tail_offset_in_rename_range..head_offset_in_rename_range
7840                                }
7841                            };
7842                        if rename_selection_range.end.0 > old_name.len() {
7843                            editor.select_all(&SelectAll, window, cx);
7844                        } else {
7845                            editor.change_selections(Default::default(), window, cx, |s| {
7846                                s.select_ranges([rename_selection_range]);
7847                            });
7848                        }
7849                        editor
7850                    });
7851                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
7852                        if e == &EditorEvent::Focused {
7853                            cx.emit(EditorEvent::FocusedIn)
7854                        }
7855                    })
7856                    .detach();
7857
7858                    let write_highlights =
7859                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
7860                    let read_highlights =
7861                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
7862                    let ranges = write_highlights
7863                        .iter()
7864                        .flat_map(|(_, ranges)| ranges.iter())
7865                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
7866                        .cloned()
7867                        .collect();
7868
7869                    this.highlight_text(
7870                        HighlightKey::Rename,
7871                        ranges,
7872                        HighlightStyle {
7873                            fade_out: Some(0.6),
7874                            ..Default::default()
7875                        },
7876                        cx,
7877                    );
7878                    let rename_focus_handle = rename_editor.focus_handle(cx);
7879                    window.focus(&rename_focus_handle, cx);
7880                    let block_id = this.insert_blocks(
7881                        [BlockProperties {
7882                            style: BlockStyle::Flex,
7883                            placement: BlockPlacement::Below(range.start),
7884                            height: Some(1),
7885                            render: Arc::new({
7886                                let rename_editor = rename_editor.clone();
7887                                move |cx: &mut BlockContext| {
7888                                    let mut text_style = cx.editor_style.text.clone();
7889                                    if let Some(highlight_style) = old_highlight_id
7890                                        .and_then(|h| cx.editor_style.syntax.get(h).cloned())
7891                                    {
7892                                        text_style = text_style.highlight(highlight_style);
7893                                    }
7894                                    div()
7895                                        .block_mouse_except_scroll()
7896                                        .pl(cx.anchor_x)
7897                                        .child(EditorElement::new(
7898                                            &rename_editor,
7899                                            EditorStyle {
7900                                                background: cx.theme().system().transparent,
7901                                                local_player: cx.editor_style.local_player,
7902                                                text: text_style,
7903                                                scrollbar_width: cx.editor_style.scrollbar_width,
7904                                                syntax: cx.editor_style.syntax.clone(),
7905                                                status: cx.editor_style.status.clone(),
7906                                                inlay_hints_style: HighlightStyle {
7907                                                    font_weight: Some(FontWeight::BOLD),
7908                                                    ..make_inlay_hints_style(cx.app)
7909                                                },
7910                                                edit_prediction_styles: make_suggestion_styles(
7911                                                    cx.app,
7912                                                ),
7913                                                ..EditorStyle::default()
7914                                            },
7915                                        ))
7916                                        .into_any_element()
7917                                }
7918                            }),
7919                            priority: 0,
7920                        }],
7921                        Some(Autoscroll::fit()),
7922                        cx,
7923                    )[0];
7924                    this.pending_rename = Some(RenameState {
7925                        range,
7926                        old_name,
7927                        editor: rename_editor,
7928                        block_id,
7929                    });
7930                })?;
7931            }
7932
7933            Ok(())
7934        }))
7935    }
7936
7937    pub fn confirm_rename(
7938        &mut self,
7939        _: &ConfirmRename,
7940        window: &mut Window,
7941        cx: &mut Context<Self>,
7942    ) -> Option<Task<Result<()>>> {
7943        if self.read_only(cx) {
7944            return None;
7945        }
7946        let rename = self.take_rename(false, window, cx)?;
7947        let workspace = self.workspace()?.downgrade();
7948        let (buffer, start) = self
7949            .buffer
7950            .read(cx)
7951            .text_anchor_for_position(rename.range.start, cx)?;
7952        let (end_buffer, _) = self
7953            .buffer
7954            .read(cx)
7955            .text_anchor_for_position(rename.range.end, cx)?;
7956        if buffer != end_buffer {
7957            return None;
7958        }
7959
7960        let old_name = rename.old_name;
7961        let new_name = rename.editor.read(cx).text(cx);
7962
7963        let rename = self.semantics_provider.as_ref()?.perform_rename(
7964            &buffer,
7965            start,
7966            new_name.clone(),
7967            cx,
7968        )?;
7969
7970        Some(cx.spawn_in(window, async move |editor, cx| {
7971            let project_transaction = rename.await?;
7972            Self::open_project_transaction(
7973                &editor,
7974                workspace,
7975                project_transaction,
7976                format!("Rename: {} → {}", old_name, new_name),
7977                cx,
7978            )
7979            .await?;
7980
7981            editor.update(cx, |editor, cx| {
7982                editor.refresh_document_highlights(cx);
7983            })?;
7984            Ok(())
7985        }))
7986    }
7987
7988    fn take_rename(
7989        &mut self,
7990        moving_cursor: bool,
7991        window: &mut Window,
7992        cx: &mut Context<Self>,
7993    ) -> Option<RenameState> {
7994        let rename = self.pending_rename.take()?;
7995        if rename.editor.focus_handle(cx).is_focused(window) {
7996            window.focus(&self.focus_handle, cx);
7997        }
7998
7999        self.remove_blocks(
8000            [rename.block_id].into_iter().collect(),
8001            Some(Autoscroll::fit()),
8002            cx,
8003        );
8004        self.clear_highlights(HighlightKey::Rename, cx);
8005        self.show_local_selections = true;
8006
8007        if moving_cursor {
8008            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
8009                editor
8010                    .selections
8011                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
8012                    .head()
8013            });
8014
8015            // Update the selection to match the position of the selection inside
8016            // the rename editor.
8017            let snapshot = self.buffer.read(cx).read(cx);
8018            let rename_range = rename.range.to_offset(&snapshot);
8019            let cursor_in_editor = snapshot
8020                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
8021                .min(rename_range.end);
8022            drop(snapshot);
8023
8024            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
8025                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
8026            });
8027        } else {
8028            self.refresh_document_highlights(cx);
8029        }
8030
8031        Some(rename)
8032    }
8033
8034    pub fn pending_rename(&self) -> Option<&RenameState> {
8035        self.pending_rename.as_ref()
8036    }
8037
8038    fn can_format_selections(&self, cx: &App) -> bool {
8039        if !self.mode.is_full() {
8040            return false;
8041        }
8042
8043        let Some(project) = &self.project else {
8044            return false;
8045        };
8046
8047        let project = project.read(cx);
8048        let multi_buffer = self.buffer.read(cx);
8049        let snapshot = multi_buffer.snapshot(cx);
8050
8051        self.selections
8052            .disjoint_anchor_ranges()
8053            .flat_map(|range| [range.start, range.end])
8054            .filter_map(|anchor| snapshot.anchor_to_buffer_anchor(anchor))
8055            .filter_map(|(_, buffer_snapshot)| multi_buffer.buffer(buffer_snapshot.remote_id()))
8056            .any(|buffer| project.supports_range_formatting(&buffer, cx))
8057    }
8058
8059    fn format(
8060        &mut self,
8061        _: &Format,
8062        window: &mut Window,
8063        cx: &mut Context<Self>,
8064    ) -> Option<Task<Result<()>>> {
8065        if self.read_only(cx) {
8066            return None;
8067        }
8068
8069        let project = match &self.project {
8070            Some(project) => project.clone(),
8071            None => return None,
8072        };
8073
8074        Some(self.perform_format(
8075            project,
8076            FormatTrigger::Manual,
8077            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
8078            window,
8079            cx,
8080        ))
8081    }
8082
8083    fn format_selections(
8084        &mut self,
8085        _: &FormatSelections,
8086        window: &mut Window,
8087        cx: &mut Context<Self>,
8088    ) -> Option<Task<Result<()>>> {
8089        if self.read_only(cx) {
8090            return None;
8091        }
8092
8093        let project = match &self.project {
8094            Some(project) => project.clone(),
8095            None => return None,
8096        };
8097
8098        let ranges = self
8099            .selections
8100            .all_adjusted(&self.display_snapshot(cx))
8101            .into_iter()
8102            .map(|selection| selection.range())
8103            .collect_vec();
8104
8105        Some(self.perform_format(
8106            project,
8107            FormatTrigger::Manual,
8108            FormatTarget::Ranges(ranges),
8109            window,
8110            cx,
8111        ))
8112    }
8113
8114    fn perform_format(
8115        &mut self,
8116        project: Entity<Project>,
8117        trigger: FormatTrigger,
8118        target: FormatTarget,
8119        window: &mut Window,
8120        cx: &mut Context<Self>,
8121    ) -> Task<Result<()>> {
8122        let buffer = self.buffer.clone();
8123        let (buffers, target) = match target {
8124            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
8125            FormatTarget::Ranges(selection_ranges) => {
8126                let multi_buffer = buffer.read(cx);
8127                let snapshot = multi_buffer.read(cx);
8128                let mut buffers = HashSet::default();
8129                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
8130                    BTreeMap::new();
8131                for selection_range in selection_ranges {
8132                    for (buffer_snapshot, buffer_range, _) in
8133                        snapshot.range_to_buffer_ranges(selection_range.start..selection_range.end)
8134                    {
8135                        let buffer_id = buffer_snapshot.remote_id();
8136                        let start = buffer_snapshot.anchor_before(buffer_range.start);
8137                        let end = buffer_snapshot.anchor_after(buffer_range.end);
8138                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
8139                        buffer_id_to_ranges
8140                            .entry(buffer_id)
8141                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
8142                            .or_insert_with(|| vec![start..end]);
8143                    }
8144                }
8145                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
8146            }
8147        };
8148
8149        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
8150        let selections_prev = transaction_id_prev
8151            .and_then(|transaction_id_prev| {
8152                // default to selections as they were after the last edit, if we have them,
8153                // instead of how they are now.
8154                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
8155                // will take you back to where you made the last edit, instead of staying where you scrolled
8156                self.selection_history
8157                    .transaction(transaction_id_prev)
8158                    .map(|t| t.undo.clone())
8159            })
8160            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
8161
8162        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
8163        let format = project.update(cx, |project, cx| {
8164            project.format(buffers, target, true, trigger, cx)
8165        });
8166
8167        cx.spawn_in(window, async move |editor, cx| {
8168            let transaction = futures::select_biased! {
8169                transaction = format.log_err().fuse() => transaction,
8170                () = timeout => {
8171                    log::warn!("timed out waiting for formatting");
8172                    None
8173                }
8174            };
8175
8176            buffer.update(cx, |buffer, cx| {
8177                if let Some(transaction) = transaction
8178                    && !buffer.is_singleton()
8179                {
8180                    buffer.push_transaction(&transaction.0, cx);
8181                }
8182                cx.notify();
8183            });
8184
8185            if let Some(transaction_id_now) =
8186                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
8187            {
8188                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
8189                if has_new_transaction {
8190                    editor
8191                        .update(cx, |editor, _| {
8192                            editor
8193                                .selection_history
8194                                .insert_transaction(transaction_id_now, selections_prev);
8195                        })
8196                        .ok();
8197                }
8198            }
8199
8200            Ok(())
8201        })
8202    }
8203
8204    fn organize_imports(
8205        &mut self,
8206        _: &OrganizeImports,
8207        window: &mut Window,
8208        cx: &mut Context<Self>,
8209    ) -> Option<Task<Result<()>>> {
8210        if self.read_only(cx) {
8211            return None;
8212        }
8213        let project = match &self.project {
8214            Some(project) => project.clone(),
8215            None => return None,
8216        };
8217        Some(self.perform_code_action_kind(
8218            project,
8219            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
8220            window,
8221            cx,
8222        ))
8223    }
8224
8225    fn perform_code_action_kind(
8226        &mut self,
8227        project: Entity<Project>,
8228        kind: CodeActionKind,
8229        window: &mut Window,
8230        cx: &mut Context<Self>,
8231    ) -> Task<Result<()>> {
8232        let buffer = self.buffer.clone();
8233        let buffers = buffer.read(cx).all_buffers();
8234        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
8235        let apply_action = project.update(cx, |project, cx| {
8236            project.apply_code_action_kind(buffers, kind, true, cx)
8237        });
8238        cx.spawn_in(window, async move |_, cx| {
8239            let transaction = futures::select_biased! {
8240                () = timeout => {
8241                    log::warn!("timed out waiting for executing code action");
8242                    None
8243                }
8244                transaction = apply_action.log_err().fuse() => transaction,
8245            };
8246            buffer.update(cx, |buffer, cx| {
8247                // check if we need this
8248                if let Some(transaction) = transaction
8249                    && !buffer.is_singleton()
8250                {
8251                    buffer.push_transaction(&transaction.0, cx);
8252                }
8253                cx.notify();
8254            });
8255            Ok(())
8256        })
8257    }
8258
8259    fn restart_language_server(
8260        &mut self,
8261        _: &RestartLanguageServer,
8262        _: &mut Window,
8263        cx: &mut Context<Self>,
8264    ) {
8265        if let Some(project) = self.project.clone() {
8266            self.buffer.update(cx, |multi_buffer, cx| {
8267                project.update(cx, |project, cx| {
8268                    project.restart_language_servers_for_buffers(
8269                        multi_buffer.all_buffers().into_iter().collect(),
8270                        HashSet::default(),
8271                        true,
8272                        cx,
8273                    );
8274                });
8275            })
8276        }
8277    }
8278
8279    fn stop_language_server(
8280        &mut self,
8281        _: &StopLanguageServer,
8282        _: &mut Window,
8283        cx: &mut Context<Self>,
8284    ) {
8285        if let Some(project) = self.project.clone() {
8286            self.buffer.update(cx, |multi_buffer, cx| {
8287                project.update(cx, |project, cx| {
8288                    project.stop_language_servers_for_buffers(
8289                        multi_buffer.all_buffers().into_iter().collect(),
8290                        HashSet::default(),
8291                        cx,
8292                    );
8293                });
8294            });
8295        }
8296    }
8297
8298    fn cancel_language_server_work(
8299        workspace: &mut Workspace,
8300        _: &actions::CancelLanguageServerWork,
8301        _: &mut Window,
8302        cx: &mut Context<Workspace>,
8303    ) {
8304        let project = workspace.project();
8305        let buffers = workspace
8306            .active_item(cx)
8307            .and_then(|item| item.act_as::<Editor>(cx))
8308            .map_or(HashSet::default(), |editor| {
8309                editor.read(cx).buffer.read(cx).all_buffers()
8310            });
8311        project.update(cx, |project, cx| {
8312            project.cancel_language_server_work_for_buffers(buffers, cx);
8313        });
8314    }
8315
8316    fn show_character_palette(
8317        &mut self,
8318        _: &ShowCharacterPalette,
8319        window: &mut Window,
8320        _: &mut Context<Self>,
8321    ) {
8322        window.show_character_palette();
8323    }
8324
8325    pub fn supports_minimap(&self, cx: &App) -> bool {
8326        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
8327    }
8328
8329    pub fn toggle_minimap(
8330        &mut self,
8331        _: &ToggleMinimap,
8332        window: &mut Window,
8333        cx: &mut Context<Editor>,
8334    ) {
8335        if self.supports_minimap(cx) {
8336            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
8337        }
8338    }
8339
8340    pub fn transact(
8341        &mut self,
8342        window: &mut Window,
8343        cx: &mut Context<Self>,
8344        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
8345    ) -> Option<TransactionId> {
8346        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
8347            this.start_transaction_at(Instant::now(), window, cx);
8348            update(this, window, cx);
8349            this.end_transaction_at(Instant::now(), cx)
8350        })
8351    }
8352
8353    pub fn start_transaction_at(
8354        &mut self,
8355        now: Instant,
8356        window: &mut Window,
8357        cx: &mut Context<Self>,
8358    ) -> Option<TransactionId> {
8359        self.end_selection(window, cx);
8360        if let Some(tx_id) = self
8361            .buffer
8362            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8363        {
8364            self.selection_history
8365                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
8366            cx.emit(EditorEvent::TransactionBegun {
8367                transaction_id: tx_id,
8368            });
8369            Some(tx_id)
8370        } else {
8371            None
8372        }
8373    }
8374
8375    pub fn end_transaction_at(
8376        &mut self,
8377        now: Instant,
8378        cx: &mut Context<Self>,
8379    ) -> Option<TransactionId> {
8380        if let Some(transaction_id) = self
8381            .buffer
8382            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8383        {
8384            if let Some(transaction) = self.selection_history.transaction_mut(transaction_id) {
8385                transaction.redo = Some(self.selections.disjoint_anchors_arc());
8386            } else {
8387                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
8388            }
8389
8390            cx.emit(EditorEvent::Edited { transaction_id });
8391            Some(transaction_id)
8392        } else {
8393            None
8394        }
8395    }
8396
8397    pub fn modify_transaction_selection_history(
8398        &mut self,
8399        transaction_id: TransactionId,
8400        modify: impl FnOnce(&mut TransactionSelections),
8401    ) -> bool {
8402        self.selection_history
8403            .transaction_mut(transaction_id)
8404            .map(modify)
8405            .is_some()
8406    }
8407
8408    pub fn toggle_focus(
8409        workspace: &mut Workspace,
8410        _: &actions::ToggleFocus,
8411        window: &mut Window,
8412        cx: &mut Context<Workspace>,
8413    ) {
8414        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
8415            return;
8416        };
8417        workspace.activate_item(&item, true, true, window, cx);
8418    }
8419
8420    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
8421        if hovered != self.gutter_hovered {
8422            self.gutter_hovered = hovered;
8423            cx.notify();
8424        }
8425    }
8426
8427    pub fn insert_blocks(
8428        &mut self,
8429        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8430        autoscroll: Option<Autoscroll>,
8431        cx: &mut Context<Self>,
8432    ) -> Vec<CustomBlockId> {
8433        let blocks = self
8434            .display_map
8435            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8436        if let Some(autoscroll) = autoscroll {
8437            self.request_autoscroll(autoscroll, cx);
8438        }
8439        cx.notify();
8440        blocks
8441    }
8442
8443    pub fn resize_blocks(
8444        &mut self,
8445        heights: HashMap<CustomBlockId, u32>,
8446        autoscroll: Option<Autoscroll>,
8447        cx: &mut Context<Self>,
8448    ) {
8449        self.display_map
8450            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
8451        if let Some(autoscroll) = autoscroll {
8452            self.request_autoscroll(autoscroll, cx);
8453        }
8454        cx.notify();
8455    }
8456
8457    pub fn replace_blocks(
8458        &mut self,
8459        renderers: HashMap<CustomBlockId, RenderBlock>,
8460        autoscroll: Option<Autoscroll>,
8461        cx: &mut Context<Self>,
8462    ) {
8463        self.display_map
8464            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
8465        if let Some(autoscroll) = autoscroll {
8466            self.request_autoscroll(autoscroll, cx);
8467        }
8468        cx.notify();
8469    }
8470
8471    pub fn remove_blocks(
8472        &mut self,
8473        block_ids: HashSet<CustomBlockId>,
8474        autoscroll: Option<Autoscroll>,
8475        cx: &mut Context<Self>,
8476    ) {
8477        self.display_map.update(cx, |display_map, cx| {
8478            display_map.remove_blocks(block_ids, cx)
8479        });
8480        if let Some(autoscroll) = autoscroll {
8481            self.request_autoscroll(autoscroll, cx);
8482        }
8483        cx.notify();
8484    }
8485
8486    pub fn row_for_block(
8487        &self,
8488        block_id: CustomBlockId,
8489        cx: &mut Context<Self>,
8490    ) -> Option<DisplayRow> {
8491        self.display_map
8492            .update(cx, |map, cx| map.row_for_block(block_id, cx))
8493    }
8494
8495    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
8496        self.focused_block = Some(focused_block);
8497    }
8498
8499    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
8500        self.focused_block.take()
8501    }
8502
8503    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
8504        self.display_map
8505            .update(cx, |map, cx| map.snapshot(cx))
8506            .longest_row()
8507    }
8508
8509    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
8510        self.display_map
8511            .update(cx, |map, cx| map.snapshot(cx))
8512            .max_point()
8513    }
8514
8515    pub fn text(&self, cx: &App) -> String {
8516        self.buffer.read(cx).read(cx).text()
8517    }
8518
8519    pub fn is_empty(&self, cx: &App) -> bool {
8520        self.buffer.read(cx).read(cx).is_empty()
8521    }
8522
8523    pub fn text_option(&self, cx: &App) -> Option<String> {
8524        let text = self.text(cx);
8525        let text = text.trim();
8526
8527        if text.is_empty() {
8528            return None;
8529        }
8530
8531        Some(text.to_string())
8532    }
8533
8534    pub fn set_text(
8535        &mut self,
8536        text: impl Into<Arc<str>>,
8537        window: &mut Window,
8538        cx: &mut Context<Self>,
8539    ) {
8540        self.transact(window, cx, |this, _, cx| {
8541            this.buffer
8542                .read(cx)
8543                .as_singleton()
8544                .expect("you can only call set_text on editors for singleton buffers")
8545                .update(cx, |buffer, cx| buffer.set_text(text, cx));
8546        });
8547    }
8548
8549    pub fn display_text(&self, cx: &mut App) -> String {
8550        self.display_map
8551            .update(cx, |map, cx| map.snapshot(cx))
8552            .text()
8553    }
8554
8555    fn create_minimap(
8556        &self,
8557        minimap_settings: MinimapSettings,
8558        window: &mut Window,
8559        cx: &mut Context<Self>,
8560    ) -> Option<Entity<Self>> {
8561        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
8562            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
8563    }
8564
8565    fn initialize_new_minimap(
8566        &self,
8567        minimap_settings: MinimapSettings,
8568        window: &mut Window,
8569        cx: &mut Context<Self>,
8570    ) -> Entity<Self> {
8571        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
8572        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
8573
8574        let mut minimap = Editor::new_internal(
8575            EditorMode::Minimap {
8576                parent: cx.weak_entity(),
8577            },
8578            self.buffer.clone(),
8579            None,
8580            Some(self.display_map.clone()),
8581            window,
8582            cx,
8583        );
8584        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8585        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
8586        minimap.scroll_manager.clone_state(
8587            &self.scroll_manager,
8588            &my_snapshot,
8589            &minimap_snapshot,
8590            cx,
8591        );
8592        minimap.set_text_style_refinement(TextStyleRefinement {
8593            font_size: Some(MINIMAP_FONT_SIZE),
8594            font_weight: Some(MINIMAP_FONT_WEIGHT),
8595            font_family: Some(MINIMAP_FONT_FAMILY),
8596            ..Default::default()
8597        });
8598        minimap.update_minimap_configuration(minimap_settings, cx);
8599        cx.new(|_| minimap)
8600    }
8601
8602    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
8603        let current_line_highlight = minimap_settings
8604            .current_line_highlight
8605            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
8606        self.set_current_line_highlight(Some(current_line_highlight));
8607    }
8608
8609    pub fn minimap(&self) -> Option<&Entity<Self>> {
8610        self.minimap
8611            .as_ref()
8612            .filter(|_| self.minimap_visibility.visible())
8613    }
8614
8615    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
8616        if self.display_map.read(cx).masked != masked {
8617            self.display_map.update(cx, |map, _| map.masked = masked);
8618        }
8619        cx.notify()
8620    }
8621
8622    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
8623        self.active_buffer(cx)?
8624            .read(cx)
8625            .file()
8626            .and_then(|f| f.as_local())
8627    }
8628
8629    fn reveal_in_finder(
8630        &mut self,
8631        _: &RevealInFileManager,
8632        _window: &mut Window,
8633        cx: &mut Context<Self>,
8634    ) {
8635        if let Some(path) = self.target_file_abs_path(cx) {
8636            if let Some(project) = self.project() {
8637                project.update(cx, |project, cx| project.reveal_path(&path, cx));
8638            } else {
8639                cx.reveal_path(&path);
8640            }
8641        }
8642    }
8643
8644    fn copy_path(
8645        &mut self,
8646        _: &zed_actions::workspace::CopyPath,
8647        _window: &mut Window,
8648        cx: &mut Context<Self>,
8649    ) {
8650        if let Some(path) = self.target_file_abs_path(cx)
8651            && let Some(path) = path.to_str()
8652        {
8653            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
8654        } else {
8655            cx.propagate();
8656        }
8657    }
8658
8659    fn copy_relative_path(
8660        &mut self,
8661        _: &zed_actions::workspace::CopyRelativePath,
8662        _window: &mut Window,
8663        cx: &mut Context<Self>,
8664    ) {
8665        if let Some(path) = self.active_buffer(cx).and_then(|buffer| {
8666            let project = self.project()?.read(cx);
8667            let path = buffer.read(cx).file()?.path();
8668            let path = path.display(project.path_style(cx));
8669            Some(path)
8670        }) {
8671            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
8672        } else {
8673            cx.propagate();
8674        }
8675    }
8676
8677    pub fn copy_file_name_without_extension(
8678        &mut self,
8679        _: &CopyFileNameWithoutExtension,
8680        _: &mut Window,
8681        cx: &mut Context<Self>,
8682    ) {
8683        if let Some(file_stem) = self.active_buffer(cx).and_then(|buffer| {
8684            let file = buffer.read(cx).file()?;
8685            file.path().file_stem()
8686        }) {
8687            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
8688        }
8689    }
8690
8691    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
8692        if let Some(file_name) = self.active_buffer(cx).and_then(|buffer| {
8693            let file = buffer.read(cx).file()?;
8694            Some(file.file_name(cx))
8695        }) {
8696            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
8697        }
8698    }
8699
8700    pub fn copy_file_location(
8701        &mut self,
8702        _: &CopyFileLocation,
8703        _: &mut Window,
8704        cx: &mut Context<Self>,
8705    ) {
8706        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
8707        let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
8708
8709        if let Some(file_location) = maybe!({
8710            let (buffer, range) = multi_buffer_snapshot
8711                .range_to_buffer_range(selection.range())
8712                .or_else(|| {
8713                    // A selection that spans multiple buffers has no single location,
8714                    // so fall back to the buffer the latest cursor is in.
8715                    let (buffer, point) =
8716                        multi_buffer_snapshot.point_to_buffer_point(selection.head())?;
8717                    Some((buffer, point..point))
8718                })?;
8719
8720            let start_line = range.start.row + 1;
8721            let end_line = range.end.row + 1;
8722
8723            let end_line = if range.end.column == 0 && end_line > start_line {
8724                end_line - 1
8725            } else {
8726                end_line
8727            };
8728
8729            let project = self.project()?.read(cx);
8730            let file = buffer.file()?;
8731            let path = file.path().display(project.path_style(cx));
8732
8733            let location = if start_line == end_line {
8734                format!("{path}:{start_line}")
8735            } else {
8736                format!("{path}:{start_line}-{end_line}")
8737            };
8738            Some(location)
8739        }) {
8740            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
8741        }
8742    }
8743
8744    pub fn insert_uuid_v4(
8745        &mut self,
8746        _: &InsertUuidV4,
8747        window: &mut Window,
8748        cx: &mut Context<Self>,
8749    ) {
8750        self.insert_uuid(UuidVersion::V4, window, cx);
8751    }
8752
8753    pub fn insert_uuid_v7(
8754        &mut self,
8755        _: &InsertUuidV7,
8756        window: &mut Window,
8757        cx: &mut Context<Self>,
8758    ) {
8759        self.insert_uuid(UuidVersion::V7, window, cx);
8760    }
8761
8762    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
8763        if self.read_only(cx) {
8764            return;
8765        }
8766        self.transact(window, cx, |this, window, cx| {
8767            let edits = this
8768                .selections
8769                .all::<Point>(&this.display_snapshot(cx))
8770                .into_iter()
8771                .map(|selection| {
8772                    let uuid = match version {
8773                        UuidVersion::V4 => uuid::Uuid::new_v4(),
8774                        UuidVersion::V7 => uuid::Uuid::now_v7(),
8775                    };
8776
8777                    (selection.range(), uuid.to_string())
8778                });
8779            this.edit(edits, cx);
8780            this.refresh_edit_prediction(
8781                true,
8782                false,
8783                EditPredictionRequestTrigger::BufferEdit,
8784                window,
8785                cx,
8786            );
8787        });
8788    }
8789
8790    pub fn open_selections_in_multibuffer(
8791        &mut self,
8792        _: &OpenSelectionsInMultibuffer,
8793        window: &mut Window,
8794        cx: &mut Context<Self>,
8795    ) {
8796        let multibuffer = self.buffer.read(cx);
8797
8798        let Some(buffer) = multibuffer.as_singleton() else {
8799            return;
8800        };
8801        let buffer_snapshot = buffer.read(cx).snapshot();
8802
8803        let Some(workspace) = self.workspace() else {
8804            return;
8805        };
8806
8807        let title = multibuffer.title(cx).to_string();
8808
8809        let locations = self
8810            .selections
8811            .all_anchors(&self.display_snapshot(cx))
8812            .iter()
8813            .map(|selection| {
8814                (
8815                    buffer.clone(),
8816                    (selection.start.text_anchor_in(&buffer_snapshot)
8817                        ..selection.end.text_anchor_in(&buffer_snapshot))
8818                        .to_point(buffer.read(cx)),
8819                )
8820            })
8821            .into_group_map();
8822
8823        cx.spawn_in(window, async move |_, cx| {
8824            workspace.update_in(cx, |workspace, window, cx| {
8825                Self::open_locations_in_multibuffer(
8826                    workspace,
8827                    locations,
8828                    format!("Selections for '{title}'"),
8829                    false,
8830                    false,
8831                    MultibufferSelectionMode::All,
8832                    window,
8833                    cx,
8834                );
8835            })
8836        })
8837        .detach();
8838    }
8839
8840    /// Adds a row highlight for the given range. If a row has multiple highlights, the
8841    /// last highlight added will be used.
8842    ///
8843    /// If the range ends at the beginning of a line, then that line will not be highlighted.
8844    pub fn highlight_rows<T: 'static>(
8845        &mut self,
8846        range: Range<Anchor>,
8847        color: fn(&App) -> Hsla,
8848        options: RowHighlightOptions,
8849        cx: &mut Context<Self>,
8850    ) {
8851        let snapshot = self.buffer().read(cx).snapshot(cx);
8852        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
8853        let ix = row_highlights.binary_search_by(|highlight| {
8854            Ordering::Equal
8855                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
8856                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
8857        });
8858
8859        if let Err(mut ix) = ix {
8860            let index = post_inc(&mut self.highlight_order);
8861
8862            // If this range intersects with the preceding highlight, then merge it with
8863            // the preceding highlight. Otherwise insert a new highlight.
8864            let mut merged = false;
8865            if ix > 0 {
8866                let prev_highlight = &mut row_highlights[ix - 1];
8867                if prev_highlight
8868                    .range
8869                    .end
8870                    .cmp(&range.start, &snapshot)
8871                    .is_ge()
8872                {
8873                    ix -= 1;
8874                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
8875                        prev_highlight.range.end = range.end;
8876                    }
8877                    merged = true;
8878                    prev_highlight.index = index;
8879                    prev_highlight.color = color;
8880                    prev_highlight.options = options;
8881                }
8882            }
8883
8884            if !merged {
8885                row_highlights.insert(
8886                    ix,
8887                    RowHighlight {
8888                        range,
8889                        index,
8890                        color,
8891                        options,
8892                        type_id: TypeId::of::<T>(),
8893                    },
8894                );
8895            }
8896
8897            // If any of the following highlights intersect with this one, merge them.
8898            while let Some(next_highlight) = row_highlights.get(ix + 1) {
8899                let highlight = &row_highlights[ix];
8900                if next_highlight
8901                    .range
8902                    .start
8903                    .cmp(&highlight.range.end, &snapshot)
8904                    .is_le()
8905                {
8906                    if next_highlight
8907                        .range
8908                        .end
8909                        .cmp(&highlight.range.end, &snapshot)
8910                        .is_gt()
8911                    {
8912                        row_highlights[ix].range.end = next_highlight.range.end;
8913                    }
8914                    row_highlights.remove(ix + 1);
8915                } else {
8916                    break;
8917                }
8918            }
8919        }
8920    }
8921
8922    /// Remove any highlighted row ranges of the given type that intersect the
8923    /// given ranges.
8924    pub fn remove_highlighted_rows<T: 'static>(
8925        &mut self,
8926        ranges_to_remove: Vec<Range<Anchor>>,
8927        cx: &mut Context<Self>,
8928    ) {
8929        let snapshot = self.buffer().read(cx).snapshot(cx);
8930        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
8931        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
8932        row_highlights.retain(|highlight| {
8933            while let Some(range_to_remove) = ranges_to_remove.peek() {
8934                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
8935                    Ordering::Less | Ordering::Equal => {
8936                        ranges_to_remove.next();
8937                    }
8938                    Ordering::Greater => {
8939                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
8940                            Ordering::Less | Ordering::Equal => {
8941                                return false;
8942                            }
8943                            Ordering::Greater => break,
8944                        }
8945                    }
8946                }
8947            }
8948
8949            true
8950        })
8951    }
8952
8953    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
8954    pub fn clear_row_highlights<T: 'static>(&mut self) {
8955        self.highlighted_rows.remove(&TypeId::of::<T>());
8956    }
8957
8958    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
8959    pub fn highlighted_rows<'a, T: 'static>(
8960        &'a self,
8961        cx: &'a App,
8962    ) -> impl 'a + Iterator<Item = (Range<Anchor>, Hsla)> {
8963        self.highlighted_rows
8964            .get(&TypeId::of::<T>())
8965            .map_or(&[] as &[_], |vec| vec.as_slice())
8966            .iter()
8967            .map(|highlight| (highlight.range.clone(), (highlight.color)(cx)))
8968    }
8969
8970    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
8971    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
8972    /// Allows to ignore certain kinds of highlights.
8973    pub fn highlighted_display_rows(
8974        &self,
8975        window: &mut Window,
8976        cx: &mut App,
8977    ) -> BTreeMap<DisplayRow, LineHighlight> {
8978        let snapshot = self.snapshot(window, cx);
8979        let mut used_highlight_orders = HashMap::default();
8980        self.highlighted_rows
8981            .values()
8982            .flat_map(|highlighted_rows| highlighted_rows.iter())
8983            .fold(
8984                BTreeMap::<DisplayRow, LineHighlight>::new(),
8985                |mut unique_rows, highlight| {
8986                    let start = highlight.range.start.to_display_point(&snapshot);
8987                    let end = highlight.range.end.to_display_point(&snapshot);
8988                    let start_row = start.row().0;
8989                    let end_row = if !highlight.range.end.is_max() && end.column() == 0 {
8990                        end.row().0.saturating_sub(1)
8991                    } else {
8992                        end.row().0
8993                    };
8994                    for row in start_row..=end_row {
8995                        let used_index =
8996                            used_highlight_orders.entry(row).or_insert(highlight.index);
8997                        if highlight.index >= *used_index {
8998                            *used_index = highlight.index;
8999                            unique_rows.insert(
9000                                DisplayRow(row),
9001                                LineHighlight {
9002                                    include_gutter: highlight.options.include_gutter,
9003                                    border: None,
9004                                    background: (highlight.color)(cx).into(),
9005                                    type_id: Some(highlight.type_id),
9006                                },
9007                            );
9008                        }
9009                    }
9010                    unique_rows
9011                },
9012            )
9013    }
9014
9015    pub fn highlighted_display_row_for_autoscroll(
9016        &self,
9017        snapshot: &DisplaySnapshot,
9018    ) -> Option<DisplayRow> {
9019        self.highlighted_rows
9020            .values()
9021            .flat_map(|highlighted_rows| highlighted_rows.iter())
9022            .filter_map(|highlight| {
9023                if highlight.options.autoscroll {
9024                    Some(highlight.range.start.to_display_point(snapshot).row())
9025                } else {
9026                    None
9027                }
9028            })
9029            .min()
9030    }
9031
9032    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
9033        self.highlight_background(
9034            HighlightKey::SearchWithinRange,
9035            ranges,
9036            |_, colors| colors.colors().editor_document_highlight_read_background,
9037            cx,
9038        )
9039    }
9040
9041    pub fn set_breadcrumb_header(&mut self, new_header: String) {
9042        self.breadcrumb_header = Some(new_header);
9043    }
9044
9045    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
9046        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
9047    }
9048
9049    pub fn highlight_background(
9050        &mut self,
9051        key: HighlightKey,
9052        ranges: &[Range<Anchor>],
9053        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
9054        cx: &mut Context<Self>,
9055    ) {
9056        self.background_highlights
9057            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
9058        self.scrollbar_marker_state.dirty = true;
9059        cx.notify();
9060    }
9061
9062    pub fn clear_background_highlights(
9063        &mut self,
9064        key: HighlightKey,
9065        cx: &mut Context<Self>,
9066    ) -> Option<BackgroundHighlight> {
9067        let text_highlights = self.background_highlights.remove(&key)?;
9068        if !text_highlights.1.is_empty() {
9069            self.scrollbar_marker_state.dirty = true;
9070            cx.notify();
9071        }
9072        Some(text_highlights)
9073    }
9074
9075    pub fn highlight_gutter<T: 'static>(
9076        &mut self,
9077        ranges: impl Into<Vec<Range<Anchor>>>,
9078        color_fetcher: fn(&App) -> Hsla,
9079        cx: &mut Context<Self>,
9080    ) {
9081        self.gutter_highlights
9082            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
9083        cx.notify();
9084    }
9085
9086    pub fn clear_gutter_highlights<T: 'static>(
9087        &mut self,
9088        cx: &mut Context<Self>,
9089    ) -> Option<GutterHighlight> {
9090        cx.notify();
9091        self.gutter_highlights.remove(&TypeId::of::<T>())
9092    }
9093
9094    pub fn insert_gutter_highlight<T: 'static>(
9095        &mut self,
9096        range: Range<Anchor>,
9097        color_fetcher: fn(&App) -> Hsla,
9098        cx: &mut Context<Self>,
9099    ) {
9100        let snapshot = self.buffer().read(cx).snapshot(cx);
9101        let mut highlights = self
9102            .gutter_highlights
9103            .remove(&TypeId::of::<T>())
9104            .map(|(_, highlights)| highlights)
9105            .unwrap_or_default();
9106        let ix = highlights.binary_search_by(|highlight| {
9107            Ordering::Equal
9108                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
9109                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
9110        });
9111        if let Err(ix) = ix {
9112            highlights.insert(ix, range);
9113        }
9114        self.gutter_highlights
9115            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
9116    }
9117
9118    pub fn remove_gutter_highlights<T: 'static>(
9119        &mut self,
9120        ranges_to_remove: Vec<Range<Anchor>>,
9121        cx: &mut Context<Self>,
9122    ) {
9123        let snapshot = self.buffer().read(cx).snapshot(cx);
9124        let Some((color_fetcher, mut gutter_highlights)) =
9125            self.gutter_highlights.remove(&TypeId::of::<T>())
9126        else {
9127            return;
9128        };
9129        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
9130        gutter_highlights.retain(|highlight| {
9131            while let Some(range_to_remove) = ranges_to_remove.peek() {
9132                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
9133                    Ordering::Less | Ordering::Equal => {
9134                        ranges_to_remove.next();
9135                    }
9136                    Ordering::Greater => {
9137                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
9138                            Ordering::Less | Ordering::Equal => {
9139                                return false;
9140                            }
9141                            Ordering::Greater => break,
9142                        }
9143                    }
9144                }
9145            }
9146
9147            true
9148        });
9149        self.gutter_highlights
9150            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
9151    }
9152
9153    #[cfg(any(test, feature = "test-support"))]
9154    pub fn all_text_highlights(
9155        &self,
9156        window: &mut Window,
9157        cx: &mut Context<Self>,
9158    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
9159        let snapshot = self.snapshot(window, cx);
9160        self.display_map.update(cx, |display_map, _| {
9161            display_map
9162                .all_text_highlights()
9163                .map(|(_, highlight)| {
9164                    let (style, ranges) = highlight.as_ref();
9165                    (
9166                        *style,
9167                        ranges
9168                            .iter()
9169                            .map(|range| range.clone().to_display_points(&snapshot))
9170                            .collect(),
9171                    )
9172                })
9173                .collect()
9174        })
9175    }
9176
9177    #[cfg(any(test, feature = "test-support"))]
9178    pub fn all_text_background_highlights(
9179        &self,
9180        window: &mut Window,
9181        cx: &mut Context<Self>,
9182    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
9183        let snapshot = self.snapshot(window, cx);
9184        let buffer = &snapshot.buffer_snapshot();
9185        let start = buffer.anchor_before(MultiBufferOffset(0));
9186        let end = buffer.anchor_after(buffer.len());
9187        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
9188    }
9189
9190    #[cfg(any(test, feature = "test-support"))]
9191    pub fn sorted_background_highlights_in_range(
9192        &self,
9193        search_range: Range<Anchor>,
9194        display_snapshot: &DisplaySnapshot,
9195        theme: &Theme,
9196    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
9197        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
9198        res.sort_by(|a, b| {
9199            a.0.start
9200                .cmp(&b.0.start)
9201                .then_with(|| a.0.end.cmp(&b.0.end))
9202                .then_with(|| a.1.cmp(&b.1))
9203        });
9204        res
9205    }
9206
9207    #[cfg(any(test, feature = "test-support"))]
9208    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
9209        let snapshot = self.buffer().read(cx).snapshot(cx);
9210
9211        let highlights = self
9212            .background_highlights
9213            .get(&HighlightKey::BufferSearchHighlights);
9214
9215        if let Some((_color, ranges)) = highlights {
9216            ranges
9217                .iter()
9218                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
9219                .collect_vec()
9220        } else {
9221            vec![]
9222        }
9223    }
9224
9225    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
9226        self.background_highlights
9227            .get(&key)
9228            .is_some_and(|(_, highlights)| !highlights.is_empty())
9229    }
9230
9231    /// Returns all background highlights for a given range.
9232    ///
9233    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
9234    pub fn background_highlights_in_range(
9235        &self,
9236        search_range: Range<Anchor>,
9237        display_snapshot: &DisplaySnapshot,
9238        theme: &Theme,
9239    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
9240        let mut results = Vec::new();
9241        for (color_fetcher, ranges) in self.background_highlights.values() {
9242            let start_ix = match ranges.binary_search_by(|probe| {
9243                let cmp = probe
9244                    .end
9245                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
9246                if cmp.is_gt() {
9247                    Ordering::Greater
9248                } else {
9249                    Ordering::Less
9250                }
9251            }) {
9252                Ok(i) | Err(i) => i,
9253            };
9254            for (index, range) in ranges[start_ix..].iter().enumerate() {
9255                if range
9256                    .start
9257                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
9258                    .is_ge()
9259                {
9260                    break;
9261                }
9262
9263                let color = color_fetcher(&(start_ix + index), theme);
9264                let start = range.start.to_display_point(display_snapshot);
9265                let end = range.end.to_display_point(display_snapshot);
9266                results.push((start..end, color))
9267            }
9268        }
9269        results
9270    }
9271
9272    pub fn gutter_highlights_in_range(
9273        &self,
9274        search_range: Range<Anchor>,
9275        display_snapshot: &DisplaySnapshot,
9276        cx: &App,
9277    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
9278        let mut results = Vec::new();
9279        for (color_fetcher, ranges) in self.gutter_highlights.values() {
9280            let color = color_fetcher(cx);
9281            let start_ix = match ranges.binary_search_by(|probe| {
9282                let cmp = probe
9283                    .end
9284                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
9285                if cmp.is_gt() {
9286                    Ordering::Greater
9287                } else {
9288                    Ordering::Less
9289                }
9290            }) {
9291                Ok(i) | Err(i) => i,
9292            };
9293            for range in &ranges[start_ix..] {
9294                if range
9295                    .start
9296                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
9297                    .is_ge()
9298                {
9299                    break;
9300                }
9301
9302                let start = range.start.to_display_point(display_snapshot);
9303                let end = range.end.to_display_point(display_snapshot);
9304                results.push((start..end, color))
9305            }
9306        }
9307        results
9308    }
9309
9310    /// Get the text ranges corresponding to the redaction query
9311    pub fn redacted_ranges(
9312        &self,
9313        search_range: Range<Anchor>,
9314        display_snapshot: &DisplaySnapshot,
9315        cx: &App,
9316    ) -> Vec<Range<DisplayPoint>> {
9317        display_snapshot
9318            .buffer_snapshot()
9319            .redacted_ranges(search_range, |file| {
9320                if let Some(file) = file {
9321                    file.is_private()
9322                        && EditorSettings::get(
9323                            Some(SettingsLocation {
9324                                worktree_id: file.worktree_id(cx),
9325                                path: file.path().as_ref(),
9326                            }),
9327                            cx,
9328                        )
9329                        .redact_private_values
9330                } else {
9331                    false
9332                }
9333            })
9334            .map(|range| {
9335                range.start.to_display_point(display_snapshot)
9336                    ..range.end.to_display_point(display_snapshot)
9337            })
9338            .collect()
9339    }
9340
9341    pub fn highlight_text_key(
9342        &mut self,
9343        key: HighlightKey,
9344        ranges: Vec<Range<Anchor>>,
9345        style: HighlightStyle,
9346        merge: bool,
9347        cx: &mut Context<Self>,
9348    ) {
9349        self.display_map.update(cx, |map, cx| {
9350            map.highlight_text(key, ranges, style, merge, cx);
9351        });
9352        cx.notify();
9353    }
9354
9355    pub fn highlight_text(
9356        &mut self,
9357        key: HighlightKey,
9358        ranges: Vec<Range<Anchor>>,
9359        style: HighlightStyle,
9360        cx: &mut Context<Self>,
9361    ) {
9362        self.display_map.update(cx, |map, cx| {
9363            map.highlight_text(key, ranges, style, false, cx)
9364        });
9365        cx.notify();
9366    }
9367
9368    pub fn text_highlights<'a>(
9369        &'a self,
9370        key: HighlightKey,
9371        cx: &'a App,
9372    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
9373        self.display_map.read(cx).text_highlights(key)
9374    }
9375
9376    pub fn set_navigation_overlays(
9377        &mut self,
9378        key: NavigationOverlayKey,
9379        overlays: Vec<NavigationTargetOverlay>,
9380        cx: &mut Context<Self>,
9381    ) {
9382        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
9383        let mut covered_text_ranges = overlays
9384            .iter()
9385            .filter_map(|overlay| overlay.covered_text_range.clone())
9386            .collect::<Vec<_>>();
9387        covered_text_ranges.sort_by(|left, right| {
9388            left.start
9389                .cmp(&right.start, &buffer_snapshot)
9390                .then_with(|| left.end.cmp(&right.end, &buffer_snapshot))
9391        });
9392
9393        self.display_map.update(cx, |map, cx| {
9394            map.clear_highlights(HighlightKey::NavigationOverlay(key));
9395            if !covered_text_ranges.is_empty() {
9396                map.highlight_text(
9397                    HighlightKey::NavigationOverlay(key),
9398                    covered_text_ranges,
9399                    HighlightStyle {
9400                        fade_out: Some(1.0),
9401                        ..Default::default()
9402                    },
9403                    false,
9404                    cx,
9405                );
9406            }
9407        });
9408
9409        if overlays.is_empty() {
9410            self.navigation_overlays.remove(&key);
9411        } else {
9412            self.navigation_overlays.insert(key, Arc::from(overlays));
9413        }
9414
9415        cx.notify();
9416    }
9417
9418    pub fn clear_navigation_overlays(&mut self, key: NavigationOverlayKey, cx: &mut Context<Self>) {
9419        let removed = self.navigation_overlays.remove(&key).is_some();
9420        let cleared = self.display_map.update(cx, |map, _| {
9421            map.clear_highlights(HighlightKey::NavigationOverlay(key))
9422        });
9423        if removed || cleared {
9424            cx.notify();
9425        }
9426    }
9427
9428    pub(crate) fn navigation_overlay_sets(
9429        &self,
9430    ) -> &HashMap<NavigationOverlayKey, Arc<[NavigationTargetOverlay]>> {
9431        &self.navigation_overlays
9432    }
9433
9434    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
9435        let cleared = self
9436            .display_map
9437            .update(cx, |map, _| map.clear_highlights(key));
9438        if cleared {
9439            cx.notify();
9440        }
9441    }
9442
9443    pub fn clear_highlights_with(
9444        &mut self,
9445        f: &mut dyn FnMut(&HighlightKey) -> bool,
9446        cx: &mut Context<Self>,
9447    ) {
9448        let cleared = self
9449            .display_map
9450            .update(cx, |map, _| map.clear_highlights_with(f));
9451        if cleared {
9452            cx.notify();
9453        }
9454    }
9455
9456    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
9457        (self.read_only(cx) || self.blink_manager.read(cx).visible())
9458            && self.focus_handle.is_focused(window)
9459    }
9460
9461    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
9462        self.show_cursor_when_unfocused = is_enabled;
9463        cx.notify();
9464    }
9465
9466    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
9467        cx.notify();
9468    }
9469
9470    fn on_debug_session_event(
9471        &mut self,
9472        _session: Entity<Session>,
9473        event: &SessionEvent,
9474        cx: &mut Context<Self>,
9475    ) {
9476        if let SessionEvent::InvalidateInlineValue = event {
9477            self.refresh_inline_values(cx);
9478        }
9479    }
9480
9481    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
9482        let Some(semantics) = self.semantics_provider.clone() else {
9483            return;
9484        };
9485
9486        if !self.inline_value_cache.enabled {
9487            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
9488            self.splice_inlays(&inlays, Vec::new(), cx);
9489            return;
9490        }
9491
9492        let current_execution_position = self
9493            .highlighted_rows
9494            .get(&TypeId::of::<ActiveDebugLine>())
9495            .and_then(|lines| lines.last().map(|line| line.range.end));
9496
9497        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
9498            let inline_values = editor
9499                .update(cx, |editor, cx| {
9500                    let Some(current_execution_position) = current_execution_position else {
9501                        return Some(Task::ready(Ok(Vec::new())));
9502                    };
9503
9504                    let (buffer, buffer_anchor) =
9505                        editor.buffer.read_with(cx, |multibuffer, cx| {
9506                            let multibuffer_snapshot = multibuffer.snapshot(cx);
9507                            let (buffer_anchor, _) = multibuffer_snapshot
9508                                .anchor_to_buffer_anchor(current_execution_position)?;
9509                            let buffer = multibuffer.buffer(buffer_anchor.buffer_id)?;
9510                            Some((buffer, buffer_anchor))
9511                        })?;
9512
9513                    let range = buffer.read(cx).anchor_before(0)..buffer_anchor;
9514
9515                    semantics.inline_values(buffer, range, cx)
9516                })
9517                .ok()
9518                .flatten()?
9519                .await
9520                .context("refreshing debugger inlays")
9521                .log_err()?;
9522
9523            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
9524
9525            for (buffer_id, inline_value) in inline_values
9526                .into_iter()
9527                .map(|hint| (hint.position.buffer_id, hint))
9528            {
9529                buffer_inline_values
9530                    .entry(buffer_id)
9531                    .or_default()
9532                    .push(inline_value);
9533            }
9534
9535            editor
9536                .update(cx, |editor, cx| {
9537                    let snapshot = editor.buffer.read(cx).snapshot(cx);
9538                    let mut new_inlays = Vec::default();
9539
9540                    for (_buffer_id, inline_values) in buffer_inline_values {
9541                        for hint in inline_values {
9542                            let Some(anchor) = snapshot.anchor_in_excerpt(hint.position) else {
9543                                continue;
9544                            };
9545                            let inlay = Inlay::debugger(
9546                                post_inc(&mut editor.next_inlay_id),
9547                                anchor,
9548                                hint.text(),
9549                            );
9550                            if !inlay.text().chars().contains(&'\n') {
9551                                new_inlays.push(inlay);
9552                            }
9553                        }
9554                    }
9555
9556                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
9557                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
9558
9559                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
9560                })
9561                .ok()?;
9562            Some(())
9563        });
9564    }
9565
9566    fn on_buffer_event(
9567        &mut self,
9568        multibuffer: &Entity<MultiBuffer>,
9569        event: &multi_buffer::Event,
9570        window: &mut Window,
9571        cx: &mut Context<Self>,
9572    ) {
9573        match event {
9574            multi_buffer::Event::Edited {
9575                edited_buffer,
9576                source,
9577            } => {
9578                self.scrollbar_marker_state.dirty = true;
9579                self.active_indent_guides_state.dirty = true;
9580                self.refresh_active_diagnostics(cx);
9581                self.refresh_code_actions_for_selection(window, cx);
9582                self.refresh_single_line_folds(window, cx);
9583                let snapshot = self.snapshot(window, cx);
9584                self.refresh_matching_bracket_highlights(&snapshot, cx);
9585                self.refresh_outline_symbols_at_cursor(cx);
9586                self.refresh_sticky_headers(&snapshot, cx);
9587                if source.is_local() && self.has_active_edit_prediction() {
9588                    self.update_visible_edit_prediction(window, cx);
9589                }
9590
9591                // Clean up orphaned review comments after edits
9592                self.cleanup_orphaned_review_comments(cx);
9593
9594                if let Some(buffer) = edited_buffer {
9595                    if buffer.read(cx).file().is_none() {
9596                        cx.emit(EditorEvent::TitleChanged);
9597                    }
9598
9599                    if self.project.is_some() {
9600                        let buffer_id = buffer.read(cx).remote_id();
9601                        self.register_buffer(buffer_id, cx);
9602                        self.update_lsp_data(Some(buffer_id), window, cx);
9603                        self.refresh_inlay_hints(
9604                            InlayHintRefreshReason::BufferEdited(buffer_id),
9605                            cx,
9606                        );
9607                    }
9608                }
9609
9610                cx.emit(EditorEvent::BufferEdited);
9611                cx.emit(SearchEvent::MatchesInvalidated);
9612
9613                let Some(project) = &self.project else { return };
9614                let (telemetry, is_via_ssh) = {
9615                    let project = project.read(cx);
9616                    let telemetry = project.client().telemetry().clone();
9617                    let is_via_ssh = project.is_via_remote_server();
9618                    (telemetry, is_via_ssh)
9619                };
9620                telemetry.log_edit_event("editor", is_via_ssh);
9621            }
9622            multi_buffer::Event::BufferRangesUpdated {
9623                buffer,
9624                ranges,
9625                path_key,
9626            } => {
9627                if let Some(hovered_link_state) = self.hovered_link_state.as_mut() {
9628                    hovered_link_state.symbol_range = None;
9629                }
9630                self.refresh_document_highlights(cx);
9631                let buffer_id = buffer.read(cx).remote_id();
9632                if self.buffer.read(cx).diff_for(buffer_id).is_none()
9633                    && let Some(project) = &self.project
9634                {
9635                    update_uncommitted_diff_for_buffer(
9636                        cx.entity(),
9637                        project,
9638                        [buffer.clone()],
9639                        self.buffer.clone(),
9640                        cx,
9641                    )
9642                    .detach();
9643                }
9644                self.register_visible_buffers(cx);
9645                self.update_lsp_data(Some(buffer_id), window, cx);
9646                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
9647                self.refresh_runnables(None, window, cx);
9648                self.bracket_fetched_tree_sitter_chunks
9649                    .retain(|range, _| range.start.buffer_id != buffer_id);
9650                self.colorize_brackets(false, cx);
9651                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
9652                self.semantic_token_state.invalidate_buffer(&buffer_id);
9653                cx.emit(EditorEvent::BufferRangesUpdated {
9654                    buffer: buffer.clone(),
9655                    ranges: ranges.clone(),
9656                    path_key: path_key.clone(),
9657                });
9658            }
9659            multi_buffer::Event::BuffersRemoved { removed_buffer_ids } => {
9660                if let Some(inlay_hints) = &mut self.inlay_hints {
9661                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
9662                }
9663                self.refresh_inlay_hints(
9664                    InlayHintRefreshReason::BuffersRemoved(removed_buffer_ids.clone()),
9665                    cx,
9666                );
9667                for buffer_id in removed_buffer_ids {
9668                    self.registered_buffers.remove(buffer_id);
9669                    self.clear_runnables(Some(*buffer_id));
9670                    self.semantic_token_state.invalidate_buffer(buffer_id);
9671                    self.lsp_document_symbols.remove(buffer_id);
9672                    self.lsp_document_links.per_buffer.remove(buffer_id);
9673                    self.display_map.update(cx, |display_map, cx| {
9674                        display_map.invalidate_semantic_highlights(*buffer_id);
9675                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
9676                    });
9677                }
9678
9679                self.display_map.update(cx, |display_map, cx| {
9680                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
9681                });
9682
9683                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
9684                cx.emit(EditorEvent::BuffersRemoved {
9685                    removed_buffer_ids: removed_buffer_ids.clone(),
9686                });
9687            }
9688            multi_buffer::Event::BuffersEdited { buffer_ids } => {
9689                self.display_map.update(cx, |map, cx| {
9690                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
9691                });
9692                cx.emit(EditorEvent::BuffersEdited {
9693                    buffer_ids: buffer_ids.clone(),
9694                });
9695            }
9696            multi_buffer::Event::Reparsed(buffer_id) => {
9697                self.refresh_runnables(Some(*buffer_id), window, cx);
9698                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
9699                self.colorize_brackets(true, cx);
9700                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
9701
9702                cx.emit(EditorEvent::Reparsed(*buffer_id));
9703            }
9704            multi_buffer::Event::DiffHunksToggled => {
9705                self.refresh_runnables(None, window, cx);
9706            }
9707            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
9708                if !is_fresh_language {
9709                    self.registered_buffers.remove(&buffer_id);
9710                }
9711                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
9712                cx.emit(EditorEvent::Reparsed(*buffer_id));
9713                self.update_edit_prediction_settings(cx);
9714                cx.notify();
9715            }
9716            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
9717            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
9718            multi_buffer::Event::FileHandleChanged => {
9719                cx.emit(EditorEvent::TitleChanged);
9720                cx.emit(EditorEvent::FileHandleChanged);
9721            }
9722            multi_buffer::Event::Reloaded | multi_buffer::Event::BufferDiffChanged => {
9723                cx.emit(EditorEvent::TitleChanged)
9724            }
9725            multi_buffer::Event::DiagnosticsUpdated => {
9726                self.update_diagnostics_state(window, cx);
9727            }
9728            _ => {}
9729        };
9730    }
9731
9732    fn on_display_map_changed(
9733        &mut self,
9734        _: Entity<DisplayMap>,
9735        _: &mut Window,
9736        cx: &mut Context<Self>,
9737    ) {
9738        cx.notify();
9739    }
9740
9741    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
9742        if !self.mode.is_full() {
9743            return None;
9744        }
9745
9746        let theme_settings = theme_settings::ThemeSettings::get_global(cx);
9747        let theme = cx.theme();
9748        let accent_colors = theme.accents().clone();
9749        let editor_background = theme.colors().editor_background;
9750        let auto_accent_colors =
9751            AccentColors(crate::bracket_colorization::bracket_colorization_accents(
9752                &accent_colors.0,
9753                theme.appearance,
9754                editor_background,
9755            ));
9756
9757        let accent_overrides = theme_settings
9758            .theme_overrides
9759            .get(theme.name.as_ref())
9760            .map(|theme_style| &theme_style.accents)
9761            .into_iter()
9762            .flatten()
9763            .chain(
9764                theme_settings
9765                    .experimental_theme_overrides
9766                    .as_ref()
9767                    .map(|overrides| &overrides.accents)
9768                    .into_iter()
9769                    .flatten(),
9770            )
9771            .flat_map(|accent| accent.0.clone().map(SharedString::from))
9772            .collect();
9773
9774        Some(AccentData {
9775            colors: auto_accent_colors,
9776            overrides: accent_overrides,
9777        })
9778    }
9779
9780    fn fetch_applicable_language_settings(
9781        &self,
9782        cx: &App,
9783    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
9784        if !self.mode.is_full() {
9785            return HashMap::default();
9786        }
9787
9788        self.buffer().read(cx).all_buffers().into_iter().fold(
9789            HashMap::default(),
9790            |mut acc, buffer| {
9791                let buffer = buffer.read(cx);
9792                let language = buffer.language().map(|language| language.name());
9793                if let hash_map::Entry::Vacant(v) = acc.entry(language) {
9794                    v.insert(LanguageSettings::for_buffer(&buffer, cx).into_owned());
9795                }
9796                acc
9797            },
9798        )
9799    }
9800
9801    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
9802        let new_language_settings = self.fetch_applicable_language_settings(cx);
9803        let language_settings_changed = new_language_settings != self.applicable_language_settings;
9804        self.applicable_language_settings = new_language_settings;
9805
9806        let new_accents = self.fetch_accent_data(cx);
9807        let accents_changed = new_accents != self.accent_data;
9808        self.accent_data = new_accents;
9809
9810        if self.diagnostics_enabled() {
9811            let new_severity = EditorSettings::get_global(cx)
9812                .diagnostics_max_severity
9813                .unwrap_or(DiagnosticSeverity::Hint);
9814            self.set_max_diagnostics_severity(new_severity, cx);
9815        }
9816        self.refresh_runnables(None, window, cx);
9817        self.update_edit_prediction_settings(cx);
9818        self.refresh_edit_prediction(
9819            true,
9820            false,
9821            EditPredictionRequestTrigger::SettingsChanged,
9822            window,
9823            cx,
9824        );
9825        self.refresh_inline_values(cx);
9826
9827        let old_cursor_shape = self.cursor_shape;
9828        let old_breadcrumbs_visible = self.breadcrumbs_visible();
9829
9830        {
9831            let editor_settings = EditorSettings::get_global(cx);
9832            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
9833            if self.breadcrumbs_visibility.settings_visibility()
9834                != editor_settings.toolbar.breadcrumbs
9835            {
9836                self.breadcrumbs_visibility =
9837                    BreadcrumbsVisibility::new(editor_settings.toolbar.breadcrumbs);
9838            }
9839            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
9840        }
9841
9842        if old_cursor_shape != self.cursor_shape {
9843            cx.emit(EditorEvent::CursorShapeChanged);
9844        }
9845
9846        if old_breadcrumbs_visible != self.breadcrumbs_visible() {
9847            cx.emit(EditorEvent::BreadcrumbsChanged);
9848        }
9849
9850        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
9851            let project_settings = ProjectSettings::get_global(cx);
9852            (
9853                project_settings.session.restore_unsaved_buffers,
9854                project_settings.diagnostics.inline.enabled,
9855                project_settings.git.inline_blame.enabled,
9856            )
9857        };
9858        self.buffer_serialization = self
9859            .should_serialize_buffer()
9860            .then(|| BufferSerialization::new(restore_unsaved_buffers));
9861
9862        if self.mode.is_full() {
9863            if self.show_inline_diagnostics != show_inline_diagnostics {
9864                self.show_inline_diagnostics = show_inline_diagnostics;
9865                self.refresh_inline_diagnostics(false, window, cx);
9866            }
9867
9868            if self.git_blame_inline_enabled != inline_blame_enabled {
9869                self.toggle_git_blame_inline_internal(false, window, cx);
9870            }
9871
9872            let minimap_settings = EditorSettings::get_global(cx).minimap;
9873            if self.minimap_visibility != MinimapVisibility::Disabled {
9874                if self.minimap_visibility.settings_visibility()
9875                    != minimap_settings.minimap_enabled()
9876                {
9877                    self.set_minimap_visibility(
9878                        MinimapVisibility::for_mode(self.mode(), cx),
9879                        window,
9880                        cx,
9881                    );
9882                } else if let Some(minimap_entity) = self.minimap.as_ref() {
9883                    minimap_entity.update(cx, |minimap_editor, cx| {
9884                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
9885                    })
9886                }
9887            }
9888
9889            if language_settings_changed || accents_changed {
9890                self.colorize_brackets(true, cx);
9891            }
9892
9893            if language_settings_changed {
9894                self.clear_disabled_lsp_folding_ranges(window, cx);
9895                self.refresh_document_symbols(None, cx);
9896                self.refresh_outline_symbols_at_cursor(cx);
9897            }
9898
9899            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
9900                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
9901            }) {
9902                if !inlay_splice.is_empty() {
9903                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
9904                }
9905                self.refresh_document_colors(None, window, cx);
9906            }
9907
9908            let code_lens_inline =
9909                self.enable_code_lens && EditorSettings::get_global(cx).code_lens.inline();
9910            let was_inline = self.code_lens.is_some();
9911            if code_lens_inline != was_inline {
9912                self.toggle_code_lens(code_lens_inline, window, cx);
9913            }
9914
9915            let lsp_document_links_enabled = EditorSettings::get_global(cx).lsp_document_links;
9916            if lsp_document_links_enabled != self.lsp_document_links.enabled {
9917                self.lsp_document_links.enabled = lsp_document_links_enabled;
9918                if lsp_document_links_enabled {
9919                    self.refresh_document_links(None, cx);
9920                } else {
9921                    self.lsp_document_links.per_buffer.clear();
9922                    self.lsp_document_links.refresh_task = Task::ready(());
9923                }
9924            }
9925
9926            self.refresh_inlay_hints(
9927                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
9928                    self.selections.newest_anchor().head(),
9929                    &self.buffer.read(cx).snapshot(cx),
9930                    cx,
9931                )),
9932                cx,
9933            );
9934
9935            let new_semantic_token_rules = ProjectSettings::get_global(cx)
9936                .global_lsp_settings
9937                .semantic_token_rules
9938                .clone();
9939            let semantic_token_rules_changed = self
9940                .semantic_token_state
9941                .update_rules(new_semantic_token_rules);
9942            if language_settings_changed || semantic_token_rules_changed {
9943                self.invalidate_semantic_tokens(None);
9944                self.refresh_semantic_tokens(None, false, cx);
9945            }
9946        }
9947
9948        cx.notify();
9949    }
9950
9951    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
9952        if !self.mode.is_full() {
9953            return;
9954        }
9955
9956        let new_accents = self.fetch_accent_data(cx);
9957        if new_accents != self.accent_data {
9958            self.accent_data = new_accents;
9959            self.colorize_brackets(true, cx);
9960        }
9961
9962        self.invalidate_semantic_tokens(None);
9963        self.refresh_semantic_tokens(None, false, cx);
9964        self.refresh_outline_symbols_at_cursor(cx);
9965    }
9966
9967    pub fn set_searchable(&mut self, searchable: bool) {
9968        self.searchable = searchable;
9969    }
9970
9971    pub fn searchable(&self) -> bool {
9972        self.searchable
9973    }
9974
9975    pub fn open_excerpts_in_split(
9976        &mut self,
9977        _: &OpenExcerptsSplit,
9978        window: &mut Window,
9979        cx: &mut Context<Self>,
9980    ) {
9981        self.open_excerpts_common(None, true, window, cx)
9982    }
9983
9984    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
9985        self.open_excerpts_common(None, false, window, cx)
9986    }
9987
9988    pub(crate) fn open_excerpts_common(
9989        &mut self,
9990        jump_data: Option<JumpData>,
9991        split: bool,
9992        window: &mut Window,
9993        cx: &mut Context<Self>,
9994    ) {
9995        if self.buffer.read(cx).is_singleton() {
9996            cx.propagate();
9997            return;
9998        }
9999
10000        let mut new_selections_by_buffer = HashMap::default();
10001        match &jump_data {
10002            Some(JumpData::MultiBufferPoint {
10003                anchor,
10004                position,
10005                line_offset_from_top,
10006            }) => {
10007                if let Some(buffer) = self.buffer.read(cx).buffer(anchor.buffer_id) {
10008                    let buffer_snapshot = buffer.read(cx).snapshot();
10009                    let jump_to_point = if buffer_snapshot.can_resolve(&anchor) {
10010                        language::ToPoint::to_point(anchor, &buffer_snapshot)
10011                    } else {
10012                        buffer_snapshot.clip_point(*position, Bias::Left)
10013                    };
10014                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
10015                    new_selections_by_buffer.insert(
10016                        buffer,
10017                        (
10018                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
10019                            Some(*line_offset_from_top),
10020                        ),
10021                    );
10022                }
10023            }
10024            Some(JumpData::MultiBufferRow {
10025                row,
10026                line_offset_from_top,
10027            }) => {
10028                let point = MultiBufferPoint::new(row.0, 0);
10029                if let Some((buffer, buffer_point)) =
10030                    self.buffer.read(cx).point_to_buffer_point(point, cx)
10031                {
10032                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
10033                    new_selections_by_buffer
10034                        .entry(buffer)
10035                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
10036                        .0
10037                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
10038                }
10039            }
10040            None => {
10041                let selections = self
10042                    .selections
10043                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
10044                let multi_buffer = self.buffer.read(cx);
10045                let multi_buffer_snapshot = multi_buffer.snapshot(cx);
10046                for selection in selections {
10047                    for (snapshot, range, anchor) in multi_buffer_snapshot
10048                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
10049                    {
10050                        if let Some((text_anchor, _)) = anchor.and_then(|anchor| {
10051                            multi_buffer_snapshot.anchor_to_buffer_anchor(anchor)
10052                        }) {
10053                            let Some(buffer_handle) = multi_buffer.buffer(text_anchor.buffer_id)
10054                            else {
10055                                continue;
10056                            };
10057                            let offset = text::ToOffset::to_offset(
10058                                &text_anchor,
10059                                &buffer_handle.read(cx).snapshot(),
10060                            );
10061                            let range = BufferOffset(offset)..BufferOffset(offset);
10062                            new_selections_by_buffer
10063                                .entry(buffer_handle)
10064                                .or_insert((Vec::new(), None))
10065                                .0
10066                                .push(range)
10067                        } else {
10068                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
10069                            else {
10070                                continue;
10071                            };
10072                            new_selections_by_buffer
10073                                .entry(buffer_handle)
10074                                .or_insert((Vec::new(), None))
10075                                .0
10076                                .push(range)
10077                        }
10078                    }
10079                }
10080            }
10081        }
10082
10083        if self.delegate_open_excerpts {
10084            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
10085                .into_iter()
10086                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
10087                .collect();
10088            if !selections_by_buffer.is_empty() {
10089                cx.emit(EditorEvent::OpenExcerptsRequested {
10090                    selections_by_buffer,
10091                    split,
10092                });
10093            }
10094            return;
10095        }
10096
10097        let Some(workspace) = self.workspace() else {
10098            cx.propagate();
10099            return;
10100        };
10101
10102        new_selections_by_buffer
10103            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
10104
10105        if new_selections_by_buffer.is_empty() {
10106            return;
10107        }
10108
10109        Self::open_buffers_in_workspace(
10110            workspace.downgrade(),
10111            new_selections_by_buffer,
10112            split,
10113            window,
10114            cx,
10115        );
10116    }
10117
10118    pub(crate) fn open_buffers_in_workspace(
10119        workspace: WeakEntity<Workspace>,
10120        new_selections_by_buffer: HashMap<
10121            Entity<language::Buffer>,
10122            (Vec<Range<BufferOffset>>, Option<u32>),
10123        >,
10124        split: bool,
10125        window: &mut Window,
10126        cx: &mut App,
10127    ) {
10128        // We defer the pane interaction because we ourselves are a workspace item
10129        // and activating a new item causes the pane to call a method on us reentrantly,
10130        // which panics if we're on the stack.
10131        window.defer(cx, move |window, cx| {
10132            workspace
10133                .update(cx, |workspace, cx| {
10134                    let pane = if split {
10135                        workspace.adjacent_pane(window, cx)
10136                    } else {
10137                        workspace.active_pane().clone()
10138                    };
10139
10140                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
10141                        let buffer_read = buffer.read(cx);
10142                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
10143                            (true, project::File::from_dyn(Some(file)).is_some())
10144                        } else {
10145                            (false, false)
10146                        };
10147
10148                        // If project file is none workspace.open_project_item will fail to open the excerpt
10149                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
10150                        // so we check if there's a tab match in that case first
10151                        let editor = (!has_file || !is_project_file)
10152                            .then(|| {
10153                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
10154                                // so `workspace.open_project_item` will never find them, always opening a new editor.
10155                                // Instead, we try to activate the existing editor in the pane first.
10156                                let (editor, pane_item_index, pane_item_id) =
10157                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
10158                                        let editor = item.downcast::<Editor>()?;
10159                                        let singleton_buffer =
10160                                            editor.read(cx).buffer().read(cx).as_singleton()?;
10161                                        if singleton_buffer == buffer {
10162                                            Some((editor, i, item.item_id()))
10163                                        } else {
10164                                            None
10165                                        }
10166                                    })?;
10167                                pane.update(cx, |pane, cx| {
10168                                    pane.activate_item(pane_item_index, true, true, window, cx);
10169                                    if !PreviewTabsSettings::get_global(cx)
10170                                        .enable_preview_from_multibuffer
10171                                    {
10172                                        pane.unpreview_item_if_preview(pane_item_id);
10173                                    }
10174                                });
10175                                Some(editor)
10176                            })
10177                            .flatten()
10178                            .unwrap_or_else(|| {
10179                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
10180                                    .enable_keep_preview_on_code_navigation;
10181                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
10182                                    .enable_preview_from_multibuffer;
10183                                workspace.open_project_item::<Self>(
10184                                    pane.clone(),
10185                                    buffer,
10186                                    true,
10187                                    true,
10188                                    keep_old_preview,
10189                                    allow_new_preview,
10190                                    window,
10191                                    cx,
10192                                )
10193                            });
10194
10195                        editor.update(cx, |editor, cx| {
10196                            if has_file && !is_project_file {
10197                                editor.set_read_only(true);
10198                            }
10199                            let autoscroll = match scroll_offset {
10200                                Some(scroll_offset) => {
10201                                    Autoscroll::top_relative(scroll_offset as ScrollOffset)
10202                                }
10203                                None => Autoscroll::newest(),
10204                            };
10205                            let nav_history = editor.nav_history.take();
10206                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
10207                            let Some(buffer_snapshot) = multibuffer_snapshot.as_singleton() else {
10208                                return;
10209                            };
10210                            editor.change_selections(
10211                                SelectionEffects::scroll(autoscroll),
10212                                window,
10213                                cx,
10214                                |s| {
10215                                    s.select_ranges(ranges.into_iter().map(|range| {
10216                                        let range = buffer_snapshot.anchor_before(range.start)
10217                                            ..buffer_snapshot.anchor_after(range.end);
10218                                        multibuffer_snapshot
10219                                            .buffer_anchor_range_to_anchor_range(range)
10220                                            .unwrap()
10221                                    }));
10222                                },
10223                            );
10224                            editor.nav_history = nav_history;
10225                        });
10226                    }
10227                })
10228                .ok();
10229        });
10230    }
10231
10232    fn selection_replacement_ranges(
10233        &self,
10234        range: Range<MultiBufferOffsetUtf16>,
10235        cx: &mut App,
10236    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
10237        let selections = self
10238            .selections
10239            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
10240        let newest_selection = selections
10241            .iter()
10242            .max_by_key(|selection| selection.id)
10243            .unwrap();
10244        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
10245        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
10246        let snapshot = self.buffer.read(cx).read(cx);
10247        selections
10248            .into_iter()
10249            .map(|mut selection| {
10250                selection.start.0.0 =
10251                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
10252                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
10253                snapshot.clip_offset_utf16(selection.start, Bias::Left)
10254                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
10255            })
10256            .collect()
10257    }
10258
10259    fn report_editor_event(
10260        &self,
10261        reported_event: ReportEditorEvent,
10262        file_extension: Option<String>,
10263        cx: &App,
10264    ) {
10265        if cfg!(any(test, feature = "test-support")) {
10266            return;
10267        }
10268
10269        let Some(project) = &self.project else { return };
10270
10271        // If None, we are in a file without an extension
10272        let file = self
10273            .buffer
10274            .read(cx)
10275            .as_singleton()
10276            .and_then(|b| b.read(cx).file());
10277        let file_extension = file_extension.or(file
10278            .as_ref()
10279            .and_then(|file| Path::new(file.file_name(cx)).extension())
10280            .and_then(|e| e.to_str())
10281            .map(|a| a.to_string()));
10282
10283        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
10284            .map(|vim_mode| vim_mode.0)
10285            .unwrap_or(false);
10286
10287        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
10288        let copilot_enabled = edit_predictions_provider
10289            == language::language_settings::EditPredictionProvider::Copilot;
10290        let copilot_enabled_for_language = self
10291            .buffer
10292            .read(cx)
10293            .language_settings(cx)
10294            .show_edit_predictions;
10295
10296        let project = project.read(cx);
10297        let event_type = reported_event.event_type();
10298
10299        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
10300            telemetry::event!(
10301                event_type,
10302                type = if auto_saved {"autosave"} else {"manual"},
10303                file_extension,
10304                vim_mode,
10305                copilot_enabled,
10306                copilot_enabled_for_language,
10307                edit_predictions_provider,
10308                is_via_ssh = project.is_via_remote_server(),
10309            );
10310        } else {
10311            telemetry::event!(
10312                event_type,
10313                file_extension,
10314                vim_mode,
10315                copilot_enabled,
10316                copilot_enabled_for_language,
10317                edit_predictions_provider,
10318                is_via_ssh = project.is_via_remote_server(),
10319            );
10320        };
10321    }
10322
10323    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
10324    /// with each line being an array of {text, highlight} objects.
10325    fn copy_highlight_json(
10326        &mut self,
10327        _: &CopyHighlightJson,
10328        _: &mut Window,
10329        cx: &mut Context<Self>,
10330    ) {
10331        #[derive(Serialize)]
10332        struct Chunk<'a> {
10333            text: String,
10334            highlight: Option<&'a str>,
10335        }
10336
10337        let snapshot = self.buffer.read(cx).snapshot(cx);
10338        let mut selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
10339        let max_point = snapshot.max_point();
10340
10341        let range = if self.selections.line_mode() {
10342            selection.start = Point::new(selection.start.row, 0);
10343            selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
10344            selection.goal = SelectionGoal::None;
10345            selection.range()
10346        } else if selection.is_empty() {
10347            Point::new(0, 0)..max_point
10348        } else {
10349            selection.range()
10350        };
10351
10352        let chunks = snapshot.chunks(
10353            range,
10354            LanguageAwareStyling {
10355                tree_sitter: true,
10356                diagnostics: true,
10357            },
10358        );
10359        let mut lines = Vec::new();
10360        let mut line: VecDeque<Chunk> = VecDeque::new();
10361
10362        let Some(style) = self.style.as_ref() else {
10363            return;
10364        };
10365
10366        for chunk in chunks {
10367            let highlight = chunk
10368                .syntax_highlight_id
10369                .and_then(|id| style.syntax.get_capture_name(id));
10370
10371            let mut chunk_lines = chunk.text.split('\n').peekable();
10372            while let Some(text) = chunk_lines.next() {
10373                let mut merged_with_last_token = false;
10374                if let Some(last_token) = line.back_mut()
10375                    && last_token.highlight == highlight
10376                {
10377                    last_token.text.push_str(text);
10378                    merged_with_last_token = true;
10379                }
10380
10381                if !merged_with_last_token {
10382                    line.push_back(Chunk {
10383                        text: text.into(),
10384                        highlight,
10385                    });
10386                }
10387
10388                if chunk_lines.peek().is_some() {
10389                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
10390                        line.pop_front();
10391                    }
10392                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
10393                        line.pop_back();
10394                    }
10395
10396                    lines.push(mem::take(&mut line));
10397                }
10398            }
10399        }
10400
10401        if line.iter().any(|chunk| !chunk.text.is_empty()) {
10402            lines.push(line);
10403        }
10404
10405        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
10406            return;
10407        };
10408        cx.write_to_clipboard(ClipboardItem::new_string(lines));
10409    }
10410
10411    pub fn open_context_menu(
10412        &mut self,
10413        _: &OpenContextMenu,
10414        window: &mut Window,
10415        cx: &mut Context<Self>,
10416    ) {
10417        self.request_autoscroll(Autoscroll::newest(), cx);
10418        let position = self
10419            .selections
10420            .newest_display(&self.display_snapshot(cx))
10421            .start;
10422        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
10423    }
10424
10425    pub fn is_focused(&self, window: &Window) -> bool {
10426        self.focus_handle.is_focused(window)
10427    }
10428
10429    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10430        cx.emit(EditorEvent::Focused);
10431
10432        if let Some(descendant) = self
10433            .last_focused_descendant
10434            .take()
10435            .and_then(|descendant| descendant.upgrade())
10436        {
10437            window.focus(&descendant, cx);
10438        } else {
10439            if let Some(blame) = self.blame.as_ref() {
10440                blame.update(cx, GitBlame::focus)
10441            }
10442
10443            self.blink_manager.update(cx, BlinkManager::enable);
10444            self.show_cursor_names(window, cx);
10445            self.buffer.update(cx, |buffer, cx| {
10446                buffer.finalize_last_transaction(cx);
10447                if self.leader_id.is_none() {
10448                    buffer.set_active_selections(
10449                        &self.selections.disjoint_anchors_arc(),
10450                        self.selections.line_mode(),
10451                        self.cursor_shape,
10452                        cx,
10453                    );
10454                }
10455            });
10456
10457            if cx.is_cursor_visible()
10458                && let Some(position_map) = self.last_position_map.clone()
10459            {
10460                EditorElement::mouse_moved(
10461                    self,
10462                    &MouseMoveEvent {
10463                        position: window.mouse_position(),
10464                        pressed_button: None,
10465                        modifiers: window.modifiers(),
10466                    },
10467                    &position_map,
10468                    None,
10469                    window,
10470                    cx,
10471                );
10472            }
10473        }
10474    }
10475
10476    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
10477        cx.emit(EditorEvent::FocusedIn)
10478    }
10479
10480    fn handle_focus_out(
10481        &mut self,
10482        event: FocusOutEvent,
10483        _window: &mut Window,
10484        cx: &mut Context<Self>,
10485    ) {
10486        if event.blurred != self.focus_handle {
10487            self.last_focused_descendant = Some(event.blurred);
10488        }
10489        self.selection_drag_state = SelectionDragState::None;
10490        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
10491    }
10492
10493    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10494        self.blink_manager.update(cx, BlinkManager::disable);
10495        self.buffer
10496            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
10497
10498        if let Some(blame) = self.blame.as_ref() {
10499            blame.update(cx, GitBlame::blur)
10500        }
10501        if !self.hover_state.focused(window, cx) {
10502            hide_hover(self, cx);
10503        }
10504        if !self
10505            .context_menu
10506            .borrow()
10507            .as_ref()
10508            .is_some_and(|context_menu| context_menu.focused(window, cx))
10509        {
10510            self.hide_context_menu(window, cx);
10511        }
10512        self.take_active_edit_prediction(true, cx);
10513        cx.emit(EditorEvent::Blurred);
10514        cx.notify();
10515    }
10516
10517    pub fn register_action_renderer(
10518        &mut self,
10519        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
10520    ) -> Subscription {
10521        let id = self.next_editor_action_id.post_inc();
10522        self.editor_actions
10523            .borrow_mut()
10524            .insert(id, Box::new(listener));
10525
10526        let editor_actions = self.editor_actions.clone();
10527        Subscription::new(move || {
10528            editor_actions.borrow_mut().remove(&id);
10529        })
10530    }
10531
10532    pub fn register_action<A: Action>(
10533        &mut self,
10534        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
10535    ) -> Subscription {
10536        let id = self.next_editor_action_id.post_inc();
10537        let listener = Arc::new(listener);
10538        self.editor_actions.borrow_mut().insert(
10539            id,
10540            Box::new(move |_, window, _| {
10541                let listener = listener.clone();
10542                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
10543                    let action = action.downcast_ref().unwrap();
10544                    if phase == DispatchPhase::Bubble {
10545                        listener(action, window, cx)
10546                    }
10547                })
10548            }),
10549        );
10550
10551        let editor_actions = self.editor_actions.clone();
10552        Subscription::new(move || {
10553            editor_actions.borrow_mut().remove(&id);
10554        })
10555    }
10556
10557    pub fn file_header_size(&self) -> u32 {
10558        FILE_HEADER_HEIGHT
10559    }
10560
10561    pub fn restore(
10562        &mut self,
10563        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
10564        window: &mut Window,
10565        cx: &mut Context<Self>,
10566    ) {
10567        self.buffer().update(cx, |multi_buffer, cx| {
10568            for (buffer_id, changes) in revert_changes {
10569                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
10570                    buffer.update(cx, |buffer, cx| {
10571                        buffer.edit(
10572                            changes
10573                                .into_iter()
10574                                .map(|(range, text)| (range, text.to_string())),
10575                            None,
10576                            cx,
10577                        );
10578                    });
10579                }
10580            }
10581        });
10582        let selections = self
10583            .selections
10584            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
10585        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
10586            s.select(selections);
10587        });
10588    }
10589
10590    pub fn to_pixel_point(
10591        &mut self,
10592        source: Anchor,
10593        editor_snapshot: &EditorSnapshot,
10594        window: &mut Window,
10595        cx: &mut App,
10596    ) -> Option<gpui::Point<Pixels>> {
10597        let source_point = source.to_display_point(editor_snapshot);
10598        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
10599    }
10600
10601    pub fn display_to_pixel_point(
10602        &mut self,
10603        source: DisplayPoint,
10604        editor_snapshot: &EditorSnapshot,
10605        window: &mut Window,
10606        cx: &mut App,
10607    ) -> Option<gpui::Point<Pixels>> {
10608        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
10609        let text_layout_details = self.text_layout_details(window, cx);
10610        let mut scroll_top = text_layout_details
10611            .scroll_anchor
10612            .scroll_position(editor_snapshot)
10613            .y;
10614        if !line_height.is_zero() {
10615            scroll_top =
10616                window.pixel_snap_f64(scroll_top * f64::from(line_height)) / f64::from(line_height);
10617        }
10618
10619        if source.row().as_f64() < scroll_top.floor() {
10620            return None;
10621        }
10622        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
10623        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
10624        Some(gpui::Point::new(source_x, source_y))
10625    }
10626
10627    pub fn register_addon<T: Addon>(&mut self, instance: T) {
10628        if self.mode.is_minimap() {
10629            return;
10630        }
10631        self.addons
10632            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
10633    }
10634
10635    pub fn unregister_addon<T: Addon>(&mut self) {
10636        self.addons.remove(&std::any::TypeId::of::<T>());
10637    }
10638
10639    pub fn addon<T: Addon>(&self) -> Option<&T> {
10640        let type_id = std::any::TypeId::of::<T>();
10641        self.addons
10642            .get(&type_id)
10643            .and_then(|item| item.to_any().downcast_ref::<T>())
10644    }
10645
10646    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
10647        let type_id = std::any::TypeId::of::<T>();
10648        self.addons
10649            .get_mut(&type_id)
10650            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
10651    }
10652
10653    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
10654        let text_layout_details = self.text_layout_details(window, cx);
10655        let style = &text_layout_details.editor_style;
10656        let font_id = window.text_system().resolve_font(&style.text.font());
10657        let font_size = style.text.font_size.to_pixels(window.rem_size());
10658        let line_height = style.text.line_height_in_pixels(window.rem_size());
10659        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
10660        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
10661
10662        CharacterDimensions {
10663            em_width,
10664            em_advance,
10665            line_height,
10666        }
10667    }
10668
10669    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
10670        self.load_diff_task.clone()
10671    }
10672
10673    fn read_metadata_from_db(
10674        &mut self,
10675        item_id: u64,
10676        workspace_id: WorkspaceId,
10677        window: &mut Window,
10678        cx: &mut Context<Editor>,
10679    ) {
10680        if self.buffer_kind(cx) == ItemBufferKind::Singleton
10681            && !self.mode.is_minimap()
10682            && WorkspaceSettings::get(None, cx).restore_on_startup
10683                != RestoreOnStartupBehavior::EmptyTab
10684        {
10685            let buffer_snapshot = OnceCell::new();
10686
10687            // Get file path for path-based fold lookup
10688            let file_path: Option<Arc<Path>> =
10689                self.buffer().read(cx).as_singleton().and_then(|buffer| {
10690                    project::File::from_dyn(buffer.read(cx).file())
10691                        .map(|file| Arc::from(file.abs_path(cx)))
10692                });
10693
10694            // Try file_folds (path-based) first, fallback to editor_folds (migration)
10695            let db = EditorDb::global(cx);
10696            let (folds, needs_migration) = if let Some(ref path) = file_path {
10697                if let Some(folds) = db.get_file_folds(workspace_id, path).log_err()
10698                    && !folds.is_empty()
10699                {
10700                    (Some(folds), false)
10701                } else if let Some(folds) = db.get_editor_folds(item_id, workspace_id).log_err()
10702                    && !folds.is_empty()
10703                {
10704                    // Found old editor_folds data, will migrate to file_folds
10705                    (Some(folds), true)
10706                } else {
10707                    (None, false)
10708                }
10709            } else {
10710                // No file path, try editor_folds as fallback
10711                let folds = db.get_editor_folds(item_id, workspace_id).log_err();
10712                (folds.filter(|f| !f.is_empty()), false)
10713            };
10714
10715            if let Some(folds) = folds {
10716                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
10717                let snapshot_len = snapshot.len().0;
10718
10719                // Helper: search for fingerprint in buffer, return offset if found
10720                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
10721                    // Ensure we start at a character boundary (defensive)
10722                    let search_start = snapshot
10723                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
10724                        .0;
10725                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
10726
10727                    let mut byte_offset = search_start;
10728                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
10729                        if byte_offset > search_end {
10730                            break;
10731                        }
10732                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
10733                            return Some(byte_offset);
10734                        }
10735                        byte_offset += ch.len_utf8();
10736                    }
10737                    None
10738                };
10739
10740                // Track search position to handle duplicate fingerprints correctly.
10741                // Folds are stored in document order, so we advance after each match.
10742                let mut search_start = 0usize;
10743
10744                // Collect db_folds for migration (only folds with valid fingerprints)
10745                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
10746
10747                let valid_folds: Vec<_> = folds
10748                    .into_iter()
10749                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
10750                        // Skip folds without fingerprints (old data before migration)
10751                        let sfp = start_fp?;
10752                        let efp = end_fp?;
10753                        let efp_len = efp.len();
10754
10755                        // Fast path: check if fingerprints match at stored offsets
10756                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
10757                        let start_matches = stored_start < snapshot_len
10758                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
10759                        let efp_check_pos = stored_end.saturating_sub(efp_len);
10760                        let end_matches = efp_check_pos >= stored_start
10761                            && stored_end <= snapshot_len
10762                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
10763
10764                        let (new_start, new_end) = if start_matches && end_matches {
10765                            // Offsets unchanged, use stored values
10766                            (stored_start, stored_end)
10767                        } else if sfp == efp {
10768                            // Short fold: identical fingerprints can only match once per search
10769                            // Use stored fold length to compute new_end
10770                            let new_start = find_fingerprint(&sfp, search_start)?;
10771                            let fold_len = stored_end - stored_start;
10772                            let new_end = new_start + fold_len;
10773                            (new_start, new_end)
10774                        } else {
10775                            // Slow path: search for fingerprints in buffer
10776                            let new_start = find_fingerprint(&sfp, search_start)?;
10777                            // Search for end_fp after start, then add efp_len to get actual fold end
10778                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
10779                            let new_end = efp_pos + efp_len;
10780                            (new_start, new_end)
10781                        };
10782
10783                        // Advance search position for next fold
10784                        search_start = new_end;
10785
10786                        // Validate fold makes sense (end must be after start)
10787                        if new_end <= new_start {
10788                            return None;
10789                        }
10790
10791                        // Collect for migration if needed
10792                        if needs_migration {
10793                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
10794                        }
10795
10796                        Some(
10797                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
10798                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
10799                        )
10800                    })
10801                    .collect();
10802
10803                if !valid_folds.is_empty() {
10804                    self.fold_ranges(valid_folds, false, window, cx);
10805
10806                    // Migrate from editor_folds to file_folds if we loaded from old table
10807                    if needs_migration {
10808                        if let Some(ref path) = file_path {
10809                            let path = path.clone();
10810                            let db = EditorDb::global(cx);
10811                            cx.spawn(async move |_, _| {
10812                                db.save_file_folds(workspace_id, path, db_folds_for_migration)
10813                                    .await
10814                                    .log_err();
10815                            })
10816                            .detach();
10817                        }
10818                    }
10819                }
10820            }
10821
10822            if let Some(selections) = db.get_editor_selections(item_id, workspace_id).log_err()
10823                && !selections.is_empty()
10824            {
10825                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
10826                // skip adding the initial selection to selection history
10827                self.selection_history.mode = SelectionHistoryMode::Skipping;
10828                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
10829                    s.select_ranges(selections.into_iter().map(|(start, end)| {
10830                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
10831                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
10832                    }));
10833                });
10834                self.selection_history.mode = SelectionHistoryMode::Normal;
10835            };
10836        }
10837
10838        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
10839    }
10840
10841    pub(crate) fn lsp_data_enabled(&self) -> bool {
10842        self.enable_lsp_data && self.mode().is_full()
10843    }
10844
10845    fn update_lsp_data(
10846        &mut self,
10847        for_buffer: Option<BufferId>,
10848        window: &mut Window,
10849        cx: &mut Context<'_, Self>,
10850    ) {
10851        if !self.lsp_data_enabled() {
10852            return;
10853        }
10854
10855        if let Some(buffer_id) = for_buffer {
10856            self.pull_diagnostics(buffer_id, window, cx);
10857        }
10858        self.refresh_semantic_tokens(for_buffer, false, cx);
10859        self.refresh_document_colors(for_buffer, window, cx);
10860        self.refresh_document_links(for_buffer, cx);
10861        self.refresh_folding_ranges(for_buffer, window, cx);
10862        self.refresh_code_lenses(for_buffer, window, cx);
10863        self.refresh_document_symbols(for_buffer, cx);
10864    }
10865
10866    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
10867        if !self.lsp_data_enabled() {
10868            return;
10869        }
10870        let visible_buffers: Vec<_> = self
10871            .visible_buffers(cx)
10872            .into_iter()
10873            .filter(|buffer| self.is_lsp_relevant(buffer.read(cx).file(), cx))
10874            .collect();
10875        for visible_buffer in visible_buffers {
10876            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
10877        }
10878    }
10879
10880    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
10881        if !self.lsp_data_enabled() {
10882            return;
10883        }
10884
10885        if !self.registered_buffers.contains_key(&buffer_id)
10886            && let Some(project) = self.project.as_ref()
10887        {
10888            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
10889                project.update(cx, |project, cx| {
10890                    self.registered_buffers.insert(
10891                        buffer_id,
10892                        project.register_buffer_with_language_servers(&buffer, cx),
10893                    );
10894                });
10895            } else {
10896                self.registered_buffers.remove(&buffer_id);
10897            }
10898        }
10899    }
10900
10901    fn create_style(&self, cx: &App) -> EditorStyle {
10902        let settings = ThemeSettings::get_global(cx);
10903
10904        let mut text_style = match self.mode {
10905            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
10906                color: cx.theme().colors().editor_foreground,
10907                font_family: settings.ui_font.family.clone(),
10908                font_features: settings.ui_font.features.clone(),
10909                font_fallbacks: settings.ui_font.fallbacks.clone(),
10910                font_size: rems(0.875).into(),
10911                font_weight: settings.ui_font.weight,
10912                line_height: relative(settings.buffer_line_height.value()),
10913                ..Default::default()
10914            },
10915            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
10916                color: cx.theme().colors().editor_foreground,
10917                font_family: settings.buffer_font.family.clone(),
10918                font_features: settings.buffer_font.features.clone(),
10919                font_fallbacks: settings.buffer_font.fallbacks.clone(),
10920                font_size: settings.buffer_font_size(cx).into(),
10921                font_weight: settings.buffer_font.weight,
10922                line_height: relative(settings.buffer_line_height.value()),
10923                ..Default::default()
10924            },
10925        };
10926        if let Some(text_style_refinement) = &self.text_style_refinement {
10927            text_style.refine(text_style_refinement)
10928        }
10929
10930        let background = match self.mode {
10931            EditorMode::SingleLine => cx.theme().system().transparent,
10932            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
10933            EditorMode::Full { .. } => cx.theme().colors().editor_background,
10934            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
10935        };
10936
10937        EditorStyle {
10938            background,
10939            border: cx.theme().colors().border,
10940            local_player: cx.theme().players().local(),
10941            text: text_style,
10942            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
10943            syntax: cx.theme().syntax().clone(),
10944            status: cx.theme().status().clone(),
10945            inlay_hints_style: make_inlay_hints_style(cx),
10946            edit_prediction_styles: make_suggestion_styles(cx),
10947            unnecessary_code_fade: settings.unnecessary_code_fade,
10948            show_underlines: self.diagnostics_enabled(),
10949        }
10950    }
10951
10952    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<HighlightedText>> {
10953        let multi_buffer = self.buffer().read(cx);
10954        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
10955        let mut breadcrumbs = if let Some(buffer) = multi_buffer.as_singleton() {
10956            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
10957                buffer
10958                    .read(cx)
10959                    .snapshot()
10960                    .resolve_file_path(
10961                        self.project
10962                            .as_ref()
10963                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
10964                            .unwrap_or_default(),
10965                        cx,
10966                    )
10967                    .unwrap_or_else(|| multi_buffer.title(cx).to_string())
10968            });
10969            vec![HighlightedText {
10970                text: text.into(),
10971                highlights: vec![],
10972            }]
10973        } else {
10974            Vec::new()
10975        };
10976
10977        if let Some((buffer_id, symbols)) = self.outline_symbols_at_cursor.as_ref()
10978            && multi_buffer.buffer(*buffer_id).is_some()
10979        {
10980            breadcrumbs.extend(symbols.iter().map(|symbol| HighlightedText {
10981                text: symbol.text.clone(),
10982                highlights: symbol.highlight_ranges.clone(),
10983            }));
10984        }
10985
10986        if breadcrumbs.is_empty() {
10987            None
10988        } else {
10989            Some(breadcrumbs)
10990        }
10991    }
10992
10993    fn disable_lsp_data(&mut self) {
10994        self.enable_lsp_data = false;
10995    }
10996
10997    fn disable_runnables(&mut self) {
10998        self.enable_runnables = false;
10999    }
11000
11001    pub fn disable_code_lens(&mut self, cx: &mut Context<Self>) {
11002        self.enable_code_lens = false;
11003        self.clear_code_lenses(cx);
11004    }
11005
11006    pub fn disable_mouse_wheel_zoom(&mut self) {
11007        self.enable_mouse_wheel_zoom = false;
11008    }
11009
11010    fn update_data_on_scroll(
11011        &mut self,
11012        debounce: bool,
11013        window: &mut Window,
11014        cx: &mut Context<'_, Self>,
11015    ) {
11016        if debounce {
11017            self.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
11018                cx.background_executor()
11019                    .timer(Duration::from_millis(50))
11020                    .await;
11021                editor
11022                    .update_in(cx, |editor, window, cx| {
11023                        editor.do_update_data_on_scroll(window, cx);
11024                    })
11025                    .ok();
11026            });
11027        } else {
11028            self.post_scroll_update = Task::ready(());
11029            self.do_update_data_on_scroll(window, cx);
11030        }
11031    }
11032
11033    fn do_update_data_on_scroll(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) {
11034        self.register_visible_buffers(cx);
11035        self.colorize_brackets(false, cx);
11036        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11037        self.resolve_visible_code_lenses(cx);
11038
11039        if !self.buffer().read(cx).is_singleton() || self.needs_initial_data_update {
11040            self.needs_initial_data_update = false;
11041            self.update_lsp_data(None, window, cx);
11042            self.refresh_runnables(None, window, cx);
11043        }
11044    }
11045
11046    /// Returns the current cursor's vertical offset, in display rows, from the
11047    /// top of the visible viewport.
11048    /// Returns `None` if the cursor is not currently on screen.
11049    pub fn cursor_top_offset(&self, cx: &mut Context<Self>) -> Option<ScrollOffset> {
11050        let visible = self.visible_line_count()?;
11051        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11052        let scroll_top = self.scroll_manager.scroll_position(&display_map, cx).y;
11053        let cursor_display_row = self
11054            .selections
11055            .newest::<Point>(&display_map)
11056            .head()
11057            .to_display_point(&display_map)
11058            .row()
11059            .as_f64();
11060
11061        match cursor_display_row - scroll_top {
11062            offset if offset < 0.0 || offset >= visible => None,
11063            offset => Some(offset),
11064        }
11065    }
11066}
11067
11068fn process_completion_for_edit(
11069    completion: &Completion,
11070    intent: CompletionIntent,
11071    buffer: &Entity<Buffer>,
11072    cursor_position: &text::Anchor,
11073    cx: &mut Context<Editor>,
11074) -> CompletionEdit {
11075    let buffer = buffer.read(cx);
11076    let buffer_snapshot = buffer.snapshot();
11077    let (snippet, new_text) = if completion.is_snippet() {
11078        let mut snippet_source = completion.new_text.clone();
11079        // Workaround for typescript language server issues so that methods don't expand within
11080        // strings and functions with type expressions. The previous point is used because the query
11081        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
11082        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
11083        let previous_point = if previous_point.column > 0 {
11084            cursor_position.to_previous_offset(&buffer_snapshot)
11085        } else {
11086            cursor_position.to_offset(&buffer_snapshot)
11087        };
11088        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
11089            && scope.prefers_label_for_snippet_in_completion()
11090            && let Some(label) = completion.label()
11091            && matches!(
11092                completion.kind(),
11093                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
11094            )
11095        {
11096            snippet_source = label;
11097        }
11098        match Snippet::parse(&snippet_source).log_err() {
11099            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
11100            None => (None, completion.new_text.clone()),
11101        }
11102    } else {
11103        (None, completion.new_text.clone())
11104    };
11105
11106    let mut range_to_replace = {
11107        let replace_range = &completion.replace_range;
11108        if let CompletionSource::Lsp {
11109            insert_range: Some(insert_range),
11110            ..
11111        } = &completion.source
11112        {
11113            debug_assert_eq!(
11114                insert_range.start, replace_range.start,
11115                "insert_range and replace_range should start at the same position"
11116            );
11117            debug_assert!(
11118                insert_range
11119                    .start
11120                    .cmp(cursor_position, &buffer_snapshot)
11121                    .is_le(),
11122                "insert_range should start before or at cursor position"
11123            );
11124            debug_assert!(
11125                replace_range
11126                    .start
11127                    .cmp(cursor_position, &buffer_snapshot)
11128                    .is_le(),
11129                "replace_range should start before or at cursor position"
11130            );
11131
11132            let should_replace = match intent {
11133                CompletionIntent::CompleteWithInsert => false,
11134                CompletionIntent::CompleteWithReplace => true,
11135                CompletionIntent::Complete | CompletionIntent::Compose => {
11136                    let insert_mode = LanguageSettings::for_buffer(&buffer, cx)
11137                        .completions
11138                        .lsp_insert_mode;
11139                    match insert_mode {
11140                        LspInsertMode::Insert => false,
11141                        LspInsertMode::Replace => true,
11142                        LspInsertMode::ReplaceSubsequence => {
11143                            let mut text_to_replace = buffer.chars_for_range(
11144                                buffer.anchor_before(replace_range.start)
11145                                    ..buffer.anchor_after(replace_range.end),
11146                            );
11147                            let mut current_needle = text_to_replace.next();
11148                            for haystack_ch in completion.label.text.chars() {
11149                                if let Some(needle_ch) = current_needle
11150                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
11151                                {
11152                                    current_needle = text_to_replace.next();
11153                                }
11154                            }
11155                            current_needle.is_none()
11156                        }
11157                        LspInsertMode::ReplaceSuffix => {
11158                            if replace_range
11159                                .end
11160                                .cmp(cursor_position, &buffer_snapshot)
11161                                .is_gt()
11162                            {
11163                                let range_after_cursor = *cursor_position..replace_range.end;
11164                                let text_after_cursor = buffer
11165                                    .text_for_range(
11166                                        buffer.anchor_before(range_after_cursor.start)
11167                                            ..buffer.anchor_after(range_after_cursor.end),
11168                                    )
11169                                    .collect::<String>()
11170                                    .to_ascii_lowercase();
11171                                completion
11172                                    .label
11173                                    .text
11174                                    .to_ascii_lowercase()
11175                                    .ends_with(&text_after_cursor)
11176                            } else {
11177                                true
11178                            }
11179                        }
11180                    }
11181                }
11182            };
11183
11184            if should_replace {
11185                replace_range.clone()
11186            } else {
11187                insert_range.clone()
11188            }
11189        } else {
11190            replace_range.clone()
11191        }
11192    };
11193
11194    if range_to_replace
11195        .end
11196        .cmp(cursor_position, &buffer_snapshot)
11197        .is_lt()
11198    {
11199        range_to_replace.end = *cursor_position;
11200    }
11201
11202    CompletionEdit {
11203        new_text,
11204        replace_range: range_to_replace,
11205        snippet,
11206    }
11207}
11208
11209struct CompletionEdit {
11210    new_text: String,
11211    replace_range: Range<text::Anchor>,
11212    snippet: Option<Snippet>,
11213}
11214
11215pub trait CollaborationHub {
11216    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
11217    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
11218    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
11219}
11220
11221impl CollaborationHub for Entity<Project> {
11222    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
11223        self.read(cx).collaborators()
11224    }
11225
11226    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
11227        self.read(cx).user_store().read(cx).participant_indices()
11228    }
11229
11230    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
11231        let this = self.read(cx);
11232        let user_ids = this.collaborators().values().map(|c| c.user_id);
11233        this.user_store().read(cx).participant_names(user_ids, cx)
11234    }
11235}
11236
11237pub trait SemanticsProvider {
11238    fn hover(
11239        &self,
11240        buffer: &Entity<Buffer>,
11241        position: text::Anchor,
11242        cx: &mut App,
11243    ) -> Option<Task<Option<Vec<project::Hover>>>>;
11244
11245    fn inline_values(
11246        &self,
11247        buffer_handle: Entity<Buffer>,
11248        range: Range<text::Anchor>,
11249        cx: &mut App,
11250    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
11251
11252    fn applicable_inlay_chunks(
11253        &self,
11254        buffer: &Entity<Buffer>,
11255        ranges: &[Range<text::Anchor>],
11256        cx: &mut App,
11257    ) -> Vec<Range<BufferRow>>;
11258
11259    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
11260
11261    fn inlay_hints(
11262        &self,
11263        invalidate: InvalidationStrategy,
11264        buffer: Entity<Buffer>,
11265        ranges: Vec<Range<text::Anchor>>,
11266        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
11267        cx: &mut App,
11268    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
11269
11270    fn semantic_tokens(
11271        &self,
11272        buffer: Entity<Buffer>,
11273        cx: &mut App,
11274    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
11275
11276    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
11277
11278    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
11279
11280    fn document_highlights(
11281        &self,
11282        buffer: &Entity<Buffer>,
11283        position: text::Anchor,
11284        cx: &mut App,
11285    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
11286
11287    fn definitions(
11288        &self,
11289        buffer: &Entity<Buffer>,
11290        position: text::Anchor,
11291        kind: GotoDefinitionKind,
11292        cx: &mut App,
11293    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
11294
11295    fn range_for_rename(
11296        &self,
11297        buffer: &Entity<Buffer>,
11298        position: text::Anchor,
11299        cx: &mut App,
11300    ) -> Task<Result<Option<Range<text::Anchor>>>>;
11301
11302    fn perform_rename(
11303        &self,
11304        buffer: &Entity<Buffer>,
11305        position: text::Anchor,
11306        new_name: String,
11307        cx: &mut App,
11308    ) -> Option<Task<Result<ProjectTransaction>>>;
11309}
11310
11311impl SemanticsProvider for WeakEntity<Project> {
11312    fn hover(
11313        &self,
11314        buffer: &Entity<Buffer>,
11315        position: text::Anchor,
11316        cx: &mut App,
11317    ) -> Option<Task<Option<Vec<project::Hover>>>> {
11318        self.update(cx, |project, cx| project.hover(buffer, position, cx))
11319            .ok()
11320    }
11321
11322    fn document_highlights(
11323        &self,
11324        buffer: &Entity<Buffer>,
11325        position: text::Anchor,
11326        cx: &mut App,
11327    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
11328        self.update(cx, |project, cx| {
11329            project.document_highlights(buffer, position, cx)
11330        })
11331        .ok()
11332    }
11333
11334    fn definitions(
11335        &self,
11336        buffer: &Entity<Buffer>,
11337        position: text::Anchor,
11338        kind: GotoDefinitionKind,
11339        cx: &mut App,
11340    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
11341        self.update(cx, |project, cx| match kind {
11342            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
11343            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
11344            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
11345            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
11346        })
11347        .ok()
11348    }
11349
11350    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
11351        self.update(cx, |project, cx| {
11352            if project
11353                .active_debug_session(cx)
11354                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
11355            {
11356                return true;
11357            }
11358
11359            buffer.update(cx, |buffer, cx| {
11360                project.any_language_server_supports_inlay_hints(buffer, cx)
11361            })
11362        })
11363        .unwrap_or(false)
11364    }
11365
11366    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
11367        self.update(cx, |project, cx| {
11368            buffer.update(cx, |buffer, cx| {
11369                project.any_language_server_supports_semantic_tokens(buffer, cx)
11370            })
11371        })
11372        .unwrap_or(false)
11373    }
11374
11375    fn inline_values(
11376        &self,
11377        buffer_handle: Entity<Buffer>,
11378        range: Range<text::Anchor>,
11379        cx: &mut App,
11380    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
11381        self.update(cx, |project, cx| {
11382            let (session, active_stack_frame) = project.active_debug_session(cx)?;
11383
11384            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
11385        })
11386        .ok()
11387        .flatten()
11388    }
11389
11390    fn applicable_inlay_chunks(
11391        &self,
11392        buffer: &Entity<Buffer>,
11393        ranges: &[Range<text::Anchor>],
11394        cx: &mut App,
11395    ) -> Vec<Range<BufferRow>> {
11396        self.update(cx, |project, cx| {
11397            project.lsp_store().update(cx, |lsp_store, cx| {
11398                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
11399            })
11400        })
11401        .unwrap_or_default()
11402    }
11403
11404    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
11405        self.update(cx, |project, cx| {
11406            project.lsp_store().update(cx, |lsp_store, _| {
11407                lsp_store.invalidate_inlay_hints(for_buffers)
11408            })
11409        })
11410        .ok();
11411    }
11412
11413    fn inlay_hints(
11414        &self,
11415        invalidate: InvalidationStrategy,
11416        buffer: Entity<Buffer>,
11417        ranges: Vec<Range<text::Anchor>>,
11418        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
11419        cx: &mut App,
11420    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
11421        self.update(cx, |project, cx| {
11422            project.lsp_store().update(cx, |lsp_store, cx| {
11423                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
11424            })
11425        })
11426        .ok()
11427    }
11428
11429    fn semantic_tokens(
11430        &self,
11431        buffer: Entity<Buffer>,
11432        cx: &mut App,
11433    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
11434        self.update(cx, |this, cx| {
11435            this.lsp_store()
11436                .update(cx, |lsp_store, cx| lsp_store.semantic_tokens(buffer, cx))
11437        })
11438        .ok()
11439    }
11440
11441    fn range_for_rename(
11442        &self,
11443        buffer: &Entity<Buffer>,
11444        position: text::Anchor,
11445        cx: &mut App,
11446    ) -> Task<Result<Option<Range<text::Anchor>>>> {
11447        let Some(this) = self.upgrade() else {
11448            return Task::ready(Ok(None));
11449        };
11450
11451        this.update(cx, |project, cx| {
11452            let buffer = buffer.clone();
11453            let task = project.prepare_rename(buffer.clone(), position, cx);
11454            cx.spawn(async move |_, cx| {
11455                Ok(match task.await? {
11456                    PrepareRenameResponse::Success(range) => Some(range),
11457                    PrepareRenameResponse::InvalidPosition => None,
11458                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
11459                        // Fallback on using TreeSitter info to determine identifier range
11460                        buffer.read_with(cx, |buffer, _| {
11461                            let snapshot = buffer.snapshot();
11462                            let (range, kind) = snapshot.surrounding_word(position, None);
11463                            if kind != Some(CharKind::Word) {
11464                                return None;
11465                            }
11466                            Some(
11467                                snapshot.anchor_before(range.start)
11468                                    ..snapshot.anchor_after(range.end),
11469                            )
11470                        })
11471                    }
11472                })
11473            })
11474        })
11475    }
11476
11477    fn perform_rename(
11478        &self,
11479        buffer: &Entity<Buffer>,
11480        position: text::Anchor,
11481        new_name: String,
11482        cx: &mut App,
11483    ) -> Option<Task<Result<ProjectTransaction>>> {
11484        self.update(cx, |project, cx| {
11485            project.perform_rename(buffer.clone(), position, new_name, cx)
11486        })
11487        .ok()
11488    }
11489}
11490
11491fn consume_contiguous_rows(
11492    contiguous_row_selections: &mut Vec<Selection<Point>>,
11493    selection: &Selection<Point>,
11494    display_map: &DisplaySnapshot,
11495    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
11496) -> (MultiBufferRow, MultiBufferRow) {
11497    contiguous_row_selections.push(selection.clone());
11498    let start_row = starting_row(selection, display_map);
11499    let mut end_row = ending_row(selection, display_map);
11500
11501    while let Some(next_selection) = selections.peek() {
11502        if next_selection.start.row <= end_row.0 {
11503            end_row = ending_row(next_selection, display_map);
11504            contiguous_row_selections.push(selections.next().unwrap().clone());
11505        } else {
11506            break;
11507        }
11508    }
11509    (start_row, end_row)
11510}
11511
11512fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
11513    if selection.start.column > 0 {
11514        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
11515    } else {
11516        MultiBufferRow(selection.start.row)
11517    }
11518}
11519
11520fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
11521    if next_selection.end.column > 0 || next_selection.is_empty() {
11522        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
11523    } else {
11524        MultiBufferRow(next_selection.end.row)
11525    }
11526}
11527
11528impl EditorSnapshot {
11529    pub fn remote_selections_in_range<'a>(
11530        &'a self,
11531        range: &'a Range<Anchor>,
11532        collaboration_hub: &dyn CollaborationHub,
11533        cx: &'a App,
11534    ) -> impl 'a + Iterator<Item = RemoteSelection> {
11535        let participant_names = collaboration_hub.user_names(cx);
11536        let participant_indices = collaboration_hub.user_participant_indices(cx);
11537        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
11538        let collaborators_by_replica_id = collaborators_by_peer_id
11539            .values()
11540            .map(|collaborator| (collaborator.replica_id, collaborator))
11541            .collect::<HashMap<_, _>>();
11542        self.buffer_snapshot()
11543            .selections_in_range(range, false)
11544            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
11545                if replica_id == ReplicaId::AGENT {
11546                    Some(RemoteSelection {
11547                        replica_id,
11548                        selection,
11549                        cursor_shape,
11550                        line_mode,
11551                        collaborator_id: CollaboratorId::Agent,
11552                        user_name: Some("Agent".into()),
11553                        color: cx.theme().players().agent(),
11554                    })
11555                } else {
11556                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
11557                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
11558                    let user_name = participant_names.get(&collaborator.user_id).cloned();
11559                    Some(RemoteSelection {
11560                        replica_id,
11561                        selection,
11562                        cursor_shape,
11563                        line_mode,
11564                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
11565                        user_name,
11566                        color: if let Some(index) = participant_index {
11567                            cx.theme().players().color_for_participant(index.0)
11568                        } else {
11569                            cx.theme().players().absent()
11570                        },
11571                    })
11572                }
11573            })
11574    }
11575
11576    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
11577        self.display_snapshot
11578            .buffer_snapshot()
11579            .language_at(position)
11580    }
11581
11582    pub fn is_focused(&self) -> bool {
11583        self.is_focused
11584    }
11585
11586    pub fn placeholder_text(&self) -> Option<String> {
11587        self.placeholder_display_snapshot
11588            .as_ref()
11589            .map(|display_map| display_map.text())
11590    }
11591
11592    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
11593        self.scroll_anchor.scroll_position(&self.display_snapshot)
11594    }
11595
11596    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
11597        let digit_count = self.widest_line_number().ilog10() + 1;
11598        column_pixels(style, digit_count as usize, window)
11599    }
11600
11601    pub fn gutter_dimensions(
11602        &self,
11603        font_id: FontId,
11604        font_size: Pixels,
11605        style: &EditorStyle,
11606        window: &mut Window,
11607        cx: &App,
11608    ) -> GutterDimensions {
11609        if self.show_gutter
11610            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
11611            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
11612        {
11613            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
11614                matches!(
11615                    ProjectSettings::get_global(cx).git.git_gutter,
11616                    GitGutterSetting::TrackedFiles
11617                )
11618            });
11619            let gutter_settings = EditorSettings::get_global(cx).gutter;
11620            let show_line_numbers = self
11621                .show_line_numbers
11622                .unwrap_or(gutter_settings.line_numbers);
11623            let line_gutter_width = if show_line_numbers {
11624                // Avoid flicker-like gutter resizes when the line number gains another digit by
11625                // only resizing the gutter on files with > 10**min_line_number_digits lines.
11626                let min_width_for_number_on_gutter =
11627                    ch_advance * gutter_settings.min_line_number_digits as f32;
11628                self.max_line_number_width(style, window)
11629                    .max(min_width_for_number_on_gutter)
11630            } else {
11631                0.0.into()
11632            };
11633
11634            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
11635            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
11636            let show_bookmarks = self.show_bookmarks.unwrap_or(gutter_settings.bookmarks);
11637
11638            let git_blame_entries_width =
11639                self.git_blame_gutter_max_author_length
11640                    .map(|max_author_length| {
11641                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
11642                        const MAX_RELATIVE_TIMESTAMP: &str = "2 years, 11 months ago";
11643
11644                        let max_char_count = max_author_length.min(renderer.max_author_length())
11645                            + ::git::SHORT_SHA_LENGTH
11646                            + MAX_RELATIVE_TIMESTAMP.len();
11647
11648                        ch_advance * max_char_count
11649                            + renderer.blame_entry_non_text_width(window, cx)
11650                    });
11651
11652            let is_singleton = self.buffer_snapshot().is_singleton();
11653
11654            let left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO)
11655                + if !is_singleton {
11656                    ch_width * 4.0
11657                // runnables, breakpoints and bookmarks are shown in the same place
11658                // if all three are there only the runnable is shown
11659                } else if show_runnables || show_breakpoints || show_bookmarks {
11660                    ch_width * 3.0
11661                } else if show_git_gutter && show_line_numbers {
11662                    ch_width * 2.0
11663                } else if show_git_gutter || show_line_numbers {
11664                    ch_width
11665                } else {
11666                    px(0.)
11667                };
11668
11669            let shows_folds = is_singleton && gutter_settings.folds;
11670
11671            let right_padding = if shows_folds && show_line_numbers {
11672                ch_width * 4.0
11673            } else if shows_folds || (!is_singleton && show_line_numbers) {
11674                ch_width * 3.0
11675            } else if show_line_numbers {
11676                ch_width
11677            } else {
11678                px(0.)
11679            };
11680
11681            GutterDimensions {
11682                left_padding,
11683                right_padding,
11684                width: line_gutter_width + left_padding + right_padding,
11685                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
11686                git_blame_entries_width,
11687            }
11688        } else if self.offset_content {
11689            GutterDimensions::default_with_margin(font_id, font_size, cx)
11690        } else {
11691            GutterDimensions::default()
11692        }
11693    }
11694
11695    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
11696    ///
11697    /// This is positive if `base` is before `line`.
11698    fn relative_line_delta(
11699        &self,
11700        current_selection_head: DisplayRow,
11701        first_visible_row: DisplayRow,
11702        consider_wrapped_lines: bool,
11703    ) -> i64 {
11704        let current_selection_head = current_selection_head.as_display_point().to_point(self);
11705        let first_visible_row = first_visible_row.as_display_point().to_point(self);
11706
11707        if consider_wrapped_lines {
11708            let wrap_snapshot = self.wrap_snapshot();
11709            let base_wrap_row = wrap_snapshot
11710                .make_wrap_point(current_selection_head, Bias::Left)
11711                .row();
11712            let wrap_row = wrap_snapshot
11713                .make_wrap_point(first_visible_row, Bias::Left)
11714                .row();
11715
11716            wrap_row.0 as i64 - base_wrap_row.0 as i64
11717        } else {
11718            let fold_snapshot = self.fold_snapshot();
11719            let base_fold_row = fold_snapshot
11720                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
11721                .row();
11722            let fold_row = fold_snapshot
11723                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
11724                .row();
11725
11726            fold_row as i64 - base_fold_row as i64
11727        }
11728    }
11729
11730    /// Returns the unsigned relative line number to display for each row in `rows`.
11731    ///
11732    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
11733    pub fn calculate_relative_line_numbers(
11734        &self,
11735        rows: &Range<DisplayRow>,
11736        current_selection_head: DisplayRow,
11737        count_wrapped_lines: bool,
11738    ) -> HashMap<DisplayRow, u32> {
11739        let mut row_infos = self
11740            .row_infos(rows.start)
11741            .take(rows.len())
11742            .enumerate()
11743            .map(|(index, row_info)| (DisplayRow(rows.start.0 + index as u32), row_info))
11744            .filter(|(_row, row_info)| {
11745                row_info.buffer_row.is_some()
11746                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
11747            })
11748            .peekable();
11749
11750        // We find the first row that actually passes the filter and calculate its
11751        // delta independently. This ensures accuracy when scrolling, as the first
11752        // visible row in `rows` might be a wrap part that is filtered out, which
11753        // would otherwise offset the counter for subsequent lines if we used
11754        // `rows.start` as the base for enumeration.
11755        let Some((first_row, _)) = row_infos.peek() else {
11756            return HashMap::default();
11757        };
11758
11759        let mut current_delta =
11760            self.relative_line_delta(current_selection_head, *first_row, count_wrapped_lines);
11761
11762        row_infos
11763            .filter_map(|(row, row_info)| {
11764                let is_deleted = row_info
11765                    .diff_status
11766                    .is_some_and(|status| status.is_deleted());
11767
11768                if !self.number_deleted_lines && is_deleted {
11769                    // Even if we don't number this line, it still counts as a unit
11770                    // of distance for the relative numbers of lines below it.
11771                    current_delta += 1;
11772                    return None;
11773                }
11774
11775                // We want to ensure here that the current line has absolute
11776                // numbering, even if we are in a soft-wrapped line. With the
11777                // exception that if we are in a deleted line, we should number this
11778                // relative with 0, as otherwise it would have no line number at all
11779                let relative_line_number = current_delta.unsigned_abs() as u32;
11780                current_delta += 1;
11781
11782                (relative_line_number != 0 || is_deleted).then_some((row, relative_line_number))
11783            })
11784            .collect()
11785    }
11786}
11787
11788pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
11789    let font_size = style.text.font_size.to_pixels(window.rem_size());
11790    let layout = window.text_system().shape_line(
11791        SharedString::from(" ".repeat(column)),
11792        font_size,
11793        &[TextRun {
11794            len: column,
11795            font: style.text.font(),
11796            color: Hsla::default(),
11797            ..Default::default()
11798        }],
11799        None,
11800    );
11801
11802    layout.width
11803}
11804
11805impl Deref for EditorSnapshot {
11806    type Target = DisplaySnapshot;
11807
11808    fn deref(&self) -> &Self::Target {
11809        &self.display_snapshot
11810    }
11811}
11812
11813#[derive(Clone, Debug, PartialEq, Eq)]
11814pub enum EditorEvent {
11815    /// Emitted when the stored review comments change (added, removed, or updated).
11816    ReviewCommentsChanged {
11817        /// The new total count of review comments.
11818        total_count: usize,
11819    },
11820    InputIgnored {
11821        text: Arc<str>,
11822    },
11823    InputHandled {
11824        utf16_range_to_replace: Option<Range<isize>>,
11825        text: Arc<str>,
11826    },
11827    BufferRangesUpdated {
11828        buffer: Entity<Buffer>,
11829        path_key: PathKey,
11830        ranges: Vec<ExcerptRange<text::Anchor>>,
11831    },
11832    BuffersRemoved {
11833        removed_buffer_ids: Vec<BufferId>,
11834    },
11835    BuffersEdited {
11836        buffer_ids: Vec<BufferId>,
11837    },
11838    BufferFoldToggled {
11839        ids: Vec<BufferId>,
11840        folded: bool,
11841    },
11842    ExpandExcerptsRequested {
11843        excerpt_anchors: Vec<Anchor>,
11844        lines: u32,
11845        direction: ExpandExcerptDirection,
11846    },
11847    OpenExcerptsRequested {
11848        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
11849        split: bool,
11850    },
11851    /// Emitted when an underlying buffer changes, including edits made through another editor.
11852    BufferEdited,
11853    /// Emitted when this editor creates, undoes, or redoes an edit transaction.
11854    Edited {
11855        /// The transaction that changed the editor's buffer.
11856        transaction_id: clock::Lamport,
11857    },
11858    Reparsed(BufferId),
11859    Focused,
11860    FocusedIn,
11861    Blurred,
11862    DirtyChanged,
11863    Saved,
11864    TitleChanged,
11865    FileHandleChanged,
11866    SelectionsChanged {
11867        local: bool,
11868    },
11869    ScrollPositionChanged {
11870        local: bool,
11871        autoscroll: bool,
11872    },
11873    TransactionUndone {
11874        transaction_id: clock::Lamport,
11875    },
11876    TransactionBegun {
11877        transaction_id: clock::Lamport,
11878    },
11879    CursorShapeChanged,
11880    BreadcrumbsChanged,
11881    OutlineSymbolsChanged,
11882    PushedToNavHistory {
11883        anchor: Anchor,
11884        is_deactivate: bool,
11885    },
11886}
11887
11888impl EventEmitter<EditorEvent> for Editor {}
11889
11890impl Focusable for Editor {
11891    fn focus_handle(&self, _cx: &App) -> FocusHandle {
11892        self.focus_handle.clone()
11893    }
11894}
11895
11896impl Render for Editor {
11897    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
11898        EditorElement::new(&cx.entity(), self.create_style(cx))
11899    }
11900}
11901
11902trait SelectionExt {
11903    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
11904    fn spanned_rows(
11905        &self,
11906        include_end_if_at_line_start: bool,
11907        map: &DisplaySnapshot,
11908    ) -> Range<MultiBufferRow>;
11909}
11910
11911impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
11912    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
11913        let start = self
11914            .start
11915            .to_point(map.buffer_snapshot())
11916            .to_display_point(map);
11917        let end = self
11918            .end
11919            .to_point(map.buffer_snapshot())
11920            .to_display_point(map);
11921        if self.reversed {
11922            end..start
11923        } else {
11924            start..end
11925        }
11926    }
11927
11928    fn spanned_rows(
11929        &self,
11930        include_end_if_at_line_start: bool,
11931        map: &DisplaySnapshot,
11932    ) -> Range<MultiBufferRow> {
11933        let start = self.start.to_point(map.buffer_snapshot());
11934        let mut end = self.end.to_point(map.buffer_snapshot());
11935        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
11936            end.row -= 1;
11937        }
11938
11939        let buffer_start = map.prev_line_boundary(start).0;
11940        let buffer_end = map.next_line_boundary(end).0;
11941        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
11942    }
11943}
11944
11945impl<T: InvalidationRegion> InvalidationStack<T> {
11946    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
11947    where
11948        S: Clone + ToOffset,
11949    {
11950        while let Some(region) = self.last() {
11951            let all_selections_inside_invalidation_ranges =
11952                if selections.len() == region.ranges().len() {
11953                    selections
11954                        .iter()
11955                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
11956                        .all(|(selection, invalidation_range)| {
11957                            let head = selection.head().to_offset(buffer);
11958                            invalidation_range.start <= head && invalidation_range.end >= head
11959                        })
11960                } else {
11961                    false
11962                };
11963
11964            if all_selections_inside_invalidation_ranges {
11965                break;
11966            } else {
11967                self.pop();
11968            }
11969        }
11970    }
11971}
11972
11973#[derive(Clone)]
11974struct ErasedEditorImpl(Entity<Editor>);
11975
11976impl ui_input::ErasedEditor for ErasedEditorImpl {
11977    fn text(&self, cx: &App) -> String {
11978        self.0.read(cx).text(cx)
11979    }
11980
11981    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
11982        self.0.update(cx, |this, cx| {
11983            this.set_text(text, window, cx);
11984        })
11985    }
11986
11987    fn clear(&self, window: &mut Window, cx: &mut App) {
11988        self.0.update(cx, |this, cx| this.clear(window, cx));
11989    }
11990
11991    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
11992        self.0.update(cx, |this, cx| {
11993            this.set_placeholder_text(text, window, cx);
11994        });
11995    }
11996
11997    fn set_multiline(&self, max_lines: Option<usize>, _window: &mut Window, cx: &mut App) {
11998        self.0.update(cx, |this, cx| {
11999            if let Some(max_lines) = max_lines {
12000                this.set_mode(EditorMode::AutoHeight {
12001                    min_lines: 1,
12002                    max_lines: Some(max_lines),
12003                });
12004                this.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
12005            } else {
12006                this.set_mode(EditorMode::SingleLine);
12007            }
12008            cx.notify();
12009        });
12010    }
12011
12012    fn focus_handle(&self, cx: &App) -> FocusHandle {
12013        self.0.read(cx).focus_handle(cx)
12014    }
12015
12016    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
12017        let settings = ThemeSettings::get_global(cx);
12018        let theme_color = cx.theme().colors();
12019
12020        let text_style = TextStyle {
12021            font_family: settings.ui_font.family.clone(),
12022            font_features: settings.ui_font.features.clone(),
12023            font_size: rems(0.875).into(),
12024            font_weight: settings.ui_font.weight,
12025            font_style: FontStyle::Normal,
12026            line_height: relative(1.2),
12027            color: theme_color.text,
12028            ..Default::default()
12029        };
12030        let editor_style = EditorStyle {
12031            background: theme_color.ghost_element_background,
12032            local_player: cx.theme().players().local(),
12033            syntax: cx.theme().syntax().clone(),
12034            text: text_style,
12035            ..Default::default()
12036        };
12037        EditorElement::new(&self.0, editor_style).into_any()
12038    }
12039
12040    fn as_any(&self) -> &dyn Any {
12041        &self.0
12042    }
12043
12044    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
12045        self.0.update(cx, |editor, cx| {
12046            let editor_offset = editor.buffer().read(cx).len(cx);
12047            editor.change_selections(
12048                SelectionEffects::scroll(Autoscroll::Next),
12049                window,
12050                cx,
12051                |s| s.select_ranges(Some(editor_offset..editor_offset)),
12052            );
12053        });
12054    }
12055
12056    fn select_all(&self, window: &mut Window, cx: &mut App) {
12057        self.0.update(cx, |editor, cx| {
12058            editor.select_all(&Default::default(), window, cx);
12059        });
12060    }
12061
12062    fn subscribe(
12063        &self,
12064        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
12065        window: &mut Window,
12066        cx: &mut App,
12067    ) -> Subscription {
12068        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
12069            let event = match event {
12070                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
12071                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
12072                _ => return,
12073            };
12074            (callback)(event, window, cx);
12075        })
12076    }
12077
12078    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
12079        self.0.update(cx, |editor, cx| {
12080            editor.set_masked(masked, cx);
12081        });
12082    }
12083
12084    fn set_read_only(&self, read_only: bool, cx: &mut App) {
12085        self.0.update(cx, |editor, cx| {
12086            editor.set_read_only(read_only);
12087            cx.notify();
12088        });
12089    }
12090}
12091impl<T> Default for InvalidationStack<T> {
12092    fn default() -> Self {
12093        Self(Default::default())
12094    }
12095}
12096
12097impl<T> Deref for InvalidationStack<T> {
12098    type Target = Vec<T>;
12099
12100    fn deref(&self) -> &Self::Target {
12101        &self.0
12102    }
12103}
12104
12105impl<T> DerefMut for InvalidationStack<T> {
12106    fn deref_mut(&mut self) -> &mut Self::Target {
12107        &mut self.0
12108    }
12109}
12110
12111impl InvalidationRegion for SnippetState {
12112    fn ranges(&self) -> &[Range<Anchor>] {
12113        &self.ranges[self.active_index]
12114    }
12115}
12116
12117pub fn styled_runs_for_code_label<'a>(
12118    label: &'a CodeLabel,
12119    syntax_theme: &'a theme::SyntaxTheme,
12120    local_player: &'a theme::PlayerColor,
12121) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
12122    let fade_out = HighlightStyle {
12123        fade_out: Some(0.35),
12124        ..Default::default()
12125    };
12126
12127    if label.runs.is_empty() {
12128        let desc_start = label.filter_range.end;
12129        let fade_run =
12130            (desc_start < label.text.len()).then(|| (desc_start..label.text.len(), fade_out));
12131        return Either::Left(fade_run.into_iter());
12132    }
12133
12134    let mut prev_end = label.filter_range.end;
12135    Either::Right(
12136        label
12137            .runs
12138            .iter()
12139            .enumerate()
12140            .flat_map(move |(ix, (range, highlight_id))| {
12141                let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
12142                    HighlightStyle {
12143                        color: Some(local_player.cursor),
12144                        ..Default::default()
12145                    }
12146                } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
12147                    HighlightStyle {
12148                        background_color: Some(local_player.selection),
12149                        ..Default::default()
12150                    }
12151                } else if let Some(style) = syntax_theme.get(*highlight_id).cloned() {
12152                    style
12153                } else {
12154                    return Default::default();
12155                };
12156
12157                let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
12158                let muted_style = style.highlight(fade_out);
12159                if range.start >= label.filter_range.end {
12160                    if range.start > prev_end {
12161                        runs.push((prev_end..range.start, fade_out));
12162                    }
12163                    runs.push((range.clone(), muted_style));
12164                } else if range.end <= label.filter_range.end {
12165                    runs.push((range.clone(), style));
12166                } else {
12167                    runs.push((range.start..label.filter_range.end, style));
12168                    runs.push((label.filter_range.end..range.end, muted_style));
12169                }
12170                prev_end = cmp::max(prev_end, range.end);
12171
12172                if ix + 1 == label.runs.len() && label.text.len() > prev_end {
12173                    runs.push((prev_end..label.text.len(), fade_out));
12174                }
12175
12176                runs
12177            }),
12178    )
12179}
12180
12181pub trait RangeToAnchorExt: Sized {
12182    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
12183
12184    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
12185        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
12186        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
12187    }
12188}
12189
12190impl<T: ToOffset> RangeToAnchorExt for Range<T> {
12191    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
12192        let start_offset = self.start.to_offset(snapshot);
12193        let end_offset = self.end.to_offset(snapshot);
12194        if start_offset == end_offset {
12195            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
12196        } else {
12197            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
12198        }
12199    }
12200}
12201
12202pub trait RowExt {
12203    fn as_f64(&self) -> f64;
12204
12205    fn next_row(&self) -> Self;
12206
12207    fn previous_row(&self) -> Self;
12208
12209    fn minus(&self, other: Self) -> u32;
12210}
12211
12212impl RowExt for DisplayRow {
12213    fn as_f64(&self) -> f64 {
12214        self.0 as _
12215    }
12216
12217    fn next_row(&self) -> Self {
12218        Self(self.0 + 1)
12219    }
12220
12221    fn previous_row(&self) -> Self {
12222        Self(self.0.saturating_sub(1))
12223    }
12224
12225    fn minus(&self, other: Self) -> u32 {
12226        self.0 - other.0
12227    }
12228}
12229
12230impl RowExt for MultiBufferRow {
12231    fn as_f64(&self) -> f64 {
12232        self.0 as _
12233    }
12234
12235    fn next_row(&self) -> Self {
12236        Self(self.0 + 1)
12237    }
12238
12239    fn previous_row(&self) -> Self {
12240        Self(self.0.saturating_sub(1))
12241    }
12242
12243    fn minus(&self, other: Self) -> u32 {
12244        self.0 - other.0
12245    }
12246}
12247
12248trait RowRangeExt {
12249    type Row;
12250
12251    fn len(&self) -> usize;
12252
12253    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
12254}
12255
12256impl RowRangeExt for Range<MultiBufferRow> {
12257    type Row = MultiBufferRow;
12258
12259    fn len(&self) -> usize {
12260        (self.end.0 - self.start.0) as usize
12261    }
12262
12263    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
12264        (self.start.0..self.end.0).map(MultiBufferRow)
12265    }
12266}
12267
12268impl RowRangeExt for Range<DisplayRow> {
12269    type Row = DisplayRow;
12270
12271    fn len(&self) -> usize {
12272        (self.end.0 - self.start.0) as usize
12273    }
12274
12275    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
12276        (self.start.0..self.end.0).map(DisplayRow)
12277    }
12278}
12279
12280/// If select range has more than one line, we
12281/// just point the cursor to range.start.
12282fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
12283    if range.start.row == range.end.row {
12284        range
12285    } else {
12286        range.start..range.start
12287    }
12288}
12289
12290const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
12291
12292#[derive(Copy, Clone, Debug)]
12293enum BreakpointPromptEditAction {
12294    Log,
12295    Condition,
12296    HitCondition,
12297}
12298
12299type PromptEditorCallback = Box<dyn FnOnce(String, &mut Editor, &mut Context<Editor>) + 'static>;
12300
12301struct PromptEditor {
12302    pub(crate) prompt: Entity<Editor>,
12303    editor: WeakEntity<Editor>,
12304    confirm_callback: Option<PromptEditorCallback>,
12305    cancel_callback: Option<PromptEditorCallback>,
12306    block_ids: HashSet<CustomBlockId>,
12307    editor_margins: Arc<Mutex<EditorMargins>>,
12308    _subscriptions: Vec<Subscription>,
12309}
12310
12311impl PromptEditor {
12312    const MAX_LINES: u8 = 4;
12313
12314    fn new(
12315        editor: WeakEntity<Editor>,
12316        placeholder_text: &str,
12317        base_text: &str,
12318        window: &mut Window,
12319        cx: &mut Context<Self>,
12320    ) -> Self {
12321        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
12322        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12323
12324        let prompt = cx.new(|cx| {
12325            let mut prompt = Editor::new(
12326                EditorMode::AutoHeight {
12327                    min_lines: 1,
12328                    max_lines: Some(Self::MAX_LINES as usize),
12329                },
12330                buffer,
12331                None,
12332                window,
12333                cx,
12334            );
12335            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
12336            prompt.set_show_cursor_when_unfocused(false, cx);
12337            prompt.set_placeholder_text(placeholder_text, window, cx);
12338
12339            prompt
12340        });
12341
12342        Self {
12343            prompt,
12344            editor,
12345            confirm_callback: None,
12346            cancel_callback: None,
12347            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
12348            block_ids: Default::default(),
12349            _subscriptions: vec![],
12350        }
12351    }
12352
12353    fn on_confirm(mut self, confirm: PromptEditorCallback) -> Self {
12354        self.confirm_callback = Some(confirm);
12355        self
12356    }
12357
12358    fn on_cancel(mut self, cancel: PromptEditorCallback) -> Self {
12359        self.cancel_callback = Some(cancel);
12360        self
12361    }
12362
12363    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
12364        self.block_ids.extend(block_ids)
12365    }
12366
12367    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
12368        if let Some(editor) = self.editor.upgrade() {
12369            let message = self
12370                .prompt
12371                .read(cx)
12372                .buffer
12373                .read(cx)
12374                .as_singleton()
12375                .expect("A multi buffer in prompt isn't possible")
12376                .read(cx)
12377                .as_rope()
12378                .to_string();
12379
12380            editor.update(cx, |editor, cx| {
12381                if let Some(confirm) = self.confirm_callback.take() {
12382                    confirm(message, editor, cx);
12383                }
12384
12385                editor.remove_blocks(self.block_ids.clone(), None, cx);
12386                cx.focus_self(window);
12387            });
12388        }
12389    }
12390
12391    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
12392        self.editor
12393            .update(cx, |editor, cx| {
12394                let message = self
12395                    .prompt
12396                    .read(cx)
12397                    .buffer
12398                    .read(cx)
12399                    .as_singleton()
12400                    .expect("A multi buffer in prompt isn't possible")
12401                    .read(cx)
12402                    .as_rope()
12403                    .to_string();
12404
12405                if let Some(cancel) = self.cancel_callback.take() {
12406                    cancel(message, editor, cx);
12407                }
12408
12409                editor.remove_blocks(self.block_ids.clone(), None, cx);
12410                window.focus(&editor.focus_handle, cx);
12411            })
12412            .log_err();
12413    }
12414
12415    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
12416        let settings = ThemeSettings::get_global(cx);
12417        let text_style = TextStyle {
12418            color: if self.prompt.read(cx).read_only(cx) {
12419                cx.theme().colors().text_disabled
12420            } else {
12421                cx.theme().colors().text
12422            },
12423            font_family: settings.buffer_font.family.clone(),
12424            font_fallbacks: settings.buffer_font.fallbacks.clone(),
12425            font_size: settings.buffer_font_size(cx).into(),
12426            font_weight: settings.buffer_font.weight,
12427            line_height: relative(settings.buffer_line_height.value()),
12428            ..Default::default()
12429        };
12430        EditorElement::new(
12431            &self.prompt,
12432            EditorStyle {
12433                background: cx.theme().colors().editor_background,
12434                local_player: cx.theme().players().local(),
12435                text: text_style,
12436                ..Default::default()
12437            },
12438        )
12439    }
12440
12441    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
12442        let focus_handle = self.prompt.focus_handle(cx);
12443        IconButton::new("cancel", IconName::Close)
12444            .icon_color(Color::Muted)
12445            .shape(IconButtonShape::Square)
12446            .tooltip(move |_window, cx| {
12447                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
12448            })
12449            .on_click(cx.listener(|this, _, window, cx| {
12450                this.cancel(&menu::Cancel, window, cx);
12451            }))
12452    }
12453
12454    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
12455        let focus_handle = self.prompt.focus_handle(cx);
12456        IconButton::new("confirm", IconName::Return)
12457            .icon_color(Color::Muted)
12458            .shape(IconButtonShape::Square)
12459            .tooltip(move |_window, cx| {
12460                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
12461            })
12462            .on_click(cx.listener(|this, _, window, cx| {
12463                this.confirm(&menu::Confirm, window, cx);
12464            }))
12465    }
12466}
12467
12468impl Render for PromptEditor {
12469    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
12470        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
12471        let editor_margins = *self.editor_margins.lock();
12472        let gutter_dimensions = editor_margins.gutter;
12473        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
12474        let right_padding = editor_margins.right + px(9.);
12475        h_flex()
12476            .key_context("Editor")
12477            .bg(cx.theme().colors().editor_background)
12478            .border_y_1()
12479            .border_color(cx.theme().status().info_border)
12480            .size_full()
12481            .py(window.line_height() / 2.5)
12482            .pr(right_padding)
12483            .on_action(cx.listener(Self::confirm))
12484            .on_action(cx.listener(Self::cancel))
12485            .child(
12486                WithRemSize::new(ui_font_size)
12487                    .h_full()
12488                    .w(left_gutter_width)
12489                    .flex()
12490                    .flex_row()
12491                    .flex_shrink_0()
12492                    .items_center()
12493                    .justify_center()
12494                    .gap_1()
12495                    .child(self.render_close_button(cx)),
12496            )
12497            .child(
12498                h_flex()
12499                    .w_full()
12500                    .justify_between()
12501                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
12502                    .child(
12503                        WithRemSize::new(ui_font_size)
12504                            .flex()
12505                            .flex_row()
12506                            .items_center()
12507                            .child(self.render_confirm_button(cx)),
12508                    ),
12509            )
12510    }
12511}
12512
12513impl Focusable for PromptEditor {
12514    fn focus_handle(&self, cx: &App) -> FocusHandle {
12515        self.prompt.focus_handle(cx)
12516    }
12517}
12518
12519#[derive(Debug, Clone, Copy, PartialEq)]
12520pub struct LineHighlight {
12521    pub background: Background,
12522    pub border: Option<gpui::Hsla>,
12523    pub include_gutter: bool,
12524    pub type_id: Option<TypeId>,
12525}
12526
12527struct LineManipulationResult {
12528    pub new_text: String,
12529    pub line_count_before: usize,
12530    pub line_count_after: usize,
12531}
12532
12533pub fn multibuffer_context_lines(cx: &App) -> u32 {
12534    EditorSettings::try_get(cx)
12535        .map(|settings| settings.excerpt_context_lines)
12536        .unwrap_or(2)
12537        .min(32)
12538}
12539
Served at tenant.openagents/omega Member data and write actions are omitted.