editor.rs

    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;
   19pub mod display_map;
   20mod editor_settings;
   21mod element;
   22mod git;
   23mod highlight_matching_bracket;
   24mod hover_links;
   25pub mod hover_popover;
   26mod indent_guides;
   27mod inlays;
   28pub mod items;
   29mod jsx_tag_auto_close;
   30mod linked_editing_ranges;
   31mod lsp_colors;
   32mod lsp_ext;
   33mod mouse_context_menu;
   34pub mod movement;
   35mod persistence;
   36mod rust_analyzer_ext;
   37pub mod scroll;
   38mod selections_collection;
   39mod split;
   40pub mod split_editor_view;
   41pub mod tasks;
   42
   43#[cfg(test)]
   44mod code_completion_tests;
   45#[cfg(test)]
   46mod edit_prediction_tests;
   47#[cfg(test)]
   48mod editor_tests;
   49mod signature_help;
   50#[cfg(any(test, feature = "test-support"))]
   51pub mod test;
   52
   53pub(crate) use actions::*;
   54pub use display_map::{ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder};
   55pub use edit_prediction_types::Direction;
   56pub use editor_settings::{
   57    CompletionDetailAlignment, CurrentLineHighlight, DocumentColorsRenderMode, EditorSettings,
   58    HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowMinimap,
   59};
   60pub use element::{
   61    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   62    render_breadcrumb_text,
   63};
   64pub use git::blame::BlameRenderer;
   65pub use hover_popover::hover_markdown_style;
   66pub use inlays::Inlay;
   67pub use items::MAX_TAB_TITLE_LEN;
   68pub use lsp::CompletionContext;
   69pub use lsp_ext::lsp_tasks;
   70pub use multi_buffer::{
   71    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   72    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   73    ToPoint,
   74};
   75pub use split::{SplitDiffFeatureFlag, SplittableEditor, ToggleLockedCursors, ToggleSplitDiff};
   76pub use split_editor_view::SplitEditorView;
   77pub use text::Bias;
   78
   79use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   80use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   81use anyhow::{Context as _, Result, anyhow, bail};
   82use blink_manager::BlinkManager;
   83use buffer_diff::DiffHunkStatus;
   84use client::{Collaborator, ParticipantIndex, parse_zed_link};
   85use clock::ReplicaId;
   86use code_context_menus::{
   87    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   88    CompletionsMenu, ContextMenuOrigin,
   89};
   90use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   91use convert_case::{Case, Casing};
   92use dap::TelemetrySpawnLocation;
   93use display_map::*;
   94use edit_prediction_types::{
   95    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDismissReason,
   96    EditPredictionGranularity, SuggestionDisplayType,
   97};
   98use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
   99use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
  100use futures::{
  101    FutureExt,
  102    future::{self, Shared, join},
  103};
  104use fuzzy::{StringMatch, StringMatchCandidate};
  105use git::blame::{GitBlame, GlobalBlameRenderer};
  106use gpui::{
  107    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  108    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  109    DispatchPhase, Edges, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
  110    Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla, KeyContext, Modifiers,
  111    MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement, Pixels, PressureStage,
  112    Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled, Subscription, Task,
  113    TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  114    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  115    pulsating_between, px, relative, size,
  116};
  117use hover_links::{HoverLink, HoveredLinkState, find_file};
  118use hover_popover::{HoverState, hide_hover};
  119use indent_guides::ActiveIndentGuidesState;
  120use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  121use itertools::{Either, Itertools};
  122use language::{
  123    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  124    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  125    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  126    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, OffsetRangeExt,
  127    OutlineItem, Point, Runnable, Selection, SelectionGoal, TextObject, TransactionId,
  128    TreeSitterOptions, WordsQuery,
  129    language_settings::{
  130        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  131        all_language_settings, language_settings,
  132    },
  133    point_from_lsp, point_to_lsp, text_diff_with_options,
  134};
  135use linked_editing_ranges::refresh_linked_ranges;
  136use lsp::{
  137    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  138    LanguageServerId,
  139};
  140use lsp_colors::LspColorData;
  141use markdown::Markdown;
  142use mouse_context_menu::MouseContextMenu;
  143use movement::TextLayoutDetails;
  144use multi_buffer::{
  145    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  146};
  147use parking_lot::Mutex;
  148use persistence::DB;
  149use project::{
  150    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  151    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  152    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  153    ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
  154    debugger::{
  155        breakpoint_store::{
  156            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  157            BreakpointStore, BreakpointStoreEvent,
  158        },
  159        session::{Session, SessionEvent},
  160    },
  161    git_store::GitStoreEvent,
  162    lsp_store::{
  163        CacheInlayHints, CompletionDocumentation, FormatTrigger, LspFormatTarget,
  164        OpenLspBufferHandle,
  165    },
  166    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  167};
  168use rand::seq::SliceRandom;
  169use regex::Regex;
  170use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  171use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  172use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  173use serde::{Deserialize, Serialize};
  174use settings::{
  175    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  176    update_settings_file,
  177};
  178use smallvec::{SmallVec, smallvec};
  179use snippet::Snippet;
  180use std::{
  181    any::{Any, TypeId},
  182    borrow::Cow,
  183    cell::{OnceCell, RefCell},
  184    cmp::{self, Ordering, Reverse},
  185    collections::hash_map,
  186    iter::{self, Peekable},
  187    mem,
  188    num::NonZeroU32,
  189    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  190    path::{Path, PathBuf},
  191    rc::Rc,
  192    sync::Arc,
  193    time::{Duration, Instant},
  194};
  195use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
  196use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _};
  197use theme::{
  198    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  199    ThemeSettings, observe_buffer_font_size_adjustment,
  200};
  201use ui::{
  202    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  203    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  204};
  205use ui_input::ErasedEditor;
  206use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  207use workspace::{
  208    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  209    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  210    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  211    item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  212    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  213    searchable::{CollapseDirection, SearchEvent},
  214};
  215use zed_actions::editor::{MoveDown, MoveUp};
  216
  217use crate::{
  218    code_context_menus::CompletionsMenuSource,
  219    editor_settings::MultiCursorModifier,
  220    hover_links::{find_url, find_url_from_range},
  221    inlays::{
  222        InlineValueCache,
  223        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  224    },
  225    scroll::{ScrollOffset, ScrollPixelOffset},
  226    selections_collection::resolve_selections_wrapping_blocks,
  227    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  228};
  229
  230pub const FILE_HEADER_HEIGHT: u32 = 2;
  231pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  232const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  233const MAX_LINE_LEN: usize = 1024;
  234const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  235const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  236pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  237#[doc(hidden)]
  238pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  239pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  240
  241pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  242pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  243pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  244pub const FETCH_COLORS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(150);
  245
  246pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  247pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  248pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  249
  250pub type RenderDiffHunkControlsFn = Arc<
  251    dyn Fn(
  252        u32,
  253        &DiffHunkStatus,
  254        Range<Anchor>,
  255        bool,
  256        Pixels,
  257        &Entity<Editor>,
  258        &mut Window,
  259        &mut App,
  260    ) -> AnyElement,
  261>;
  262
  263enum ReportEditorEvent {
  264    Saved { auto_saved: bool },
  265    EditorOpened,
  266    Closed,
  267}
  268
  269impl ReportEditorEvent {
  270    pub fn event_type(&self) -> &'static str {
  271        match self {
  272            Self::Saved { .. } => "Editor Saved",
  273            Self::EditorOpened => "Editor Opened",
  274            Self::Closed => "Editor Closed",
  275        }
  276    }
  277}
  278
  279pub enum ActiveDebugLine {}
  280pub enum DebugStackFrameLine {}
  281enum DocumentHighlightRead {}
  282enum DocumentHighlightWrite {}
  283enum InputComposition {}
  284pub enum PendingInput {}
  285enum SelectedTextHighlight {}
  286
  287pub enum ConflictsOuter {}
  288pub enum ConflictsOurs {}
  289pub enum ConflictsTheirs {}
  290pub enum ConflictsOursMarker {}
  291pub enum ConflictsTheirsMarker {}
  292
  293pub struct HunkAddedColor;
  294pub struct HunkRemovedColor;
  295
  296#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  297pub enum Navigated {
  298    Yes,
  299    No,
  300}
  301
  302impl Navigated {
  303    pub fn from_bool(yes: bool) -> Navigated {
  304        if yes { Navigated::Yes } else { Navigated::No }
  305    }
  306}
  307
  308#[derive(Debug, Clone, PartialEq, Eq)]
  309enum DisplayDiffHunk {
  310    Folded {
  311        display_row: DisplayRow,
  312    },
  313    Unfolded {
  314        is_created_file: bool,
  315        diff_base_byte_range: Range<usize>,
  316        display_row_range: Range<DisplayRow>,
  317        multi_buffer_range: Range<Anchor>,
  318        status: DiffHunkStatus,
  319        word_diffs: Vec<Range<MultiBufferOffset>>,
  320    },
  321}
  322
  323pub enum HideMouseCursorOrigin {
  324    TypingAction,
  325    MovementAction,
  326}
  327
  328pub fn init(cx: &mut App) {
  329    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  330
  331    workspace::register_project_item::<Editor>(cx);
  332    workspace::FollowableViewRegistry::register::<Editor>(cx);
  333    workspace::register_serializable_item::<Editor>(cx);
  334
  335    cx.observe_new(
  336        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  337            workspace.register_action(Editor::new_file);
  338            workspace.register_action(Editor::new_file_split);
  339            workspace.register_action(Editor::new_file_vertical);
  340            workspace.register_action(Editor::new_file_horizontal);
  341            workspace.register_action(Editor::cancel_language_server_work);
  342            workspace.register_action(Editor::toggle_focus);
  343        },
  344    )
  345    .detach();
  346
  347    cx.on_action(move |_: &workspace::NewFile, cx| {
  348        let app_state = workspace::AppState::global(cx);
  349        if let Some(app_state) = app_state.upgrade() {
  350            workspace::open_new(
  351                Default::default(),
  352                app_state,
  353                cx,
  354                |workspace, window, cx| {
  355                    Editor::new_file(workspace, &Default::default(), window, cx)
  356                },
  357            )
  358            .detach();
  359        }
  360    })
  361    .on_action(move |_: &workspace::NewWindow, cx| {
  362        let app_state = workspace::AppState::global(cx);
  363        if let Some(app_state) = app_state.upgrade() {
  364            workspace::open_new(
  365                Default::default(),
  366                app_state,
  367                cx,
  368                |workspace, window, cx| {
  369                    cx.activate(true);
  370                    Editor::new_file(workspace, &Default::default(), window, cx)
  371                },
  372            )
  373            .detach();
  374        }
  375    });
  376    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  377        Arc::new(ErasedEditorImpl(
  378            cx.new(|cx| Editor::single_line(window, cx)),
  379        )) as Arc<dyn ErasedEditor>
  380    });
  381    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  382}
  383
  384pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  385    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  386}
  387
  388pub trait DiagnosticRenderer {
  389    fn render_group(
  390        &self,
  391        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  392        buffer_id: BufferId,
  393        snapshot: EditorSnapshot,
  394        editor: WeakEntity<Editor>,
  395        language_registry: Option<Arc<LanguageRegistry>>,
  396        cx: &mut App,
  397    ) -> Vec<BlockProperties<Anchor>>;
  398
  399    fn render_hover(
  400        &self,
  401        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  402        range: Range<Point>,
  403        buffer_id: BufferId,
  404        language_registry: Option<Arc<LanguageRegistry>>,
  405        cx: &mut App,
  406    ) -> Option<Entity<markdown::Markdown>>;
  407
  408    fn open_link(
  409        &self,
  410        editor: &mut Editor,
  411        link: SharedString,
  412        window: &mut Window,
  413        cx: &mut Context<Editor>,
  414    );
  415}
  416
  417pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  418
  419impl GlobalDiagnosticRenderer {
  420    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  421        cx.try_global::<Self>().map(|g| g.0.clone())
  422    }
  423}
  424
  425impl gpui::Global for GlobalDiagnosticRenderer {}
  426pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  427    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  428}
  429
  430pub struct SearchWithinRange;
  431
  432trait InvalidationRegion {
  433    fn ranges(&self) -> &[Range<Anchor>];
  434}
  435
  436#[derive(Clone, Debug, PartialEq)]
  437pub enum SelectPhase {
  438    Begin {
  439        position: DisplayPoint,
  440        add: bool,
  441        click_count: usize,
  442    },
  443    BeginColumnar {
  444        position: DisplayPoint,
  445        reset: bool,
  446        mode: ColumnarMode,
  447        goal_column: u32,
  448    },
  449    Extend {
  450        position: DisplayPoint,
  451        click_count: usize,
  452    },
  453    Update {
  454        position: DisplayPoint,
  455        goal_column: u32,
  456        scroll_delta: gpui::Point<f32>,
  457    },
  458    End,
  459}
  460
  461#[derive(Clone, Debug, PartialEq)]
  462pub enum ColumnarMode {
  463    FromMouse,
  464    FromSelection,
  465}
  466
  467#[derive(Clone, Debug)]
  468pub enum SelectMode {
  469    Character,
  470    Word(Range<Anchor>),
  471    Line(Range<Anchor>),
  472    All,
  473}
  474
  475#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  476pub enum SizingBehavior {
  477    /// The editor will layout itself using `size_full` and will include the vertical
  478    /// scroll margin as requested by user settings.
  479    #[default]
  480    Default,
  481    /// The editor will layout itself using `size_full`, but will not have any
  482    /// vertical overscroll.
  483    ExcludeOverscrollMargin,
  484    /// The editor will request a vertical size according to its content and will be
  485    /// layouted without a vertical scroll margin.
  486    SizeByContent,
  487}
  488
  489#[derive(Clone, PartialEq, Eq, Debug)]
  490pub enum EditorMode {
  491    SingleLine,
  492    AutoHeight {
  493        min_lines: usize,
  494        max_lines: Option<usize>,
  495    },
  496    Full {
  497        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  498        scale_ui_elements_with_buffer_font_size: bool,
  499        /// When set to `true`, the editor will render a background for the active line.
  500        show_active_line_background: bool,
  501        /// Determines the sizing behavior for this editor
  502        sizing_behavior: SizingBehavior,
  503    },
  504    Minimap {
  505        parent: WeakEntity<Editor>,
  506    },
  507}
  508
  509impl EditorMode {
  510    pub fn full() -> Self {
  511        Self::Full {
  512            scale_ui_elements_with_buffer_font_size: true,
  513            show_active_line_background: true,
  514            sizing_behavior: SizingBehavior::Default,
  515        }
  516    }
  517
  518    #[inline]
  519    pub fn is_full(&self) -> bool {
  520        matches!(self, Self::Full { .. })
  521    }
  522
  523    #[inline]
  524    pub fn is_single_line(&self) -> bool {
  525        matches!(self, Self::SingleLine { .. })
  526    }
  527
  528    #[inline]
  529    fn is_minimap(&self) -> bool {
  530        matches!(self, Self::Minimap { .. })
  531    }
  532}
  533
  534#[derive(Copy, Clone, Debug)]
  535pub enum SoftWrap {
  536    /// Prefer not to wrap at all.
  537    ///
  538    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  539    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  540    GitDiff,
  541    /// Prefer a single line generally, unless an overly long line is encountered.
  542    None,
  543    /// Soft wrap lines that exceed the editor width.
  544    EditorWidth,
  545    /// Soft wrap lines at the preferred line length.
  546    Column(u32),
  547    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  548    Bounded(u32),
  549}
  550
  551#[derive(Clone)]
  552pub struct EditorStyle {
  553    pub background: Hsla,
  554    pub border: Hsla,
  555    pub local_player: PlayerColor,
  556    pub text: TextStyle,
  557    pub scrollbar_width: Pixels,
  558    pub syntax: Arc<SyntaxTheme>,
  559    pub status: StatusColors,
  560    pub inlay_hints_style: HighlightStyle,
  561    pub edit_prediction_styles: EditPredictionStyles,
  562    pub unnecessary_code_fade: f32,
  563    pub show_underlines: bool,
  564}
  565
  566impl Default for EditorStyle {
  567    fn default() -> Self {
  568        Self {
  569            background: Hsla::default(),
  570            border: Hsla::default(),
  571            local_player: PlayerColor::default(),
  572            text: TextStyle::default(),
  573            scrollbar_width: Pixels::default(),
  574            syntax: Default::default(),
  575            // HACK: Status colors don't have a real default.
  576            // We should look into removing the status colors from the editor
  577            // style and retrieve them directly from the theme.
  578            status: StatusColors::dark(),
  579            inlay_hints_style: HighlightStyle::default(),
  580            edit_prediction_styles: EditPredictionStyles {
  581                insertion: HighlightStyle::default(),
  582                whitespace: HighlightStyle::default(),
  583            },
  584            unnecessary_code_fade: Default::default(),
  585            show_underlines: true,
  586        }
  587    }
  588}
  589
  590pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  591    let show_background = language_settings::language_settings(None, None, cx)
  592        .inlay_hints
  593        .show_background;
  594
  595    let mut style = cx.theme().syntax().get("hint");
  596
  597    if style.color.is_none() {
  598        style.color = Some(cx.theme().status().hint);
  599    }
  600
  601    if !show_background {
  602        style.background_color = None;
  603        return style;
  604    }
  605
  606    if style.background_color.is_none() {
  607        style.background_color = Some(cx.theme().status().hint_background);
  608    }
  609
  610    style
  611}
  612
  613pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  614    EditPredictionStyles {
  615        insertion: HighlightStyle {
  616            color: Some(cx.theme().status().predictive),
  617            ..HighlightStyle::default()
  618        },
  619        whitespace: HighlightStyle {
  620            background_color: Some(cx.theme().status().created_background),
  621            ..HighlightStyle::default()
  622        },
  623    }
  624}
  625
  626type CompletionId = usize;
  627
  628pub(crate) enum EditDisplayMode {
  629    TabAccept,
  630    DiffPopover,
  631    Inline,
  632}
  633
  634enum EditPrediction {
  635    Edit {
  636        edits: Vec<(Range<Anchor>, Arc<str>)>,
  637        /// Predicted cursor position as (anchor, offset_from_anchor).
  638        /// The anchor is in multibuffer coordinates; after applying edits,
  639        /// resolve the anchor and add the offset to get the final cursor position.
  640        cursor_position: Option<(Anchor, usize)>,
  641        edit_preview: Option<EditPreview>,
  642        display_mode: EditDisplayMode,
  643        snapshot: BufferSnapshot,
  644    },
  645    /// Move to a specific location in the active editor
  646    MoveWithin {
  647        target: Anchor,
  648        snapshot: BufferSnapshot,
  649    },
  650    /// Move to a specific location in a different editor (not the active one)
  651    MoveOutside {
  652        target: language::Anchor,
  653        snapshot: BufferSnapshot,
  654    },
  655}
  656
  657struct EditPredictionState {
  658    inlay_ids: Vec<InlayId>,
  659    completion: EditPrediction,
  660    completion_id: Option<SharedString>,
  661    invalidation_range: Option<Range<Anchor>>,
  662}
  663
  664enum EditPredictionSettings {
  665    Disabled,
  666    Enabled {
  667        show_in_menu: bool,
  668        preview_requires_modifier: bool,
  669    },
  670}
  671
  672enum EditPredictionHighlight {}
  673
  674#[derive(Debug, Clone)]
  675struct InlineDiagnostic {
  676    message: SharedString,
  677    group_id: usize,
  678    is_primary: bool,
  679    start: Point,
  680    severity: lsp::DiagnosticSeverity,
  681}
  682
  683pub enum MenuEditPredictionsPolicy {
  684    Never,
  685    ByProvider,
  686}
  687
  688pub enum EditPredictionPreview {
  689    /// Modifier is not pressed
  690    Inactive { released_too_fast: bool },
  691    /// Modifier pressed
  692    Active {
  693        since: Instant,
  694        previous_scroll_position: Option<SharedScrollAnchor>,
  695    },
  696}
  697
  698impl EditPredictionPreview {
  699    pub fn released_too_fast(&self) -> bool {
  700        match self {
  701            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  702            EditPredictionPreview::Active { .. } => false,
  703        }
  704    }
  705
  706    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  707        if let EditPredictionPreview::Active {
  708            previous_scroll_position,
  709            ..
  710        } = self
  711        {
  712            *previous_scroll_position = scroll_position;
  713        }
  714    }
  715}
  716
  717pub struct ContextMenuOptions {
  718    pub min_entries_visible: usize,
  719    pub max_entries_visible: usize,
  720    pub placement: Option<ContextMenuPlacement>,
  721}
  722
  723#[derive(Debug, Clone, PartialEq, Eq)]
  724pub enum ContextMenuPlacement {
  725    Above,
  726    Below,
  727}
  728
  729#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  730struct EditorActionId(usize);
  731
  732impl EditorActionId {
  733    pub fn post_inc(&mut self) -> Self {
  734        let answer = self.0;
  735
  736        *self = Self(answer + 1);
  737
  738        Self(answer)
  739    }
  740}
  741
  742// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  743// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  744
  745type BackgroundHighlight = (
  746    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  747    Arc<[Range<Anchor>]>,
  748);
  749type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  750
  751#[derive(Default)]
  752struct ScrollbarMarkerState {
  753    scrollbar_size: Size<Pixels>,
  754    dirty: bool,
  755    markers: Arc<[PaintQuad]>,
  756    pending_refresh: Option<Task<Result<()>>>,
  757}
  758
  759impl ScrollbarMarkerState {
  760    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  761        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  762    }
  763}
  764
  765#[derive(Clone, Copy, PartialEq, Eq)]
  766pub enum MinimapVisibility {
  767    Disabled,
  768    Enabled {
  769        /// The configuration currently present in the users settings.
  770        setting_configuration: bool,
  771        /// Whether to override the currently set visibility from the users setting.
  772        toggle_override: bool,
  773    },
  774}
  775
  776impl MinimapVisibility {
  777    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  778        if mode.is_full() {
  779            Self::Enabled {
  780                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  781                toggle_override: false,
  782            }
  783        } else {
  784            Self::Disabled
  785        }
  786    }
  787
  788    fn hidden(&self) -> Self {
  789        match *self {
  790            Self::Enabled {
  791                setting_configuration,
  792                ..
  793            } => Self::Enabled {
  794                setting_configuration,
  795                toggle_override: setting_configuration,
  796            },
  797            Self::Disabled => Self::Disabled,
  798        }
  799    }
  800
  801    fn disabled(&self) -> bool {
  802        matches!(*self, Self::Disabled)
  803    }
  804
  805    fn settings_visibility(&self) -> bool {
  806        match *self {
  807            Self::Enabled {
  808                setting_configuration,
  809                ..
  810            } => setting_configuration,
  811            _ => false,
  812        }
  813    }
  814
  815    fn visible(&self) -> bool {
  816        match *self {
  817            Self::Enabled {
  818                setting_configuration,
  819                toggle_override,
  820            } => setting_configuration ^ toggle_override,
  821            _ => false,
  822        }
  823    }
  824
  825    fn toggle_visibility(&self) -> Self {
  826        match *self {
  827            Self::Enabled {
  828                toggle_override,
  829                setting_configuration,
  830            } => Self::Enabled {
  831                setting_configuration,
  832                toggle_override: !toggle_override,
  833            },
  834            Self::Disabled => Self::Disabled,
  835        }
  836    }
  837}
  838
  839#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  840pub enum BufferSerialization {
  841    All,
  842    NonDirtyBuffers,
  843}
  844
  845impl BufferSerialization {
  846    fn new(restore_unsaved_buffers: bool) -> Self {
  847        if restore_unsaved_buffers {
  848            Self::All
  849        } else {
  850            Self::NonDirtyBuffers
  851        }
  852    }
  853}
  854
  855#[derive(Clone, Debug)]
  856struct RunnableTasks {
  857    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  858    offset: multi_buffer::Anchor,
  859    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  860    column: u32,
  861    // Values of all named captures, including those starting with '_'
  862    extra_variables: HashMap<String, String>,
  863    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  864    context_range: Range<BufferOffset>,
  865}
  866
  867impl RunnableTasks {
  868    fn resolve<'a>(
  869        &'a self,
  870        cx: &'a task::TaskContext,
  871    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  872        self.templates.iter().filter_map(|(kind, template)| {
  873            template
  874                .resolve_task(&kind.to_id_base(), cx)
  875                .map(|task| (kind.clone(), task))
  876        })
  877    }
  878}
  879
  880#[derive(Clone)]
  881pub struct ResolvedTasks {
  882    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  883    position: Anchor,
  884}
  885
  886/// Addons allow storing per-editor state in other crates (e.g. Vim)
  887pub trait Addon: 'static {
  888    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  889
  890    fn render_buffer_header_controls(
  891        &self,
  892        _: &ExcerptInfo,
  893        _: &Window,
  894        _: &App,
  895    ) -> Option<AnyElement> {
  896        None
  897    }
  898
  899    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  900        None
  901    }
  902
  903    fn to_any(&self) -> &dyn std::any::Any;
  904
  905    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  906        None
  907    }
  908}
  909
  910struct ChangeLocation {
  911    current: Option<Vec<Anchor>>,
  912    original: Vec<Anchor>,
  913}
  914impl ChangeLocation {
  915    fn locations(&self) -> &[Anchor] {
  916        self.current.as_ref().unwrap_or(&self.original)
  917    }
  918}
  919
  920/// A set of caret positions, registered when the editor was edited.
  921pub struct ChangeList {
  922    changes: Vec<ChangeLocation>,
  923    /// Currently "selected" change.
  924    position: Option<usize>,
  925}
  926
  927impl ChangeList {
  928    pub fn new() -> Self {
  929        Self {
  930            changes: Vec::new(),
  931            position: None,
  932        }
  933    }
  934
  935    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  936    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  937    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  938        if self.changes.is_empty() {
  939            return None;
  940        }
  941
  942        let prev = self.position.unwrap_or(self.changes.len());
  943        let next = if direction == Direction::Prev {
  944            prev.saturating_sub(count)
  945        } else {
  946            (prev + count).min(self.changes.len() - 1)
  947        };
  948        self.position = Some(next);
  949        self.changes.get(next).map(|change| change.locations())
  950    }
  951
  952    /// Adds a new change to the list, resetting the change list position.
  953    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  954        self.position.take();
  955        if let Some(last) = self.changes.last_mut()
  956            && group
  957        {
  958            last.current = Some(new_positions)
  959        } else {
  960            self.changes.push(ChangeLocation {
  961                original: new_positions,
  962                current: None,
  963            });
  964        }
  965    }
  966
  967    pub fn last(&self) -> Option<&[Anchor]> {
  968        self.changes.last().map(|change| change.locations())
  969    }
  970
  971    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  972        self.changes.last().map(|change| change.original.as_slice())
  973    }
  974
  975    pub fn invert_last_group(&mut self) {
  976        if let Some(last) = self.changes.last_mut()
  977            && let Some(current) = last.current.as_mut()
  978        {
  979            mem::swap(&mut last.original, current);
  980        }
  981    }
  982}
  983
  984#[derive(Clone)]
  985struct InlineBlamePopoverState {
  986    scroll_handle: ScrollHandle,
  987    commit_message: Option<ParsedCommitMessage>,
  988    markdown: Entity<Markdown>,
  989}
  990
  991struct InlineBlamePopover {
  992    position: gpui::Point<Pixels>,
  993    hide_task: Option<Task<()>>,
  994    popover_bounds: Option<Bounds<Pixels>>,
  995    popover_state: InlineBlamePopoverState,
  996    keyboard_grace: bool,
  997}
  998
  999enum SelectionDragState {
 1000    /// State when no drag related activity is detected.
 1001    None,
 1002    /// State when the mouse is down on a selection that is about to be dragged.
 1003    ReadyToDrag {
 1004        selection: Selection<Anchor>,
 1005        click_position: gpui::Point<Pixels>,
 1006        mouse_down_time: Instant,
 1007    },
 1008    /// State when the mouse is dragging the selection in the editor.
 1009    Dragging {
 1010        selection: Selection<Anchor>,
 1011        drop_cursor: Selection<Anchor>,
 1012        hide_drop_cursor: bool,
 1013    },
 1014}
 1015
 1016enum ColumnarSelectionState {
 1017    FromMouse {
 1018        selection_tail: Anchor,
 1019        display_point: Option<DisplayPoint>,
 1020    },
 1021    FromSelection {
 1022        selection_tail: Anchor,
 1023    },
 1024}
 1025
 1026/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1027/// a breakpoint on them.
 1028#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1029struct PhantomBreakpointIndicator {
 1030    display_row: DisplayRow,
 1031    /// There's a small debounce between hovering over the line and showing the indicator.
 1032    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1033    is_active: bool,
 1034    collides_with_existing_breakpoint: bool,
 1035}
 1036
 1037/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1038/// in diff view mode.
 1039#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1040pub(crate) struct PhantomDiffReviewIndicator {
 1041    /// The starting anchor of the selection (or the only row if not dragging).
 1042    pub start: Anchor,
 1043    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1044    pub end: Anchor,
 1045    /// There's a small debounce between hovering over the line and showing the indicator.
 1046    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1047    pub is_active: bool,
 1048}
 1049
 1050#[derive(Clone, Debug)]
 1051pub(crate) struct DiffReviewDragState {
 1052    pub start_anchor: Anchor,
 1053    pub current_anchor: Anchor,
 1054}
 1055
 1056impl DiffReviewDragState {
 1057    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1058        let start = self.start_anchor.to_display_point(snapshot).row();
 1059        let current = self.current_anchor.to_display_point(snapshot).row();
 1060
 1061        (start..=current).sorted()
 1062    }
 1063}
 1064
 1065/// Identifies a specific hunk in the diff buffer.
 1066/// Used as a key to group comments by their location.
 1067#[derive(Clone, Debug)]
 1068pub struct DiffHunkKey {
 1069    /// The file path (relative to worktree) this hunk belongs to.
 1070    pub file_path: Arc<util::rel_path::RelPath>,
 1071    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1072    pub hunk_start_anchor: Anchor,
 1073}
 1074
 1075/// A review comment stored locally before being sent to the Agent panel.
 1076#[derive(Clone)]
 1077pub struct StoredReviewComment {
 1078    /// Unique identifier for this comment (for edit/delete operations).
 1079    pub id: usize,
 1080    /// The comment text entered by the user.
 1081    pub comment: String,
 1082    /// Anchors for the code range being reviewed.
 1083    pub range: Range<Anchor>,
 1084    /// Timestamp when the comment was created (for chronological ordering).
 1085    pub created_at: Instant,
 1086    /// Whether this comment is currently being edited inline.
 1087    pub is_editing: bool,
 1088}
 1089
 1090impl StoredReviewComment {
 1091    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1092        Self {
 1093            id,
 1094            comment,
 1095            range: anchor_range,
 1096            created_at: Instant::now(),
 1097            is_editing: false,
 1098        }
 1099    }
 1100}
 1101
 1102/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1103pub(crate) struct DiffReviewOverlay {
 1104    pub anchor_range: Range<Anchor>,
 1105    /// The block ID for the overlay.
 1106    pub block_id: CustomBlockId,
 1107    /// The editor entity for the review input.
 1108    pub prompt_editor: Entity<Editor>,
 1109    /// The hunk key this overlay belongs to.
 1110    pub hunk_key: DiffHunkKey,
 1111    /// Whether the comments section is expanded.
 1112    pub comments_expanded: bool,
 1113    /// Editors for comments currently being edited inline.
 1114    /// Key: comment ID, Value: Editor entity for inline editing.
 1115    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1116    /// Subscriptions for inline edit editors' action handlers.
 1117    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1118    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1119    /// The current user's avatar URI for display in comment rows.
 1120    pub user_avatar_uri: Option<SharedUri>,
 1121    /// Subscription to keep the action handler alive.
 1122    _subscription: Subscription,
 1123}
 1124
 1125/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1126///
 1127/// See the [module level documentation](self) for more information.
 1128pub struct Editor {
 1129    focus_handle: FocusHandle,
 1130    last_focused_descendant: Option<WeakFocusHandle>,
 1131    /// The text buffer being edited
 1132    buffer: Entity<MultiBuffer>,
 1133    /// Map of how text in the buffer should be displayed.
 1134    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1135    pub display_map: Entity<DisplayMap>,
 1136    placeholder_display_map: Option<Entity<DisplayMap>>,
 1137    pub selections: SelectionsCollection,
 1138    pub scroll_manager: ScrollManager,
 1139    /// When inline assist editors are linked, they all render cursors because
 1140    /// typing enters text into each of them, even the ones that aren't focused.
 1141    pub(crate) show_cursor_when_unfocused: bool,
 1142    columnar_selection_state: Option<ColumnarSelectionState>,
 1143    add_selections_state: Option<AddSelectionsState>,
 1144    select_next_state: Option<SelectNextState>,
 1145    select_prev_state: Option<SelectNextState>,
 1146    selection_history: SelectionHistory,
 1147    defer_selection_effects: bool,
 1148    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1149    autoclose_regions: Vec<AutocloseRegion>,
 1150    snippet_stack: InvalidationStack<SnippetState>,
 1151    select_syntax_node_history: SelectSyntaxNodeHistory,
 1152    ime_transaction: Option<TransactionId>,
 1153    pub diagnostics_max_severity: DiagnosticSeverity,
 1154    active_diagnostics: ActiveDiagnostic,
 1155    show_inline_diagnostics: bool,
 1156    inline_diagnostics_update: Task<()>,
 1157    inline_diagnostics_enabled: bool,
 1158    diagnostics_enabled: bool,
 1159    word_completions_enabled: bool,
 1160    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1161    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1162    hard_wrap: Option<usize>,
 1163    project: Option<Entity<Project>>,
 1164    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1165    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1166    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1167    blink_manager: Entity<BlinkManager>,
 1168    show_cursor_names: bool,
 1169    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1170    pub show_local_selections: bool,
 1171    mode: EditorMode,
 1172    show_breadcrumbs: bool,
 1173    show_gutter: bool,
 1174    show_scrollbars: ScrollbarAxes,
 1175    minimap_visibility: MinimapVisibility,
 1176    offset_content: bool,
 1177    disable_expand_excerpt_buttons: bool,
 1178    delegate_expand_excerpts: bool,
 1179    delegate_stage_and_restore: bool,
 1180    show_line_numbers: Option<bool>,
 1181    use_relative_line_numbers: Option<bool>,
 1182    show_git_diff_gutter: Option<bool>,
 1183    show_code_actions: Option<bool>,
 1184    show_runnables: Option<bool>,
 1185    show_breakpoints: Option<bool>,
 1186    show_diff_review_button: bool,
 1187    show_wrap_guides: Option<bool>,
 1188    show_indent_guides: Option<bool>,
 1189    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1190    highlight_order: usize,
 1191    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1192    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1193    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1194    scrollbar_marker_state: ScrollbarMarkerState,
 1195    active_indent_guides_state: ActiveIndentGuidesState,
 1196    nav_history: Option<ItemNavHistory>,
 1197    context_menu: RefCell<Option<CodeContextMenu>>,
 1198    context_menu_options: Option<ContextMenuOptions>,
 1199    mouse_context_menu: Option<MouseContextMenu>,
 1200    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1201    inline_blame_popover: Option<InlineBlamePopover>,
 1202    inline_blame_popover_show_task: Option<Task<()>>,
 1203    signature_help_state: SignatureHelpState,
 1204    auto_signature_help: Option<bool>,
 1205    find_all_references_task_sources: Vec<Anchor>,
 1206    next_completion_id: CompletionId,
 1207    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1208    code_actions_task: Option<Task<Result<()>>>,
 1209    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1210    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1211    debounced_selection_highlight_complete: bool,
 1212    document_highlights_task: Option<Task<()>>,
 1213    linked_editing_range_task: Option<Task<Option<()>>>,
 1214    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1215    pending_rename: Option<RenameState>,
 1216    searchable: bool,
 1217    cursor_shape: CursorShape,
 1218    /// Whether the cursor is offset one character to the left when something is
 1219    /// selected (needed for vim visual mode)
 1220    cursor_offset_on_selection: bool,
 1221    current_line_highlight: Option<CurrentLineHighlight>,
 1222    /// Whether to collapse search match ranges to just their start position.
 1223    /// When true, navigating to a match positions the cursor at the match
 1224    /// without selecting the matched text.
 1225    collapse_matches: bool,
 1226    autoindent_mode: Option<AutoindentMode>,
 1227    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1228    input_enabled: bool,
 1229    use_modal_editing: bool,
 1230    read_only: bool,
 1231    leader_id: Option<CollaboratorId>,
 1232    remote_id: Option<ViewId>,
 1233    pub hover_state: HoverState,
 1234    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1235    prev_pressure_stage: Option<PressureStage>,
 1236    gutter_hovered: bool,
 1237    hovered_link_state: Option<HoveredLinkState>,
 1238    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1239    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1240    active_edit_prediction: Option<EditPredictionState>,
 1241    /// Used to prevent flickering as the user types while the menu is open
 1242    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1243    edit_prediction_settings: EditPredictionSettings,
 1244    edit_predictions_hidden_for_vim_mode: bool,
 1245    show_edit_predictions_override: Option<bool>,
 1246    show_completions_on_input_override: Option<bool>,
 1247    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1248    edit_prediction_preview: EditPredictionPreview,
 1249    edit_prediction_indent_conflict: bool,
 1250    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1251    next_inlay_id: usize,
 1252    next_color_inlay_id: usize,
 1253    _subscriptions: Vec<Subscription>,
 1254    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1255    gutter_dimensions: GutterDimensions,
 1256    style: Option<EditorStyle>,
 1257    text_style_refinement: Option<TextStyleRefinement>,
 1258    next_editor_action_id: EditorActionId,
 1259    editor_actions: Rc<
 1260        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1261    >,
 1262    use_autoclose: bool,
 1263    use_auto_surround: bool,
 1264    auto_replace_emoji_shortcode: bool,
 1265    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1266    show_git_blame_gutter: bool,
 1267    show_git_blame_inline: bool,
 1268    show_git_blame_inline_delay_task: Option<Task<()>>,
 1269    git_blame_inline_enabled: bool,
 1270    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1271    buffer_serialization: Option<BufferSerialization>,
 1272    show_selection_menu: Option<bool>,
 1273    blame: Option<Entity<GitBlame>>,
 1274    blame_subscription: Option<Subscription>,
 1275    custom_context_menu: Option<
 1276        Box<
 1277            dyn 'static
 1278                + Fn(
 1279                    &mut Self,
 1280                    DisplayPoint,
 1281                    &mut Window,
 1282                    &mut Context<Self>,
 1283                ) -> Option<Entity<ui::ContextMenu>>,
 1284        >,
 1285    >,
 1286    last_bounds: Option<Bounds<Pixels>>,
 1287    last_position_map: Option<Rc<PositionMap>>,
 1288    expect_bounds_change: Option<Bounds<Pixels>>,
 1289    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1290    tasks_update_task: Option<Task<()>>,
 1291    breakpoint_store: Option<Entity<BreakpointStore>>,
 1292    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1293    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1294    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1295    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1296    /// when hunks have comments stored.
 1297    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1298    /// Stored review comments grouped by hunk.
 1299    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1300    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1301    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1302    /// Counter for generating unique comment IDs.
 1303    next_review_comment_id: usize,
 1304    hovered_diff_hunk_row: Option<DisplayRow>,
 1305    pull_diagnostics_task: Task<()>,
 1306    in_project_search: bool,
 1307    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1308    breadcrumb_header: Option<String>,
 1309    focused_block: Option<FocusedBlock>,
 1310    next_scroll_position: NextScrollCursorCenterTopBottom,
 1311    addons: HashMap<TypeId, Box<dyn Addon>>,
 1312    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1313    load_diff_task: Option<Shared<Task<()>>>,
 1314    /// Whether we are temporarily displaying a diff other than git's
 1315    temporary_diff_override: bool,
 1316    selection_mark_mode: bool,
 1317    toggle_fold_multiple_buffers: Task<()>,
 1318    _scroll_cursor_center_top_bottom_task: Task<()>,
 1319    serialize_selections: Task<()>,
 1320    serialize_folds: Task<()>,
 1321    mouse_cursor_hidden: bool,
 1322    minimap: Option<Entity<Self>>,
 1323    hide_mouse_mode: HideMouseMode,
 1324    pub change_list: ChangeList,
 1325    inline_value_cache: InlineValueCache,
 1326    number_deleted_lines: bool,
 1327
 1328    selection_drag_state: SelectionDragState,
 1329    colors: Option<LspColorData>,
 1330    post_scroll_update: Task<()>,
 1331    refresh_colors_task: Task<()>,
 1332    inlay_hints: Option<LspInlayHintData>,
 1333    folding_newlines: Task<()>,
 1334    select_next_is_case_sensitive: Option<bool>,
 1335    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1336    on_local_selections_changed:
 1337        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1338    suppress_selection_callback: bool,
 1339    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1340    accent_data: Option<AccentData>,
 1341    fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1342}
 1343
 1344#[derive(Debug, PartialEq)]
 1345struct AccentData {
 1346    colors: AccentColors,
 1347    overrides: Vec<SharedString>,
 1348}
 1349
 1350fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1351    if debounce_ms > 0 {
 1352        Some(Duration::from_millis(debounce_ms))
 1353    } else {
 1354        None
 1355    }
 1356}
 1357
 1358#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1359enum NextScrollCursorCenterTopBottom {
 1360    #[default]
 1361    Center,
 1362    Top,
 1363    Bottom,
 1364}
 1365
 1366impl NextScrollCursorCenterTopBottom {
 1367    fn next(&self) -> Self {
 1368        match self {
 1369            Self::Center => Self::Top,
 1370            Self::Top => Self::Bottom,
 1371            Self::Bottom => Self::Center,
 1372        }
 1373    }
 1374}
 1375
 1376#[derive(Clone)]
 1377pub struct EditorSnapshot {
 1378    pub mode: EditorMode,
 1379    show_gutter: bool,
 1380    offset_content: bool,
 1381    show_line_numbers: Option<bool>,
 1382    number_deleted_lines: bool,
 1383    show_git_diff_gutter: Option<bool>,
 1384    show_code_actions: Option<bool>,
 1385    show_runnables: Option<bool>,
 1386    show_breakpoints: Option<bool>,
 1387    git_blame_gutter_max_author_length: Option<usize>,
 1388    pub display_snapshot: DisplaySnapshot,
 1389    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1390    is_focused: bool,
 1391    scroll_anchor: SharedScrollAnchor,
 1392    ongoing_scroll: OngoingScroll,
 1393    current_line_highlight: CurrentLineHighlight,
 1394    gutter_hovered: bool,
 1395}
 1396
 1397#[derive(Default, Debug, Clone, Copy)]
 1398pub struct GutterDimensions {
 1399    pub left_padding: Pixels,
 1400    pub right_padding: Pixels,
 1401    pub width: Pixels,
 1402    pub margin: Pixels,
 1403    pub git_blame_entries_width: Option<Pixels>,
 1404}
 1405
 1406impl GutterDimensions {
 1407    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1408        Self {
 1409            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1410            ..Default::default()
 1411        }
 1412    }
 1413
 1414    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1415        -cx.text_system().descent(font_id, font_size)
 1416    }
 1417    /// The full width of the space taken up by the gutter.
 1418    pub fn full_width(&self) -> Pixels {
 1419        self.margin + self.width
 1420    }
 1421
 1422    /// The width of the space reserved for the fold indicators,
 1423    /// use alongside 'justify_end' and `gutter_width` to
 1424    /// right align content with the line numbers
 1425    pub fn fold_area_width(&self) -> Pixels {
 1426        self.margin + self.right_padding
 1427    }
 1428}
 1429
 1430struct CharacterDimensions {
 1431    em_width: Pixels,
 1432    em_advance: Pixels,
 1433    line_height: Pixels,
 1434}
 1435
 1436#[derive(Debug)]
 1437pub struct RemoteSelection {
 1438    pub replica_id: ReplicaId,
 1439    pub selection: Selection<Anchor>,
 1440    pub cursor_shape: CursorShape,
 1441    pub collaborator_id: CollaboratorId,
 1442    pub line_mode: bool,
 1443    pub user_name: Option<SharedString>,
 1444    pub color: PlayerColor,
 1445}
 1446
 1447#[derive(Clone, Debug)]
 1448struct SelectionHistoryEntry {
 1449    selections: Arc<[Selection<Anchor>]>,
 1450    select_next_state: Option<SelectNextState>,
 1451    select_prev_state: Option<SelectNextState>,
 1452    add_selections_state: Option<AddSelectionsState>,
 1453}
 1454
 1455#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1456enum SelectionHistoryMode {
 1457    #[default]
 1458    Normal,
 1459    Undoing,
 1460    Redoing,
 1461    Skipping,
 1462}
 1463
 1464#[derive(Clone, PartialEq, Eq, Hash)]
 1465struct HoveredCursor {
 1466    replica_id: ReplicaId,
 1467    selection_id: usize,
 1468}
 1469
 1470#[derive(Debug)]
 1471/// SelectionEffects controls the side-effects of updating the selection.
 1472///
 1473/// The default behaviour does "what you mostly want":
 1474/// - it pushes to the nav history if the cursor moved by >10 lines
 1475/// - it re-triggers completion requests
 1476/// - it scrolls to fit
 1477///
 1478/// You might want to modify these behaviours. For example when doing a "jump"
 1479/// like go to definition, we always want to add to nav history; but when scrolling
 1480/// in vim mode we never do.
 1481///
 1482/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1483/// move.
 1484#[derive(Clone)]
 1485pub struct SelectionEffects {
 1486    nav_history: Option<bool>,
 1487    completions: bool,
 1488    scroll: Option<Autoscroll>,
 1489}
 1490
 1491impl Default for SelectionEffects {
 1492    fn default() -> Self {
 1493        Self {
 1494            nav_history: None,
 1495            completions: true,
 1496            scroll: Some(Autoscroll::fit()),
 1497        }
 1498    }
 1499}
 1500impl SelectionEffects {
 1501    pub fn scroll(scroll: Autoscroll) -> Self {
 1502        Self {
 1503            scroll: Some(scroll),
 1504            ..Default::default()
 1505        }
 1506    }
 1507
 1508    pub fn no_scroll() -> Self {
 1509        Self {
 1510            scroll: None,
 1511            ..Default::default()
 1512        }
 1513    }
 1514
 1515    pub fn completions(self, completions: bool) -> Self {
 1516        Self {
 1517            completions,
 1518            ..self
 1519        }
 1520    }
 1521
 1522    pub fn nav_history(self, nav_history: bool) -> Self {
 1523        Self {
 1524            nav_history: Some(nav_history),
 1525            ..self
 1526        }
 1527    }
 1528}
 1529
 1530struct DeferredSelectionEffectsState {
 1531    changed: bool,
 1532    effects: SelectionEffects,
 1533    old_cursor_position: Anchor,
 1534    history_entry: SelectionHistoryEntry,
 1535}
 1536
 1537#[derive(Default)]
 1538struct SelectionHistory {
 1539    #[allow(clippy::type_complexity)]
 1540    selections_by_transaction:
 1541        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1542    mode: SelectionHistoryMode,
 1543    undo_stack: VecDeque<SelectionHistoryEntry>,
 1544    redo_stack: VecDeque<SelectionHistoryEntry>,
 1545}
 1546
 1547impl SelectionHistory {
 1548    #[track_caller]
 1549    fn insert_transaction(
 1550        &mut self,
 1551        transaction_id: TransactionId,
 1552        selections: Arc<[Selection<Anchor>]>,
 1553    ) {
 1554        if selections.is_empty() {
 1555            log::error!(
 1556                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1557                std::panic::Location::caller()
 1558            );
 1559            return;
 1560        }
 1561        self.selections_by_transaction
 1562            .insert(transaction_id, (selections, None));
 1563    }
 1564
 1565    #[allow(clippy::type_complexity)]
 1566    fn transaction(
 1567        &self,
 1568        transaction_id: TransactionId,
 1569    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1570        self.selections_by_transaction.get(&transaction_id)
 1571    }
 1572
 1573    #[allow(clippy::type_complexity)]
 1574    fn transaction_mut(
 1575        &mut self,
 1576        transaction_id: TransactionId,
 1577    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1578        self.selections_by_transaction.get_mut(&transaction_id)
 1579    }
 1580
 1581    fn push(&mut self, entry: SelectionHistoryEntry) {
 1582        if !entry.selections.is_empty() {
 1583            match self.mode {
 1584                SelectionHistoryMode::Normal => {
 1585                    self.push_undo(entry);
 1586                    self.redo_stack.clear();
 1587                }
 1588                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1589                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1590                SelectionHistoryMode::Skipping => {}
 1591            }
 1592        }
 1593    }
 1594
 1595    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1596        if self
 1597            .undo_stack
 1598            .back()
 1599            .is_none_or(|e| e.selections != entry.selections)
 1600        {
 1601            self.undo_stack.push_back(entry);
 1602            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1603                self.undo_stack.pop_front();
 1604            }
 1605        }
 1606    }
 1607
 1608    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1609        if self
 1610            .redo_stack
 1611            .back()
 1612            .is_none_or(|e| e.selections != entry.selections)
 1613        {
 1614            self.redo_stack.push_back(entry);
 1615            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1616                self.redo_stack.pop_front();
 1617            }
 1618        }
 1619    }
 1620}
 1621
 1622#[derive(Clone, Copy)]
 1623pub struct RowHighlightOptions {
 1624    pub autoscroll: bool,
 1625    pub include_gutter: bool,
 1626}
 1627
 1628impl Default for RowHighlightOptions {
 1629    fn default() -> Self {
 1630        Self {
 1631            autoscroll: Default::default(),
 1632            include_gutter: true,
 1633        }
 1634    }
 1635}
 1636
 1637struct RowHighlight {
 1638    index: usize,
 1639    range: Range<Anchor>,
 1640    color: Hsla,
 1641    options: RowHighlightOptions,
 1642    type_id: TypeId,
 1643}
 1644
 1645#[derive(Clone, Debug)]
 1646struct AddSelectionsState {
 1647    groups: Vec<AddSelectionsGroup>,
 1648}
 1649
 1650#[derive(Clone, Debug)]
 1651struct AddSelectionsGroup {
 1652    above: bool,
 1653    stack: Vec<usize>,
 1654}
 1655
 1656#[derive(Clone)]
 1657struct SelectNextState {
 1658    query: AhoCorasick,
 1659    wordwise: bool,
 1660    done: bool,
 1661}
 1662
 1663impl std::fmt::Debug for SelectNextState {
 1664    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1665        f.debug_struct(std::any::type_name::<Self>())
 1666            .field("wordwise", &self.wordwise)
 1667            .field("done", &self.done)
 1668            .finish()
 1669    }
 1670}
 1671
 1672#[derive(Debug)]
 1673struct AutocloseRegion {
 1674    selection_id: usize,
 1675    range: Range<Anchor>,
 1676    pair: BracketPair,
 1677}
 1678
 1679#[derive(Debug)]
 1680struct SnippetState {
 1681    ranges: Vec<Vec<Range<Anchor>>>,
 1682    active_index: usize,
 1683    choices: Vec<Option<Vec<String>>>,
 1684}
 1685
 1686#[doc(hidden)]
 1687pub struct RenameState {
 1688    pub range: Range<Anchor>,
 1689    pub old_name: Arc<str>,
 1690    pub editor: Entity<Editor>,
 1691    block_id: CustomBlockId,
 1692}
 1693
 1694struct InvalidationStack<T>(Vec<T>);
 1695
 1696struct RegisteredEditPredictionDelegate {
 1697    provider: Arc<dyn EditPredictionDelegateHandle>,
 1698    _subscription: Subscription,
 1699}
 1700
 1701#[derive(Debug, PartialEq, Eq)]
 1702pub struct ActiveDiagnosticGroup {
 1703    pub active_range: Range<Anchor>,
 1704    pub active_message: String,
 1705    pub group_id: usize,
 1706    pub blocks: HashSet<CustomBlockId>,
 1707}
 1708
 1709#[derive(Debug, PartialEq, Eq)]
 1710
 1711pub(crate) enum ActiveDiagnostic {
 1712    None,
 1713    All,
 1714    Group(ActiveDiagnosticGroup),
 1715}
 1716
 1717#[derive(Serialize, Deserialize, Clone, Debug)]
 1718pub struct ClipboardSelection {
 1719    /// The number of bytes in this selection.
 1720    pub len: usize,
 1721    /// Whether this was a full-line selection.
 1722    pub is_entire_line: bool,
 1723    /// The indentation of the first line when this content was originally copied.
 1724    pub first_line_indent: u32,
 1725    #[serde(default)]
 1726    pub file_path: Option<PathBuf>,
 1727    #[serde(default)]
 1728    pub line_range: Option<RangeInclusive<u32>>,
 1729}
 1730
 1731impl ClipboardSelection {
 1732    pub fn for_buffer(
 1733        len: usize,
 1734        is_entire_line: bool,
 1735        range: Range<Point>,
 1736        buffer: &MultiBufferSnapshot,
 1737        project: Option<&Entity<Project>>,
 1738        cx: &App,
 1739    ) -> Self {
 1740        let first_line_indent = buffer
 1741            .indent_size_for_line(MultiBufferRow(range.start.row))
 1742            .len;
 1743
 1744        let file_path = util::maybe!({
 1745            let project = project?.read(cx);
 1746            let file = buffer.file_at(range.start)?;
 1747            let project_path = ProjectPath {
 1748                worktree_id: file.worktree_id(cx),
 1749                path: file.path().clone(),
 1750            };
 1751            project.absolute_path(&project_path, cx)
 1752        });
 1753
 1754        let line_range = file_path.as_ref().and_then(|_| {
 1755            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1756            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1757            if start_excerpt_id == end_excerpt_id {
 1758                Some(start_point.row..=end_point.row)
 1759            } else {
 1760                None
 1761            }
 1762        });
 1763
 1764        Self {
 1765            len,
 1766            is_entire_line,
 1767            first_line_indent,
 1768            file_path,
 1769            line_range,
 1770        }
 1771    }
 1772}
 1773
 1774// selections, scroll behavior, was newest selection reversed
 1775type SelectSyntaxNodeHistoryState = (
 1776    Box<[Selection<MultiBufferOffset>]>,
 1777    SelectSyntaxNodeScrollBehavior,
 1778    bool,
 1779);
 1780
 1781#[derive(Default)]
 1782struct SelectSyntaxNodeHistory {
 1783    stack: Vec<SelectSyntaxNodeHistoryState>,
 1784    // disable temporarily to allow changing selections without losing the stack
 1785    pub disable_clearing: bool,
 1786}
 1787
 1788impl SelectSyntaxNodeHistory {
 1789    pub fn try_clear(&mut self) {
 1790        if !self.disable_clearing {
 1791            self.stack.clear();
 1792        }
 1793    }
 1794
 1795    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1796        self.stack.push(selection);
 1797    }
 1798
 1799    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1800        self.stack.pop()
 1801    }
 1802}
 1803
 1804enum SelectSyntaxNodeScrollBehavior {
 1805    CursorTop,
 1806    FitSelection,
 1807    CursorBottom,
 1808}
 1809
 1810#[derive(Debug, Clone, Copy)]
 1811pub(crate) struct NavigationData {
 1812    cursor_anchor: Anchor,
 1813    cursor_position: Point,
 1814    scroll_anchor: ScrollAnchor,
 1815    scroll_top_row: u32,
 1816}
 1817
 1818#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1819pub enum GotoDefinitionKind {
 1820    Symbol,
 1821    Declaration,
 1822    Type,
 1823    Implementation,
 1824}
 1825
 1826pub enum FormatTarget {
 1827    Buffers(HashSet<Entity<Buffer>>),
 1828    Ranges(Vec<Range<MultiBufferPoint>>),
 1829}
 1830
 1831pub(crate) struct FocusedBlock {
 1832    id: BlockId,
 1833    focus_handle: WeakFocusHandle,
 1834}
 1835
 1836#[derive(Clone, Debug)]
 1837pub enum JumpData {
 1838    MultiBufferRow {
 1839        row: MultiBufferRow,
 1840        line_offset_from_top: u32,
 1841    },
 1842    MultiBufferPoint {
 1843        excerpt_id: ExcerptId,
 1844        position: Point,
 1845        anchor: text::Anchor,
 1846        line_offset_from_top: u32,
 1847    },
 1848}
 1849
 1850pub enum MultibufferSelectionMode {
 1851    First,
 1852    All,
 1853}
 1854
 1855#[derive(Clone, Copy, Debug, Default)]
 1856pub struct RewrapOptions {
 1857    pub override_language_settings: bool,
 1858    pub preserve_existing_whitespace: bool,
 1859}
 1860
 1861impl Editor {
 1862    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1863        let buffer = cx.new(|cx| Buffer::local("", cx));
 1864        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1865        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1866    }
 1867
 1868    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1869        let buffer = cx.new(|cx| Buffer::local("", cx));
 1870        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1871        Self::new(EditorMode::full(), buffer, None, window, cx)
 1872    }
 1873
 1874    pub fn auto_height(
 1875        min_lines: usize,
 1876        max_lines: usize,
 1877        window: &mut Window,
 1878        cx: &mut Context<Self>,
 1879    ) -> Self {
 1880        let buffer = cx.new(|cx| Buffer::local("", cx));
 1881        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1882        Self::new(
 1883            EditorMode::AutoHeight {
 1884                min_lines,
 1885                max_lines: Some(max_lines),
 1886            },
 1887            buffer,
 1888            None,
 1889            window,
 1890            cx,
 1891        )
 1892    }
 1893
 1894    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1895    /// The editor grows as tall as needed to fit its content.
 1896    pub fn auto_height_unbounded(
 1897        min_lines: usize,
 1898        window: &mut Window,
 1899        cx: &mut Context<Self>,
 1900    ) -> Self {
 1901        let buffer = cx.new(|cx| Buffer::local("", cx));
 1902        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1903        Self::new(
 1904            EditorMode::AutoHeight {
 1905                min_lines,
 1906                max_lines: None,
 1907            },
 1908            buffer,
 1909            None,
 1910            window,
 1911            cx,
 1912        )
 1913    }
 1914
 1915    pub fn for_buffer(
 1916        buffer: Entity<Buffer>,
 1917        project: Option<Entity<Project>>,
 1918        window: &mut Window,
 1919        cx: &mut Context<Self>,
 1920    ) -> Self {
 1921        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1922        Self::new(EditorMode::full(), buffer, project, window, cx)
 1923    }
 1924
 1925    pub fn for_multibuffer(
 1926        buffer: Entity<MultiBuffer>,
 1927        project: Option<Entity<Project>>,
 1928        window: &mut Window,
 1929        cx: &mut Context<Self>,
 1930    ) -> Self {
 1931        Self::new(EditorMode::full(), buffer, project, window, cx)
 1932    }
 1933
 1934    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1935        let mut clone = Self::new(
 1936            self.mode.clone(),
 1937            self.buffer.clone(),
 1938            self.project.clone(),
 1939            window,
 1940            cx,
 1941        );
 1942        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1943            let snapshot = display_map.snapshot(cx);
 1944            clone.display_map.update(cx, |display_map, cx| {
 1945                display_map.set_state(&snapshot, cx);
 1946            });
 1947            snapshot
 1948        });
 1949        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1950        clone.folds_did_change(cx);
 1951        clone.selections.clone_state(&self.selections);
 1952        clone
 1953            .scroll_manager
 1954            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1955        clone.searchable = self.searchable;
 1956        clone.read_only = self.read_only;
 1957        clone
 1958    }
 1959
 1960    pub fn new(
 1961        mode: EditorMode,
 1962        buffer: Entity<MultiBuffer>,
 1963        project: Option<Entity<Project>>,
 1964        window: &mut Window,
 1965        cx: &mut Context<Self>,
 1966    ) -> Self {
 1967        Editor::new_internal(mode, buffer, project, None, window, cx)
 1968    }
 1969
 1970    pub fn sticky_headers(
 1971        &self,
 1972        display_snapshot: &DisplaySnapshot,
 1973        style: &EditorStyle,
 1974        cx: &App,
 1975    ) -> Option<Vec<OutlineItem<Anchor>>> {
 1976        let multi_buffer = self.buffer().read(cx);
 1977        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 1978        let multi_buffer_visible_start = self
 1979            .scroll_manager
 1980            .native_anchor(display_snapshot, cx)
 1981            .anchor
 1982            .to_point(&multi_buffer_snapshot);
 1983        let max_row = multi_buffer_snapshot.max_point().row;
 1984
 1985        let start_row = (multi_buffer_visible_start.row).min(max_row);
 1986        let end_row = (multi_buffer_visible_start.row + 10).min(max_row);
 1987
 1988        if let Some((excerpt_id, _, buffer)) = multi_buffer.read(cx).as_singleton() {
 1989            let outline_items = buffer
 1990                .outline_items_containing(
 1991                    Point::new(start_row, 0)..Point::new(end_row, 0),
 1992                    true,
 1993                    Some(style.syntax.as_ref()),
 1994                )
 1995                .into_iter()
 1996                .map(|outline_item| OutlineItem {
 1997                    depth: outline_item.depth,
 1998                    range: Anchor::range_in_buffer(*excerpt_id, outline_item.range),
 1999                    source_range_for_text: Anchor::range_in_buffer(
 2000                        *excerpt_id,
 2001                        outline_item.source_range_for_text,
 2002                    ),
 2003                    text: outline_item.text,
 2004                    highlight_ranges: outline_item.highlight_ranges,
 2005                    name_ranges: outline_item.name_ranges,
 2006                    body_range: outline_item
 2007                        .body_range
 2008                        .map(|range| Anchor::range_in_buffer(*excerpt_id, range)),
 2009                    annotation_range: outline_item
 2010                        .annotation_range
 2011                        .map(|range| Anchor::range_in_buffer(*excerpt_id, range)),
 2012                });
 2013            return Some(outline_items.collect());
 2014        }
 2015
 2016        None
 2017    }
 2018
 2019    fn new_internal(
 2020        mode: EditorMode,
 2021        multi_buffer: Entity<MultiBuffer>,
 2022        project: Option<Entity<Project>>,
 2023        display_map: Option<Entity<DisplayMap>>,
 2024        window: &mut Window,
 2025        cx: &mut Context<Self>,
 2026    ) -> Self {
 2027        debug_assert!(
 2028            display_map.is_none() || mode.is_minimap(),
 2029            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2030        );
 2031
 2032        let full_mode = mode.is_full();
 2033        let is_minimap = mode.is_minimap();
 2034        let diagnostics_max_severity = if full_mode {
 2035            EditorSettings::get_global(cx)
 2036                .diagnostics_max_severity
 2037                .unwrap_or(DiagnosticSeverity::Hint)
 2038        } else {
 2039            DiagnosticSeverity::Off
 2040        };
 2041        let style = window.text_style();
 2042        let font_size = style.font_size.to_pixels(window.rem_size());
 2043        let editor = cx.entity().downgrade();
 2044        let fold_placeholder = FoldPlaceholder {
 2045            constrain_width: false,
 2046            render: Arc::new(move |fold_id, fold_range, cx| {
 2047                let editor = editor.clone();
 2048                div()
 2049                    .id(fold_id)
 2050                    .bg(cx.theme().colors().ghost_element_background)
 2051                    .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
 2052                    .active(|style| style.bg(cx.theme().colors().ghost_element_active))
 2053                    .rounded_xs()
 2054                    .size_full()
 2055                    .cursor_pointer()
 2056                    .child("")
 2057                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2058                    .on_click(move |_, _window, cx| {
 2059                        editor
 2060                            .update(cx, |editor, cx| {
 2061                                editor.unfold_ranges(
 2062                                    &[fold_range.start..fold_range.end],
 2063                                    true,
 2064                                    false,
 2065                                    cx,
 2066                                );
 2067                                cx.stop_propagation();
 2068                            })
 2069                            .ok();
 2070                    })
 2071                    .into_any()
 2072            }),
 2073            merge_adjacent: true,
 2074            ..FoldPlaceholder::default()
 2075        };
 2076        let display_map = display_map.unwrap_or_else(|| {
 2077            cx.new(|cx| {
 2078                DisplayMap::new(
 2079                    multi_buffer.clone(),
 2080                    style.font(),
 2081                    font_size,
 2082                    None,
 2083                    FILE_HEADER_HEIGHT,
 2084                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2085                    fold_placeholder,
 2086                    diagnostics_max_severity,
 2087                    cx,
 2088                )
 2089            })
 2090        });
 2091
 2092        let selections = SelectionsCollection::new();
 2093
 2094        let blink_manager = cx.new(|cx| {
 2095            let mut blink_manager = BlinkManager::new(
 2096                CURSOR_BLINK_INTERVAL,
 2097                |cx| EditorSettings::get_global(cx).cursor_blink,
 2098                cx,
 2099            );
 2100            if is_minimap {
 2101                blink_manager.disable(cx);
 2102            }
 2103            blink_manager
 2104        });
 2105
 2106        let soft_wrap_mode_override =
 2107            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2108
 2109        let mut project_subscriptions = Vec::new();
 2110        if full_mode && let Some(project) = project.as_ref() {
 2111            project_subscriptions.push(cx.subscribe_in(
 2112                project,
 2113                window,
 2114                |editor, _, event, window, cx| match event {
 2115                    project::Event::RefreshCodeLens => {
 2116                        // we always query lens with actions, without storing them, always refreshing them
 2117                    }
 2118                    project::Event::RefreshInlayHints {
 2119                        server_id,
 2120                        request_id,
 2121                    } => {
 2122                        editor.refresh_inlay_hints(
 2123                            InlayHintRefreshReason::RefreshRequested {
 2124                                server_id: *server_id,
 2125                                request_id: *request_id,
 2126                            },
 2127                            cx,
 2128                        );
 2129                    }
 2130                    project::Event::LanguageServerRemoved(..) => {
 2131                        if editor.tasks_update_task.is_none() {
 2132                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2133                        }
 2134                        editor.registered_buffers.clear();
 2135                        editor.register_visible_buffers(cx);
 2136                    }
 2137                    project::Event::LanguageServerAdded(..) => {
 2138                        if editor.tasks_update_task.is_none() {
 2139                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2140                        }
 2141                    }
 2142                    project::Event::SnippetEdit(id, snippet_edits) => {
 2143                        // todo(lw): Non singletons
 2144                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2145                            let snapshot = buffer.read(cx).snapshot();
 2146                            let focus_handle = editor.focus_handle(cx);
 2147                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2148                                for (range, snippet) in snippet_edits {
 2149                                    let buffer_range =
 2150                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2151                                    editor
 2152                                        .insert_snippet(
 2153                                            &[MultiBufferOffset(buffer_range.start)
 2154                                                ..MultiBufferOffset(buffer_range.end)],
 2155                                            snippet.clone(),
 2156                                            window,
 2157                                            cx,
 2158                                        )
 2159                                        .ok();
 2160                                }
 2161                            }
 2162                        }
 2163                    }
 2164                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2165                        let buffer_id = *buffer_id;
 2166                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2167                            editor.register_buffer(buffer_id, cx);
 2168                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2169                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2170                            refresh_linked_ranges(editor, window, cx);
 2171                            editor.refresh_code_actions(window, cx);
 2172                            editor.refresh_document_highlights(cx);
 2173                        }
 2174                    }
 2175
 2176                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2177                        let Some(workspace) = editor.workspace() else {
 2178                            return;
 2179                        };
 2180                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2181                        else {
 2182                            return;
 2183                        };
 2184
 2185                        if active_editor.entity_id() == cx.entity_id() {
 2186                            let entity_id = cx.entity_id();
 2187                            workspace.update(cx, |this, cx| {
 2188                                this.panes_mut()
 2189                                    .iter_mut()
 2190                                    .filter(|pane| pane.entity_id() != entity_id)
 2191                                    .for_each(|p| {
 2192                                        p.update(cx, |pane, _| {
 2193                                            pane.nav_history_mut().rename_item(
 2194                                                entity_id,
 2195                                                project_path.clone(),
 2196                                                abs_path.clone().into(),
 2197                                            );
 2198                                        })
 2199                                    });
 2200                            });
 2201
 2202                            Self::open_transaction_for_hidden_buffers(
 2203                                workspace,
 2204                                transaction.clone(),
 2205                                "Rename".to_string(),
 2206                                window,
 2207                                cx,
 2208                            );
 2209                        }
 2210                    }
 2211
 2212                    project::Event::WorkspaceEditApplied(transaction) => {
 2213                        let Some(workspace) = editor.workspace() else {
 2214                            return;
 2215                        };
 2216                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2217                        else {
 2218                            return;
 2219                        };
 2220
 2221                        if active_editor.entity_id() == cx.entity_id() {
 2222                            Self::open_transaction_for_hidden_buffers(
 2223                                workspace,
 2224                                transaction.clone(),
 2225                                "LSP Edit".to_string(),
 2226                                window,
 2227                                cx,
 2228                            );
 2229                        }
 2230                    }
 2231
 2232                    _ => {}
 2233                },
 2234            ));
 2235            if let Some(task_inventory) = project
 2236                .read(cx)
 2237                .task_store()
 2238                .read(cx)
 2239                .task_inventory()
 2240                .cloned()
 2241            {
 2242                project_subscriptions.push(cx.observe_in(
 2243                    &task_inventory,
 2244                    window,
 2245                    |editor, _, window, cx| {
 2246                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2247                    },
 2248                ));
 2249            };
 2250
 2251            project_subscriptions.push(cx.subscribe_in(
 2252                &project.read(cx).breakpoint_store(),
 2253                window,
 2254                |editor, _, event, window, cx| match event {
 2255                    BreakpointStoreEvent::ClearDebugLines => {
 2256                        editor.clear_row_highlights::<ActiveDebugLine>();
 2257                        editor.refresh_inline_values(cx);
 2258                    }
 2259                    BreakpointStoreEvent::SetDebugLine => {
 2260                        if editor.go_to_active_debug_line(window, cx) {
 2261                            cx.stop_propagation();
 2262                        }
 2263
 2264                        editor.refresh_inline_values(cx);
 2265                    }
 2266                    _ => {}
 2267                },
 2268            ));
 2269            let git_store = project.read(cx).git_store().clone();
 2270            let project = project.clone();
 2271            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2272                if let GitStoreEvent::RepositoryAdded = event {
 2273                    this.load_diff_task = Some(
 2274                        update_uncommitted_diff_for_buffer(
 2275                            cx.entity(),
 2276                            &project,
 2277                            this.buffer.read(cx).all_buffers(),
 2278                            this.buffer.clone(),
 2279                            cx,
 2280                        )
 2281                        .shared(),
 2282                    );
 2283                }
 2284            }));
 2285        }
 2286
 2287        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2288
 2289        let inlay_hint_settings =
 2290            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2291        let focus_handle = cx.focus_handle();
 2292        if !is_minimap {
 2293            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2294                .detach();
 2295            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2296                .detach();
 2297            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2298                .detach();
 2299            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2300                .detach();
 2301            cx.observe_pending_input(window, Self::observe_pending_input)
 2302                .detach();
 2303        }
 2304
 2305        let show_indent_guides =
 2306            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2307                Some(false)
 2308            } else {
 2309                None
 2310            };
 2311
 2312        let breakpoint_store = match (&mode, project.as_ref()) {
 2313            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2314            _ => None,
 2315        };
 2316
 2317        let mut code_action_providers = Vec::new();
 2318        let mut load_uncommitted_diff = None;
 2319        if let Some(project) = project.clone() {
 2320            load_uncommitted_diff = Some(
 2321                update_uncommitted_diff_for_buffer(
 2322                    cx.entity(),
 2323                    &project,
 2324                    multi_buffer.read(cx).all_buffers(),
 2325                    multi_buffer.clone(),
 2326                    cx,
 2327                )
 2328                .shared(),
 2329            );
 2330            code_action_providers.push(Rc::new(project) as Rc<_>);
 2331        }
 2332
 2333        let mut editor = Self {
 2334            focus_handle,
 2335            show_cursor_when_unfocused: false,
 2336            last_focused_descendant: None,
 2337            buffer: multi_buffer.clone(),
 2338            display_map: display_map.clone(),
 2339            placeholder_display_map: None,
 2340            selections,
 2341            scroll_manager: ScrollManager::new(cx),
 2342            columnar_selection_state: None,
 2343            add_selections_state: None,
 2344            select_next_state: None,
 2345            select_prev_state: None,
 2346            selection_history: SelectionHistory::default(),
 2347            defer_selection_effects: false,
 2348            deferred_selection_effects_state: None,
 2349            autoclose_regions: Vec::new(),
 2350            snippet_stack: InvalidationStack::default(),
 2351            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2352            ime_transaction: None,
 2353            active_diagnostics: ActiveDiagnostic::None,
 2354            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2355            inline_diagnostics_update: Task::ready(()),
 2356            inline_diagnostics: Vec::new(),
 2357            soft_wrap_mode_override,
 2358            diagnostics_max_severity,
 2359            hard_wrap: None,
 2360            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2361            semantics_provider: project.clone().map(|project| Rc::new(project) as _),
 2362            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2363            project,
 2364            blink_manager: blink_manager.clone(),
 2365            show_local_selections: true,
 2366            show_scrollbars: ScrollbarAxes {
 2367                horizontal: full_mode,
 2368                vertical: full_mode,
 2369            },
 2370            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2371            offset_content: !matches!(mode, EditorMode::SingleLine),
 2372            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2373            show_gutter: full_mode,
 2374            show_line_numbers: (!full_mode).then_some(false),
 2375            use_relative_line_numbers: None,
 2376            disable_expand_excerpt_buttons: !full_mode,
 2377            delegate_expand_excerpts: false,
 2378            delegate_stage_and_restore: false,
 2379            show_git_diff_gutter: None,
 2380            show_code_actions: None,
 2381            show_runnables: None,
 2382            show_breakpoints: None,
 2383            show_diff_review_button: false,
 2384            show_wrap_guides: None,
 2385            show_indent_guides,
 2386            buffers_with_disabled_indent_guides: HashSet::default(),
 2387            highlight_order: 0,
 2388            highlighted_rows: HashMap::default(),
 2389            background_highlights: HashMap::default(),
 2390            gutter_highlights: HashMap::default(),
 2391            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2392            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2393            nav_history: None,
 2394            context_menu: RefCell::new(None),
 2395            context_menu_options: None,
 2396            mouse_context_menu: None,
 2397            completion_tasks: Vec::new(),
 2398            inline_blame_popover: None,
 2399            inline_blame_popover_show_task: None,
 2400            signature_help_state: SignatureHelpState::default(),
 2401            auto_signature_help: None,
 2402            find_all_references_task_sources: Vec::new(),
 2403            next_completion_id: 0,
 2404            next_inlay_id: 0,
 2405            code_action_providers,
 2406            available_code_actions: None,
 2407            code_actions_task: None,
 2408            quick_selection_highlight_task: None,
 2409            debounced_selection_highlight_task: None,
 2410            debounced_selection_highlight_complete: false,
 2411            document_highlights_task: None,
 2412            linked_editing_range_task: None,
 2413            pending_rename: None,
 2414            searchable: !is_minimap,
 2415            cursor_shape: EditorSettings::get_global(cx)
 2416                .cursor_shape
 2417                .unwrap_or_default(),
 2418            cursor_offset_on_selection: false,
 2419            current_line_highlight: None,
 2420            autoindent_mode: Some(AutoindentMode::EachLine),
 2421            collapse_matches: false,
 2422            workspace: None,
 2423            input_enabled: !is_minimap,
 2424            use_modal_editing: full_mode,
 2425            read_only: is_minimap,
 2426            use_autoclose: true,
 2427            use_auto_surround: true,
 2428            auto_replace_emoji_shortcode: false,
 2429            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2430            leader_id: None,
 2431            remote_id: None,
 2432            hover_state: HoverState::default(),
 2433            pending_mouse_down: None,
 2434            prev_pressure_stage: None,
 2435            hovered_link_state: None,
 2436            edit_prediction_provider: None,
 2437            active_edit_prediction: None,
 2438            stale_edit_prediction_in_menu: None,
 2439            edit_prediction_preview: EditPredictionPreview::Inactive {
 2440                released_too_fast: false,
 2441            },
 2442            inline_diagnostics_enabled: full_mode,
 2443            diagnostics_enabled: full_mode,
 2444            word_completions_enabled: full_mode,
 2445            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2446            gutter_hovered: false,
 2447            pixel_position_of_newest_cursor: None,
 2448            last_bounds: None,
 2449            last_position_map: None,
 2450            expect_bounds_change: None,
 2451            gutter_dimensions: GutterDimensions::default(),
 2452            style: None,
 2453            show_cursor_names: false,
 2454            hovered_cursors: HashMap::default(),
 2455            next_editor_action_id: EditorActionId::default(),
 2456            editor_actions: Rc::default(),
 2457            edit_predictions_hidden_for_vim_mode: false,
 2458            show_edit_predictions_override: None,
 2459            show_completions_on_input_override: None,
 2460            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2461            edit_prediction_settings: EditPredictionSettings::Disabled,
 2462            edit_prediction_indent_conflict: false,
 2463            edit_prediction_requires_modifier_in_indent_conflict: true,
 2464            custom_context_menu: None,
 2465            show_git_blame_gutter: false,
 2466            show_git_blame_inline: false,
 2467            show_selection_menu: None,
 2468            show_git_blame_inline_delay_task: None,
 2469            git_blame_inline_enabled: full_mode
 2470                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2471            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2472            buffer_serialization: is_minimap.not().then(|| {
 2473                BufferSerialization::new(
 2474                    ProjectSettings::get_global(cx)
 2475                        .session
 2476                        .restore_unsaved_buffers,
 2477                )
 2478            }),
 2479            blame: None,
 2480            blame_subscription: None,
 2481            tasks: BTreeMap::default(),
 2482
 2483            breakpoint_store,
 2484            gutter_breakpoint_indicator: (None, None),
 2485            gutter_diff_review_indicator: (None, None),
 2486            diff_review_drag_state: None,
 2487            diff_review_overlays: Vec::new(),
 2488            stored_review_comments: Vec::new(),
 2489            next_review_comment_id: 0,
 2490            hovered_diff_hunk_row: None,
 2491            _subscriptions: (!is_minimap)
 2492                .then(|| {
 2493                    vec![
 2494                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2495                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2496                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2497                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2498                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2499                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2500                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2501                        cx.observe_window_activation(window, |editor, window, cx| {
 2502                            let active = window.is_window_active();
 2503                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2504                                if active {
 2505                                    blink_manager.enable(cx);
 2506                                } else {
 2507                                    blink_manager.disable(cx);
 2508                                }
 2509                            });
 2510                            if active {
 2511                                editor.show_mouse_cursor(cx);
 2512                            }
 2513                        }),
 2514                    ]
 2515                })
 2516                .unwrap_or_default(),
 2517            tasks_update_task: None,
 2518            pull_diagnostics_task: Task::ready(()),
 2519            colors: None,
 2520            refresh_colors_task: Task::ready(()),
 2521            inlay_hints: None,
 2522            next_color_inlay_id: 0,
 2523            post_scroll_update: Task::ready(()),
 2524            linked_edit_ranges: Default::default(),
 2525            in_project_search: false,
 2526            previous_search_ranges: None,
 2527            breadcrumb_header: None,
 2528            focused_block: None,
 2529            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2530            addons: HashMap::default(),
 2531            registered_buffers: HashMap::default(),
 2532            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2533            selection_mark_mode: false,
 2534            toggle_fold_multiple_buffers: Task::ready(()),
 2535            serialize_selections: Task::ready(()),
 2536            serialize_folds: Task::ready(()),
 2537            text_style_refinement: None,
 2538            load_diff_task: load_uncommitted_diff,
 2539            temporary_diff_override: false,
 2540            mouse_cursor_hidden: false,
 2541            minimap: None,
 2542            hide_mouse_mode: EditorSettings::get_global(cx)
 2543                .hide_mouse
 2544                .unwrap_or_default(),
 2545            change_list: ChangeList::new(),
 2546            mode,
 2547            selection_drag_state: SelectionDragState::None,
 2548            folding_newlines: Task::ready(()),
 2549            lookup_key: None,
 2550            select_next_is_case_sensitive: None,
 2551            on_local_selections_changed: None,
 2552            suppress_selection_callback: false,
 2553            applicable_language_settings: HashMap::default(),
 2554            accent_data: None,
 2555            fetched_tree_sitter_chunks: HashMap::default(),
 2556            number_deleted_lines: false,
 2557        };
 2558
 2559        if is_minimap {
 2560            return editor;
 2561        }
 2562
 2563        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2564        editor.accent_data = editor.fetch_accent_data(cx);
 2565
 2566        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2567            editor
 2568                ._subscriptions
 2569                .push(cx.observe(breakpoints, |_, _, cx| {
 2570                    cx.notify();
 2571                }));
 2572        }
 2573        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2574        editor._subscriptions.extend(project_subscriptions);
 2575
 2576        editor._subscriptions.push(cx.subscribe_in(
 2577            &cx.entity(),
 2578            window,
 2579            |editor, _, e: &EditorEvent, window, cx| match e {
 2580                EditorEvent::ScrollPositionChanged { local, .. } => {
 2581                    if *local {
 2582                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2583                        editor.inline_blame_popover.take();
 2584                        let snapshot = editor.snapshot(window, cx);
 2585                        let new_anchor = editor
 2586                            .scroll_manager
 2587                            .native_anchor(&snapshot.display_snapshot, cx);
 2588                        editor.update_restoration_data(cx, move |data| {
 2589                            data.scroll_position = (
 2590                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2591                                new_anchor.offset,
 2592                            );
 2593                        });
 2594
 2595                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2596                            cx.background_executor()
 2597                                .timer(Duration::from_millis(50))
 2598                                .await;
 2599                            editor
 2600                                .update_in(cx, |editor, window, cx| {
 2601                                    editor.register_visible_buffers(cx);
 2602                                    editor.refresh_colors_for_visible_range(None, window, cx);
 2603                                    editor.refresh_inlay_hints(
 2604                                        InlayHintRefreshReason::NewLinesShown,
 2605                                        cx,
 2606                                    );
 2607                                    editor.colorize_brackets(false, cx);
 2608                                })
 2609                                .ok();
 2610                        });
 2611                    }
 2612                }
 2613                EditorEvent::Edited { .. } => {
 2614                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2615                        .map(|vim_mode| vim_mode.0)
 2616                        .unwrap_or(false);
 2617                    if !vim_mode {
 2618                        let display_map = editor.display_snapshot(cx);
 2619                        let selections = editor.selections.all_adjusted_display(&display_map);
 2620                        let pop_state = editor
 2621                            .change_list
 2622                            .last()
 2623                            .map(|previous| {
 2624                                previous.len() == selections.len()
 2625                                    && previous.iter().enumerate().all(|(ix, p)| {
 2626                                        p.to_display_point(&display_map).row()
 2627                                            == selections[ix].head().row()
 2628                                    })
 2629                            })
 2630                            .unwrap_or(false);
 2631                        let new_positions = selections
 2632                            .into_iter()
 2633                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2634                            .collect();
 2635                        editor
 2636                            .change_list
 2637                            .push_to_change_list(pop_state, new_positions);
 2638                    }
 2639                }
 2640                _ => (),
 2641            },
 2642        ));
 2643
 2644        if let Some(dap_store) = editor
 2645            .project
 2646            .as_ref()
 2647            .map(|project| project.read(cx).dap_store())
 2648        {
 2649            let weak_editor = cx.weak_entity();
 2650
 2651            editor
 2652                ._subscriptions
 2653                .push(
 2654                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2655                        let session_entity = cx.entity();
 2656                        weak_editor
 2657                            .update(cx, |editor, cx| {
 2658                                editor._subscriptions.push(
 2659                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2660                                );
 2661                            })
 2662                            .ok();
 2663                    }),
 2664                );
 2665
 2666            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2667                editor
 2668                    ._subscriptions
 2669                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2670            }
 2671        }
 2672
 2673        // skip adding the initial selection to selection history
 2674        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2675        editor.end_selection(window, cx);
 2676        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2677
 2678        editor.scroll_manager.show_scrollbars(window, cx);
 2679        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2680
 2681        if full_mode {
 2682            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2683            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2684
 2685            if editor.git_blame_inline_enabled {
 2686                editor.start_git_blame_inline(false, window, cx);
 2687            }
 2688
 2689            editor.go_to_active_debug_line(window, cx);
 2690
 2691            editor.minimap =
 2692                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2693            editor.colors = Some(LspColorData::new(cx));
 2694            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2695
 2696            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2697                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2698            }
 2699            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2700        }
 2701
 2702        editor
 2703    }
 2704
 2705    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2706        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2707    }
 2708
 2709    pub fn deploy_mouse_context_menu(
 2710        &mut self,
 2711        position: gpui::Point<Pixels>,
 2712        context_menu: Entity<ContextMenu>,
 2713        window: &mut Window,
 2714        cx: &mut Context<Self>,
 2715    ) {
 2716        self.mouse_context_menu = Some(MouseContextMenu::new(
 2717            self,
 2718            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2719            context_menu,
 2720            window,
 2721            cx,
 2722        ));
 2723    }
 2724
 2725    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2726        self.mouse_context_menu
 2727            .as_ref()
 2728            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2729    }
 2730
 2731    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2732        if self
 2733            .selections
 2734            .pending_anchor()
 2735            .is_some_and(|pending_selection| {
 2736                let snapshot = self.buffer().read(cx).snapshot(cx);
 2737                pending_selection.range().includes(range, &snapshot)
 2738            })
 2739        {
 2740            return true;
 2741        }
 2742
 2743        self.selections
 2744            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2745            .into_iter()
 2746            .any(|selection| {
 2747                // This is needed to cover a corner case, if we just check for an existing
 2748                // selection in the fold range, having a cursor at the start of the fold
 2749                // marks it as selected. Non-empty selections don't cause this.
 2750                let length = selection.end - selection.start;
 2751                length > 0
 2752            })
 2753    }
 2754
 2755    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2756        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2757    }
 2758
 2759    fn key_context_internal(
 2760        &self,
 2761        has_active_edit_prediction: bool,
 2762        window: &mut Window,
 2763        cx: &mut App,
 2764    ) -> KeyContext {
 2765        let mut key_context = KeyContext::new_with_defaults();
 2766        key_context.add("Editor");
 2767        let mode = match self.mode {
 2768            EditorMode::SingleLine => "single_line",
 2769            EditorMode::AutoHeight { .. } => "auto_height",
 2770            EditorMode::Minimap { .. } => "minimap",
 2771            EditorMode::Full { .. } => "full",
 2772        };
 2773
 2774        if EditorSettings::jupyter_enabled(cx) {
 2775            key_context.add("jupyter");
 2776        }
 2777
 2778        key_context.set("mode", mode);
 2779        if self.pending_rename.is_some() {
 2780            key_context.add("renaming");
 2781        }
 2782
 2783        if let Some(snippet_stack) = self.snippet_stack.last() {
 2784            key_context.add("in_snippet");
 2785
 2786            if snippet_stack.active_index > 0 {
 2787                key_context.add("has_previous_tabstop");
 2788            }
 2789
 2790            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2791                key_context.add("has_next_tabstop");
 2792            }
 2793        }
 2794
 2795        match self.context_menu.borrow().as_ref() {
 2796            Some(CodeContextMenu::Completions(menu)) => {
 2797                if menu.visible() {
 2798                    key_context.add("menu");
 2799                    key_context.add("showing_completions");
 2800                }
 2801            }
 2802            Some(CodeContextMenu::CodeActions(menu)) => {
 2803                if menu.visible() {
 2804                    key_context.add("menu");
 2805                    key_context.add("showing_code_actions")
 2806                }
 2807            }
 2808            None => {}
 2809        }
 2810
 2811        if self.signature_help_state.has_multiple_signatures() {
 2812            key_context.add("showing_signature_help");
 2813        }
 2814
 2815        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2816        if !self.focus_handle(cx).contains_focused(window, cx)
 2817            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2818        {
 2819            for addon in self.addons.values() {
 2820                addon.extend_key_context(&mut key_context, cx)
 2821            }
 2822        }
 2823
 2824        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2825            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2826                Some(
 2827                    file.full_path(cx)
 2828                        .extension()?
 2829                        .to_string_lossy()
 2830                        .to_lowercase(),
 2831                )
 2832            }) {
 2833                key_context.set("extension", extension);
 2834            }
 2835        } else {
 2836            key_context.add("multibuffer");
 2837        }
 2838
 2839        if has_active_edit_prediction {
 2840            if self.edit_prediction_in_conflict() {
 2841                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2842            } else {
 2843                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2844                key_context.add("copilot_suggestion");
 2845            }
 2846        }
 2847
 2848        if self.selection_mark_mode {
 2849            key_context.add("selection_mode");
 2850        }
 2851
 2852        let disjoint = self.selections.disjoint_anchors();
 2853        let snapshot = self.snapshot(window, cx);
 2854        let snapshot = snapshot.buffer_snapshot();
 2855        if self.mode == EditorMode::SingleLine
 2856            && let [selection] = disjoint
 2857            && selection.start == selection.end
 2858            && selection.end.to_offset(snapshot) == snapshot.len()
 2859        {
 2860            key_context.add("end_of_input");
 2861        }
 2862
 2863        if self.has_any_expanded_diff_hunks(cx) {
 2864            key_context.add("diffs_expanded");
 2865        }
 2866
 2867        key_context
 2868    }
 2869
 2870    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2871        self.last_bounds.as_ref()
 2872    }
 2873
 2874    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2875        if self.mouse_cursor_hidden {
 2876            self.mouse_cursor_hidden = false;
 2877            cx.notify();
 2878        }
 2879    }
 2880
 2881    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2882        let hide_mouse_cursor = match origin {
 2883            HideMouseCursorOrigin::TypingAction => {
 2884                matches!(
 2885                    self.hide_mouse_mode,
 2886                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2887                )
 2888            }
 2889            HideMouseCursorOrigin::MovementAction => {
 2890                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2891            }
 2892        };
 2893        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2894            self.mouse_cursor_hidden = hide_mouse_cursor;
 2895            cx.notify();
 2896        }
 2897    }
 2898
 2899    pub fn edit_prediction_in_conflict(&self) -> bool {
 2900        if !self.show_edit_predictions_in_menu() {
 2901            return false;
 2902        }
 2903
 2904        let showing_completions = self
 2905            .context_menu
 2906            .borrow()
 2907            .as_ref()
 2908            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2909
 2910        showing_completions
 2911            || self.edit_prediction_requires_modifier()
 2912            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2913            // bindings to insert tab characters.
 2914            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2915    }
 2916
 2917    pub fn accept_edit_prediction_keybind(
 2918        &self,
 2919        granularity: EditPredictionGranularity,
 2920        window: &mut Window,
 2921        cx: &mut App,
 2922    ) -> AcceptEditPredictionBinding {
 2923        let key_context = self.key_context_internal(true, window, cx);
 2924        let in_conflict = self.edit_prediction_in_conflict();
 2925
 2926        let bindings =
 2927            match granularity {
 2928                EditPredictionGranularity::Word => window
 2929                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2930                EditPredictionGranularity::Line => window
 2931                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2932                EditPredictionGranularity::Full => {
 2933                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2934                }
 2935            };
 2936
 2937        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2938            !in_conflict
 2939                || binding
 2940                    .keystrokes()
 2941                    .first()
 2942                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 2943        }))
 2944    }
 2945
 2946    pub fn new_file(
 2947        workspace: &mut Workspace,
 2948        _: &workspace::NewFile,
 2949        window: &mut Window,
 2950        cx: &mut Context<Workspace>,
 2951    ) {
 2952        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 2953            "Failed to create buffer",
 2954            window,
 2955            cx,
 2956            |e, _, _| match e.error_code() {
 2957                ErrorCode::RemoteUpgradeRequired => Some(format!(
 2958                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 2959                e.error_tag("required").unwrap_or("the latest version")
 2960            )),
 2961                _ => None,
 2962            },
 2963        );
 2964    }
 2965
 2966    pub fn new_in_workspace(
 2967        workspace: &mut Workspace,
 2968        window: &mut Window,
 2969        cx: &mut Context<Workspace>,
 2970    ) -> Task<Result<Entity<Editor>>> {
 2971        let project = workspace.project().clone();
 2972        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 2973
 2974        cx.spawn_in(window, async move |workspace, cx| {
 2975            let buffer = create.await?;
 2976            workspace.update_in(cx, |workspace, window, cx| {
 2977                let editor =
 2978                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 2979                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 2980                editor
 2981            })
 2982        })
 2983    }
 2984
 2985    fn new_file_vertical(
 2986        workspace: &mut Workspace,
 2987        _: &workspace::NewFileSplitVertical,
 2988        window: &mut Window,
 2989        cx: &mut Context<Workspace>,
 2990    ) {
 2991        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 2992    }
 2993
 2994    fn new_file_horizontal(
 2995        workspace: &mut Workspace,
 2996        _: &workspace::NewFileSplitHorizontal,
 2997        window: &mut Window,
 2998        cx: &mut Context<Workspace>,
 2999    ) {
 3000        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3001    }
 3002
 3003    fn new_file_split(
 3004        workspace: &mut Workspace,
 3005        action: &workspace::NewFileSplit,
 3006        window: &mut Window,
 3007        cx: &mut Context<Workspace>,
 3008    ) {
 3009        Self::new_file_in_direction(workspace, action.0, window, cx)
 3010    }
 3011
 3012    fn new_file_in_direction(
 3013        workspace: &mut Workspace,
 3014        direction: SplitDirection,
 3015        window: &mut Window,
 3016        cx: &mut Context<Workspace>,
 3017    ) {
 3018        let project = workspace.project().clone();
 3019        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3020
 3021        cx.spawn_in(window, async move |workspace, cx| {
 3022            let buffer = create.await?;
 3023            workspace.update_in(cx, move |workspace, window, cx| {
 3024                workspace.split_item(
 3025                    direction,
 3026                    Box::new(
 3027                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3028                    ),
 3029                    window,
 3030                    cx,
 3031                )
 3032            })?;
 3033            anyhow::Ok(())
 3034        })
 3035        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3036            match e.error_code() {
 3037                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3038                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3039                e.error_tag("required").unwrap_or("the latest version")
 3040            )),
 3041                _ => None,
 3042            }
 3043        });
 3044    }
 3045
 3046    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3047        self.leader_id
 3048    }
 3049
 3050    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3051        &self.buffer
 3052    }
 3053
 3054    pub fn project(&self) -> Option<&Entity<Project>> {
 3055        self.project.as_ref()
 3056    }
 3057
 3058    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3059        self.workspace.as_ref()?.0.upgrade()
 3060    }
 3061
 3062    /// Returns the workspace serialization ID if this editor should be serialized.
 3063    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3064        self.workspace
 3065            .as_ref()
 3066            .filter(|_| self.should_serialize_buffer())
 3067            .and_then(|workspace| workspace.1)
 3068    }
 3069
 3070    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3071        self.buffer().read(cx).title(cx)
 3072    }
 3073
 3074    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3075        let git_blame_gutter_max_author_length = self
 3076            .render_git_blame_gutter(cx)
 3077            .then(|| {
 3078                if let Some(blame) = self.blame.as_ref() {
 3079                    let max_author_length =
 3080                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3081                    Some(max_author_length)
 3082                } else {
 3083                    None
 3084                }
 3085            })
 3086            .flatten();
 3087
 3088        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3089
 3090        EditorSnapshot {
 3091            mode: self.mode.clone(),
 3092            show_gutter: self.show_gutter,
 3093            offset_content: self.offset_content,
 3094            show_line_numbers: self.show_line_numbers,
 3095            number_deleted_lines: self.number_deleted_lines,
 3096            show_git_diff_gutter: self.show_git_diff_gutter,
 3097            show_code_actions: self.show_code_actions,
 3098            show_runnables: self.show_runnables,
 3099            show_breakpoints: self.show_breakpoints,
 3100            git_blame_gutter_max_author_length,
 3101            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3102            display_snapshot,
 3103            placeholder_display_snapshot: self
 3104                .placeholder_display_map
 3105                .as_ref()
 3106                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3107            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3108            is_focused: self.focus_handle.is_focused(window),
 3109            current_line_highlight: self
 3110                .current_line_highlight
 3111                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3112            gutter_hovered: self.gutter_hovered,
 3113        }
 3114    }
 3115
 3116    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3117        self.buffer.read(cx).language_at(point, cx)
 3118    }
 3119
 3120    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3121        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3122    }
 3123
 3124    pub fn active_excerpt(
 3125        &self,
 3126        cx: &App,
 3127    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3128        self.buffer
 3129            .read(cx)
 3130            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3131    }
 3132
 3133    pub fn mode(&self) -> &EditorMode {
 3134        &self.mode
 3135    }
 3136
 3137    pub fn set_mode(&mut self, mode: EditorMode) {
 3138        self.mode = mode;
 3139    }
 3140
 3141    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3142        self.collaboration_hub.as_deref()
 3143    }
 3144
 3145    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3146        self.collaboration_hub = Some(hub);
 3147    }
 3148
 3149    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3150        self.in_project_search = in_project_search;
 3151    }
 3152
 3153    pub fn set_custom_context_menu(
 3154        &mut self,
 3155        f: impl 'static
 3156        + Fn(
 3157            &mut Self,
 3158            DisplayPoint,
 3159            &mut Window,
 3160            &mut Context<Self>,
 3161        ) -> Option<Entity<ui::ContextMenu>>,
 3162    ) {
 3163        self.custom_context_menu = Some(Box::new(f))
 3164    }
 3165
 3166    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3167        self.completion_provider = provider;
 3168    }
 3169
 3170    #[cfg(any(test, feature = "test-support"))]
 3171    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3172        self.completion_provider.clone()
 3173    }
 3174
 3175    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3176        self.semantics_provider.clone()
 3177    }
 3178
 3179    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3180        self.semantics_provider = provider;
 3181    }
 3182
 3183    pub fn set_edit_prediction_provider<T>(
 3184        &mut self,
 3185        provider: Option<Entity<T>>,
 3186        window: &mut Window,
 3187        cx: &mut Context<Self>,
 3188    ) where
 3189        T: EditPredictionDelegate,
 3190    {
 3191        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3192            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3193                if this.focus_handle.is_focused(window) {
 3194                    this.update_visible_edit_prediction(window, cx);
 3195                }
 3196            }),
 3197            provider: Arc::new(provider),
 3198        });
 3199        self.update_edit_prediction_settings(cx);
 3200        self.refresh_edit_prediction(false, false, window, cx);
 3201    }
 3202
 3203    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3204        self.placeholder_display_map
 3205            .as_ref()
 3206            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3207    }
 3208
 3209    pub fn set_placeholder_text(
 3210        &mut self,
 3211        placeholder_text: &str,
 3212        window: &mut Window,
 3213        cx: &mut Context<Self>,
 3214    ) {
 3215        let multibuffer = cx
 3216            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3217
 3218        let style = window.text_style();
 3219
 3220        self.placeholder_display_map = Some(cx.new(|cx| {
 3221            DisplayMap::new(
 3222                multibuffer,
 3223                style.font(),
 3224                style.font_size.to_pixels(window.rem_size()),
 3225                None,
 3226                FILE_HEADER_HEIGHT,
 3227                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3228                Default::default(),
 3229                DiagnosticSeverity::Off,
 3230                cx,
 3231            )
 3232        }));
 3233        cx.notify();
 3234    }
 3235
 3236    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3237        self.cursor_shape = cursor_shape;
 3238
 3239        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3240        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3241
 3242        cx.notify();
 3243    }
 3244
 3245    pub fn cursor_shape(&self) -> CursorShape {
 3246        self.cursor_shape
 3247    }
 3248
 3249    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3250        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3251    }
 3252
 3253    pub fn set_current_line_highlight(
 3254        &mut self,
 3255        current_line_highlight: Option<CurrentLineHighlight>,
 3256    ) {
 3257        self.current_line_highlight = current_line_highlight;
 3258    }
 3259
 3260    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3261        self.collapse_matches = collapse_matches;
 3262    }
 3263
 3264    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3265        if self.collapse_matches {
 3266            return range.start..range.start;
 3267        }
 3268        range.clone()
 3269    }
 3270
 3271    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3272        self.display_map.read(cx).clip_at_line_ends
 3273    }
 3274
 3275    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3276        if self.display_map.read(cx).clip_at_line_ends != clip {
 3277            self.display_map
 3278                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3279        }
 3280    }
 3281
 3282    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3283        self.input_enabled = input_enabled;
 3284    }
 3285
 3286    pub fn set_edit_predictions_hidden_for_vim_mode(
 3287        &mut self,
 3288        hidden: bool,
 3289        window: &mut Window,
 3290        cx: &mut Context<Self>,
 3291    ) {
 3292        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3293            self.edit_predictions_hidden_for_vim_mode = hidden;
 3294            if hidden {
 3295                self.update_visible_edit_prediction(window, cx);
 3296            } else {
 3297                self.refresh_edit_prediction(true, false, window, cx);
 3298            }
 3299        }
 3300    }
 3301
 3302    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3303        self.menu_edit_predictions_policy = value;
 3304    }
 3305
 3306    pub fn set_autoindent(&mut self, autoindent: bool) {
 3307        if autoindent {
 3308            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3309        } else {
 3310            self.autoindent_mode = None;
 3311        }
 3312    }
 3313
 3314    pub fn capability(&self, cx: &App) -> Capability {
 3315        if self.read_only {
 3316            Capability::ReadOnly
 3317        } else {
 3318            self.buffer.read(cx).capability()
 3319        }
 3320    }
 3321
 3322    pub fn read_only(&self, cx: &App) -> bool {
 3323        self.read_only || self.buffer.read(cx).read_only()
 3324    }
 3325
 3326    pub fn set_read_only(&mut self, read_only: bool) {
 3327        self.read_only = read_only;
 3328    }
 3329
 3330    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3331        self.use_autoclose = autoclose;
 3332    }
 3333
 3334    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3335        self.use_auto_surround = auto_surround;
 3336    }
 3337
 3338    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3339        self.auto_replace_emoji_shortcode = auto_replace;
 3340    }
 3341
 3342    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3343        self.buffer_serialization = should_serialize.then(|| {
 3344            BufferSerialization::new(
 3345                ProjectSettings::get_global(cx)
 3346                    .session
 3347                    .restore_unsaved_buffers,
 3348            )
 3349        })
 3350    }
 3351
 3352    fn should_serialize_buffer(&self) -> bool {
 3353        self.buffer_serialization.is_some()
 3354    }
 3355
 3356    pub fn toggle_edit_predictions(
 3357        &mut self,
 3358        _: &ToggleEditPrediction,
 3359        window: &mut Window,
 3360        cx: &mut Context<Self>,
 3361    ) {
 3362        if self.show_edit_predictions_override.is_some() {
 3363            self.set_show_edit_predictions(None, window, cx);
 3364        } else {
 3365            let show_edit_predictions = !self.edit_predictions_enabled();
 3366            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3367        }
 3368    }
 3369
 3370    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3371        self.show_completions_on_input_override = show_completions_on_input;
 3372    }
 3373
 3374    pub fn set_show_edit_predictions(
 3375        &mut self,
 3376        show_edit_predictions: Option<bool>,
 3377        window: &mut Window,
 3378        cx: &mut Context<Self>,
 3379    ) {
 3380        self.show_edit_predictions_override = show_edit_predictions;
 3381        self.update_edit_prediction_settings(cx);
 3382
 3383        if let Some(false) = show_edit_predictions {
 3384            self.discard_edit_prediction(false, cx);
 3385        } else {
 3386            self.refresh_edit_prediction(false, true, window, cx);
 3387        }
 3388    }
 3389
 3390    fn edit_predictions_disabled_in_scope(
 3391        &self,
 3392        buffer: &Entity<Buffer>,
 3393        buffer_position: language::Anchor,
 3394        cx: &App,
 3395    ) -> bool {
 3396        let snapshot = buffer.read(cx).snapshot();
 3397        let settings = snapshot.settings_at(buffer_position, cx);
 3398
 3399        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3400            return false;
 3401        };
 3402
 3403        scope.override_name().is_some_and(|scope_name| {
 3404            settings
 3405                .edit_predictions_disabled_in
 3406                .iter()
 3407                .any(|s| s == scope_name)
 3408        })
 3409    }
 3410
 3411    pub fn set_use_modal_editing(&mut self, to: bool) {
 3412        self.use_modal_editing = to;
 3413    }
 3414
 3415    pub fn use_modal_editing(&self) -> bool {
 3416        self.use_modal_editing
 3417    }
 3418
 3419    fn selections_did_change(
 3420        &mut self,
 3421        local: bool,
 3422        old_cursor_position: &Anchor,
 3423        effects: SelectionEffects,
 3424        window: &mut Window,
 3425        cx: &mut Context<Self>,
 3426    ) {
 3427        window.invalidate_character_coordinates();
 3428
 3429        // Copy selections to primary selection buffer
 3430        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3431        if local {
 3432            let selections = self
 3433                .selections
 3434                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3435            let buffer_handle = self.buffer.read(cx).read(cx);
 3436
 3437            let mut text = String::new();
 3438            for (index, selection) in selections.iter().enumerate() {
 3439                let text_for_selection = buffer_handle
 3440                    .text_for_range(selection.start..selection.end)
 3441                    .collect::<String>();
 3442
 3443                text.push_str(&text_for_selection);
 3444                if index != selections.len() - 1 {
 3445                    text.push('\n');
 3446                }
 3447            }
 3448
 3449            if !text.is_empty() {
 3450                cx.write_to_primary(ClipboardItem::new_string(text));
 3451            }
 3452        }
 3453
 3454        let selection_anchors = self.selections.disjoint_anchors_arc();
 3455
 3456        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3457            self.buffer.update(cx, |buffer, cx| {
 3458                buffer.set_active_selections(
 3459                    &selection_anchors,
 3460                    self.selections.line_mode(),
 3461                    self.cursor_shape,
 3462                    cx,
 3463                )
 3464            });
 3465        }
 3466        let display_map = self
 3467            .display_map
 3468            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3469        let buffer = display_map.buffer_snapshot();
 3470        if self.selections.count() == 1 {
 3471            self.add_selections_state = None;
 3472        }
 3473        self.select_next_state = None;
 3474        self.select_prev_state = None;
 3475        self.select_syntax_node_history.try_clear();
 3476        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3477        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3478        self.take_rename(false, window, cx);
 3479
 3480        let newest_selection = self.selections.newest_anchor();
 3481        let new_cursor_position = newest_selection.head();
 3482        let selection_start = newest_selection.start;
 3483
 3484        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3485            self.push_to_nav_history(
 3486                *old_cursor_position,
 3487                Some(new_cursor_position.to_point(buffer)),
 3488                false,
 3489                effects.nav_history == Some(true),
 3490                cx,
 3491            );
 3492        }
 3493
 3494        if local {
 3495            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3496                self.register_buffer(buffer_id, cx);
 3497            }
 3498
 3499            let mut context_menu = self.context_menu.borrow_mut();
 3500            let completion_menu = match context_menu.as_ref() {
 3501                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3502                Some(CodeContextMenu::CodeActions(_)) => {
 3503                    *context_menu = None;
 3504                    None
 3505                }
 3506                None => None,
 3507            };
 3508            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3509            drop(context_menu);
 3510
 3511            if effects.completions
 3512                && let Some(completion_position) = completion_position
 3513            {
 3514                let start_offset = selection_start.to_offset(buffer);
 3515                let position_matches = start_offset == completion_position.to_offset(buffer);
 3516                let continue_showing = if let Some((snap, ..)) =
 3517                    buffer.point_to_buffer_offset(completion_position)
 3518                    && !snap.capability.editable()
 3519                {
 3520                    false
 3521                } else if position_matches {
 3522                    if self.snippet_stack.is_empty() {
 3523                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3524                            == Some(CharKind::Word)
 3525                    } else {
 3526                        // Snippet choices can be shown even when the cursor is in whitespace.
 3527                        // Dismissing the menu with actions like backspace is handled by
 3528                        // invalidation regions.
 3529                        true
 3530                    }
 3531                } else {
 3532                    false
 3533                };
 3534
 3535                if continue_showing {
 3536                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3537                } else {
 3538                    self.hide_context_menu(window, cx);
 3539                }
 3540            }
 3541
 3542            hide_hover(self, cx);
 3543
 3544            if old_cursor_position.to_display_point(&display_map).row()
 3545                != new_cursor_position.to_display_point(&display_map).row()
 3546            {
 3547                self.available_code_actions.take();
 3548            }
 3549            self.refresh_code_actions(window, cx);
 3550            self.refresh_document_highlights(cx);
 3551            refresh_linked_ranges(self, window, cx);
 3552
 3553            self.refresh_selected_text_highlights(false, window, cx);
 3554            self.refresh_matching_bracket_highlights(window, cx);
 3555            self.update_visible_edit_prediction(window, cx);
 3556            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3557            self.inline_blame_popover.take();
 3558            if self.git_blame_inline_enabled {
 3559                self.start_inline_blame_timer(window, cx);
 3560            }
 3561        }
 3562
 3563        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3564
 3565        if local && !self.suppress_selection_callback {
 3566            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3567                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3568                callback(cursor_position, window, cx);
 3569            }
 3570        }
 3571
 3572        cx.emit(EditorEvent::SelectionsChanged { local });
 3573
 3574        let selections = &self.selections.disjoint_anchors_arc();
 3575        if selections.len() == 1 {
 3576            cx.emit(SearchEvent::ActiveMatchChanged)
 3577        }
 3578        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3579            let inmemory_selections = selections
 3580                .iter()
 3581                .map(|s| {
 3582                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3583                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3584                })
 3585                .collect();
 3586            self.update_restoration_data(cx, |data| {
 3587                data.selections = inmemory_selections;
 3588            });
 3589
 3590            if WorkspaceSettings::get(None, cx).restore_on_startup
 3591                != RestoreOnStartupBehavior::EmptyTab
 3592                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3593            {
 3594                let snapshot = self.buffer().read(cx).snapshot(cx);
 3595                let selections = selections.clone();
 3596                let background_executor = cx.background_executor().clone();
 3597                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3598                self.serialize_selections = cx.background_spawn(async move {
 3599                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3600                    let db_selections = selections
 3601                        .iter()
 3602                        .map(|selection| {
 3603                            (
 3604                                selection.start.to_offset(&snapshot).0,
 3605                                selection.end.to_offset(&snapshot).0,
 3606                            )
 3607                        })
 3608                        .collect();
 3609
 3610                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3611                        .await
 3612                        .with_context(|| {
 3613                            format!(
 3614                                "persisting editor selections for editor {editor_id}, \
 3615                                workspace {workspace_id:?}"
 3616                            )
 3617                        })
 3618                        .log_err();
 3619                });
 3620            }
 3621        }
 3622
 3623        cx.notify();
 3624    }
 3625
 3626    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3627        use text::ToOffset as _;
 3628        use text::ToPoint as _;
 3629
 3630        if self.mode.is_minimap()
 3631            || WorkspaceSettings::get(None, cx).restore_on_startup
 3632                == RestoreOnStartupBehavior::EmptyTab
 3633        {
 3634            return;
 3635        }
 3636
 3637        if !self.buffer().read(cx).is_singleton() {
 3638            return;
 3639        }
 3640
 3641        let display_snapshot = self
 3642            .display_map
 3643            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3644        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3645            return;
 3646        };
 3647        let inmemory_folds = display_snapshot
 3648            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3649            .map(|fold| {
 3650                fold.range.start.text_anchor.to_point(&snapshot)
 3651                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3652            })
 3653            .collect();
 3654        self.update_restoration_data(cx, |data| {
 3655            data.folds = inmemory_folds;
 3656        });
 3657
 3658        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3659            return;
 3660        };
 3661        let background_executor = cx.background_executor().clone();
 3662        let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3663        const FINGERPRINT_LEN: usize = 32;
 3664        let db_folds = display_snapshot
 3665            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3666            .map(|fold| {
 3667                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3668                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3669
 3670                // Extract fingerprints - content at fold boundaries for validation on restore
 3671                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3672                // content that might change independently.
 3673                // start_fp: first min(32, fold_len) bytes of fold content
 3674                // end_fp: last min(32, fold_len) bytes of fold content
 3675                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3676                let fold_len = end - start;
 3677                let start_fp_end = snapshot
 3678                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3679                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3680                let end_fp_start = snapshot
 3681                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3682                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3683
 3684                (start, end, start_fp, end_fp)
 3685            })
 3686            .collect::<Vec<_>>();
 3687        self.serialize_folds = cx.background_spawn(async move {
 3688            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3689            DB.save_editor_folds(editor_id, workspace_id, db_folds)
 3690                .await
 3691                .with_context(|| {
 3692                    format!(
 3693                        "persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"
 3694                    )
 3695                })
 3696                .log_err();
 3697        });
 3698    }
 3699
 3700    pub fn sync_selections(
 3701        &mut self,
 3702        other: Entity<Editor>,
 3703        cx: &mut Context<Self>,
 3704    ) -> gpui::Subscription {
 3705        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3706        if !other_selections.is_empty() {
 3707            self.selections
 3708                .change_with(&self.display_snapshot(cx), |selections| {
 3709                    selections.select_anchors(other_selections);
 3710                });
 3711        }
 3712
 3713        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3714            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3715                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3716                if other_selections.is_empty() {
 3717                    return;
 3718                }
 3719                let snapshot = this.display_snapshot(cx);
 3720                this.selections.change_with(&snapshot, |selections| {
 3721                    selections.select_anchors(other_selections);
 3722                });
 3723            }
 3724        });
 3725
 3726        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3727            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3728                let these_selections = this.selections.disjoint_anchors().to_vec();
 3729                if these_selections.is_empty() {
 3730                    return;
 3731                }
 3732                other.update(cx, |other_editor, cx| {
 3733                    let snapshot = other_editor.display_snapshot(cx);
 3734                    other_editor
 3735                        .selections
 3736                        .change_with(&snapshot, |selections| {
 3737                            selections.select_anchors(these_selections);
 3738                        })
 3739                });
 3740            }
 3741        });
 3742
 3743        Subscription::join(other_subscription, this_subscription)
 3744    }
 3745
 3746    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3747        if self.buffer().read(cx).is_singleton() {
 3748            return;
 3749        }
 3750        let snapshot = self.buffer.read(cx).snapshot(cx);
 3751        let buffer_ids: HashSet<BufferId> = self
 3752            .selections
 3753            .disjoint_anchor_ranges()
 3754            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3755            .collect();
 3756        for buffer_id in buffer_ids {
 3757            self.unfold_buffer(buffer_id, cx);
 3758        }
 3759    }
 3760
 3761    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3762    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3763    /// effects of selection change occur at the end of the transaction.
 3764    pub fn change_selections<R>(
 3765        &mut self,
 3766        effects: SelectionEffects,
 3767        window: &mut Window,
 3768        cx: &mut Context<Self>,
 3769        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3770    ) -> R {
 3771        let snapshot = self.display_snapshot(cx);
 3772        if let Some(state) = &mut self.deferred_selection_effects_state {
 3773            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3774            state.effects.completions = effects.completions;
 3775            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3776            let (changed, result) = self.selections.change_with(&snapshot, change);
 3777            state.changed |= changed;
 3778            return result;
 3779        }
 3780        let mut state = DeferredSelectionEffectsState {
 3781            changed: false,
 3782            effects,
 3783            old_cursor_position: self.selections.newest_anchor().head(),
 3784            history_entry: SelectionHistoryEntry {
 3785                selections: self.selections.disjoint_anchors_arc(),
 3786                select_next_state: self.select_next_state.clone(),
 3787                select_prev_state: self.select_prev_state.clone(),
 3788                add_selections_state: self.add_selections_state.clone(),
 3789            },
 3790        };
 3791        let (changed, result) = self.selections.change_with(&snapshot, change);
 3792        state.changed = state.changed || changed;
 3793        if self.defer_selection_effects {
 3794            self.deferred_selection_effects_state = Some(state);
 3795        } else {
 3796            self.apply_selection_effects(state, window, cx);
 3797        }
 3798        result
 3799    }
 3800
 3801    /// Defers the effects of selection change, so that the effects of multiple calls to
 3802    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3803    /// to selection history and the state of popovers based on selection position aren't
 3804    /// erroneously updated.
 3805    pub fn with_selection_effects_deferred<R>(
 3806        &mut self,
 3807        window: &mut Window,
 3808        cx: &mut Context<Self>,
 3809        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3810    ) -> R {
 3811        let already_deferred = self.defer_selection_effects;
 3812        self.defer_selection_effects = true;
 3813        let result = update(self, window, cx);
 3814        if !already_deferred {
 3815            self.defer_selection_effects = false;
 3816            if let Some(state) = self.deferred_selection_effects_state.take() {
 3817                self.apply_selection_effects(state, window, cx);
 3818            }
 3819        }
 3820        result
 3821    }
 3822
 3823    fn apply_selection_effects(
 3824        &mut self,
 3825        state: DeferredSelectionEffectsState,
 3826        window: &mut Window,
 3827        cx: &mut Context<Self>,
 3828    ) {
 3829        if state.changed {
 3830            self.selection_history.push(state.history_entry);
 3831
 3832            if let Some(autoscroll) = state.effects.scroll {
 3833                self.request_autoscroll(autoscroll, cx);
 3834            }
 3835
 3836            let old_cursor_position = &state.old_cursor_position;
 3837
 3838            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3839
 3840            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3841                self.show_signature_help_auto(window, cx);
 3842            }
 3843        }
 3844    }
 3845
 3846    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3847    where
 3848        I: IntoIterator<Item = (Range<S>, T)>,
 3849        S: ToOffset,
 3850        T: Into<Arc<str>>,
 3851    {
 3852        if self.read_only(cx) {
 3853            return;
 3854        }
 3855
 3856        self.buffer
 3857            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3858    }
 3859
 3860    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3861    where
 3862        I: IntoIterator<Item = (Range<S>, T)>,
 3863        S: ToOffset,
 3864        T: Into<Arc<str>>,
 3865    {
 3866        if self.read_only(cx) {
 3867            return;
 3868        }
 3869
 3870        self.buffer.update(cx, |buffer, cx| {
 3871            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3872        });
 3873    }
 3874
 3875    pub fn edit_with_block_indent<I, S, T>(
 3876        &mut self,
 3877        edits: I,
 3878        original_indent_columns: Vec<Option<u32>>,
 3879        cx: &mut Context<Self>,
 3880    ) where
 3881        I: IntoIterator<Item = (Range<S>, T)>,
 3882        S: ToOffset,
 3883        T: Into<Arc<str>>,
 3884    {
 3885        if self.read_only(cx) {
 3886            return;
 3887        }
 3888
 3889        self.buffer.update(cx, |buffer, cx| {
 3890            buffer.edit(
 3891                edits,
 3892                Some(AutoindentMode::Block {
 3893                    original_indent_columns,
 3894                }),
 3895                cx,
 3896            )
 3897        });
 3898    }
 3899
 3900    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3901        self.hide_context_menu(window, cx);
 3902
 3903        match phase {
 3904            SelectPhase::Begin {
 3905                position,
 3906                add,
 3907                click_count,
 3908            } => self.begin_selection(position, add, click_count, window, cx),
 3909            SelectPhase::BeginColumnar {
 3910                position,
 3911                goal_column,
 3912                reset,
 3913                mode,
 3914            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 3915            SelectPhase::Extend {
 3916                position,
 3917                click_count,
 3918            } => self.extend_selection(position, click_count, window, cx),
 3919            SelectPhase::Update {
 3920                position,
 3921                goal_column,
 3922                scroll_delta,
 3923            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 3924            SelectPhase::End => self.end_selection(window, cx),
 3925        }
 3926    }
 3927
 3928    fn extend_selection(
 3929        &mut self,
 3930        position: DisplayPoint,
 3931        click_count: usize,
 3932        window: &mut Window,
 3933        cx: &mut Context<Self>,
 3934    ) {
 3935        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3936        let tail = self
 3937            .selections
 3938            .newest::<MultiBufferOffset>(&display_map)
 3939            .tail();
 3940        let click_count = click_count.max(match self.selections.select_mode() {
 3941            SelectMode::Character => 1,
 3942            SelectMode::Word(_) => 2,
 3943            SelectMode::Line(_) => 3,
 3944            SelectMode::All => 4,
 3945        });
 3946        self.begin_selection(position, false, click_count, window, cx);
 3947
 3948        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 3949
 3950        let current_selection = match self.selections.select_mode() {
 3951            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 3952            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 3953        };
 3954
 3955        let mut pending_selection = self
 3956            .selections
 3957            .pending_anchor()
 3958            .cloned()
 3959            .expect("extend_selection not called with pending selection");
 3960
 3961        if pending_selection
 3962            .start
 3963            .cmp(&current_selection.start, display_map.buffer_snapshot())
 3964            == Ordering::Greater
 3965        {
 3966            pending_selection.start = current_selection.start;
 3967        }
 3968        if pending_selection
 3969            .end
 3970            .cmp(&current_selection.end, display_map.buffer_snapshot())
 3971            == Ordering::Less
 3972        {
 3973            pending_selection.end = current_selection.end;
 3974            pending_selection.reversed = true;
 3975        }
 3976
 3977        let mut pending_mode = self.selections.pending_mode().unwrap();
 3978        match &mut pending_mode {
 3979            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 3980            _ => {}
 3981        }
 3982
 3983        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 3984            SelectionEffects::scroll(Autoscroll::fit())
 3985        } else {
 3986            SelectionEffects::no_scroll()
 3987        };
 3988
 3989        self.change_selections(effects, window, cx, |s| {
 3990            s.set_pending(pending_selection.clone(), pending_mode);
 3991            s.set_is_extending(true);
 3992        });
 3993    }
 3994
 3995    fn begin_selection(
 3996        &mut self,
 3997        position: DisplayPoint,
 3998        add: bool,
 3999        click_count: usize,
 4000        window: &mut Window,
 4001        cx: &mut Context<Self>,
 4002    ) {
 4003        if !self.focus_handle.is_focused(window) {
 4004            self.last_focused_descendant = None;
 4005            window.focus(&self.focus_handle, cx);
 4006        }
 4007
 4008        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4009        let buffer = display_map.buffer_snapshot();
 4010        let position = display_map.clip_point(position, Bias::Left);
 4011
 4012        let start;
 4013        let end;
 4014        let mode;
 4015        let mut auto_scroll;
 4016        match click_count {
 4017            1 => {
 4018                start = buffer.anchor_before(position.to_point(&display_map));
 4019                end = start;
 4020                mode = SelectMode::Character;
 4021                auto_scroll = true;
 4022            }
 4023            2 => {
 4024                let position = display_map
 4025                    .clip_point(position, Bias::Left)
 4026                    .to_offset(&display_map, Bias::Left);
 4027                let (range, _) = buffer.surrounding_word(position, None);
 4028                start = buffer.anchor_before(range.start);
 4029                end = buffer.anchor_before(range.end);
 4030                mode = SelectMode::Word(start..end);
 4031                auto_scroll = true;
 4032            }
 4033            3 => {
 4034                let position = display_map
 4035                    .clip_point(position, Bias::Left)
 4036                    .to_point(&display_map);
 4037                let line_start = display_map.prev_line_boundary(position).0;
 4038                let next_line_start = buffer.clip_point(
 4039                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4040                    Bias::Left,
 4041                );
 4042                start = buffer.anchor_before(line_start);
 4043                end = buffer.anchor_before(next_line_start);
 4044                mode = SelectMode::Line(start..end);
 4045                auto_scroll = true;
 4046            }
 4047            _ => {
 4048                start = buffer.anchor_before(MultiBufferOffset(0));
 4049                end = buffer.anchor_before(buffer.len());
 4050                mode = SelectMode::All;
 4051                auto_scroll = false;
 4052            }
 4053        }
 4054        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4055
 4056        let point_to_delete: Option<usize> = {
 4057            let selected_points: Vec<Selection<Point>> =
 4058                self.selections.disjoint_in_range(start..end, &display_map);
 4059
 4060            if !add || click_count > 1 {
 4061                None
 4062            } else if !selected_points.is_empty() {
 4063                Some(selected_points[0].id)
 4064            } else {
 4065                let clicked_point_already_selected =
 4066                    self.selections.disjoint_anchors().iter().find(|selection| {
 4067                        selection.start.to_point(buffer) == start.to_point(buffer)
 4068                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4069                    });
 4070
 4071                clicked_point_already_selected.map(|selection| selection.id)
 4072            }
 4073        };
 4074
 4075        let selections_count = self.selections.count();
 4076        let effects = if auto_scroll {
 4077            SelectionEffects::default()
 4078        } else {
 4079            SelectionEffects::no_scroll()
 4080        };
 4081
 4082        self.change_selections(effects, window, cx, |s| {
 4083            if let Some(point_to_delete) = point_to_delete {
 4084                s.delete(point_to_delete);
 4085
 4086                if selections_count == 1 {
 4087                    s.set_pending_anchor_range(start..end, mode);
 4088                }
 4089            } else {
 4090                if !add {
 4091                    s.clear_disjoint();
 4092                }
 4093
 4094                s.set_pending_anchor_range(start..end, mode);
 4095            }
 4096        });
 4097    }
 4098
 4099    fn begin_columnar_selection(
 4100        &mut self,
 4101        position: DisplayPoint,
 4102        goal_column: u32,
 4103        reset: bool,
 4104        mode: ColumnarMode,
 4105        window: &mut Window,
 4106        cx: &mut Context<Self>,
 4107    ) {
 4108        if !self.focus_handle.is_focused(window) {
 4109            self.last_focused_descendant = None;
 4110            window.focus(&self.focus_handle, cx);
 4111        }
 4112
 4113        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4114
 4115        if reset {
 4116            let pointer_position = display_map
 4117                .buffer_snapshot()
 4118                .anchor_before(position.to_point(&display_map));
 4119
 4120            self.change_selections(
 4121                SelectionEffects::scroll(Autoscroll::newest()),
 4122                window,
 4123                cx,
 4124                |s| {
 4125                    s.clear_disjoint();
 4126                    s.set_pending_anchor_range(
 4127                        pointer_position..pointer_position,
 4128                        SelectMode::Character,
 4129                    );
 4130                },
 4131            );
 4132        };
 4133
 4134        let tail = self.selections.newest::<Point>(&display_map).tail();
 4135        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4136        self.columnar_selection_state = match mode {
 4137            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4138                selection_tail: selection_anchor,
 4139                display_point: if reset {
 4140                    if position.column() != goal_column {
 4141                        Some(DisplayPoint::new(position.row(), goal_column))
 4142                    } else {
 4143                        None
 4144                    }
 4145                } else {
 4146                    None
 4147                },
 4148            }),
 4149            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4150                selection_tail: selection_anchor,
 4151            }),
 4152        };
 4153
 4154        if !reset {
 4155            self.select_columns(position, goal_column, &display_map, window, cx);
 4156        }
 4157    }
 4158
 4159    fn update_selection(
 4160        &mut self,
 4161        position: DisplayPoint,
 4162        goal_column: u32,
 4163        scroll_delta: gpui::Point<f32>,
 4164        window: &mut Window,
 4165        cx: &mut Context<Self>,
 4166    ) {
 4167        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4168
 4169        if self.columnar_selection_state.is_some() {
 4170            self.select_columns(position, goal_column, &display_map, window, cx);
 4171        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4172            let buffer = display_map.buffer_snapshot();
 4173            let head;
 4174            let tail;
 4175            let mode = self.selections.pending_mode().unwrap();
 4176            match &mode {
 4177                SelectMode::Character => {
 4178                    head = position.to_point(&display_map);
 4179                    tail = pending.tail().to_point(buffer);
 4180                }
 4181                SelectMode::Word(original_range) => {
 4182                    let offset = display_map
 4183                        .clip_point(position, Bias::Left)
 4184                        .to_offset(&display_map, Bias::Left);
 4185                    let original_range = original_range.to_offset(buffer);
 4186
 4187                    let head_offset = if buffer.is_inside_word(offset, None)
 4188                        || original_range.contains(&offset)
 4189                    {
 4190                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4191                        if word_range.start < original_range.start {
 4192                            word_range.start
 4193                        } else {
 4194                            word_range.end
 4195                        }
 4196                    } else {
 4197                        offset
 4198                    };
 4199
 4200                    head = head_offset.to_point(buffer);
 4201                    if head_offset <= original_range.start {
 4202                        tail = original_range.end.to_point(buffer);
 4203                    } else {
 4204                        tail = original_range.start.to_point(buffer);
 4205                    }
 4206                }
 4207                SelectMode::Line(original_range) => {
 4208                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4209
 4210                    let position = display_map
 4211                        .clip_point(position, Bias::Left)
 4212                        .to_point(&display_map);
 4213                    let line_start = display_map.prev_line_boundary(position).0;
 4214                    let next_line_start = buffer.clip_point(
 4215                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4216                        Bias::Left,
 4217                    );
 4218
 4219                    if line_start < original_range.start {
 4220                        head = line_start
 4221                    } else {
 4222                        head = next_line_start
 4223                    }
 4224
 4225                    if head <= original_range.start {
 4226                        tail = original_range.end;
 4227                    } else {
 4228                        tail = original_range.start;
 4229                    }
 4230                }
 4231                SelectMode::All => {
 4232                    return;
 4233                }
 4234            };
 4235
 4236            if head < tail {
 4237                pending.start = buffer.anchor_before(head);
 4238                pending.end = buffer.anchor_before(tail);
 4239                pending.reversed = true;
 4240            } else {
 4241                pending.start = buffer.anchor_before(tail);
 4242                pending.end = buffer.anchor_before(head);
 4243                pending.reversed = false;
 4244            }
 4245
 4246            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4247                s.set_pending(pending.clone(), mode);
 4248            });
 4249        } else {
 4250            log::error!("update_selection dispatched with no pending selection");
 4251            return;
 4252        }
 4253
 4254        self.apply_scroll_delta(scroll_delta, window, cx);
 4255        cx.notify();
 4256    }
 4257
 4258    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4259        self.columnar_selection_state.take();
 4260        if let Some(pending_mode) = self.selections.pending_mode() {
 4261            let selections = self
 4262                .selections
 4263                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4264            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4265                s.select(selections);
 4266                s.clear_pending();
 4267                if s.is_extending() {
 4268                    s.set_is_extending(false);
 4269                } else {
 4270                    s.set_select_mode(pending_mode);
 4271                }
 4272            });
 4273        }
 4274    }
 4275
 4276    fn select_columns(
 4277        &mut self,
 4278        head: DisplayPoint,
 4279        goal_column: u32,
 4280        display_map: &DisplaySnapshot,
 4281        window: &mut Window,
 4282        cx: &mut Context<Self>,
 4283    ) {
 4284        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4285            return;
 4286        };
 4287
 4288        let tail = match columnar_state {
 4289            ColumnarSelectionState::FromMouse {
 4290                selection_tail,
 4291                display_point,
 4292            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4293            ColumnarSelectionState::FromSelection { selection_tail } => {
 4294                selection_tail.to_display_point(display_map)
 4295            }
 4296        };
 4297
 4298        let start_row = cmp::min(tail.row(), head.row());
 4299        let end_row = cmp::max(tail.row(), head.row());
 4300        let start_column = cmp::min(tail.column(), goal_column);
 4301        let end_column = cmp::max(tail.column(), goal_column);
 4302        let reversed = start_column < tail.column();
 4303
 4304        let selection_ranges = (start_row.0..=end_row.0)
 4305            .map(DisplayRow)
 4306            .filter_map(|row| {
 4307                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4308                    || start_column <= display_map.line_len(row))
 4309                    && !display_map.is_block_line(row)
 4310                {
 4311                    let start = display_map
 4312                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4313                        .to_point(display_map);
 4314                    let end = display_map
 4315                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4316                        .to_point(display_map);
 4317                    if reversed {
 4318                        Some(end..start)
 4319                    } else {
 4320                        Some(start..end)
 4321                    }
 4322                } else {
 4323                    None
 4324                }
 4325            })
 4326            .collect::<Vec<_>>();
 4327        if selection_ranges.is_empty() {
 4328            return;
 4329        }
 4330
 4331        let ranges = match columnar_state {
 4332            ColumnarSelectionState::FromMouse { .. } => {
 4333                let mut non_empty_ranges = selection_ranges
 4334                    .iter()
 4335                    .filter(|selection_range| selection_range.start != selection_range.end)
 4336                    .peekable();
 4337                if non_empty_ranges.peek().is_some() {
 4338                    non_empty_ranges.cloned().collect()
 4339                } else {
 4340                    selection_ranges
 4341                }
 4342            }
 4343            _ => selection_ranges,
 4344        };
 4345
 4346        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4347            s.select_ranges(ranges);
 4348        });
 4349        cx.notify();
 4350    }
 4351
 4352    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4353        self.selections
 4354            .all_adjusted(snapshot)
 4355            .iter()
 4356            .any(|selection| !selection.is_empty())
 4357    }
 4358
 4359    pub fn has_pending_nonempty_selection(&self) -> bool {
 4360        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4361            Some(Selection { start, end, .. }) => start != end,
 4362            None => false,
 4363        };
 4364
 4365        pending_nonempty_selection
 4366            || (self.columnar_selection_state.is_some()
 4367                && self.selections.disjoint_anchors().len() > 1)
 4368    }
 4369
 4370    pub fn has_pending_selection(&self) -> bool {
 4371        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4372    }
 4373
 4374    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4375        self.selection_mark_mode = false;
 4376        self.selection_drag_state = SelectionDragState::None;
 4377
 4378        if self.dismiss_menus_and_popups(true, window, cx) {
 4379            cx.notify();
 4380            return;
 4381        }
 4382        if self.clear_expanded_diff_hunks(cx) {
 4383            cx.notify();
 4384            return;
 4385        }
 4386        if self.show_git_blame_gutter {
 4387            self.show_git_blame_gutter = false;
 4388            cx.notify();
 4389            return;
 4390        }
 4391
 4392        if self.mode.is_full()
 4393            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4394        {
 4395            cx.notify();
 4396            return;
 4397        }
 4398
 4399        cx.propagate();
 4400    }
 4401
 4402    pub fn dismiss_menus_and_popups(
 4403        &mut self,
 4404        is_user_requested: bool,
 4405        window: &mut Window,
 4406        cx: &mut Context<Self>,
 4407    ) -> bool {
 4408        let mut dismissed = false;
 4409
 4410        dismissed |= self.take_rename(false, window, cx).is_some();
 4411        dismissed |= self.hide_blame_popover(true, cx);
 4412        dismissed |= hide_hover(self, cx);
 4413        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4414        dismissed |= self.hide_context_menu(window, cx).is_some();
 4415        dismissed |= self.mouse_context_menu.take().is_some();
 4416        dismissed |= is_user_requested && self.discard_edit_prediction(true, cx);
 4417        dismissed |= self.snippet_stack.pop().is_some();
 4418        if self.diff_review_drag_state.is_some() {
 4419            self.cancel_diff_review_drag(cx);
 4420            dismissed = true;
 4421        }
 4422        if !self.diff_review_overlays.is_empty() {
 4423            self.dismiss_all_diff_review_overlays(cx);
 4424            dismissed = true;
 4425        }
 4426
 4427        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4428            self.dismiss_diagnostics(cx);
 4429            dismissed = true;
 4430        }
 4431
 4432        dismissed
 4433    }
 4434
 4435    fn linked_editing_ranges_for(
 4436        &self,
 4437        selection: Range<text::Anchor>,
 4438        cx: &App,
 4439    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4440        if self.linked_edit_ranges.is_empty() {
 4441            return None;
 4442        }
 4443        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4444            selection.end.buffer_id.and_then(|end_buffer_id| {
 4445                if selection.start.buffer_id != Some(end_buffer_id) {
 4446                    return None;
 4447                }
 4448                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4449                let snapshot = buffer.read(cx).snapshot();
 4450                self.linked_edit_ranges
 4451                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4452                    .map(|ranges| (ranges, snapshot, buffer))
 4453            })?;
 4454        use text::ToOffset as TO;
 4455        // find offset from the start of current range to current cursor position
 4456        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4457
 4458        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4459        let start_difference = start_offset - start_byte_offset;
 4460        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4461        let end_difference = end_offset - start_byte_offset;
 4462        // Current range has associated linked ranges.
 4463        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4464        for range in linked_ranges.iter() {
 4465            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4466            let end_offset = start_offset + end_difference;
 4467            let start_offset = start_offset + start_difference;
 4468            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4469                continue;
 4470            }
 4471            if self.selections.disjoint_anchor_ranges().any(|s| {
 4472                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4473                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4474                {
 4475                    return false;
 4476                }
 4477                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4478                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4479            }) {
 4480                continue;
 4481            }
 4482            let start = buffer_snapshot.anchor_after(start_offset);
 4483            let end = buffer_snapshot.anchor_after(end_offset);
 4484            linked_edits
 4485                .entry(buffer.clone())
 4486                .or_default()
 4487                .push(start..end);
 4488        }
 4489        Some(linked_edits)
 4490    }
 4491
 4492    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4493        let text: Arc<str> = text.into();
 4494
 4495        if self.read_only(cx) {
 4496            return;
 4497        }
 4498
 4499        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4500
 4501        self.unfold_buffers_with_selections(cx);
 4502
 4503        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4504        let mut bracket_inserted = false;
 4505        let mut edits = Vec::new();
 4506        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4507        let mut new_selections = Vec::with_capacity(selections.len());
 4508        let mut new_autoclose_regions = Vec::new();
 4509        let snapshot = self.buffer.read(cx).read(cx);
 4510        let mut clear_linked_edit_ranges = false;
 4511        let mut all_selections_read_only = true;
 4512        let mut has_adjacent_edits = false;
 4513        let mut in_adjacent_group = false;
 4514
 4515        let mut regions = self
 4516            .selections_with_autoclose_regions(selections, &snapshot)
 4517            .peekable();
 4518
 4519        while let Some((selection, autoclose_region)) = regions.next() {
 4520            if snapshot
 4521                .point_to_buffer_point(selection.head())
 4522                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4523            {
 4524                continue;
 4525            }
 4526            if snapshot
 4527                .point_to_buffer_point(selection.tail())
 4528                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4529            {
 4530                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4531                continue;
 4532            }
 4533            all_selections_read_only = false;
 4534
 4535            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4536                // Determine if the inserted text matches the opening or closing
 4537                // bracket of any of this language's bracket pairs.
 4538                let mut bracket_pair = None;
 4539                let mut is_bracket_pair_start = false;
 4540                let mut is_bracket_pair_end = false;
 4541                if !text.is_empty() {
 4542                    let mut bracket_pair_matching_end = None;
 4543                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4544                    //  and they are removing the character that triggered IME popup.
 4545                    for (pair, enabled) in scope.brackets() {
 4546                        if !pair.close && !pair.surround {
 4547                            continue;
 4548                        }
 4549
 4550                        if enabled && pair.start.ends_with(text.as_ref()) {
 4551                            let prefix_len = pair.start.len() - text.len();
 4552                            let preceding_text_matches_prefix = prefix_len == 0
 4553                                || (selection.start.column >= (prefix_len as u32)
 4554                                    && snapshot.contains_str_at(
 4555                                        Point::new(
 4556                                            selection.start.row,
 4557                                            selection.start.column - (prefix_len as u32),
 4558                                        ),
 4559                                        &pair.start[..prefix_len],
 4560                                    ));
 4561                            if preceding_text_matches_prefix {
 4562                                bracket_pair = Some(pair.clone());
 4563                                is_bracket_pair_start = true;
 4564                                break;
 4565                            }
 4566                        }
 4567                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4568                        {
 4569                            // take first bracket pair matching end, but don't break in case a later bracket
 4570                            // pair matches start
 4571                            bracket_pair_matching_end = Some(pair.clone());
 4572                        }
 4573                    }
 4574                    if let Some(end) = bracket_pair_matching_end
 4575                        && bracket_pair.is_none()
 4576                    {
 4577                        bracket_pair = Some(end);
 4578                        is_bracket_pair_end = true;
 4579                    }
 4580                }
 4581
 4582                if let Some(bracket_pair) = bracket_pair {
 4583                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4584                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4585                    let auto_surround =
 4586                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4587                    if selection.is_empty() {
 4588                        if is_bracket_pair_start {
 4589                            // If the inserted text is a suffix of an opening bracket and the
 4590                            // selection is preceded by the rest of the opening bracket, then
 4591                            // insert the closing bracket.
 4592                            let following_text_allows_autoclose = snapshot
 4593                                .chars_at(selection.start)
 4594                                .next()
 4595                                .is_none_or(|c| scope.should_autoclose_before(c));
 4596
 4597                            let preceding_text_allows_autoclose = selection.start.column == 0
 4598                                || snapshot
 4599                                    .reversed_chars_at(selection.start)
 4600                                    .next()
 4601                                    .is_none_or(|c| {
 4602                                        bracket_pair.start != bracket_pair.end
 4603                                            || !snapshot
 4604                                                .char_classifier_at(selection.start)
 4605                                                .is_word(c)
 4606                                    });
 4607
 4608                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4609                                && bracket_pair.start.len() == 1
 4610                            {
 4611                                let target = bracket_pair.start.chars().next().unwrap();
 4612                                let mut byte_offset = 0u32;
 4613                                let current_line_count = snapshot
 4614                                    .reversed_chars_at(selection.start)
 4615                                    .take_while(|&c| c != '\n')
 4616                                    .filter(|c| {
 4617                                        byte_offset += c.len_utf8() as u32;
 4618                                        if *c != target {
 4619                                            return false;
 4620                                        }
 4621
 4622                                        let point = Point::new(
 4623                                            selection.start.row,
 4624                                            selection.start.column.saturating_sub(byte_offset),
 4625                                        );
 4626
 4627                                        let is_enabled = snapshot
 4628                                            .language_scope_at(point)
 4629                                            .and_then(|scope| {
 4630                                                scope
 4631                                                    .brackets()
 4632                                                    .find(|(pair, _)| {
 4633                                                        pair.start == bracket_pair.start
 4634                                                    })
 4635                                                    .map(|(_, enabled)| enabled)
 4636                                            })
 4637                                            .unwrap_or(true);
 4638
 4639                                        let is_delimiter = snapshot
 4640                                            .language_scope_at(Point::new(
 4641                                                point.row,
 4642                                                point.column + 1,
 4643                                            ))
 4644                                            .and_then(|scope| {
 4645                                                scope
 4646                                                    .brackets()
 4647                                                    .find(|(pair, _)| {
 4648                                                        pair.start == bracket_pair.start
 4649                                                    })
 4650                                                    .map(|(_, enabled)| !enabled)
 4651                                            })
 4652                                            .unwrap_or(false);
 4653
 4654                                        is_enabled && !is_delimiter
 4655                                    })
 4656                                    .count();
 4657                                current_line_count % 2 == 1
 4658                            } else {
 4659                                false
 4660                            };
 4661
 4662                            if autoclose
 4663                                && bracket_pair.close
 4664                                && following_text_allows_autoclose
 4665                                && preceding_text_allows_autoclose
 4666                                && !is_closing_quote
 4667                            {
 4668                                let anchor = snapshot.anchor_before(selection.end);
 4669                                new_selections.push((selection.map(|_| anchor), text.len()));
 4670                                new_autoclose_regions.push((
 4671                                    anchor,
 4672                                    text.len(),
 4673                                    selection.id,
 4674                                    bracket_pair.clone(),
 4675                                ));
 4676                                edits.push((
 4677                                    selection.range(),
 4678                                    format!("{}{}", text, bracket_pair.end).into(),
 4679                                ));
 4680                                bracket_inserted = true;
 4681                                continue;
 4682                            }
 4683                        }
 4684
 4685                        if let Some(region) = autoclose_region {
 4686                            // If the selection is followed by an auto-inserted closing bracket,
 4687                            // then don't insert that closing bracket again; just move the selection
 4688                            // past the closing bracket.
 4689                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4690                                && text.as_ref() == region.pair.end.as_str()
 4691                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4692                            if should_skip {
 4693                                let anchor = snapshot.anchor_after(selection.end);
 4694                                new_selections
 4695                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4696                                continue;
 4697                            }
 4698                        }
 4699
 4700                        let always_treat_brackets_as_autoclosed = snapshot
 4701                            .language_settings_at(selection.start, cx)
 4702                            .always_treat_brackets_as_autoclosed;
 4703                        if always_treat_brackets_as_autoclosed
 4704                            && is_bracket_pair_end
 4705                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4706                        {
 4707                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4708                            // and the inserted text is a closing bracket and the selection is followed
 4709                            // by the closing bracket then move the selection past the closing bracket.
 4710                            let anchor = snapshot.anchor_after(selection.end);
 4711                            new_selections.push((selection.map(|_| anchor), text.len()));
 4712                            continue;
 4713                        }
 4714                    }
 4715                    // If an opening bracket is 1 character long and is typed while
 4716                    // text is selected, then surround that text with the bracket pair.
 4717                    else if auto_surround
 4718                        && bracket_pair.surround
 4719                        && is_bracket_pair_start
 4720                        && bracket_pair.start.chars().count() == 1
 4721                    {
 4722                        edits.push((selection.start..selection.start, text.clone()));
 4723                        edits.push((
 4724                            selection.end..selection.end,
 4725                            bracket_pair.end.as_str().into(),
 4726                        ));
 4727                        bracket_inserted = true;
 4728                        new_selections.push((
 4729                            Selection {
 4730                                id: selection.id,
 4731                                start: snapshot.anchor_after(selection.start),
 4732                                end: snapshot.anchor_before(selection.end),
 4733                                reversed: selection.reversed,
 4734                                goal: selection.goal,
 4735                            },
 4736                            0,
 4737                        ));
 4738                        continue;
 4739                    }
 4740                }
 4741            }
 4742
 4743            if self.auto_replace_emoji_shortcode
 4744                && selection.is_empty()
 4745                && text.as_ref().ends_with(':')
 4746                && let Some(possible_emoji_short_code) =
 4747                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4748                && !possible_emoji_short_code.is_empty()
 4749                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4750            {
 4751                let emoji_shortcode_start = Point::new(
 4752                    selection.start.row,
 4753                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4754                );
 4755
 4756                // Remove shortcode from buffer
 4757                edits.push((
 4758                    emoji_shortcode_start..selection.start,
 4759                    "".to_string().into(),
 4760                ));
 4761                new_selections.push((
 4762                    Selection {
 4763                        id: selection.id,
 4764                        start: snapshot.anchor_after(emoji_shortcode_start),
 4765                        end: snapshot.anchor_before(selection.start),
 4766                        reversed: selection.reversed,
 4767                        goal: selection.goal,
 4768                    },
 4769                    0,
 4770                ));
 4771
 4772                // Insert emoji
 4773                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4774                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4775                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4776
 4777                continue;
 4778            }
 4779
 4780            let next_is_adjacent = regions
 4781                .peek()
 4782                .is_some_and(|(next, _)| selection.end == next.start);
 4783
 4784            // If not handling any auto-close operation, then just replace the selected
 4785            // text with the given input and move the selection to the end of the
 4786            // newly inserted text.
 4787            let anchor = if in_adjacent_group || next_is_adjacent {
 4788                // After edits the right bias would shift those anchor to the next visible fragment
 4789                // but we want to resolve to the previous one
 4790                snapshot.anchor_before(selection.end)
 4791            } else {
 4792                snapshot.anchor_after(selection.end)
 4793            };
 4794
 4795            if !self.linked_edit_ranges.is_empty() {
 4796                let start_anchor = snapshot.anchor_before(selection.start);
 4797
 4798                let is_word_char = text.chars().next().is_none_or(|char| {
 4799                    let classifier = snapshot
 4800                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4801                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4802                    classifier.is_word(char)
 4803                });
 4804
 4805                if is_word_char {
 4806                    if let Some(ranges) = self
 4807                        .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
 4808                    {
 4809                        for (buffer, edits) in ranges {
 4810                            linked_edits
 4811                                .entry(buffer.clone())
 4812                                .or_default()
 4813                                .extend(edits.into_iter().map(|range| (range, text.clone())));
 4814                        }
 4815                    }
 4816                } else {
 4817                    clear_linked_edit_ranges = true;
 4818                }
 4819            }
 4820
 4821            new_selections.push((selection.map(|_| anchor), 0));
 4822            edits.push((selection.start..selection.end, text.clone()));
 4823
 4824            has_adjacent_edits |= next_is_adjacent;
 4825            in_adjacent_group = next_is_adjacent;
 4826        }
 4827
 4828        if all_selections_read_only {
 4829            return;
 4830        }
 4831
 4832        drop(regions);
 4833        drop(snapshot);
 4834
 4835        self.transact(window, cx, |this, window, cx| {
 4836            if clear_linked_edit_ranges {
 4837                this.linked_edit_ranges.clear();
 4838            }
 4839            let initial_buffer_versions =
 4840                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4841
 4842            this.buffer.update(cx, |buffer, cx| {
 4843                if has_adjacent_edits {
 4844                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4845                } else {
 4846                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4847                }
 4848            });
 4849            for (buffer, edits) in linked_edits {
 4850                buffer.update(cx, |buffer, cx| {
 4851                    let snapshot = buffer.snapshot();
 4852                    let edits = edits
 4853                        .into_iter()
 4854                        .map(|(range, text)| {
 4855                            use text::ToPoint as TP;
 4856                            let end_point = TP::to_point(&range.end, &snapshot);
 4857                            let start_point = TP::to_point(&range.start, &snapshot);
 4858                            (start_point..end_point, text)
 4859                        })
 4860                        .sorted_by_key(|(range, _)| range.start);
 4861                    buffer.edit(edits, None, cx);
 4862                })
 4863            }
 4864            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4865            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4866            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4867            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4868                new_anchor_selections,
 4869                &map,
 4870            )
 4871            .zip(new_selection_deltas)
 4872            .map(|(selection, delta)| Selection {
 4873                id: selection.id,
 4874                start: selection.start + delta,
 4875                end: selection.end + delta,
 4876                reversed: selection.reversed,
 4877                goal: SelectionGoal::None,
 4878            })
 4879            .collect::<Vec<_>>();
 4880
 4881            let mut i = 0;
 4882            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4883                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4884                let start = map.buffer_snapshot().anchor_before(position);
 4885                let end = map.buffer_snapshot().anchor_after(position);
 4886                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4887                    match existing_state
 4888                        .range
 4889                        .start
 4890                        .cmp(&start, map.buffer_snapshot())
 4891                    {
 4892                        Ordering::Less => i += 1,
 4893                        Ordering::Greater => break,
 4894                        Ordering::Equal => {
 4895                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4896                                Ordering::Less => i += 1,
 4897                                Ordering::Equal => break,
 4898                                Ordering::Greater => break,
 4899                            }
 4900                        }
 4901                    }
 4902                }
 4903                this.autoclose_regions.insert(
 4904                    i,
 4905                    AutocloseRegion {
 4906                        selection_id,
 4907                        range: start..end,
 4908                        pair,
 4909                    },
 4910                );
 4911            }
 4912
 4913            let had_active_edit_prediction = this.has_active_edit_prediction();
 4914            this.change_selections(
 4915                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4916                window,
 4917                cx,
 4918                |s| s.select(new_selections),
 4919            );
 4920
 4921            if !bracket_inserted
 4922                && let Some(on_type_format_task) =
 4923                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 4924            {
 4925                on_type_format_task.detach_and_log_err(cx);
 4926            }
 4927
 4928            let editor_settings = EditorSettings::get_global(cx);
 4929            if bracket_inserted
 4930                && (editor_settings.auto_signature_help
 4931                    || editor_settings.show_signature_help_after_edits)
 4932            {
 4933                this.show_signature_help(&ShowSignatureHelp, window, cx);
 4934            }
 4935
 4936            let trigger_in_words =
 4937                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 4938            if this.hard_wrap.is_some() {
 4939                let latest: Range<Point> = this.selections.newest(&map).range();
 4940                if latest.is_empty()
 4941                    && this
 4942                        .buffer()
 4943                        .read(cx)
 4944                        .snapshot(cx)
 4945                        .line_len(MultiBufferRow(latest.start.row))
 4946                        == latest.start.column
 4947                {
 4948                    this.rewrap_impl(
 4949                        RewrapOptions {
 4950                            override_language_settings: true,
 4951                            preserve_existing_whitespace: true,
 4952                        },
 4953                        cx,
 4954                    )
 4955                }
 4956            }
 4957            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 4958            refresh_linked_ranges(this, window, cx);
 4959            this.refresh_edit_prediction(true, false, window, cx);
 4960            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 4961        });
 4962    }
 4963
 4964    fn find_possible_emoji_shortcode_at_position(
 4965        snapshot: &MultiBufferSnapshot,
 4966        position: Point,
 4967    ) -> Option<String> {
 4968        let mut chars = Vec::new();
 4969        let mut found_colon = false;
 4970        for char in snapshot.reversed_chars_at(position).take(100) {
 4971            // Found a possible emoji shortcode in the middle of the buffer
 4972            if found_colon {
 4973                if char.is_whitespace() {
 4974                    chars.reverse();
 4975                    return Some(chars.iter().collect());
 4976                }
 4977                // If the previous character is not a whitespace, we are in the middle of a word
 4978                // and we only want to complete the shortcode if the word is made up of other emojis
 4979                let mut containing_word = String::new();
 4980                for ch in snapshot
 4981                    .reversed_chars_at(position)
 4982                    .skip(chars.len() + 1)
 4983                    .take(100)
 4984                {
 4985                    if ch.is_whitespace() {
 4986                        break;
 4987                    }
 4988                    containing_word.push(ch);
 4989                }
 4990                let containing_word = containing_word.chars().rev().collect::<String>();
 4991                if util::word_consists_of_emojis(containing_word.as_str()) {
 4992                    chars.reverse();
 4993                    return Some(chars.iter().collect());
 4994                }
 4995            }
 4996
 4997            if char.is_whitespace() || !char.is_ascii() {
 4998                return None;
 4999            }
 5000            if char == ':' {
 5001                found_colon = true;
 5002            } else {
 5003                chars.push(char);
 5004            }
 5005        }
 5006        // Found a possible emoji shortcode at the beginning of the buffer
 5007        chars.reverse();
 5008        Some(chars.iter().collect())
 5009    }
 5010
 5011    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5012        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5013        self.transact(window, cx, |this, window, cx| {
 5014            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5015                let selections = this
 5016                    .selections
 5017                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5018                let multi_buffer = this.buffer.read(cx);
 5019                let buffer = multi_buffer.snapshot(cx);
 5020                selections
 5021                    .iter()
 5022                    .map(|selection| {
 5023                        let start_point = selection.start.to_point(&buffer);
 5024                        let mut existing_indent =
 5025                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5026                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5027                        let start = selection.start;
 5028                        let end = selection.end;
 5029                        let selection_is_empty = start == end;
 5030                        let language_scope = buffer.language_scope_at(start);
 5031                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5032                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5033                                &buffer,
 5034                                start..end,
 5035                                language,
 5036                            )
 5037                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5038                                    &buffer,
 5039                                    start..end,
 5040                                );
 5041
 5042                            let mut newline_config = NewlineConfig::Newline {
 5043                                additional_indent: IndentSize::spaces(0),
 5044                                extra_line_additional_indent: if needs_extra_newline {
 5045                                    Some(IndentSize::spaces(0))
 5046                                } else {
 5047                                    None
 5048                                },
 5049                                prevent_auto_indent: false,
 5050                            };
 5051
 5052                            let comment_delimiter = maybe!({
 5053                                if !selection_is_empty {
 5054                                    return None;
 5055                                }
 5056
 5057                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5058                                    return None;
 5059                                }
 5060
 5061                                return comment_delimiter_for_newline(
 5062                                    &start_point,
 5063                                    &buffer,
 5064                                    language,
 5065                                );
 5066                            });
 5067
 5068                            let doc_delimiter = maybe!({
 5069                                if !selection_is_empty {
 5070                                    return None;
 5071                                }
 5072
 5073                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5074                                    return None;
 5075                                }
 5076
 5077                                return documentation_delimiter_for_newline(
 5078                                    &start_point,
 5079                                    &buffer,
 5080                                    language,
 5081                                    &mut newline_config,
 5082                                );
 5083                            });
 5084
 5085                            let list_delimiter = maybe!({
 5086                                if !selection_is_empty {
 5087                                    return None;
 5088                                }
 5089
 5090                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5091                                    return None;
 5092                                }
 5093
 5094                                return list_delimiter_for_newline(
 5095                                    &start_point,
 5096                                    &buffer,
 5097                                    language,
 5098                                    &mut newline_config,
 5099                                );
 5100                            });
 5101
 5102                            (
 5103                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5104                                newline_config,
 5105                            )
 5106                        } else {
 5107                            (
 5108                                None,
 5109                                NewlineConfig::Newline {
 5110                                    additional_indent: IndentSize::spaces(0),
 5111                                    extra_line_additional_indent: None,
 5112                                    prevent_auto_indent: false,
 5113                                },
 5114                            )
 5115                        };
 5116
 5117                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5118                            NewlineConfig::ClearCurrentLine => {
 5119                                let row_start =
 5120                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5121                                (row_start, String::new(), false)
 5122                            }
 5123                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5124                                let row_start =
 5125                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5126                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5127                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5128                                let reduced_indent =
 5129                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5130                                let mut new_text = String::new();
 5131                                new_text.extend(reduced_indent.chars());
 5132                                new_text.push_str(continuation);
 5133                                (row_start, new_text, true)
 5134                            }
 5135                            NewlineConfig::Newline {
 5136                                additional_indent,
 5137                                extra_line_additional_indent,
 5138                                prevent_auto_indent,
 5139                            } => {
 5140                                let capacity_for_delimiter =
 5141                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5142                                let extra_line_len = extra_line_additional_indent
 5143                                    .map(|i| 1 + existing_indent.len as usize + i.len as usize)
 5144                                    .unwrap_or(0);
 5145                                let mut new_text = String::with_capacity(
 5146                                    1 + capacity_for_delimiter
 5147                                        + existing_indent.len as usize
 5148                                        + additional_indent.len as usize
 5149                                        + extra_line_len,
 5150                                );
 5151                                new_text.push('\n');
 5152                                new_text.extend(existing_indent.chars());
 5153                                new_text.extend(additional_indent.chars());
 5154                                if let Some(delimiter) = &delimiter {
 5155                                    new_text.push_str(delimiter);
 5156                                }
 5157                                if let Some(extra_indent) = extra_line_additional_indent {
 5158                                    new_text.push('\n');
 5159                                    new_text.extend(existing_indent.chars());
 5160                                    new_text.extend(extra_indent.chars());
 5161                                }
 5162                                (start, new_text, *prevent_auto_indent)
 5163                            }
 5164                        };
 5165
 5166                        let anchor = buffer.anchor_after(end);
 5167                        let new_selection = selection.map(|_| anchor);
 5168                        (
 5169                            ((edit_start..end, new_text), prevent_auto_indent),
 5170                            (newline_config.has_extra_line(), new_selection),
 5171                        )
 5172                    })
 5173                    .unzip()
 5174            };
 5175
 5176            let mut auto_indent_edits = Vec::new();
 5177            let mut edits = Vec::new();
 5178            for (edit, prevent_auto_indent) in edits_with_flags {
 5179                if prevent_auto_indent {
 5180                    edits.push(edit);
 5181                } else {
 5182                    auto_indent_edits.push(edit);
 5183                }
 5184            }
 5185            if !edits.is_empty() {
 5186                this.edit(edits, cx);
 5187            }
 5188            if !auto_indent_edits.is_empty() {
 5189                this.edit_with_autoindent(auto_indent_edits, cx);
 5190            }
 5191
 5192            let buffer = this.buffer.read(cx).snapshot(cx);
 5193            let new_selections = selection_info
 5194                .into_iter()
 5195                .map(|(extra_newline_inserted, new_selection)| {
 5196                    let mut cursor = new_selection.end.to_point(&buffer);
 5197                    if extra_newline_inserted {
 5198                        cursor.row -= 1;
 5199                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5200                    }
 5201                    new_selection.map(|_| cursor)
 5202                })
 5203                .collect();
 5204
 5205            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5206            this.refresh_edit_prediction(true, false, window, cx);
 5207            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5208                task.detach_and_log_err(cx);
 5209            }
 5210        });
 5211    }
 5212
 5213    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5214        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5215
 5216        let buffer = self.buffer.read(cx);
 5217        let snapshot = buffer.snapshot(cx);
 5218
 5219        let mut edits = Vec::new();
 5220        let mut rows = Vec::new();
 5221
 5222        for (rows_inserted, selection) in self
 5223            .selections
 5224            .all_adjusted(&self.display_snapshot(cx))
 5225            .into_iter()
 5226            .enumerate()
 5227        {
 5228            let cursor = selection.head();
 5229            let row = cursor.row;
 5230
 5231            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5232
 5233            let newline = "\n".to_string();
 5234            edits.push((start_of_line..start_of_line, newline));
 5235
 5236            rows.push(row + rows_inserted as u32);
 5237        }
 5238
 5239        self.transact(window, cx, |editor, window, cx| {
 5240            editor.edit(edits, cx);
 5241
 5242            editor.change_selections(Default::default(), window, cx, |s| {
 5243                let mut index = 0;
 5244                s.move_cursors_with(|map, _, _| {
 5245                    let row = rows[index];
 5246                    index += 1;
 5247
 5248                    let point = Point::new(row, 0);
 5249                    let boundary = map.next_line_boundary(point).1;
 5250                    let clipped = map.clip_point(boundary, Bias::Left);
 5251
 5252                    (clipped, SelectionGoal::None)
 5253                });
 5254            });
 5255
 5256            let mut indent_edits = Vec::new();
 5257            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5258            for row in rows {
 5259                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5260                for (row, indent) in indents {
 5261                    if indent.len == 0 {
 5262                        continue;
 5263                    }
 5264
 5265                    let text = match indent.kind {
 5266                        IndentKind::Space => " ".repeat(indent.len as usize),
 5267                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5268                    };
 5269                    let point = Point::new(row.0, 0);
 5270                    indent_edits.push((point..point, text));
 5271                }
 5272            }
 5273            editor.edit(indent_edits, cx);
 5274            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5275                format.detach_and_log_err(cx);
 5276            }
 5277        });
 5278    }
 5279
 5280    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5281        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5282
 5283        let buffer = self.buffer.read(cx);
 5284        let snapshot = buffer.snapshot(cx);
 5285
 5286        let mut edits = Vec::new();
 5287        let mut rows = Vec::new();
 5288        let mut rows_inserted = 0;
 5289
 5290        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5291            let cursor = selection.head();
 5292            let row = cursor.row;
 5293
 5294            let point = Point::new(row + 1, 0);
 5295            let start_of_line = snapshot.clip_point(point, Bias::Left);
 5296
 5297            let newline = "\n".to_string();
 5298            edits.push((start_of_line..start_of_line, newline));
 5299
 5300            rows_inserted += 1;
 5301            rows.push(row + rows_inserted);
 5302        }
 5303
 5304        self.transact(window, cx, |editor, window, cx| {
 5305            editor.edit(edits, cx);
 5306
 5307            editor.change_selections(Default::default(), window, cx, |s| {
 5308                let mut index = 0;
 5309                s.move_cursors_with(|map, _, _| {
 5310                    let row = rows[index];
 5311                    index += 1;
 5312
 5313                    let point = Point::new(row, 0);
 5314                    let boundary = map.next_line_boundary(point).1;
 5315                    let clipped = map.clip_point(boundary, Bias::Left);
 5316
 5317                    (clipped, SelectionGoal::None)
 5318                });
 5319            });
 5320
 5321            let mut indent_edits = Vec::new();
 5322            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5323            for row in rows {
 5324                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5325                for (row, indent) in indents {
 5326                    if indent.len == 0 {
 5327                        continue;
 5328                    }
 5329
 5330                    let text = match indent.kind {
 5331                        IndentKind::Space => " ".repeat(indent.len as usize),
 5332                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5333                    };
 5334                    let point = Point::new(row.0, 0);
 5335                    indent_edits.push((point..point, text));
 5336                }
 5337            }
 5338            editor.edit(indent_edits, cx);
 5339            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5340                format.detach_and_log_err(cx);
 5341            }
 5342        });
 5343    }
 5344
 5345    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5346        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5347            original_indent_columns: Vec::new(),
 5348        });
 5349        self.insert_with_autoindent_mode(text, autoindent, window, cx);
 5350    }
 5351
 5352    fn insert_with_autoindent_mode(
 5353        &mut self,
 5354        text: &str,
 5355        autoindent_mode: Option<AutoindentMode>,
 5356        window: &mut Window,
 5357        cx: &mut Context<Self>,
 5358    ) {
 5359        if self.read_only(cx) {
 5360            return;
 5361        }
 5362
 5363        let text: Arc<str> = text.into();
 5364        self.transact(window, cx, |this, window, cx| {
 5365            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5366            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5367                let anchors = {
 5368                    let snapshot = buffer.read(cx);
 5369                    old_selections
 5370                        .iter()
 5371                        .map(|s| {
 5372                            let anchor = snapshot.anchor_after(s.head());
 5373                            s.map(|_| anchor)
 5374                        })
 5375                        .collect::<Vec<_>>()
 5376                };
 5377                buffer.edit(
 5378                    old_selections
 5379                        .iter()
 5380                        .map(|s| (s.start..s.end, text.clone())),
 5381                    autoindent_mode,
 5382                    cx,
 5383                );
 5384                anchors
 5385            });
 5386
 5387            this.change_selections(Default::default(), window, cx, |s| {
 5388                s.select_anchors(selection_anchors);
 5389            });
 5390
 5391            cx.notify();
 5392        });
 5393    }
 5394
 5395    fn trigger_completion_on_input(
 5396        &mut self,
 5397        text: &str,
 5398        trigger_in_words: bool,
 5399        window: &mut Window,
 5400        cx: &mut Context<Self>,
 5401    ) {
 5402        let completions_source = self
 5403            .context_menu
 5404            .borrow()
 5405            .as_ref()
 5406            .and_then(|menu| match menu {
 5407                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5408                CodeContextMenu::CodeActions(_) => None,
 5409            });
 5410
 5411        match completions_source {
 5412            Some(CompletionsMenuSource::Words { .. }) => {
 5413                self.open_or_update_completions_menu(
 5414                    Some(CompletionsMenuSource::Words {
 5415                        ignore_threshold: false,
 5416                    }),
 5417                    None,
 5418                    trigger_in_words,
 5419                    window,
 5420                    cx,
 5421                );
 5422            }
 5423            _ => self.open_or_update_completions_menu(
 5424                None,
 5425                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5426                true,
 5427                window,
 5428                cx,
 5429            ),
 5430        }
 5431    }
 5432
 5433    /// If any empty selections is touching the start of its innermost containing autoclose
 5434    /// region, expand it to select the brackets.
 5435    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5436        let selections = self
 5437            .selections
 5438            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5439        let buffer = self.buffer.read(cx).read(cx);
 5440        let new_selections = self
 5441            .selections_with_autoclose_regions(selections, &buffer)
 5442            .map(|(mut selection, region)| {
 5443                if !selection.is_empty() {
 5444                    return selection;
 5445                }
 5446
 5447                if let Some(region) = region {
 5448                    let mut range = region.range.to_offset(&buffer);
 5449                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5450                        range.start -= region.pair.start.len();
 5451                        if buffer.contains_str_at(range.start, &region.pair.start)
 5452                            && buffer.contains_str_at(range.end, &region.pair.end)
 5453                        {
 5454                            range.end += region.pair.end.len();
 5455                            selection.start = range.start;
 5456                            selection.end = range.end;
 5457
 5458                            return selection;
 5459                        }
 5460                    }
 5461                }
 5462
 5463                let always_treat_brackets_as_autoclosed = buffer
 5464                    .language_settings_at(selection.start, cx)
 5465                    .always_treat_brackets_as_autoclosed;
 5466
 5467                if !always_treat_brackets_as_autoclosed {
 5468                    return selection;
 5469                }
 5470
 5471                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5472                    for (pair, enabled) in scope.brackets() {
 5473                        if !enabled || !pair.close {
 5474                            continue;
 5475                        }
 5476
 5477                        if buffer.contains_str_at(selection.start, &pair.end) {
 5478                            let pair_start_len = pair.start.len();
 5479                            if buffer.contains_str_at(
 5480                                selection.start.saturating_sub_usize(pair_start_len),
 5481                                &pair.start,
 5482                            ) {
 5483                                selection.start -= pair_start_len;
 5484                                selection.end += pair.end.len();
 5485
 5486                                return selection;
 5487                            }
 5488                        }
 5489                    }
 5490                }
 5491
 5492                selection
 5493            })
 5494            .collect();
 5495
 5496        drop(buffer);
 5497        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5498            selections.select(new_selections)
 5499        });
 5500    }
 5501
 5502    /// Iterate the given selections, and for each one, find the smallest surrounding
 5503    /// autoclose region. This uses the ordering of the selections and the autoclose
 5504    /// regions to avoid repeated comparisons.
 5505    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5506        &'a self,
 5507        selections: impl IntoIterator<Item = Selection<D>>,
 5508        buffer: &'a MultiBufferSnapshot,
 5509    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5510        let mut i = 0;
 5511        let mut regions = self.autoclose_regions.as_slice();
 5512        selections.into_iter().map(move |selection| {
 5513            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5514
 5515            let mut enclosing = None;
 5516            while let Some(pair_state) = regions.get(i) {
 5517                if pair_state.range.end.to_offset(buffer) < range.start {
 5518                    regions = &regions[i + 1..];
 5519                    i = 0;
 5520                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5521                    break;
 5522                } else {
 5523                    if pair_state.selection_id == selection.id {
 5524                        enclosing = Some(pair_state);
 5525                    }
 5526                    i += 1;
 5527                }
 5528            }
 5529
 5530            (selection, enclosing)
 5531        })
 5532    }
 5533
 5534    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5535    fn invalidate_autoclose_regions(
 5536        &mut self,
 5537        mut selections: &[Selection<Anchor>],
 5538        buffer: &MultiBufferSnapshot,
 5539    ) {
 5540        self.autoclose_regions.retain(|state| {
 5541            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5542                return false;
 5543            }
 5544
 5545            let mut i = 0;
 5546            while let Some(selection) = selections.get(i) {
 5547                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5548                    selections = &selections[1..];
 5549                    continue;
 5550                }
 5551                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5552                    break;
 5553                }
 5554                if selection.id == state.selection_id {
 5555                    return true;
 5556                } else {
 5557                    i += 1;
 5558                }
 5559            }
 5560            false
 5561        });
 5562    }
 5563
 5564    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5565        let offset = position.to_offset(buffer);
 5566        let (word_range, kind) =
 5567            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5568        if offset > word_range.start && kind == Some(CharKind::Word) {
 5569            Some(
 5570                buffer
 5571                    .text_for_range(word_range.start..offset)
 5572                    .collect::<String>(),
 5573            )
 5574        } else {
 5575            None
 5576        }
 5577    }
 5578
 5579    pub fn visible_excerpts(
 5580        &self,
 5581        lsp_related_only: bool,
 5582        cx: &mut Context<Editor>,
 5583    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5584        let project = self.project().cloned();
 5585        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5586        let multi_buffer = self.buffer().read(cx);
 5587        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5588        let multi_buffer_visible_start = self
 5589            .scroll_manager
 5590            .native_anchor(&display_snapshot, cx)
 5591            .anchor
 5592            .to_point(&multi_buffer_snapshot);
 5593        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5594            multi_buffer_visible_start
 5595                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5596            Bias::Left,
 5597        );
 5598        multi_buffer_snapshot
 5599            .range_to_buffer_ranges(multi_buffer_visible_start..=multi_buffer_visible_end)
 5600            .into_iter()
 5601            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5602            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5603                if !lsp_related_only {
 5604                    return Some((
 5605                        excerpt_id,
 5606                        (
 5607                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5608                            buffer.version().clone(),
 5609                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5610                        ),
 5611                    ));
 5612                }
 5613
 5614                let project = project.as_ref()?.read(cx);
 5615                let buffer_file = project::File::from_dyn(buffer.file())?;
 5616                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5617                let worktree_entry = buffer_worktree
 5618                    .read(cx)
 5619                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5620                if worktree_entry.is_ignored {
 5621                    None
 5622                } else {
 5623                    Some((
 5624                        excerpt_id,
 5625                        (
 5626                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5627                            buffer.version().clone(),
 5628                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5629                        ),
 5630                    ))
 5631                }
 5632            })
 5633            .collect()
 5634    }
 5635
 5636    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5637        TextLayoutDetails {
 5638            text_system: window.text_system().clone(),
 5639            editor_style: self.style.clone().unwrap(),
 5640            rem_size: window.rem_size(),
 5641            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5642            visible_rows: self.visible_line_count(),
 5643            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5644        }
 5645    }
 5646
 5647    fn trigger_on_type_formatting(
 5648        &self,
 5649        input: String,
 5650        window: &mut Window,
 5651        cx: &mut Context<Self>,
 5652    ) -> Option<Task<Result<()>>> {
 5653        if input.chars().count() != 1 {
 5654            return None;
 5655        }
 5656
 5657        let project = self.project()?;
 5658        let position = self.selections.newest_anchor().head();
 5659        let (buffer, buffer_position) = self
 5660            .buffer
 5661            .read(cx)
 5662            .text_anchor_for_position(position, cx)?;
 5663
 5664        let settings = language_settings::language_settings(
 5665            buffer
 5666                .read(cx)
 5667                .language_at(buffer_position)
 5668                .map(|l| l.name()),
 5669            buffer.read(cx).file(),
 5670            cx,
 5671        );
 5672        if !settings.use_on_type_format {
 5673            return None;
 5674        }
 5675
 5676        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5677        // hence we do LSP request & edit on host side only — add formats to host's history.
 5678        let push_to_lsp_host_history = true;
 5679        // If this is not the host, append its history with new edits.
 5680        let push_to_client_history = project.read(cx).is_via_collab();
 5681
 5682        let on_type_formatting = project.update(cx, |project, cx| {
 5683            project.on_type_format(
 5684                buffer.clone(),
 5685                buffer_position,
 5686                input,
 5687                push_to_lsp_host_history,
 5688                cx,
 5689            )
 5690        });
 5691        Some(cx.spawn_in(window, async move |editor, cx| {
 5692            if let Some(transaction) = on_type_formatting.await? {
 5693                if push_to_client_history {
 5694                    buffer.update(cx, |buffer, _| {
 5695                        buffer.push_transaction(transaction, Instant::now());
 5696                        buffer.finalize_last_transaction();
 5697                    });
 5698                }
 5699                editor.update(cx, |editor, cx| {
 5700                    editor.refresh_document_highlights(cx);
 5701                })?;
 5702            }
 5703            Ok(())
 5704        }))
 5705    }
 5706
 5707    pub fn show_word_completions(
 5708        &mut self,
 5709        _: &ShowWordCompletions,
 5710        window: &mut Window,
 5711        cx: &mut Context<Self>,
 5712    ) {
 5713        self.open_or_update_completions_menu(
 5714            Some(CompletionsMenuSource::Words {
 5715                ignore_threshold: true,
 5716            }),
 5717            None,
 5718            false,
 5719            window,
 5720            cx,
 5721        );
 5722    }
 5723
 5724    pub fn show_completions(
 5725        &mut self,
 5726        _: &ShowCompletions,
 5727        window: &mut Window,
 5728        cx: &mut Context<Self>,
 5729    ) {
 5730        self.open_or_update_completions_menu(None, None, false, window, cx);
 5731    }
 5732
 5733    fn open_or_update_completions_menu(
 5734        &mut self,
 5735        requested_source: Option<CompletionsMenuSource>,
 5736        trigger: Option<String>,
 5737        trigger_in_words: bool,
 5738        window: &mut Window,
 5739        cx: &mut Context<Self>,
 5740    ) {
 5741        if self.pending_rename.is_some() {
 5742            return;
 5743        }
 5744
 5745        let completions_source = self
 5746            .context_menu
 5747            .borrow()
 5748            .as_ref()
 5749            .and_then(|menu| match menu {
 5750                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5751                CodeContextMenu::CodeActions(_) => None,
 5752            });
 5753
 5754        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5755
 5756        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5757        // inserted and selected. To handle that case, the start of the selection is used so that
 5758        // the menu starts with all choices.
 5759        let position = self
 5760            .selections
 5761            .newest_anchor()
 5762            .start
 5763            .bias_right(&multibuffer_snapshot);
 5764        if position.diff_base_anchor.is_some() {
 5765            return;
 5766        }
 5767        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5768        let Some(buffer) = buffer_position
 5769            .text_anchor
 5770            .buffer_id
 5771            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5772        else {
 5773            return;
 5774        };
 5775        let buffer_snapshot = buffer.read(cx).snapshot();
 5776
 5777        let menu_is_open = matches!(
 5778            self.context_menu.borrow().as_ref(),
 5779            Some(CodeContextMenu::Completions(_))
 5780        );
 5781
 5782        let language = buffer_snapshot
 5783            .language_at(buffer_position.text_anchor)
 5784            .map(|language| language.name());
 5785
 5786        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5787        let completion_settings = language_settings.completions.clone();
 5788
 5789        let show_completions_on_input = self
 5790            .show_completions_on_input_override
 5791            .unwrap_or(language_settings.show_completions_on_input);
 5792        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5793            return;
 5794        }
 5795
 5796        let query: Option<Arc<String>> =
 5797            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5798                .map(|query| query.into());
 5799
 5800        drop(multibuffer_snapshot);
 5801
 5802        // Hide the current completions menu when query is empty. Without this, cached
 5803        // completions from before the trigger char may be reused (#32774).
 5804        if query.is_none() && menu_is_open {
 5805            self.hide_context_menu(window, cx);
 5806        }
 5807
 5808        let mut ignore_word_threshold = false;
 5809        let provider = match requested_source {
 5810            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5811            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5812                ignore_word_threshold = ignore_threshold;
 5813                None
 5814            }
 5815            Some(CompletionsMenuSource::SnippetChoices)
 5816            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5817                log::error!("bug: SnippetChoices requested_source is not handled");
 5818                None
 5819            }
 5820        };
 5821
 5822        let sort_completions = provider
 5823            .as_ref()
 5824            .is_some_and(|provider| provider.sort_completions());
 5825
 5826        let filter_completions = provider
 5827            .as_ref()
 5828            .is_none_or(|provider| provider.filter_completions());
 5829
 5830        let was_snippets_only = matches!(
 5831            completions_source,
 5832            Some(CompletionsMenuSource::SnippetsOnly)
 5833        );
 5834
 5835        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5836            if filter_completions {
 5837                menu.filter(
 5838                    query.clone().unwrap_or_default(),
 5839                    buffer_position.text_anchor,
 5840                    &buffer,
 5841                    provider.clone(),
 5842                    window,
 5843                    cx,
 5844                );
 5845            }
 5846            // When `is_incomplete` is false, no need to re-query completions when the current query
 5847            // is a suffix of the initial query.
 5848            let was_complete = !menu.is_incomplete;
 5849            if was_complete && !was_snippets_only {
 5850                // If the new query is a suffix of the old query (typing more characters) and
 5851                // the previous result was complete, the existing completions can be filtered.
 5852                //
 5853                // Note that snippet completions are always complete.
 5854                let query_matches = match (&menu.initial_query, &query) {
 5855                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 5856                    (None, _) => true,
 5857                    _ => false,
 5858                };
 5859                if query_matches {
 5860                    let position_matches = if menu.initial_position == position {
 5861                        true
 5862                    } else {
 5863                        let snapshot = self.buffer.read(cx).read(cx);
 5864                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 5865                    };
 5866                    if position_matches {
 5867                        return;
 5868                    }
 5869                }
 5870            }
 5871        };
 5872
 5873        let Anchor {
 5874            excerpt_id: buffer_excerpt_id,
 5875            text_anchor: buffer_position,
 5876            ..
 5877        } = buffer_position;
 5878
 5879        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 5880            buffer_snapshot.surrounding_word(buffer_position, None)
 5881        {
 5882            let word_to_exclude = buffer_snapshot
 5883                .text_for_range(word_range.clone())
 5884                .collect::<String>();
 5885            (
 5886                buffer_snapshot.anchor_before(word_range.start)
 5887                    ..buffer_snapshot.anchor_after(buffer_position),
 5888                Some(word_to_exclude),
 5889            )
 5890        } else {
 5891            (buffer_position..buffer_position, None)
 5892        };
 5893
 5894        let show_completion_documentation = buffer_snapshot
 5895            .settings_at(buffer_position, cx)
 5896            .show_completion_documentation;
 5897
 5898        // The document can be large, so stay in reasonable bounds when searching for words,
 5899        // otherwise completion pop-up might be slow to appear.
 5900        const WORD_LOOKUP_ROWS: u32 = 5_000;
 5901        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 5902        let min_word_search = buffer_snapshot.clip_point(
 5903            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 5904            Bias::Left,
 5905        );
 5906        let max_word_search = buffer_snapshot.clip_point(
 5907            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 5908            Bias::Right,
 5909        );
 5910        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 5911            ..buffer_snapshot.point_to_offset(max_word_search);
 5912
 5913        let skip_digits = query
 5914            .as_ref()
 5915            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 5916
 5917        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 5918            trigger.as_ref().is_none_or(|trigger| {
 5919                provider.is_completion_trigger(
 5920                    &buffer,
 5921                    position.text_anchor,
 5922                    trigger,
 5923                    trigger_in_words,
 5924                    cx,
 5925                )
 5926            })
 5927        });
 5928
 5929        let provider_responses = if let Some(provider) = &provider
 5930            && load_provider_completions
 5931        {
 5932            let trigger_character =
 5933                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 5934            let completion_context = CompletionContext {
 5935                trigger_kind: match &trigger_character {
 5936                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 5937                    None => CompletionTriggerKind::INVOKED,
 5938                },
 5939                trigger_character,
 5940            };
 5941
 5942            provider.completions(
 5943                buffer_excerpt_id,
 5944                &buffer,
 5945                buffer_position,
 5946                completion_context,
 5947                window,
 5948                cx,
 5949            )
 5950        } else {
 5951            Task::ready(Ok(Vec::new()))
 5952        };
 5953
 5954        let load_word_completions = if !self.word_completions_enabled {
 5955            false
 5956        } else if requested_source
 5957            == Some(CompletionsMenuSource::Words {
 5958                ignore_threshold: true,
 5959            })
 5960        {
 5961            true
 5962        } else {
 5963            load_provider_completions
 5964                && completion_settings.words != WordsCompletionMode::Disabled
 5965                && (ignore_word_threshold || {
 5966                    let words_min_length = completion_settings.words_min_length;
 5967                    // check whether word has at least `words_min_length` characters
 5968                    let query_chars = query.iter().flat_map(|q| q.chars());
 5969                    query_chars.take(words_min_length).count() == words_min_length
 5970                })
 5971        };
 5972
 5973        let mut words = if load_word_completions {
 5974            cx.background_spawn({
 5975                let buffer_snapshot = buffer_snapshot.clone();
 5976                async move {
 5977                    buffer_snapshot.words_in_range(WordsQuery {
 5978                        fuzzy_contents: None,
 5979                        range: word_search_range,
 5980                        skip_digits,
 5981                    })
 5982                }
 5983            })
 5984        } else {
 5985            Task::ready(BTreeMap::default())
 5986        };
 5987
 5988        let snippets = if let Some(provider) = &provider
 5989            && provider.show_snippets()
 5990            && let Some(project) = self.project()
 5991        {
 5992            let char_classifier = buffer_snapshot
 5993                .char_classifier_at(buffer_position)
 5994                .scope_context(Some(CharScopeContext::Completion));
 5995            project.update(cx, |project, cx| {
 5996                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 5997            })
 5998        } else {
 5999            Task::ready(Ok(CompletionResponse {
 6000                completions: Vec::new(),
 6001                display_options: Default::default(),
 6002                is_incomplete: false,
 6003            }))
 6004        };
 6005
 6006        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6007
 6008        let id = post_inc(&mut self.next_completion_id);
 6009        let task = cx.spawn_in(window, async move |editor, cx| {
 6010            let Ok(()) = editor.update(cx, |this, _| {
 6011                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6012            }) else {
 6013                return;
 6014            };
 6015
 6016            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6017            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6018            let mut completions = Vec::new();
 6019            let mut is_incomplete = false;
 6020            let mut display_options: Option<CompletionDisplayOptions> = None;
 6021            if let Some(provider_responses) = provider_responses.await.log_err()
 6022                && !provider_responses.is_empty()
 6023            {
 6024                for response in provider_responses {
 6025                    completions.extend(response.completions);
 6026                    is_incomplete = is_incomplete || response.is_incomplete;
 6027                    match display_options.as_mut() {
 6028                        None => {
 6029                            display_options = Some(response.display_options);
 6030                        }
 6031                        Some(options) => options.merge(&response.display_options),
 6032                    }
 6033                }
 6034                if completion_settings.words == WordsCompletionMode::Fallback {
 6035                    words = Task::ready(BTreeMap::default());
 6036                }
 6037            }
 6038            let display_options = display_options.unwrap_or_default();
 6039
 6040            let mut words = words.await;
 6041            if let Some(word_to_exclude) = &word_to_exclude {
 6042                words.remove(word_to_exclude);
 6043            }
 6044            for lsp_completion in &completions {
 6045                words.remove(&lsp_completion.new_text);
 6046            }
 6047            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6048                replace_range: word_replace_range.clone(),
 6049                new_text: word.clone(),
 6050                label: CodeLabel::plain(word, None),
 6051                match_start: None,
 6052                snippet_deduplication_key: None,
 6053                icon_path: None,
 6054                documentation: None,
 6055                source: CompletionSource::BufferWord {
 6056                    word_range,
 6057                    resolved: false,
 6058                },
 6059                insert_text_mode: Some(InsertTextMode::AS_IS),
 6060                confirm: None,
 6061            }));
 6062
 6063            completions.extend(
 6064                snippets
 6065                    .await
 6066                    .into_iter()
 6067                    .flat_map(|response| response.completions),
 6068            );
 6069
 6070            let menu = if completions.is_empty() {
 6071                None
 6072            } else {
 6073                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6074                    let languages = editor
 6075                        .workspace
 6076                        .as_ref()
 6077                        .and_then(|(workspace, _)| workspace.upgrade())
 6078                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6079                    let menu = CompletionsMenu::new(
 6080                        id,
 6081                        requested_source.unwrap_or(if load_provider_completions {
 6082                            CompletionsMenuSource::Normal
 6083                        } else {
 6084                            CompletionsMenuSource::SnippetsOnly
 6085                        }),
 6086                        sort_completions,
 6087                        show_completion_documentation,
 6088                        position,
 6089                        query.clone(),
 6090                        is_incomplete,
 6091                        buffer.clone(),
 6092                        completions.into(),
 6093                        editor
 6094                            .context_menu()
 6095                            .borrow_mut()
 6096                            .as_ref()
 6097                            .map(|menu| menu.primary_scroll_handle()),
 6098                        display_options,
 6099                        snippet_sort_order,
 6100                        languages,
 6101                        language,
 6102                        cx,
 6103                    );
 6104
 6105                    let query = if filter_completions { query } else { None };
 6106                    let matches_task = menu.do_async_filtering(
 6107                        query.unwrap_or_default(),
 6108                        buffer_position,
 6109                        &buffer,
 6110                        cx,
 6111                    );
 6112                    (menu, matches_task)
 6113                }) else {
 6114                    return;
 6115                };
 6116
 6117                let matches = matches_task.await;
 6118
 6119                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6120                    // Newer menu already set, so exit.
 6121                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6122                        editor.context_menu.borrow().as_ref()
 6123                        && prev_menu.id > id
 6124                    {
 6125                        return;
 6126                    };
 6127
 6128                    // Only valid to take prev_menu because either the new menu is immediately set
 6129                    // below, or the menu is hidden.
 6130                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6131                        editor.context_menu.borrow_mut().take()
 6132                    {
 6133                        let position_matches =
 6134                            if prev_menu.initial_position == menu.initial_position {
 6135                                true
 6136                            } else {
 6137                                let snapshot = editor.buffer.read(cx).read(cx);
 6138                                prev_menu.initial_position.to_offset(&snapshot)
 6139                                    == menu.initial_position.to_offset(&snapshot)
 6140                            };
 6141                        if position_matches {
 6142                            // Preserve markdown cache before `set_filter_results` because it will
 6143                            // try to populate the documentation cache.
 6144                            menu.preserve_markdown_cache(prev_menu);
 6145                        }
 6146                    };
 6147
 6148                    menu.set_filter_results(matches, provider, window, cx);
 6149                }) else {
 6150                    return;
 6151                };
 6152
 6153                menu.visible().then_some(menu)
 6154            };
 6155
 6156            editor
 6157                .update_in(cx, |editor, window, cx| {
 6158                    if editor.focus_handle.is_focused(window)
 6159                        && let Some(menu) = menu
 6160                    {
 6161                        *editor.context_menu.borrow_mut() =
 6162                            Some(CodeContextMenu::Completions(menu));
 6163
 6164                        crate::hover_popover::hide_hover(editor, cx);
 6165                        if editor.show_edit_predictions_in_menu() {
 6166                            editor.update_visible_edit_prediction(window, cx);
 6167                        } else {
 6168                            editor.discard_edit_prediction(false, cx);
 6169                        }
 6170
 6171                        cx.notify();
 6172                        return;
 6173                    }
 6174
 6175                    if editor.completion_tasks.len() <= 1 {
 6176                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6177                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6178                        // If it was already hidden and we don't show edit predictions in the menu,
 6179                        // we should also show the edit prediction when available.
 6180                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6181                            editor.update_visible_edit_prediction(window, cx);
 6182                        }
 6183                    }
 6184                })
 6185                .ok();
 6186        });
 6187
 6188        self.completion_tasks.push((id, task));
 6189    }
 6190
 6191    #[cfg(feature = "test-support")]
 6192    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6193        let menu = self.context_menu.borrow();
 6194        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6195            let completions = menu.completions.borrow();
 6196            Some(completions.to_vec())
 6197        } else {
 6198            None
 6199        }
 6200    }
 6201
 6202    pub fn with_completions_menu_matching_id<R>(
 6203        &self,
 6204        id: CompletionId,
 6205        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6206    ) -> R {
 6207        let mut context_menu = self.context_menu.borrow_mut();
 6208        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6209            return f(None);
 6210        };
 6211        if completions_menu.id != id {
 6212            return f(None);
 6213        }
 6214        f(Some(completions_menu))
 6215    }
 6216
 6217    pub fn confirm_completion(
 6218        &mut self,
 6219        action: &ConfirmCompletion,
 6220        window: &mut Window,
 6221        cx: &mut Context<Self>,
 6222    ) -> Option<Task<Result<()>>> {
 6223        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6224        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6225    }
 6226
 6227    pub fn confirm_completion_insert(
 6228        &mut self,
 6229        _: &ConfirmCompletionInsert,
 6230        window: &mut Window,
 6231        cx: &mut Context<Self>,
 6232    ) -> Option<Task<Result<()>>> {
 6233        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6234        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6235    }
 6236
 6237    pub fn confirm_completion_replace(
 6238        &mut self,
 6239        _: &ConfirmCompletionReplace,
 6240        window: &mut Window,
 6241        cx: &mut Context<Self>,
 6242    ) -> Option<Task<Result<()>>> {
 6243        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6244        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6245    }
 6246
 6247    pub fn compose_completion(
 6248        &mut self,
 6249        action: &ComposeCompletion,
 6250        window: &mut Window,
 6251        cx: &mut Context<Self>,
 6252    ) -> Option<Task<Result<()>>> {
 6253        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6254        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6255    }
 6256
 6257    fn do_completion(
 6258        &mut self,
 6259        item_ix: Option<usize>,
 6260        intent: CompletionIntent,
 6261        window: &mut Window,
 6262        cx: &mut Context<Editor>,
 6263    ) -> Option<Task<Result<()>>> {
 6264        use language::ToOffset as _;
 6265
 6266        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6267        else {
 6268            return None;
 6269        };
 6270
 6271        let candidate_id = {
 6272            let entries = completions_menu.entries.borrow();
 6273            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6274            if self.show_edit_predictions_in_menu() {
 6275                self.discard_edit_prediction(true, cx);
 6276            }
 6277            mat.candidate_id
 6278        };
 6279
 6280        let completion = completions_menu
 6281            .completions
 6282            .borrow()
 6283            .get(candidate_id)?
 6284            .clone();
 6285        cx.stop_propagation();
 6286
 6287        let buffer_handle = completions_menu.buffer.clone();
 6288
 6289        let CompletionEdit {
 6290            new_text,
 6291            snippet,
 6292            replace_range,
 6293        } = process_completion_for_edit(
 6294            &completion,
 6295            intent,
 6296            &buffer_handle,
 6297            &completions_menu.initial_position.text_anchor,
 6298            cx,
 6299        );
 6300
 6301        let buffer = buffer_handle.read(cx);
 6302        let snapshot = self.buffer.read(cx).snapshot(cx);
 6303        let newest_anchor = self.selections.newest_anchor();
 6304        let replace_range_multibuffer = {
 6305            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6306            excerpt.map_range_from_buffer(replace_range.clone())
 6307        };
 6308        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6309            return None;
 6310        }
 6311
 6312        let old_text = buffer
 6313            .text_for_range(replace_range.clone())
 6314            .collect::<String>();
 6315        let lookbehind = newest_anchor
 6316            .start
 6317            .text_anchor
 6318            .to_offset(buffer)
 6319            .saturating_sub(replace_range.start.0);
 6320        let lookahead = replace_range
 6321            .end
 6322            .0
 6323            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6324        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6325        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6326
 6327        let selections = self
 6328            .selections
 6329            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6330        let mut ranges = Vec::new();
 6331        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 6332
 6333        for selection in &selections {
 6334            let range = if selection.id == newest_anchor.id {
 6335                replace_range_multibuffer.clone()
 6336            } else {
 6337                let mut range = selection.range();
 6338
 6339                // if prefix is present, don't duplicate it
 6340                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6341                    range.start = range.start.saturating_sub_usize(lookbehind);
 6342
 6343                    // if suffix is also present, mimic the newest cursor and replace it
 6344                    if selection.id != newest_anchor.id
 6345                        && snapshot.contains_str_at(range.end, suffix)
 6346                    {
 6347                        range.end += lookahead;
 6348                    }
 6349                }
 6350                range
 6351            };
 6352
 6353            ranges.push(range.clone());
 6354
 6355            if !self.linked_edit_ranges.is_empty() {
 6356                let start_anchor = snapshot.anchor_before(range.start);
 6357                let end_anchor = snapshot.anchor_after(range.end);
 6358                if let Some(ranges) = self
 6359                    .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
 6360                {
 6361                    for (buffer, edits) in ranges {
 6362                        linked_edits
 6363                            .entry(buffer.clone())
 6364                            .or_default()
 6365                            .extend(edits.into_iter().map(|range| (range, new_text.to_owned())));
 6366                    }
 6367                }
 6368            }
 6369        }
 6370
 6371        let common_prefix_len = old_text
 6372            .chars()
 6373            .zip(new_text.chars())
 6374            .take_while(|(a, b)| a == b)
 6375            .map(|(a, _)| a.len_utf8())
 6376            .sum::<usize>();
 6377
 6378        cx.emit(EditorEvent::InputHandled {
 6379            utf16_range_to_replace: None,
 6380            text: new_text[common_prefix_len..].into(),
 6381        });
 6382
 6383        self.transact(window, cx, |editor, window, cx| {
 6384            if let Some(mut snippet) = snippet {
 6385                snippet.text = new_text.to_string();
 6386                editor
 6387                    .insert_snippet(&ranges, snippet, window, cx)
 6388                    .log_err();
 6389            } else {
 6390                editor.buffer.update(cx, |multi_buffer, cx| {
 6391                    let auto_indent = match completion.insert_text_mode {
 6392                        Some(InsertTextMode::AS_IS) => None,
 6393                        _ => editor.autoindent_mode.clone(),
 6394                    };
 6395                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6396                    multi_buffer.edit(edits, auto_indent, cx);
 6397                });
 6398            }
 6399            for (buffer, edits) in linked_edits {
 6400                buffer.update(cx, |buffer, cx| {
 6401                    let snapshot = buffer.snapshot();
 6402                    let edits = edits
 6403                        .into_iter()
 6404                        .map(|(range, text)| {
 6405                            use text::ToPoint as TP;
 6406                            let end_point = TP::to_point(&range.end, &snapshot);
 6407                            let start_point = TP::to_point(&range.start, &snapshot);
 6408                            (start_point..end_point, text)
 6409                        })
 6410                        .sorted_by_key(|(range, _)| range.start);
 6411                    buffer.edit(edits, None, cx);
 6412                })
 6413            }
 6414
 6415            editor.refresh_edit_prediction(true, false, window, cx);
 6416        });
 6417        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6418
 6419        let show_new_completions_on_confirm = completion
 6420            .confirm
 6421            .as_ref()
 6422            .is_some_and(|confirm| confirm(intent, window, cx));
 6423        if show_new_completions_on_confirm {
 6424            self.open_or_update_completions_menu(None, None, false, window, cx);
 6425        }
 6426
 6427        let provider = self.completion_provider.as_ref()?;
 6428
 6429        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6430        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6431            let CompletionSource::Lsp {
 6432                lsp_completion,
 6433                server_id,
 6434                ..
 6435            } = &completion.source
 6436            else {
 6437                return None;
 6438            };
 6439            let lsp_command = lsp_completion.command.as_ref()?;
 6440            let available_commands = lsp_store
 6441                .read(cx)
 6442                .lsp_server_capabilities
 6443                .get(server_id)
 6444                .and_then(|server_capabilities| {
 6445                    server_capabilities
 6446                        .execute_command_provider
 6447                        .as_ref()
 6448                        .map(|options| options.commands.as_slice())
 6449                })?;
 6450            if available_commands.contains(&lsp_command.command) {
 6451                Some(CodeAction {
 6452                    server_id: *server_id,
 6453                    range: language::Anchor::MIN..language::Anchor::MIN,
 6454                    lsp_action: LspAction::Command(lsp_command.clone()),
 6455                    resolved: false,
 6456                })
 6457            } else {
 6458                None
 6459            }
 6460        });
 6461
 6462        drop(completion);
 6463        let apply_edits = provider.apply_additional_edits_for_completion(
 6464            buffer_handle.clone(),
 6465            completions_menu.completions.clone(),
 6466            candidate_id,
 6467            true,
 6468            cx,
 6469        );
 6470
 6471        let editor_settings = EditorSettings::get_global(cx);
 6472        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6473            // After the code completion is finished, users often want to know what signatures are needed.
 6474            // so we should automatically call signature_help
 6475            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6476        }
 6477
 6478        Some(cx.spawn_in(window, async move |editor, cx| {
 6479            apply_edits.await?;
 6480
 6481            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6482                let title = command.lsp_action.title().to_owned();
 6483                let project_transaction = lsp_store
 6484                    .update(cx, |lsp_store, cx| {
 6485                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6486                    })
 6487                    .await
 6488                    .context("applying post-completion command")?;
 6489                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6490                    Self::open_project_transaction(
 6491                        &editor,
 6492                        workspace.downgrade(),
 6493                        project_transaction,
 6494                        title,
 6495                        cx,
 6496                    )
 6497                    .await?;
 6498                }
 6499            }
 6500
 6501            Ok(())
 6502        }))
 6503    }
 6504
 6505    pub fn toggle_code_actions(
 6506        &mut self,
 6507        action: &ToggleCodeActions,
 6508        window: &mut Window,
 6509        cx: &mut Context<Self>,
 6510    ) {
 6511        let quick_launch = action.quick_launch;
 6512        let mut context_menu = self.context_menu.borrow_mut();
 6513        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6514            if code_actions.deployed_from == action.deployed_from {
 6515                // Toggle if we're selecting the same one
 6516                *context_menu = None;
 6517                cx.notify();
 6518                return;
 6519            } else {
 6520                // Otherwise, clear it and start a new one
 6521                *context_menu = None;
 6522                cx.notify();
 6523            }
 6524        }
 6525        drop(context_menu);
 6526        let snapshot = self.snapshot(window, cx);
 6527        let deployed_from = action.deployed_from.clone();
 6528        let action = action.clone();
 6529        self.completion_tasks.clear();
 6530        self.discard_edit_prediction(false, cx);
 6531
 6532        let multibuffer_point = match &action.deployed_from {
 6533            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6534                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6535            }
 6536            _ => self
 6537                .selections
 6538                .newest::<Point>(&snapshot.display_snapshot)
 6539                .head(),
 6540        };
 6541        let Some((buffer, buffer_row)) = snapshot
 6542            .buffer_snapshot()
 6543            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6544            .and_then(|(buffer_snapshot, range)| {
 6545                self.buffer()
 6546                    .read(cx)
 6547                    .buffer(buffer_snapshot.remote_id())
 6548                    .map(|buffer| (buffer, range.start.row))
 6549            })
 6550        else {
 6551            return;
 6552        };
 6553        let buffer_id = buffer.read(cx).remote_id();
 6554        let tasks = self
 6555            .tasks
 6556            .get(&(buffer_id, buffer_row))
 6557            .map(|t| Arc::new(t.to_owned()));
 6558
 6559        if !self.focus_handle.is_focused(window) {
 6560            return;
 6561        }
 6562        let project = self.project.clone();
 6563
 6564        let code_actions_task = match deployed_from {
 6565            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6566            _ => self.code_actions(buffer_row, window, cx),
 6567        };
 6568
 6569        let runnable_task = match deployed_from {
 6570            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6571            _ => {
 6572                let mut task_context_task = Task::ready(None);
 6573                if let Some(tasks) = &tasks
 6574                    && let Some(project) = project
 6575                {
 6576                    task_context_task =
 6577                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6578                }
 6579
 6580                cx.spawn_in(window, {
 6581                    let buffer = buffer.clone();
 6582                    async move |editor, cx| {
 6583                        let task_context = task_context_task.await;
 6584
 6585                        let resolved_tasks =
 6586                            tasks
 6587                                .zip(task_context.clone())
 6588                                .map(|(tasks, task_context)| ResolvedTasks {
 6589                                    templates: tasks.resolve(&task_context).collect(),
 6590                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6591                                        multibuffer_point.row,
 6592                                        tasks.column,
 6593                                    )),
 6594                                });
 6595                        let debug_scenarios = editor
 6596                            .update(cx, |editor, cx| {
 6597                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6598                            })?
 6599                            .await;
 6600                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6601                    }
 6602                })
 6603            }
 6604        };
 6605
 6606        cx.spawn_in(window, async move |editor, cx| {
 6607            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6608            let code_actions = code_actions_task.await;
 6609            let spawn_straight_away = quick_launch
 6610                && resolved_tasks
 6611                    .as_ref()
 6612                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6613                && code_actions
 6614                    .as_ref()
 6615                    .is_none_or(|actions| actions.is_empty())
 6616                && debug_scenarios.is_empty();
 6617
 6618            editor.update_in(cx, |editor, window, cx| {
 6619                crate::hover_popover::hide_hover(editor, cx);
 6620                let actions = CodeActionContents::new(
 6621                    resolved_tasks,
 6622                    code_actions,
 6623                    debug_scenarios,
 6624                    task_context.unwrap_or_default(),
 6625                );
 6626
 6627                // Don't show the menu if there are no actions available
 6628                if actions.is_empty() {
 6629                    cx.notify();
 6630                    return Task::ready(Ok(()));
 6631                }
 6632
 6633                *editor.context_menu.borrow_mut() =
 6634                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6635                        buffer,
 6636                        actions,
 6637                        selected_item: Default::default(),
 6638                        scroll_handle: UniformListScrollHandle::default(),
 6639                        deployed_from,
 6640                    }));
 6641                cx.notify();
 6642                if spawn_straight_away
 6643                    && let Some(task) = editor.confirm_code_action(
 6644                        &ConfirmCodeAction { item_ix: Some(0) },
 6645                        window,
 6646                        cx,
 6647                    )
 6648                {
 6649                    return task;
 6650                }
 6651
 6652                Task::ready(Ok(()))
 6653            })
 6654        })
 6655        .detach_and_log_err(cx);
 6656    }
 6657
 6658    fn debug_scenarios(
 6659        &mut self,
 6660        resolved_tasks: &Option<ResolvedTasks>,
 6661        buffer: &Entity<Buffer>,
 6662        cx: &mut App,
 6663    ) -> Task<Vec<task::DebugScenario>> {
 6664        maybe!({
 6665            let project = self.project()?;
 6666            let dap_store = project.read(cx).dap_store();
 6667            let mut scenarios = vec![];
 6668            let resolved_tasks = resolved_tasks.as_ref()?;
 6669            let buffer = buffer.read(cx);
 6670            let language = buffer.language()?;
 6671            let file = buffer.file();
 6672            let debug_adapter = language_settings(language.name().into(), file, cx)
 6673                .debuggers
 6674                .first()
 6675                .map(SharedString::from)
 6676                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6677
 6678            dap_store.update(cx, |dap_store, cx| {
 6679                for (_, task) in &resolved_tasks.templates {
 6680                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6681                        task.original_task().clone(),
 6682                        debug_adapter.clone().into(),
 6683                        task.display_label().to_owned().into(),
 6684                        cx,
 6685                    );
 6686                    scenarios.push(maybe_scenario);
 6687                }
 6688            });
 6689            Some(cx.background_spawn(async move {
 6690                futures::future::join_all(scenarios)
 6691                    .await
 6692                    .into_iter()
 6693                    .flatten()
 6694                    .collect::<Vec<_>>()
 6695            }))
 6696        })
 6697        .unwrap_or_else(|| Task::ready(vec![]))
 6698    }
 6699
 6700    fn code_actions(
 6701        &mut self,
 6702        buffer_row: u32,
 6703        window: &mut Window,
 6704        cx: &mut Context<Self>,
 6705    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6706        let mut task = self.code_actions_task.take();
 6707        cx.spawn_in(window, async move |editor, cx| {
 6708            while let Some(prev_task) = task {
 6709                prev_task.await.log_err();
 6710                task = editor
 6711                    .update(cx, |this, _| this.code_actions_task.take())
 6712                    .ok()?;
 6713            }
 6714
 6715            editor
 6716                .update(cx, |editor, cx| {
 6717                    editor
 6718                        .available_code_actions
 6719                        .clone()
 6720                        .and_then(|(location, code_actions)| {
 6721                            let snapshot = location.buffer.read(cx).snapshot();
 6722                            let point_range = location.range.to_point(&snapshot);
 6723                            let point_range = point_range.start.row..=point_range.end.row;
 6724                            if point_range.contains(&buffer_row) {
 6725                                Some(code_actions)
 6726                            } else {
 6727                                None
 6728                            }
 6729                        })
 6730                })
 6731                .ok()
 6732                .flatten()
 6733        })
 6734    }
 6735
 6736    pub fn confirm_code_action(
 6737        &mut self,
 6738        action: &ConfirmCodeAction,
 6739        window: &mut Window,
 6740        cx: &mut Context<Self>,
 6741    ) -> Option<Task<Result<()>>> {
 6742        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6743
 6744        let actions_menu =
 6745            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6746                menu
 6747            } else {
 6748                return None;
 6749            };
 6750
 6751        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6752        let action = actions_menu.actions.get(action_ix)?;
 6753        let title = action.label();
 6754        let buffer = actions_menu.buffer;
 6755        let workspace = self.workspace()?;
 6756
 6757        match action {
 6758            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6759                workspace.update(cx, |workspace, cx| {
 6760                    workspace.schedule_resolved_task(
 6761                        task_source_kind,
 6762                        resolved_task,
 6763                        false,
 6764                        window,
 6765                        cx,
 6766                    );
 6767
 6768                    Some(Task::ready(Ok(())))
 6769                })
 6770            }
 6771            CodeActionsItem::CodeAction {
 6772                excerpt_id,
 6773                action,
 6774                provider,
 6775            } => {
 6776                let apply_code_action =
 6777                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6778                let workspace = workspace.downgrade();
 6779                Some(cx.spawn_in(window, async move |editor, cx| {
 6780                    let project_transaction = apply_code_action.await?;
 6781                    Self::open_project_transaction(
 6782                        &editor,
 6783                        workspace,
 6784                        project_transaction,
 6785                        title,
 6786                        cx,
 6787                    )
 6788                    .await
 6789                }))
 6790            }
 6791            CodeActionsItem::DebugScenario(scenario) => {
 6792                let context = actions_menu.actions.context;
 6793
 6794                workspace.update(cx, |workspace, cx| {
 6795                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6796                    workspace.start_debug_session(
 6797                        scenario,
 6798                        context,
 6799                        Some(buffer),
 6800                        None,
 6801                        window,
 6802                        cx,
 6803                    );
 6804                });
 6805                Some(Task::ready(Ok(())))
 6806            }
 6807        }
 6808    }
 6809
 6810    fn open_transaction_for_hidden_buffers(
 6811        workspace: Entity<Workspace>,
 6812        transaction: ProjectTransaction,
 6813        title: String,
 6814        window: &mut Window,
 6815        cx: &mut Context<Self>,
 6816    ) {
 6817        if transaction.0.is_empty() {
 6818            return;
 6819        }
 6820
 6821        let edited_buffers_already_open = {
 6822            let other_editors: Vec<Entity<Editor>> = workspace
 6823                .read(cx)
 6824                .panes()
 6825                .iter()
 6826                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6827                .filter(|editor| editor.entity_id() != cx.entity_id())
 6828                .collect();
 6829
 6830            transaction.0.keys().all(|buffer| {
 6831                other_editors.iter().any(|editor| {
 6832                    let multi_buffer = editor.read(cx).buffer();
 6833                    multi_buffer.read(cx).is_singleton()
 6834                        && multi_buffer
 6835                            .read(cx)
 6836                            .as_singleton()
 6837                            .map_or(false, |singleton| {
 6838                                singleton.entity_id() == buffer.entity_id()
 6839                            })
 6840                })
 6841            })
 6842        };
 6843        if !edited_buffers_already_open {
 6844            let workspace = workspace.downgrade();
 6845            cx.defer_in(window, move |_, window, cx| {
 6846                cx.spawn_in(window, async move |editor, cx| {
 6847                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6848                        .await
 6849                        .ok()
 6850                })
 6851                .detach();
 6852            });
 6853        }
 6854    }
 6855
 6856    pub async fn open_project_transaction(
 6857        editor: &WeakEntity<Editor>,
 6858        workspace: WeakEntity<Workspace>,
 6859        transaction: ProjectTransaction,
 6860        title: String,
 6861        cx: &mut AsyncWindowContext,
 6862    ) -> Result<()> {
 6863        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 6864        cx.update(|_, cx| {
 6865            entries.sort_unstable_by_key(|(buffer, _)| {
 6866                buffer.read(cx).file().map(|f| f.path().clone())
 6867            });
 6868        })?;
 6869        if entries.is_empty() {
 6870            return Ok(());
 6871        }
 6872
 6873        // If the project transaction's edits are all contained within this editor, then
 6874        // avoid opening a new editor to display them.
 6875
 6876        if let [(buffer, transaction)] = &*entries {
 6877            let excerpt = editor.update(cx, |editor, cx| {
 6878                editor
 6879                    .buffer()
 6880                    .read(cx)
 6881                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 6882            })?;
 6883            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 6884                && excerpted_buffer == *buffer
 6885            {
 6886                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 6887                    let excerpt_range = excerpt_range.to_offset(buffer);
 6888                    buffer
 6889                        .edited_ranges_for_transaction::<usize>(transaction)
 6890                        .all(|range| {
 6891                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 6892                        })
 6893                });
 6894
 6895                if all_edits_within_excerpt {
 6896                    return Ok(());
 6897                }
 6898            }
 6899        }
 6900
 6901        let mut ranges_to_highlight = Vec::new();
 6902        let excerpt_buffer = cx.new(|cx| {
 6903            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 6904            for (buffer_handle, transaction) in &entries {
 6905                let edited_ranges = buffer_handle
 6906                    .read(cx)
 6907                    .edited_ranges_for_transaction::<Point>(transaction)
 6908                    .collect::<Vec<_>>();
 6909                let (ranges, _) = multibuffer.set_excerpts_for_path(
 6910                    PathKey::for_buffer(buffer_handle, cx),
 6911                    buffer_handle.clone(),
 6912                    edited_ranges,
 6913                    multibuffer_context_lines(cx),
 6914                    cx,
 6915                );
 6916
 6917                ranges_to_highlight.extend(ranges);
 6918            }
 6919            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 6920            multibuffer
 6921        });
 6922
 6923        workspace.update_in(cx, |workspace, window, cx| {
 6924            let project = workspace.project().clone();
 6925            let editor =
 6926                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 6927            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 6928            editor.update(cx, |editor, cx| {
 6929                editor.highlight_background::<Self>(
 6930                    &ranges_to_highlight,
 6931                    |_, theme| theme.colors().editor_highlighted_line_background,
 6932                    cx,
 6933                );
 6934            });
 6935        })?;
 6936
 6937        Ok(())
 6938    }
 6939
 6940    pub fn clear_code_action_providers(&mut self) {
 6941        self.code_action_providers.clear();
 6942        self.available_code_actions.take();
 6943    }
 6944
 6945    pub fn add_code_action_provider(
 6946        &mut self,
 6947        provider: Rc<dyn CodeActionProvider>,
 6948        window: &mut Window,
 6949        cx: &mut Context<Self>,
 6950    ) {
 6951        if self
 6952            .code_action_providers
 6953            .iter()
 6954            .any(|existing_provider| existing_provider.id() == provider.id())
 6955        {
 6956            return;
 6957        }
 6958
 6959        self.code_action_providers.push(provider);
 6960        self.refresh_code_actions(window, cx);
 6961    }
 6962
 6963    pub fn remove_code_action_provider(
 6964        &mut self,
 6965        id: Arc<str>,
 6966        window: &mut Window,
 6967        cx: &mut Context<Self>,
 6968    ) {
 6969        self.code_action_providers
 6970            .retain(|provider| provider.id() != id);
 6971        self.refresh_code_actions(window, cx);
 6972    }
 6973
 6974    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 6975        !self.code_action_providers.is_empty()
 6976            && EditorSettings::get_global(cx).toolbar.code_actions
 6977    }
 6978
 6979    pub fn has_available_code_actions(&self) -> bool {
 6980        self.available_code_actions
 6981            .as_ref()
 6982            .is_some_and(|(_, actions)| !actions.is_empty())
 6983    }
 6984
 6985    fn render_inline_code_actions(
 6986        &self,
 6987        icon_size: ui::IconSize,
 6988        display_row: DisplayRow,
 6989        is_active: bool,
 6990        cx: &mut Context<Self>,
 6991    ) -> AnyElement {
 6992        let show_tooltip = !self.context_menu_visible();
 6993        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 6994            .icon_size(icon_size)
 6995            .shape(ui::IconButtonShape::Square)
 6996            .icon_color(ui::Color::Hidden)
 6997            .toggle_state(is_active)
 6998            .when(show_tooltip, |this| {
 6999                this.tooltip({
 7000                    let focus_handle = self.focus_handle.clone();
 7001                    move |_window, cx| {
 7002                        Tooltip::for_action_in(
 7003                            "Toggle Code Actions",
 7004                            &ToggleCodeActions {
 7005                                deployed_from: None,
 7006                                quick_launch: false,
 7007                            },
 7008                            &focus_handle,
 7009                            cx,
 7010                        )
 7011                    }
 7012                })
 7013            })
 7014            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7015                window.focus(&editor.focus_handle(cx), cx);
 7016                editor.toggle_code_actions(
 7017                    &crate::actions::ToggleCodeActions {
 7018                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7019                            display_row,
 7020                        )),
 7021                        quick_launch: false,
 7022                    },
 7023                    window,
 7024                    cx,
 7025                );
 7026            }))
 7027            .into_any_element()
 7028    }
 7029
 7030    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7031        &self.context_menu
 7032    }
 7033
 7034    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7035        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7036            cx.background_executor()
 7037                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7038                .await;
 7039
 7040            let (start_buffer, start, _, end, newest_selection) = this
 7041                .update(cx, |this, cx| {
 7042                    let newest_selection = this.selections.newest_anchor().clone();
 7043                    if newest_selection.head().diff_base_anchor.is_some() {
 7044                        return None;
 7045                    }
 7046                    let display_snapshot = this.display_snapshot(cx);
 7047                    let newest_selection_adjusted =
 7048                        this.selections.newest_adjusted(&display_snapshot);
 7049                    let buffer = this.buffer.read(cx);
 7050
 7051                    let (start_buffer, start) =
 7052                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7053                    let (end_buffer, end) =
 7054                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7055
 7056                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7057                })?
 7058                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7059                .context(
 7060                    "Expected selection to lie in a single buffer when refreshing code actions",
 7061                )?;
 7062            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7063                let providers = this.code_action_providers.clone();
 7064                let tasks = this
 7065                    .code_action_providers
 7066                    .iter()
 7067                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7068                    .collect::<Vec<_>>();
 7069                (providers, tasks)
 7070            })?;
 7071
 7072            let mut actions = Vec::new();
 7073            for (provider, provider_actions) in
 7074                providers.into_iter().zip(future::join_all(tasks).await)
 7075            {
 7076                if let Some(provider_actions) = provider_actions.log_err() {
 7077                    actions.extend(provider_actions.into_iter().map(|action| {
 7078                        AvailableCodeAction {
 7079                            excerpt_id: newest_selection.start.excerpt_id,
 7080                            action,
 7081                            provider: provider.clone(),
 7082                        }
 7083                    }));
 7084                }
 7085            }
 7086
 7087            this.update(cx, |this, cx| {
 7088                this.available_code_actions = if actions.is_empty() {
 7089                    None
 7090                } else {
 7091                    Some((
 7092                        Location {
 7093                            buffer: start_buffer,
 7094                            range: start..end,
 7095                        },
 7096                        actions.into(),
 7097                    ))
 7098                };
 7099                cx.notify();
 7100            })
 7101        }));
 7102    }
 7103
 7104    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7105        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7106            self.show_git_blame_inline = false;
 7107
 7108            self.show_git_blame_inline_delay_task =
 7109                Some(cx.spawn_in(window, async move |this, cx| {
 7110                    cx.background_executor().timer(delay).await;
 7111
 7112                    this.update(cx, |this, cx| {
 7113                        this.show_git_blame_inline = true;
 7114                        cx.notify();
 7115                    })
 7116                    .log_err();
 7117                }));
 7118        }
 7119    }
 7120
 7121    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7122        let snapshot = self.snapshot(window, cx);
 7123        let cursor = self
 7124            .selections
 7125            .newest::<Point>(&snapshot.display_snapshot)
 7126            .head();
 7127        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7128        else {
 7129            return;
 7130        };
 7131
 7132        if self.blame.is_none() {
 7133            self.start_git_blame(true, window, cx);
 7134        }
 7135        let Some(blame) = self.blame.as_ref() else {
 7136            return;
 7137        };
 7138
 7139        let row_info = RowInfo {
 7140            buffer_id: Some(buffer.remote_id()),
 7141            buffer_row: Some(point.row),
 7142            ..Default::default()
 7143        };
 7144        let Some((buffer, blame_entry)) = blame
 7145            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7146            .flatten()
 7147        else {
 7148            return;
 7149        };
 7150
 7151        let anchor = self.selections.newest_anchor().head();
 7152        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7153        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7154            self.show_blame_popover(
 7155                buffer,
 7156                &blame_entry,
 7157                position + last_bounds.origin,
 7158                true,
 7159                cx,
 7160            );
 7161        };
 7162    }
 7163
 7164    fn show_blame_popover(
 7165        &mut self,
 7166        buffer: BufferId,
 7167        blame_entry: &BlameEntry,
 7168        position: gpui::Point<Pixels>,
 7169        ignore_timeout: bool,
 7170        cx: &mut Context<Self>,
 7171    ) {
 7172        if let Some(state) = &mut self.inline_blame_popover {
 7173            state.hide_task.take();
 7174        } else {
 7175            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7176            let blame_entry = blame_entry.clone();
 7177            let show_task = cx.spawn(async move |editor, cx| {
 7178                if !ignore_timeout {
 7179                    cx.background_executor()
 7180                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7181                        .await;
 7182                }
 7183                editor
 7184                    .update(cx, |editor, cx| {
 7185                        editor.inline_blame_popover_show_task.take();
 7186                        let Some(blame) = editor.blame.as_ref() else {
 7187                            return;
 7188                        };
 7189                        let blame = blame.read(cx);
 7190                        let details = blame.details_for_entry(buffer, &blame_entry);
 7191                        let markdown = cx.new(|cx| {
 7192                            Markdown::new(
 7193                                details
 7194                                    .as_ref()
 7195                                    .map(|message| message.message.clone())
 7196                                    .unwrap_or_default(),
 7197                                None,
 7198                                None,
 7199                                cx,
 7200                            )
 7201                        });
 7202                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7203                            position,
 7204                            hide_task: None,
 7205                            popover_bounds: None,
 7206                            popover_state: InlineBlamePopoverState {
 7207                                scroll_handle: ScrollHandle::new(),
 7208                                commit_message: details,
 7209                                markdown,
 7210                            },
 7211                            keyboard_grace: ignore_timeout,
 7212                        });
 7213                        cx.notify();
 7214                    })
 7215                    .ok();
 7216            });
 7217            self.inline_blame_popover_show_task = Some(show_task);
 7218        }
 7219    }
 7220
 7221    pub fn has_mouse_context_menu(&self) -> bool {
 7222        self.mouse_context_menu.is_some()
 7223    }
 7224
 7225    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7226        self.inline_blame_popover_show_task.take();
 7227        if let Some(state) = &mut self.inline_blame_popover {
 7228            let hide_task = cx.spawn(async move |editor, cx| {
 7229                if !ignore_timeout {
 7230                    cx.background_executor()
 7231                        .timer(std::time::Duration::from_millis(100))
 7232                        .await;
 7233                }
 7234                editor
 7235                    .update(cx, |editor, cx| {
 7236                        editor.inline_blame_popover.take();
 7237                        cx.notify();
 7238                    })
 7239                    .ok();
 7240            });
 7241            state.hide_task = Some(hide_task);
 7242            true
 7243        } else {
 7244            false
 7245        }
 7246    }
 7247
 7248    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7249        if self.pending_rename.is_some() {
 7250            return None;
 7251        }
 7252
 7253        let provider = self.semantics_provider.clone()?;
 7254        let buffer = self.buffer.read(cx);
 7255        let newest_selection = self.selections.newest_anchor().clone();
 7256        let cursor_position = newest_selection.head();
 7257        let (cursor_buffer, cursor_buffer_position) =
 7258            buffer.text_anchor_for_position(cursor_position, cx)?;
 7259        let (tail_buffer, tail_buffer_position) =
 7260            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7261        if cursor_buffer != tail_buffer {
 7262            return None;
 7263        }
 7264
 7265        let snapshot = cursor_buffer.read(cx).snapshot();
 7266        let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7267        let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7268        if start_word_range != end_word_range {
 7269            self.document_highlights_task.take();
 7270            self.clear_background_highlights::<DocumentHighlightRead>(cx);
 7271            self.clear_background_highlights::<DocumentHighlightWrite>(cx);
 7272            return None;
 7273        }
 7274
 7275        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7276        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7277            cx.background_executor()
 7278                .timer(Duration::from_millis(debounce))
 7279                .await;
 7280
 7281            let highlights = if let Some(highlights) = cx.update(|cx| {
 7282                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7283            }) {
 7284                highlights.await.log_err()
 7285            } else {
 7286                None
 7287            };
 7288
 7289            if let Some(highlights) = highlights {
 7290                this.update(cx, |this, cx| {
 7291                    if this.pending_rename.is_some() {
 7292                        return;
 7293                    }
 7294
 7295                    let buffer = this.buffer.read(cx);
 7296                    if buffer
 7297                        .text_anchor_for_position(cursor_position, cx)
 7298                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7299                    {
 7300                        return;
 7301                    }
 7302
 7303                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7304                    let mut write_ranges = Vec::new();
 7305                    let mut read_ranges = Vec::new();
 7306                    for highlight in highlights {
 7307                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7308                        for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
 7309                        {
 7310                            let start = highlight
 7311                                .range
 7312                                .start
 7313                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7314                            let end = highlight
 7315                                .range
 7316                                .end
 7317                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7318                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7319                                continue;
 7320                            }
 7321
 7322                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7323                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7324                                write_ranges.push(range);
 7325                            } else {
 7326                                read_ranges.push(range);
 7327                            }
 7328                        }
 7329                    }
 7330
 7331                    this.highlight_background::<DocumentHighlightRead>(
 7332                        &read_ranges,
 7333                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7334                        cx,
 7335                    );
 7336                    this.highlight_background::<DocumentHighlightWrite>(
 7337                        &write_ranges,
 7338                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7339                        cx,
 7340                    );
 7341                    cx.notify();
 7342                })
 7343                .log_err();
 7344            }
 7345        }));
 7346        None
 7347    }
 7348
 7349    fn prepare_highlight_query_from_selection(
 7350        &mut self,
 7351        window: &Window,
 7352        cx: &mut Context<Editor>,
 7353    ) -> Option<(String, Range<Anchor>)> {
 7354        if matches!(self.mode, EditorMode::SingleLine) {
 7355            return None;
 7356        }
 7357        if !EditorSettings::get_global(cx).selection_highlight {
 7358            return None;
 7359        }
 7360        if self.selections.count() != 1 || self.selections.line_mode() {
 7361            return None;
 7362        }
 7363        let snapshot = self.snapshot(window, cx);
 7364        let selection = self.selections.newest::<Point>(&snapshot);
 7365        // If the selection spans multiple rows OR it is empty
 7366        if selection.start.row != selection.end.row
 7367            || selection.start.column == selection.end.column
 7368        {
 7369            return None;
 7370        }
 7371        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7372        let query = snapshot
 7373            .buffer_snapshot()
 7374            .text_for_range(selection_anchor_range.clone())
 7375            .collect::<String>();
 7376        if query.trim().is_empty() {
 7377            return None;
 7378        }
 7379        Some((query, selection_anchor_range))
 7380    }
 7381
 7382    #[ztracing::instrument(skip_all)]
 7383    fn update_selection_occurrence_highlights(
 7384        &mut self,
 7385        query_text: String,
 7386        query_range: Range<Anchor>,
 7387        multi_buffer_range_to_query: Range<Point>,
 7388        use_debounce: bool,
 7389        window: &mut Window,
 7390        cx: &mut Context<Editor>,
 7391    ) -> Task<()> {
 7392        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7393        cx.spawn_in(window, async move |editor, cx| {
 7394            if use_debounce {
 7395                cx.background_executor()
 7396                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7397                    .await;
 7398            }
 7399            let match_task = cx.background_spawn(async move {
 7400                let buffer_ranges = multi_buffer_snapshot
 7401                    .range_to_buffer_ranges(
 7402                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7403                    )
 7404                    .into_iter()
 7405                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7406                let mut match_ranges = Vec::new();
 7407                let Ok(regex) = project::search::SearchQuery::text(
 7408                    query_text.clone(),
 7409                    false,
 7410                    false,
 7411                    false,
 7412                    Default::default(),
 7413                    Default::default(),
 7414                    false,
 7415                    None,
 7416                ) else {
 7417                    return Vec::default();
 7418                };
 7419                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7420                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7421                    match_ranges.extend(
 7422                        regex
 7423                            .search(
 7424                                buffer_snapshot,
 7425                                Some(search_range.start.0..search_range.end.0),
 7426                            )
 7427                            .await
 7428                            .into_iter()
 7429                            .filter_map(|match_range| {
 7430                                let match_start = buffer_snapshot
 7431                                    .anchor_after(search_range.start + match_range.start);
 7432                                let match_end = buffer_snapshot
 7433                                    .anchor_before(search_range.start + match_range.end);
 7434                                let match_anchor_range =
 7435                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7436                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7437                            }),
 7438                    );
 7439                }
 7440                match_ranges
 7441            });
 7442            let match_ranges = match_task.await;
 7443            editor
 7444                .update_in(cx, |editor, _, cx| {
 7445                    if use_debounce {
 7446                        editor.clear_background_highlights::<SelectedTextHighlight>(cx);
 7447                        editor.debounced_selection_highlight_complete = true;
 7448                    } else if editor.debounced_selection_highlight_complete {
 7449                        return;
 7450                    }
 7451                    if !match_ranges.is_empty() {
 7452                        editor.highlight_background::<SelectedTextHighlight>(
 7453                            &match_ranges,
 7454                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7455                            cx,
 7456                        )
 7457                    }
 7458                })
 7459                .log_err();
 7460        })
 7461    }
 7462
 7463    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7464        struct NewlineFold;
 7465        let type_id = std::any::TypeId::of::<NewlineFold>();
 7466        if !self.mode.is_single_line() {
 7467            return;
 7468        }
 7469        let snapshot = self.snapshot(window, cx);
 7470        if snapshot.buffer_snapshot().max_point().row == 0 {
 7471            return;
 7472        }
 7473        let task = cx.background_spawn(async move {
 7474            let new_newlines = snapshot
 7475                .buffer_chars_at(MultiBufferOffset(0))
 7476                .filter_map(|(c, i)| {
 7477                    if c == '\n' {
 7478                        Some(
 7479                            snapshot.buffer_snapshot().anchor_after(i)
 7480                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7481                        )
 7482                    } else {
 7483                        None
 7484                    }
 7485                })
 7486                .collect::<Vec<_>>();
 7487            let existing_newlines = snapshot
 7488                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7489                .filter_map(|fold| {
 7490                    if fold.placeholder.type_tag == Some(type_id) {
 7491                        Some(fold.range.start..fold.range.end)
 7492                    } else {
 7493                        None
 7494                    }
 7495                })
 7496                .collect::<Vec<_>>();
 7497
 7498            (new_newlines, existing_newlines)
 7499        });
 7500        self.folding_newlines = cx.spawn(async move |this, cx| {
 7501            let (new_newlines, existing_newlines) = task.await;
 7502            if new_newlines == existing_newlines {
 7503                return;
 7504            }
 7505            let placeholder = FoldPlaceholder {
 7506                render: Arc::new(move |_, _, cx| {
 7507                    div()
 7508                        .bg(cx.theme().status().hint_background)
 7509                        .border_b_1()
 7510                        .size_full()
 7511                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7512                        .border_color(cx.theme().status().hint)
 7513                        .child("\\n")
 7514                        .into_any()
 7515                }),
 7516                constrain_width: false,
 7517                merge_adjacent: false,
 7518                type_tag: Some(type_id),
 7519            };
 7520            let creases = new_newlines
 7521                .into_iter()
 7522                .map(|range| Crease::simple(range, placeholder.clone()))
 7523                .collect();
 7524            this.update(cx, |this, cx| {
 7525                this.display_map.update(cx, |display_map, cx| {
 7526                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7527                    display_map.fold(creases, cx);
 7528                });
 7529            })
 7530            .ok();
 7531        });
 7532    }
 7533
 7534    #[ztracing::instrument(skip_all)]
 7535    fn refresh_selected_text_highlights(
 7536        &mut self,
 7537        on_buffer_edit: bool,
 7538        window: &mut Window,
 7539        cx: &mut Context<Editor>,
 7540    ) {
 7541        let Some((query_text, query_range)) =
 7542            self.prepare_highlight_query_from_selection(window, cx)
 7543        else {
 7544            self.clear_background_highlights::<SelectedTextHighlight>(cx);
 7545            self.quick_selection_highlight_task.take();
 7546            self.debounced_selection_highlight_task.take();
 7547            self.debounced_selection_highlight_complete = false;
 7548            return;
 7549        };
 7550        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7551        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7552        let query_changed = self
 7553            .quick_selection_highlight_task
 7554            .as_ref()
 7555            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7556        if query_changed {
 7557            self.debounced_selection_highlight_complete = false;
 7558        }
 7559        if on_buffer_edit || query_changed {
 7560            let multi_buffer_visible_start = self
 7561                .scroll_manager
 7562                .native_anchor(&display_snapshot, cx)
 7563                .anchor
 7564                .to_point(&multi_buffer_snapshot);
 7565            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7566                multi_buffer_visible_start
 7567                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7568                Bias::Left,
 7569            );
 7570            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7571            self.quick_selection_highlight_task = Some((
 7572                query_range.clone(),
 7573                self.update_selection_occurrence_highlights(
 7574                    query_text.clone(),
 7575                    query_range.clone(),
 7576                    multi_buffer_visible_range,
 7577                    false,
 7578                    window,
 7579                    cx,
 7580                ),
 7581            ));
 7582        }
 7583        if on_buffer_edit
 7584            || self
 7585                .debounced_selection_highlight_task
 7586                .as_ref()
 7587                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7588        {
 7589            let multi_buffer_start = multi_buffer_snapshot
 7590                .anchor_before(MultiBufferOffset(0))
 7591                .to_point(&multi_buffer_snapshot);
 7592            let multi_buffer_end = multi_buffer_snapshot
 7593                .anchor_after(multi_buffer_snapshot.len())
 7594                .to_point(&multi_buffer_snapshot);
 7595            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7596            self.debounced_selection_highlight_task = Some((
 7597                query_range.clone(),
 7598                self.update_selection_occurrence_highlights(
 7599                    query_text,
 7600                    query_range,
 7601                    multi_buffer_full_range,
 7602                    true,
 7603                    window,
 7604                    cx,
 7605                ),
 7606            ));
 7607        }
 7608    }
 7609
 7610    pub fn refresh_edit_prediction(
 7611        &mut self,
 7612        debounce: bool,
 7613        user_requested: bool,
 7614        window: &mut Window,
 7615        cx: &mut Context<Self>,
 7616    ) -> Option<()> {
 7617        if DisableAiSettings::get_global(cx).disable_ai {
 7618            return None;
 7619        }
 7620
 7621        let provider = self.edit_prediction_provider()?;
 7622        let cursor = self.selections.newest_anchor().head();
 7623        let (buffer, cursor_buffer_position) =
 7624            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7625
 7626        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7627            self.discard_edit_prediction(false, cx);
 7628            return None;
 7629        }
 7630
 7631        self.update_visible_edit_prediction(window, cx);
 7632
 7633        if !user_requested
 7634            && (!self.should_show_edit_predictions()
 7635                || !self.is_focused(window)
 7636                || buffer.read(cx).is_empty())
 7637        {
 7638            self.discard_edit_prediction(false, cx);
 7639            return None;
 7640        }
 7641
 7642        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7643        Some(())
 7644    }
 7645
 7646    fn show_edit_predictions_in_menu(&self) -> bool {
 7647        match self.edit_prediction_settings {
 7648            EditPredictionSettings::Disabled => false,
 7649            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7650        }
 7651    }
 7652
 7653    pub fn edit_predictions_enabled(&self) -> bool {
 7654        match self.edit_prediction_settings {
 7655            EditPredictionSettings::Disabled => false,
 7656            EditPredictionSettings::Enabled { .. } => true,
 7657        }
 7658    }
 7659
 7660    fn edit_prediction_requires_modifier(&self) -> bool {
 7661        match self.edit_prediction_settings {
 7662            EditPredictionSettings::Disabled => false,
 7663            EditPredictionSettings::Enabled {
 7664                preview_requires_modifier,
 7665                ..
 7666            } => preview_requires_modifier,
 7667        }
 7668    }
 7669
 7670    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7671        if self.edit_prediction_provider.is_none() || DisableAiSettings::get_global(cx).disable_ai {
 7672            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7673            self.discard_edit_prediction(false, cx);
 7674        } else {
 7675            let selection = self.selections.newest_anchor();
 7676            let cursor = selection.head();
 7677
 7678            if let Some((buffer, cursor_buffer_position)) =
 7679                self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7680            {
 7681                self.edit_prediction_settings =
 7682                    self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7683            }
 7684        }
 7685    }
 7686
 7687    fn edit_prediction_settings_at_position(
 7688        &self,
 7689        buffer: &Entity<Buffer>,
 7690        buffer_position: language::Anchor,
 7691        cx: &App,
 7692    ) -> EditPredictionSettings {
 7693        if !self.mode.is_full()
 7694            || !self.show_edit_predictions_override.unwrap_or(true)
 7695            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7696        {
 7697            return EditPredictionSettings::Disabled;
 7698        }
 7699
 7700        let buffer = buffer.read(cx);
 7701
 7702        let file = buffer.file();
 7703
 7704        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7705            return EditPredictionSettings::Disabled;
 7706        };
 7707
 7708        let by_provider = matches!(
 7709            self.menu_edit_predictions_policy,
 7710            MenuEditPredictionsPolicy::ByProvider
 7711        );
 7712
 7713        let show_in_menu = by_provider
 7714            && self
 7715                .edit_prediction_provider
 7716                .as_ref()
 7717                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7718
 7719        let preview_requires_modifier =
 7720            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7721
 7722        EditPredictionSettings::Enabled {
 7723            show_in_menu,
 7724            preview_requires_modifier,
 7725        }
 7726    }
 7727
 7728    fn should_show_edit_predictions(&self) -> bool {
 7729        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7730    }
 7731
 7732    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7733        matches!(
 7734            self.edit_prediction_preview,
 7735            EditPredictionPreview::Active { .. }
 7736        )
 7737    }
 7738
 7739    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7740        let cursor = self.selections.newest_anchor().head();
 7741        if let Some((buffer, cursor_position)) =
 7742            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7743        {
 7744            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7745        } else {
 7746            false
 7747        }
 7748    }
 7749
 7750    pub fn supports_minimap(&self, cx: &App) -> bool {
 7751        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7752    }
 7753
 7754    fn edit_predictions_enabled_in_buffer(
 7755        &self,
 7756        buffer: &Entity<Buffer>,
 7757        buffer_position: language::Anchor,
 7758        cx: &App,
 7759    ) -> bool {
 7760        maybe!({
 7761            if self.read_only(cx) {
 7762                return Some(false);
 7763            }
 7764            let provider = self.edit_prediction_provider()?;
 7765            if !provider.is_enabled(buffer, buffer_position, cx) {
 7766                return Some(false);
 7767            }
 7768            let buffer = buffer.read(cx);
 7769            let Some(file) = buffer.file() else {
 7770                return Some(true);
 7771            };
 7772            let settings = all_language_settings(Some(file), cx);
 7773            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7774        })
 7775        .unwrap_or(false)
 7776    }
 7777
 7778    pub fn show_edit_prediction(
 7779        &mut self,
 7780        _: &ShowEditPrediction,
 7781        window: &mut Window,
 7782        cx: &mut Context<Self>,
 7783    ) {
 7784        if !self.has_active_edit_prediction() {
 7785            self.refresh_edit_prediction(false, true, window, cx);
 7786            return;
 7787        }
 7788
 7789        self.update_visible_edit_prediction(window, cx);
 7790    }
 7791
 7792    pub fn display_cursor_names(
 7793        &mut self,
 7794        _: &DisplayCursorNames,
 7795        window: &mut Window,
 7796        cx: &mut Context<Self>,
 7797    ) {
 7798        self.show_cursor_names(window, cx);
 7799    }
 7800
 7801    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7802        self.show_cursor_names = true;
 7803        cx.notify();
 7804        cx.spawn_in(window, async move |this, cx| {
 7805            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 7806            this.update(cx, |this, cx| {
 7807                this.show_cursor_names = false;
 7808                cx.notify()
 7809            })
 7810            .ok()
 7811        })
 7812        .detach();
 7813    }
 7814
 7815    pub fn accept_partial_edit_prediction(
 7816        &mut self,
 7817        granularity: EditPredictionGranularity,
 7818        window: &mut Window,
 7819        cx: &mut Context<Self>,
 7820    ) {
 7821        if self.show_edit_predictions_in_menu() {
 7822            self.hide_context_menu(window, cx);
 7823        }
 7824
 7825        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 7826            return;
 7827        };
 7828
 7829        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 7830            return;
 7831        }
 7832
 7833        match &active_edit_prediction.completion {
 7834            EditPrediction::MoveWithin { target, .. } => {
 7835                let target = *target;
 7836
 7837                if matches!(granularity, EditPredictionGranularity::Full) {
 7838                    if let Some(position_map) = &self.last_position_map {
 7839                        let target_row = target.to_display_point(&position_map.snapshot).row();
 7840                        let is_visible = position_map.visible_row_range.contains(&target_row);
 7841
 7842                        if is_visible || !self.edit_prediction_requires_modifier() {
 7843                            self.unfold_ranges(&[target..target], true, false, cx);
 7844                            self.change_selections(
 7845                                SelectionEffects::scroll(Autoscroll::newest()),
 7846                                window,
 7847                                cx,
 7848                                |selections| {
 7849                                    selections.select_anchor_ranges([target..target]);
 7850                                },
 7851                            );
 7852                            self.clear_row_highlights::<EditPredictionPreview>();
 7853                            self.edit_prediction_preview
 7854                                .set_previous_scroll_position(None);
 7855                        } else {
 7856                            // Highlight and request scroll
 7857                            self.edit_prediction_preview
 7858                                .set_previous_scroll_position(Some(
 7859                                    position_map.snapshot.scroll_anchor,
 7860                                ));
 7861                            self.highlight_rows::<EditPredictionPreview>(
 7862                                target..target,
 7863                                cx.theme().colors().editor_highlighted_line_background,
 7864                                RowHighlightOptions {
 7865                                    autoscroll: true,
 7866                                    ..Default::default()
 7867                                },
 7868                                cx,
 7869                            );
 7870                            self.request_autoscroll(Autoscroll::fit(), cx);
 7871                        }
 7872                    }
 7873                } else {
 7874                    self.change_selections(
 7875                        SelectionEffects::scroll(Autoscroll::newest()),
 7876                        window,
 7877                        cx,
 7878                        |selections| {
 7879                            selections.select_anchor_ranges([target..target]);
 7880                        },
 7881                    );
 7882                }
 7883            }
 7884            EditPrediction::MoveOutside { snapshot, target } => {
 7885                if let Some(workspace) = self.workspace() {
 7886                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 7887                        .detach_and_log_err(cx);
 7888                }
 7889            }
 7890            EditPrediction::Edit {
 7891                edits,
 7892                cursor_position,
 7893                ..
 7894            } => {
 7895                self.report_edit_prediction_event(
 7896                    active_edit_prediction.completion_id.clone(),
 7897                    true,
 7898                    cx,
 7899                );
 7900
 7901                match granularity {
 7902                    EditPredictionGranularity::Full => {
 7903                        if let Some(provider) = self.edit_prediction_provider() {
 7904                            provider.accept(cx);
 7905                        }
 7906
 7907                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 7908
 7909                        // Compute fallback cursor position BEFORE applying the edit,
 7910                        // so the anchor tracks through the edit correctly
 7911                        let fallback_cursor_target = {
 7912                            let snapshot = self.buffer.read(cx).snapshot(cx);
 7913                            edits.last().unwrap().0.end.bias_right(&snapshot)
 7914                        };
 7915
 7916                        self.buffer.update(cx, |buffer, cx| {
 7917                            buffer.edit(edits.iter().cloned(), None, cx)
 7918                        });
 7919
 7920                        // Resolve cursor position after the edit is applied
 7921                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 7922                            // The anchor tracks through the edit, then we add the offset
 7923                            let snapshot = self.buffer.read(cx).snapshot(cx);
 7924                            let base_offset = anchor.to_offset(&snapshot).0;
 7925                            let target_offset =
 7926                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 7927                            snapshot.anchor_after(target_offset)
 7928                        } else {
 7929                            fallback_cursor_target
 7930                        };
 7931
 7932                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 7933                            s.select_anchor_ranges([cursor_target..cursor_target]);
 7934                        });
 7935
 7936                        let selections = self.selections.disjoint_anchors_arc();
 7937                        if let Some(transaction_id_now) =
 7938                            self.buffer.read(cx).last_transaction_id(cx)
 7939                        {
 7940                            if transaction_id_prev != Some(transaction_id_now) {
 7941                                self.selection_history
 7942                                    .insert_transaction(transaction_id_now, selections);
 7943                            }
 7944                        }
 7945
 7946                        self.update_visible_edit_prediction(window, cx);
 7947                        if self.active_edit_prediction.is_none() {
 7948                            self.refresh_edit_prediction(true, true, window, cx);
 7949                        }
 7950                        cx.notify();
 7951                    }
 7952                    _ => {
 7953                        let snapshot = self.buffer.read(cx).snapshot(cx);
 7954                        let cursor_offset = self
 7955                            .selections
 7956                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 7957                            .head();
 7958
 7959                        let insertion = edits.iter().find_map(|(range, text)| {
 7960                            let range = range.to_offset(&snapshot);
 7961                            if range.is_empty() && range.start == cursor_offset {
 7962                                Some(text)
 7963                            } else {
 7964                                None
 7965                            }
 7966                        });
 7967
 7968                        if let Some(text) = insertion {
 7969                            let text_to_insert = match granularity {
 7970                                EditPredictionGranularity::Word => {
 7971                                    let mut partial = text
 7972                                        .chars()
 7973                                        .by_ref()
 7974                                        .take_while(|c| c.is_alphabetic())
 7975                                        .collect::<String>();
 7976                                    if partial.is_empty() {
 7977                                        partial = text
 7978                                            .chars()
 7979                                            .by_ref()
 7980                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 7981                                            .collect::<String>();
 7982                                    }
 7983                                    partial
 7984                                }
 7985                                EditPredictionGranularity::Line => {
 7986                                    if let Some(line) = text.split_inclusive('\n').next() {
 7987                                        line.to_string()
 7988                                    } else {
 7989                                        text.to_string()
 7990                                    }
 7991                                }
 7992                                EditPredictionGranularity::Full => unreachable!(),
 7993                            };
 7994
 7995                            cx.emit(EditorEvent::InputHandled {
 7996                                utf16_range_to_replace: None,
 7997                                text: text_to_insert.clone().into(),
 7998                            });
 7999
 8000                            self.insert_with_autoindent_mode(&text_to_insert, None, window, cx);
 8001                            self.refresh_edit_prediction(true, true, window, cx);
 8002                            cx.notify();
 8003                        } else {
 8004                            self.accept_partial_edit_prediction(
 8005                                EditPredictionGranularity::Full,
 8006                                window,
 8007                                cx,
 8008                            );
 8009                        }
 8010                    }
 8011                }
 8012            }
 8013        }
 8014
 8015        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8016    }
 8017
 8018    pub fn accept_next_word_edit_prediction(
 8019        &mut self,
 8020        _: &AcceptNextWordEditPrediction,
 8021        window: &mut Window,
 8022        cx: &mut Context<Self>,
 8023    ) {
 8024        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8025    }
 8026
 8027    pub fn accept_next_line_edit_prediction(
 8028        &mut self,
 8029        _: &AcceptNextLineEditPrediction,
 8030        window: &mut Window,
 8031        cx: &mut Context<Self>,
 8032    ) {
 8033        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8034    }
 8035
 8036    pub fn accept_edit_prediction(
 8037        &mut self,
 8038        _: &AcceptEditPrediction,
 8039        window: &mut Window,
 8040        cx: &mut Context<Self>,
 8041    ) {
 8042        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8043    }
 8044
 8045    fn discard_edit_prediction(
 8046        &mut self,
 8047        should_report_edit_prediction_event: bool,
 8048        cx: &mut Context<Self>,
 8049    ) -> bool {
 8050        if should_report_edit_prediction_event {
 8051            let completion_id = self
 8052                .active_edit_prediction
 8053                .as_ref()
 8054                .and_then(|active_completion| active_completion.completion_id.clone());
 8055
 8056            self.report_edit_prediction_event(completion_id, false, cx);
 8057        }
 8058
 8059        if let Some(provider) = self.edit_prediction_provider() {
 8060            let reason = if should_report_edit_prediction_event {
 8061                EditPredictionDismissReason::Rejected
 8062            } else {
 8063                EditPredictionDismissReason::Ignored
 8064            };
 8065            provider.discard(reason, cx);
 8066        }
 8067
 8068        self.take_active_edit_prediction(cx)
 8069    }
 8070
 8071    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8072        let Some(provider) = self.edit_prediction_provider() else {
 8073            return;
 8074        };
 8075
 8076        let Some((_, buffer, _)) = self
 8077            .buffer
 8078            .read(cx)
 8079            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8080        else {
 8081            return;
 8082        };
 8083
 8084        let extension = buffer
 8085            .read(cx)
 8086            .file()
 8087            .and_then(|file| Some(file.path().extension()?.to_string()));
 8088
 8089        let event_type = match accepted {
 8090            true => "Edit Prediction Accepted",
 8091            false => "Edit Prediction Discarded",
 8092        };
 8093        telemetry::event!(
 8094            event_type,
 8095            provider = provider.name(),
 8096            prediction_id = id,
 8097            suggestion_accepted = accepted,
 8098            file_extension = extension,
 8099        );
 8100    }
 8101
 8102    fn open_editor_at_anchor(
 8103        snapshot: &language::BufferSnapshot,
 8104        target: language::Anchor,
 8105        workspace: &Entity<Workspace>,
 8106        window: &mut Window,
 8107        cx: &mut App,
 8108    ) -> Task<Result<()>> {
 8109        workspace.update(cx, |workspace, cx| {
 8110            let path = snapshot.file().map(|file| file.full_path(cx));
 8111            let Some(path) =
 8112                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8113            else {
 8114                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8115            };
 8116            let target = text::ToPoint::to_point(&target, snapshot);
 8117            let item = workspace.open_path(path, None, true, window, cx);
 8118            window.spawn(cx, async move |cx| {
 8119                let Some(editor) = item.await?.downcast::<Editor>() else {
 8120                    return Ok(());
 8121                };
 8122                editor
 8123                    .update_in(cx, |editor, window, cx| {
 8124                        editor.go_to_singleton_buffer_point(target, window, cx);
 8125                    })
 8126                    .ok();
 8127                anyhow::Ok(())
 8128            })
 8129        })
 8130    }
 8131
 8132    pub fn has_active_edit_prediction(&self) -> bool {
 8133        self.active_edit_prediction.is_some()
 8134    }
 8135
 8136    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8137        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8138            return false;
 8139        };
 8140
 8141        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8142        self.clear_highlights::<EditPredictionHighlight>(cx);
 8143        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8144        true
 8145    }
 8146
 8147    /// Returns true when we're displaying the edit prediction popover below the cursor
 8148    /// like we are not previewing and the LSP autocomplete menu is visible
 8149    /// or we are in `when_holding_modifier` mode.
 8150    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8151        if self.edit_prediction_preview_is_active()
 8152            || !self.show_edit_predictions_in_menu()
 8153            || !self.edit_predictions_enabled()
 8154        {
 8155            return false;
 8156        }
 8157
 8158        if self.has_visible_completions_menu() {
 8159            return true;
 8160        }
 8161
 8162        has_completion && self.edit_prediction_requires_modifier()
 8163    }
 8164
 8165    fn handle_modifiers_changed(
 8166        &mut self,
 8167        modifiers: Modifiers,
 8168        position_map: &PositionMap,
 8169        window: &mut Window,
 8170        cx: &mut Context<Self>,
 8171    ) {
 8172        // Ensure that the edit prediction preview is updated, even when not
 8173        // enabled, if there's an active edit prediction preview.
 8174        if self.show_edit_predictions_in_menu()
 8175            || matches!(
 8176                self.edit_prediction_preview,
 8177                EditPredictionPreview::Active { .. }
 8178            )
 8179        {
 8180            self.update_edit_prediction_preview(&modifiers, window, cx);
 8181        }
 8182
 8183        self.update_selection_mode(&modifiers, position_map, window, cx);
 8184
 8185        let mouse_position = window.mouse_position();
 8186        if !position_map.text_hitbox.is_hovered(window) {
 8187            return;
 8188        }
 8189
 8190        self.update_hovered_link(
 8191            position_map.point_for_position(mouse_position),
 8192            &position_map.snapshot,
 8193            modifiers,
 8194            window,
 8195            cx,
 8196        )
 8197    }
 8198
 8199    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8200        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8201            MultiCursorModifier::Alt => modifiers.secondary(),
 8202            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8203        }
 8204    }
 8205
 8206    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8207        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8208            MultiCursorModifier::Alt => modifiers.alt,
 8209            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8210        }
 8211    }
 8212
 8213    fn columnar_selection_mode(
 8214        modifiers: &Modifiers,
 8215        cx: &mut Context<Self>,
 8216    ) -> Option<ColumnarMode> {
 8217        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8218            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8219                Some(ColumnarMode::FromMouse)
 8220            } else if Self::is_alt_pressed(modifiers, cx) {
 8221                Some(ColumnarMode::FromSelection)
 8222            } else {
 8223                None
 8224            }
 8225        } else {
 8226            None
 8227        }
 8228    }
 8229
 8230    fn update_selection_mode(
 8231        &mut self,
 8232        modifiers: &Modifiers,
 8233        position_map: &PositionMap,
 8234        window: &mut Window,
 8235        cx: &mut Context<Self>,
 8236    ) {
 8237        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8238            return;
 8239        };
 8240        if self.selections.pending_anchor().is_none() {
 8241            return;
 8242        }
 8243
 8244        let mouse_position = window.mouse_position();
 8245        let point_for_position = position_map.point_for_position(mouse_position);
 8246        let position = point_for_position.previous_valid;
 8247
 8248        self.select(
 8249            SelectPhase::BeginColumnar {
 8250                position,
 8251                reset: false,
 8252                mode,
 8253                goal_column: point_for_position.exact_unclipped.column(),
 8254            },
 8255            window,
 8256            cx,
 8257        );
 8258    }
 8259
 8260    fn update_edit_prediction_preview(
 8261        &mut self,
 8262        modifiers: &Modifiers,
 8263        window: &mut Window,
 8264        cx: &mut Context<Self>,
 8265    ) {
 8266        let mut modifiers_held = false;
 8267
 8268        // Check bindings for all granularities.
 8269        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8270        let granularities = [
 8271            EditPredictionGranularity::Full,
 8272            EditPredictionGranularity::Line,
 8273            EditPredictionGranularity::Word,
 8274        ];
 8275
 8276        for granularity in granularities {
 8277            if let Some(keystroke) = self
 8278                .accept_edit_prediction_keybind(granularity, window, cx)
 8279                .keystroke()
 8280            {
 8281                modifiers_held = modifiers_held
 8282                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8283            }
 8284        }
 8285
 8286        if modifiers_held {
 8287            if matches!(
 8288                self.edit_prediction_preview,
 8289                EditPredictionPreview::Inactive { .. }
 8290            ) {
 8291                self.edit_prediction_preview = EditPredictionPreview::Active {
 8292                    previous_scroll_position: None,
 8293                    since: Instant::now(),
 8294                };
 8295
 8296                self.update_visible_edit_prediction(window, cx);
 8297                cx.notify();
 8298            }
 8299        } else if let EditPredictionPreview::Active {
 8300            previous_scroll_position,
 8301            since,
 8302        } = self.edit_prediction_preview
 8303        {
 8304            if let (Some(previous_scroll_position), Some(position_map)) =
 8305                (previous_scroll_position, self.last_position_map.as_ref())
 8306            {
 8307                self.set_scroll_position(
 8308                    previous_scroll_position
 8309                        .scroll_position(&position_map.snapshot.display_snapshot),
 8310                    window,
 8311                    cx,
 8312                );
 8313            }
 8314
 8315            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8316                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8317            };
 8318            self.clear_row_highlights::<EditPredictionPreview>();
 8319            self.update_visible_edit_prediction(window, cx);
 8320            cx.notify();
 8321        }
 8322    }
 8323
 8324    fn update_visible_edit_prediction(
 8325        &mut self,
 8326        _window: &mut Window,
 8327        cx: &mut Context<Self>,
 8328    ) -> Option<()> {
 8329        if DisableAiSettings::get_global(cx).disable_ai {
 8330            return None;
 8331        }
 8332
 8333        if self.ime_transaction.is_some() {
 8334            self.discard_edit_prediction(false, cx);
 8335            return None;
 8336        }
 8337
 8338        let selection = self.selections.newest_anchor();
 8339        let cursor = selection.head();
 8340        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8341        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8342        let excerpt_id = cursor.excerpt_id;
 8343
 8344        let show_in_menu = self.show_edit_predictions_in_menu();
 8345        let completions_menu_has_precedence = !show_in_menu
 8346            && (self.context_menu.borrow().is_some()
 8347                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8348
 8349        if completions_menu_has_precedence
 8350            || !offset_selection.is_empty()
 8351            || self
 8352                .active_edit_prediction
 8353                .as_ref()
 8354                .is_some_and(|completion| {
 8355                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8356                        return false;
 8357                    };
 8358                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8359                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8360                    !invalidation_range.contains(&offset_selection.head())
 8361                })
 8362        {
 8363            self.discard_edit_prediction(false, cx);
 8364            return None;
 8365        }
 8366
 8367        self.take_active_edit_prediction(cx);
 8368        let Some(provider) = self.edit_prediction_provider() else {
 8369            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8370            return None;
 8371        };
 8372
 8373        let (buffer, cursor_buffer_position) =
 8374            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8375
 8376        self.edit_prediction_settings =
 8377            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8378
 8379        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8380
 8381        if self.edit_prediction_indent_conflict {
 8382            let cursor_point = cursor.to_point(&multibuffer);
 8383            let mut suggested_indent = None;
 8384            multibuffer.suggested_indents_callback(
 8385                cursor_point.row..cursor_point.row + 1,
 8386                |_, indent| {
 8387                    suggested_indent = Some(indent);
 8388                    ControlFlow::Break(())
 8389                },
 8390                cx,
 8391            );
 8392
 8393            if let Some(indent) = suggested_indent
 8394                && indent.len == cursor_point.column
 8395            {
 8396                self.edit_prediction_indent_conflict = false;
 8397            }
 8398        }
 8399
 8400        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8401
 8402        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8403        {
 8404            edit_prediction_types::EditPrediction::Local {
 8405                id,
 8406                edits,
 8407                cursor_position,
 8408                edit_preview,
 8409            } => (id, edits, cursor_position, edit_preview),
 8410            edit_prediction_types::EditPrediction::Jump {
 8411                id,
 8412                snapshot,
 8413                target,
 8414            } => {
 8415                if let Some(provider) = &self.edit_prediction_provider {
 8416                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8417                }
 8418                self.stale_edit_prediction_in_menu = None;
 8419                self.active_edit_prediction = Some(EditPredictionState {
 8420                    inlay_ids: vec![],
 8421                    completion: EditPrediction::MoveOutside { snapshot, target },
 8422                    completion_id: id,
 8423                    invalidation_range: None,
 8424                });
 8425                cx.notify();
 8426                return Some(());
 8427            }
 8428        };
 8429
 8430        let edits = edits
 8431            .into_iter()
 8432            .flat_map(|(range, new_text)| {
 8433                Some((
 8434                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8435                    new_text,
 8436                ))
 8437            })
 8438            .collect::<Vec<_>>();
 8439        if edits.is_empty() {
 8440            return None;
 8441        }
 8442
 8443        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8444            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8445            Some((anchor, predicted.offset))
 8446        });
 8447
 8448        let first_edit_start = edits.first().unwrap().0.start;
 8449        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8450        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8451
 8452        let last_edit_end = edits.last().unwrap().0.end;
 8453        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8454        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8455
 8456        let cursor_row = cursor.to_point(&multibuffer).row;
 8457
 8458        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8459
 8460        let mut inlay_ids = Vec::new();
 8461        let invalidation_row_range;
 8462        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8463            Some(cursor_row..edit_end_row)
 8464        } else if cursor_row > edit_end_row {
 8465            Some(edit_start_row..cursor_row)
 8466        } else {
 8467            None
 8468        };
 8469        let supports_jump = self
 8470            .edit_prediction_provider
 8471            .as_ref()
 8472            .map(|provider| provider.provider.supports_jump_to_edit())
 8473            .unwrap_or(true);
 8474
 8475        let is_move = supports_jump
 8476            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8477        let completion = if is_move {
 8478            if let Some(provider) = &self.edit_prediction_provider {
 8479                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8480            }
 8481            invalidation_row_range =
 8482                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8483            let target = first_edit_start;
 8484            EditPrediction::MoveWithin { target, snapshot }
 8485        } else {
 8486            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8487                && !self.edit_predictions_hidden_for_vim_mode;
 8488
 8489            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8490                if provider.show_tab_accept_marker() {
 8491                    EditDisplayMode::TabAccept
 8492                } else {
 8493                    EditDisplayMode::Inline
 8494                }
 8495            } else {
 8496                EditDisplayMode::DiffPopover
 8497            };
 8498
 8499            if show_completions_in_buffer {
 8500                if let Some(provider) = &self.edit_prediction_provider {
 8501                    let suggestion_display_type = match display_mode {
 8502                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8503                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8504                            SuggestionDisplayType::GhostText
 8505                        }
 8506                    };
 8507                    provider.provider.did_show(suggestion_display_type, cx);
 8508                }
 8509                if edits
 8510                    .iter()
 8511                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8512                {
 8513                    let mut inlays = Vec::new();
 8514                    for (range, new_text) in &edits {
 8515                        let inlay = Inlay::edit_prediction(
 8516                            post_inc(&mut self.next_inlay_id),
 8517                            range.start,
 8518                            new_text.as_ref(),
 8519                        );
 8520                        inlay_ids.push(inlay.id);
 8521                        inlays.push(inlay);
 8522                    }
 8523
 8524                    self.splice_inlays(&[], inlays, cx);
 8525                } else {
 8526                    let background_color = cx.theme().status().deleted_background;
 8527                    self.highlight_text::<EditPredictionHighlight>(
 8528                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8529                        HighlightStyle {
 8530                            background_color: Some(background_color),
 8531                            ..Default::default()
 8532                        },
 8533                        cx,
 8534                    );
 8535                }
 8536            }
 8537
 8538            invalidation_row_range = edit_start_row..edit_end_row;
 8539
 8540            EditPrediction::Edit {
 8541                edits,
 8542                cursor_position,
 8543                edit_preview,
 8544                display_mode,
 8545                snapshot,
 8546            }
 8547        };
 8548
 8549        let invalidation_range = multibuffer
 8550            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8551            ..multibuffer.anchor_after(Point::new(
 8552                invalidation_row_range.end,
 8553                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8554            ));
 8555
 8556        self.stale_edit_prediction_in_menu = None;
 8557        self.active_edit_prediction = Some(EditPredictionState {
 8558            inlay_ids,
 8559            completion,
 8560            completion_id,
 8561            invalidation_range: Some(invalidation_range),
 8562        });
 8563
 8564        cx.notify();
 8565
 8566        Some(())
 8567    }
 8568
 8569    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8570        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8571    }
 8572
 8573    fn clear_tasks(&mut self) {
 8574        self.tasks.clear()
 8575    }
 8576
 8577    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8578        if self.tasks.insert(key, value).is_some() {
 8579            // This case should hopefully be rare, but just in case...
 8580            log::error!(
 8581                "multiple different run targets found on a single line, only the last target will be rendered"
 8582            )
 8583        }
 8584    }
 8585
 8586    /// Get all display points of breakpoints that will be rendered within editor
 8587    ///
 8588    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8589    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8590    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8591    fn active_breakpoints(
 8592        &self,
 8593        range: Range<DisplayRow>,
 8594        window: &mut Window,
 8595        cx: &mut Context<Self>,
 8596    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8597        let mut breakpoint_display_points = HashMap::default();
 8598
 8599        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8600            return breakpoint_display_points;
 8601        };
 8602
 8603        let snapshot = self.snapshot(window, cx);
 8604
 8605        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8606        let Some(project) = self.project() else {
 8607            return breakpoint_display_points;
 8608        };
 8609
 8610        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8611            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8612
 8613        for (buffer_snapshot, range, excerpt_id) in
 8614            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8615        {
 8616            let Some(buffer) = project
 8617                .read(cx)
 8618                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8619            else {
 8620                continue;
 8621            };
 8622            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8623                &buffer,
 8624                Some(
 8625                    buffer_snapshot.anchor_before(range.start)
 8626                        ..buffer_snapshot.anchor_after(range.end),
 8627                ),
 8628                buffer_snapshot,
 8629                cx,
 8630            );
 8631            for (breakpoint, state) in breakpoints {
 8632                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8633                let position = multi_buffer_anchor
 8634                    .to_point(&multi_buffer_snapshot)
 8635                    .to_display_point(&snapshot);
 8636
 8637                breakpoint_display_points.insert(
 8638                    position.row(),
 8639                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8640                );
 8641            }
 8642        }
 8643
 8644        breakpoint_display_points
 8645    }
 8646
 8647    fn breakpoint_context_menu(
 8648        &self,
 8649        anchor: Anchor,
 8650        window: &mut Window,
 8651        cx: &mut Context<Self>,
 8652    ) -> Entity<ui::ContextMenu> {
 8653        let weak_editor = cx.weak_entity();
 8654        let focus_handle = self.focus_handle(cx);
 8655
 8656        let row = self
 8657            .buffer
 8658            .read(cx)
 8659            .snapshot(cx)
 8660            .summary_for_anchor::<Point>(&anchor)
 8661            .row;
 8662
 8663        let breakpoint = self
 8664            .breakpoint_at_row(row, window, cx)
 8665            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8666
 8667        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8668            "Edit Log Breakpoint"
 8669        } else {
 8670            "Set Log Breakpoint"
 8671        };
 8672
 8673        let condition_breakpoint_msg = if breakpoint
 8674            .as_ref()
 8675            .is_some_and(|bp| bp.1.condition.is_some())
 8676        {
 8677            "Edit Condition Breakpoint"
 8678        } else {
 8679            "Set Condition Breakpoint"
 8680        };
 8681
 8682        let hit_condition_breakpoint_msg = if breakpoint
 8683            .as_ref()
 8684            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8685        {
 8686            "Edit Hit Condition Breakpoint"
 8687        } else {
 8688            "Set Hit Condition Breakpoint"
 8689        };
 8690
 8691        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8692            "Unset Breakpoint"
 8693        } else {
 8694            "Set Breakpoint"
 8695        };
 8696
 8697        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8698
 8699        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8700            BreakpointState::Enabled => Some("Disable"),
 8701            BreakpointState::Disabled => Some("Enable"),
 8702        });
 8703
 8704        let (anchor, breakpoint) =
 8705            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8706
 8707        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8708            menu.on_blur_subscription(Subscription::new(|| {}))
 8709                .context(focus_handle)
 8710                .when(run_to_cursor, |this| {
 8711                    let weak_editor = weak_editor.clone();
 8712                    this.entry("Run to cursor", None, move |window, cx| {
 8713                        weak_editor
 8714                            .update(cx, |editor, cx| {
 8715                                editor.change_selections(
 8716                                    SelectionEffects::no_scroll(),
 8717                                    window,
 8718                                    cx,
 8719                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8720                                );
 8721                            })
 8722                            .ok();
 8723
 8724                        window.dispatch_action(Box::new(RunToCursor), cx);
 8725                    })
 8726                    .separator()
 8727                })
 8728                .when_some(toggle_state_msg, |this, msg| {
 8729                    this.entry(msg, None, {
 8730                        let weak_editor = weak_editor.clone();
 8731                        let breakpoint = breakpoint.clone();
 8732                        move |_window, cx| {
 8733                            weak_editor
 8734                                .update(cx, |this, cx| {
 8735                                    this.edit_breakpoint_at_anchor(
 8736                                        anchor,
 8737                                        breakpoint.as_ref().clone(),
 8738                                        BreakpointEditAction::InvertState,
 8739                                        cx,
 8740                                    );
 8741                                })
 8742                                .log_err();
 8743                        }
 8744                    })
 8745                })
 8746                .entry(set_breakpoint_msg, None, {
 8747                    let weak_editor = weak_editor.clone();
 8748                    let breakpoint = breakpoint.clone();
 8749                    move |_window, cx| {
 8750                        weak_editor
 8751                            .update(cx, |this, cx| {
 8752                                this.edit_breakpoint_at_anchor(
 8753                                    anchor,
 8754                                    breakpoint.as_ref().clone(),
 8755                                    BreakpointEditAction::Toggle,
 8756                                    cx,
 8757                                );
 8758                            })
 8759                            .log_err();
 8760                    }
 8761                })
 8762                .entry(log_breakpoint_msg, None, {
 8763                    let breakpoint = breakpoint.clone();
 8764                    let weak_editor = weak_editor.clone();
 8765                    move |window, cx| {
 8766                        weak_editor
 8767                            .update(cx, |this, cx| {
 8768                                this.add_edit_breakpoint_block(
 8769                                    anchor,
 8770                                    breakpoint.as_ref(),
 8771                                    BreakpointPromptEditAction::Log,
 8772                                    window,
 8773                                    cx,
 8774                                );
 8775                            })
 8776                            .log_err();
 8777                    }
 8778                })
 8779                .entry(condition_breakpoint_msg, None, {
 8780                    let breakpoint = breakpoint.clone();
 8781                    let weak_editor = weak_editor.clone();
 8782                    move |window, cx| {
 8783                        weak_editor
 8784                            .update(cx, |this, cx| {
 8785                                this.add_edit_breakpoint_block(
 8786                                    anchor,
 8787                                    breakpoint.as_ref(),
 8788                                    BreakpointPromptEditAction::Condition,
 8789                                    window,
 8790                                    cx,
 8791                                );
 8792                            })
 8793                            .log_err();
 8794                    }
 8795                })
 8796                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8797                    weak_editor
 8798                        .update(cx, |this, cx| {
 8799                            this.add_edit_breakpoint_block(
 8800                                anchor,
 8801                                breakpoint.as_ref(),
 8802                                BreakpointPromptEditAction::HitCondition,
 8803                                window,
 8804                                cx,
 8805                            );
 8806                        })
 8807                        .log_err();
 8808                })
 8809        })
 8810    }
 8811
 8812    fn render_breakpoint(
 8813        &self,
 8814        position: Anchor,
 8815        row: DisplayRow,
 8816        breakpoint: &Breakpoint,
 8817        state: Option<BreakpointSessionState>,
 8818        cx: &mut Context<Self>,
 8819    ) -> IconButton {
 8820        let is_rejected = state.is_some_and(|s| !s.verified);
 8821        // Is it a breakpoint that shows up when hovering over gutter?
 8822        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 8823            (false, false),
 8824            |PhantomBreakpointIndicator {
 8825                 is_active,
 8826                 display_row,
 8827                 collides_with_existing_breakpoint,
 8828             }| {
 8829                (
 8830                    is_active && display_row == row,
 8831                    collides_with_existing_breakpoint,
 8832                )
 8833            },
 8834        );
 8835
 8836        let (color, icon) = {
 8837            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 8838                (false, false) => ui::IconName::DebugBreakpoint,
 8839                (true, false) => ui::IconName::DebugLogBreakpoint,
 8840                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 8841                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 8842            };
 8843
 8844            let theme_colors = cx.theme().colors();
 8845
 8846            let color = if is_phantom {
 8847                if collides_with_existing {
 8848                    Color::Custom(
 8849                        theme_colors
 8850                            .debugger_accent
 8851                            .blend(theme_colors.text.opacity(0.6)),
 8852                    )
 8853                } else {
 8854                    Color::Hint
 8855                }
 8856            } else if is_rejected {
 8857                Color::Disabled
 8858            } else {
 8859                Color::Debugger
 8860            };
 8861
 8862            (color, icon)
 8863        };
 8864
 8865        let breakpoint = Arc::from(breakpoint.clone());
 8866
 8867        let alt_as_text = gpui::Keystroke {
 8868            modifiers: Modifiers::secondary_key(),
 8869            ..Default::default()
 8870        };
 8871        let primary_action_text = if breakpoint.is_disabled() {
 8872            "Enable breakpoint"
 8873        } else if is_phantom && !collides_with_existing {
 8874            "Set breakpoint"
 8875        } else {
 8876            "Unset breakpoint"
 8877        };
 8878        let focus_handle = self.focus_handle.clone();
 8879
 8880        let meta = if is_rejected {
 8881            SharedString::from("No executable code is associated with this line.")
 8882        } else if collides_with_existing && !breakpoint.is_disabled() {
 8883            SharedString::from(format!(
 8884                "{alt_as_text}-click to disable,\nright-click for more options."
 8885            ))
 8886        } else {
 8887            SharedString::from("Right-click for more options.")
 8888        };
 8889        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 8890            .icon_size(IconSize::XSmall)
 8891            .size(ui::ButtonSize::None)
 8892            .when(is_rejected, |this| {
 8893                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 8894            })
 8895            .icon_color(color)
 8896            .style(ButtonStyle::Transparent)
 8897            .on_click(cx.listener({
 8898                move |editor, event: &ClickEvent, window, cx| {
 8899                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 8900                        BreakpointEditAction::InvertState
 8901                    } else {
 8902                        BreakpointEditAction::Toggle
 8903                    };
 8904
 8905                    window.focus(&editor.focus_handle(cx), cx);
 8906                    editor.edit_breakpoint_at_anchor(
 8907                        position,
 8908                        breakpoint.as_ref().clone(),
 8909                        edit_action,
 8910                        cx,
 8911                    );
 8912                }
 8913            }))
 8914            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 8915                editor.set_breakpoint_context_menu(
 8916                    row,
 8917                    Some(position),
 8918                    event.position(),
 8919                    window,
 8920                    cx,
 8921                );
 8922            }))
 8923            .tooltip(move |_window, cx| {
 8924                Tooltip::with_meta_in(
 8925                    primary_action_text,
 8926                    Some(&ToggleBreakpoint),
 8927                    meta.clone(),
 8928                    &focus_handle,
 8929                    cx,
 8930                )
 8931            })
 8932    }
 8933
 8934    fn build_tasks_context(
 8935        project: &Entity<Project>,
 8936        buffer: &Entity<Buffer>,
 8937        buffer_row: u32,
 8938        tasks: &Arc<RunnableTasks>,
 8939        cx: &mut Context<Self>,
 8940    ) -> Task<Option<task::TaskContext>> {
 8941        let position = Point::new(buffer_row, tasks.column);
 8942        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 8943        let location = Location {
 8944            buffer: buffer.clone(),
 8945            range: range_start..range_start,
 8946        };
 8947        // Fill in the environmental variables from the tree-sitter captures
 8948        let mut captured_task_variables = TaskVariables::default();
 8949        for (capture_name, value) in tasks.extra_variables.clone() {
 8950            captured_task_variables.insert(
 8951                task::VariableName::Custom(capture_name.into()),
 8952                value.clone(),
 8953            );
 8954        }
 8955        project.update(cx, |project, cx| {
 8956            project.task_store().update(cx, |task_store, cx| {
 8957                task_store.task_context_for_location(captured_task_variables, location, cx)
 8958            })
 8959        })
 8960    }
 8961
 8962    pub fn spawn_nearest_task(
 8963        &mut self,
 8964        action: &SpawnNearestTask,
 8965        window: &mut Window,
 8966        cx: &mut Context<Self>,
 8967    ) {
 8968        let Some((workspace, _)) = self.workspace.clone() else {
 8969            return;
 8970        };
 8971        let Some(project) = self.project.clone() else {
 8972            return;
 8973        };
 8974
 8975        // Try to find a closest, enclosing node using tree-sitter that has a task
 8976        let Some((buffer, buffer_row, tasks)) = self
 8977            .find_enclosing_node_task(cx)
 8978            // Or find the task that's closest in row-distance.
 8979            .or_else(|| self.find_closest_task(cx))
 8980        else {
 8981            return;
 8982        };
 8983
 8984        let reveal_strategy = action.reveal;
 8985        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 8986        cx.spawn_in(window, async move |_, cx| {
 8987            let context = task_context.await?;
 8988            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 8989
 8990            let resolved = &mut resolved_task.resolved;
 8991            resolved.reveal = reveal_strategy;
 8992
 8993            workspace
 8994                .update_in(cx, |workspace, window, cx| {
 8995                    workspace.schedule_resolved_task(
 8996                        task_source_kind,
 8997                        resolved_task,
 8998                        false,
 8999                        window,
 9000                        cx,
 9001                    );
 9002                })
 9003                .ok()
 9004        })
 9005        .detach();
 9006    }
 9007
 9008    fn find_closest_task(
 9009        &mut self,
 9010        cx: &mut Context<Self>,
 9011    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9012        let cursor_row = self
 9013            .selections
 9014            .newest_adjusted(&self.display_snapshot(cx))
 9015            .head()
 9016            .row;
 9017
 9018        let ((buffer_id, row), tasks) = self
 9019            .tasks
 9020            .iter()
 9021            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 9022
 9023        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 9024        let tasks = Arc::new(tasks.to_owned());
 9025        Some((buffer, *row, tasks))
 9026    }
 9027
 9028    fn find_enclosing_node_task(
 9029        &mut self,
 9030        cx: &mut Context<Self>,
 9031    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9032        let snapshot = self.buffer.read(cx).snapshot(cx);
 9033        let offset = self
 9034            .selections
 9035            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 9036            .head();
 9037        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 9038        let offset = excerpt.map_offset_to_buffer(offset);
 9039        let buffer_id = excerpt.buffer().remote_id();
 9040
 9041        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 9042        let mut cursor = layer.node().walk();
 9043
 9044        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 9045            if cursor.node().end_byte() == offset.0 {
 9046                cursor.goto_next_sibling();
 9047            }
 9048        }
 9049
 9050        // Ascend to the smallest ancestor that contains the range and has a task.
 9051        loop {
 9052            let node = cursor.node();
 9053            let node_range = node.byte_range();
 9054            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 9055
 9056            // Check if this node contains our offset
 9057            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 9058                // If it contains offset, check for task
 9059                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 9060                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 9061                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 9062                }
 9063            }
 9064
 9065            if !cursor.goto_parent() {
 9066                break;
 9067            }
 9068        }
 9069        None
 9070    }
 9071
 9072    fn render_run_indicator(
 9073        &self,
 9074        _style: &EditorStyle,
 9075        is_active: bool,
 9076        row: DisplayRow,
 9077        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 9078        cx: &mut Context<Self>,
 9079    ) -> IconButton {
 9080        let color = Color::Muted;
 9081        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 9082
 9083        IconButton::new(
 9084            ("run_indicator", row.0 as usize),
 9085            ui::IconName::PlayOutlined,
 9086        )
 9087        .shape(ui::IconButtonShape::Square)
 9088        .icon_size(IconSize::XSmall)
 9089        .icon_color(color)
 9090        .toggle_state(is_active)
 9091        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9092            let quick_launch = match e {
 9093                ClickEvent::Keyboard(_) => true,
 9094                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9095            };
 9096
 9097            window.focus(&editor.focus_handle(cx), cx);
 9098            editor.toggle_code_actions(
 9099                &ToggleCodeActions {
 9100                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9101                    quick_launch,
 9102                },
 9103                window,
 9104                cx,
 9105            );
 9106        }))
 9107        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9108            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9109        }))
 9110    }
 9111
 9112    pub fn context_menu_visible(&self) -> bool {
 9113        !self.edit_prediction_preview_is_active()
 9114            && self
 9115                .context_menu
 9116                .borrow()
 9117                .as_ref()
 9118                .is_some_and(|menu| menu.visible())
 9119    }
 9120
 9121    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9122        self.context_menu
 9123            .borrow()
 9124            .as_ref()
 9125            .map(|menu| menu.origin())
 9126    }
 9127
 9128    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9129        self.context_menu_options = Some(options);
 9130    }
 9131
 9132    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9133    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9134
 9135    fn render_edit_prediction_popover(
 9136        &mut self,
 9137        text_bounds: &Bounds<Pixels>,
 9138        content_origin: gpui::Point<Pixels>,
 9139        right_margin: Pixels,
 9140        editor_snapshot: &EditorSnapshot,
 9141        visible_row_range: Range<DisplayRow>,
 9142        scroll_top: ScrollOffset,
 9143        scroll_bottom: ScrollOffset,
 9144        line_layouts: &[LineWithInvisibles],
 9145        line_height: Pixels,
 9146        scroll_position: gpui::Point<ScrollOffset>,
 9147        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9148        newest_selection_head: Option<DisplayPoint>,
 9149        editor_width: Pixels,
 9150        style: &EditorStyle,
 9151        window: &mut Window,
 9152        cx: &mut App,
 9153    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9154        if self.mode().is_minimap() {
 9155            return None;
 9156        }
 9157        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9158
 9159        if self.edit_prediction_visible_in_cursor_popover(true) {
 9160            return None;
 9161        }
 9162
 9163        match &active_edit_prediction.completion {
 9164            EditPrediction::MoveWithin { target, .. } => {
 9165                let target_display_point = target.to_display_point(editor_snapshot);
 9166
 9167                if self.edit_prediction_requires_modifier() {
 9168                    if !self.edit_prediction_preview_is_active() {
 9169                        return None;
 9170                    }
 9171
 9172                    self.render_edit_prediction_modifier_jump_popover(
 9173                        text_bounds,
 9174                        content_origin,
 9175                        visible_row_range,
 9176                        line_layouts,
 9177                        line_height,
 9178                        scroll_pixel_position,
 9179                        newest_selection_head,
 9180                        target_display_point,
 9181                        window,
 9182                        cx,
 9183                    )
 9184                } else {
 9185                    self.render_edit_prediction_eager_jump_popover(
 9186                        text_bounds,
 9187                        content_origin,
 9188                        editor_snapshot,
 9189                        visible_row_range,
 9190                        scroll_top,
 9191                        scroll_bottom,
 9192                        line_height,
 9193                        scroll_pixel_position,
 9194                        target_display_point,
 9195                        editor_width,
 9196                        window,
 9197                        cx,
 9198                    )
 9199                }
 9200            }
 9201            EditPrediction::Edit {
 9202                display_mode: EditDisplayMode::Inline,
 9203                ..
 9204            } => None,
 9205            EditPrediction::Edit {
 9206                display_mode: EditDisplayMode::TabAccept,
 9207                edits,
 9208                ..
 9209            } => {
 9210                let range = &edits.first()?.0;
 9211                let target_display_point = range.end.to_display_point(editor_snapshot);
 9212
 9213                self.render_edit_prediction_end_of_line_popover(
 9214                    "Accept",
 9215                    editor_snapshot,
 9216                    visible_row_range,
 9217                    target_display_point,
 9218                    line_height,
 9219                    scroll_pixel_position,
 9220                    content_origin,
 9221                    editor_width,
 9222                    window,
 9223                    cx,
 9224                )
 9225            }
 9226            EditPrediction::Edit {
 9227                edits,
 9228                edit_preview,
 9229                display_mode: EditDisplayMode::DiffPopover,
 9230                snapshot,
 9231                ..
 9232            } => self.render_edit_prediction_diff_popover(
 9233                text_bounds,
 9234                content_origin,
 9235                right_margin,
 9236                editor_snapshot,
 9237                visible_row_range,
 9238                line_layouts,
 9239                line_height,
 9240                scroll_position,
 9241                scroll_pixel_position,
 9242                newest_selection_head,
 9243                editor_width,
 9244                style,
 9245                edits,
 9246                edit_preview,
 9247                snapshot,
 9248                window,
 9249                cx,
 9250            ),
 9251            EditPrediction::MoveOutside { snapshot, .. } => {
 9252                let mut element = self
 9253                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9254                    .into_any();
 9255
 9256                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9257                let origin_x = text_bounds.size.width - size.width - px(30.);
 9258                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9259                element.prepaint_at(origin, window, cx);
 9260
 9261                Some((element, origin))
 9262            }
 9263        }
 9264    }
 9265
 9266    fn render_edit_prediction_modifier_jump_popover(
 9267        &mut self,
 9268        text_bounds: &Bounds<Pixels>,
 9269        content_origin: gpui::Point<Pixels>,
 9270        visible_row_range: Range<DisplayRow>,
 9271        line_layouts: &[LineWithInvisibles],
 9272        line_height: Pixels,
 9273        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9274        newest_selection_head: Option<DisplayPoint>,
 9275        target_display_point: DisplayPoint,
 9276        window: &mut Window,
 9277        cx: &mut App,
 9278    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9279        let scrolled_content_origin =
 9280            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9281
 9282        const SCROLL_PADDING_Y: Pixels = px(12.);
 9283
 9284        if target_display_point.row() < visible_row_range.start {
 9285            return self.render_edit_prediction_scroll_popover(
 9286                |_| SCROLL_PADDING_Y,
 9287                IconName::ArrowUp,
 9288                visible_row_range,
 9289                line_layouts,
 9290                newest_selection_head,
 9291                scrolled_content_origin,
 9292                window,
 9293                cx,
 9294            );
 9295        } else if target_display_point.row() >= visible_row_range.end {
 9296            return self.render_edit_prediction_scroll_popover(
 9297                |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9298                IconName::ArrowDown,
 9299                visible_row_range,
 9300                line_layouts,
 9301                newest_selection_head,
 9302                scrolled_content_origin,
 9303                window,
 9304                cx,
 9305            );
 9306        }
 9307
 9308        const POLE_WIDTH: Pixels = px(2.);
 9309
 9310        let line_layout =
 9311            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9312        let target_column = target_display_point.column() as usize;
 9313
 9314        let target_x = line_layout.x_for_index(target_column);
 9315        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9316            - scroll_pixel_position.y;
 9317
 9318        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9319
 9320        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9321        border_color.l += 0.001;
 9322
 9323        let mut element = v_flex()
 9324            .items_end()
 9325            .when(flag_on_right, |el| el.items_start())
 9326            .child(if flag_on_right {
 9327                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9328                    .rounded_bl(px(0.))
 9329                    .rounded_tl(px(0.))
 9330                    .border_l_2()
 9331                    .border_color(border_color)
 9332            } else {
 9333                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9334                    .rounded_br(px(0.))
 9335                    .rounded_tr(px(0.))
 9336                    .border_r_2()
 9337                    .border_color(border_color)
 9338            })
 9339            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9340            .into_any();
 9341
 9342        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9343
 9344        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9345            - point(
 9346                if flag_on_right {
 9347                    POLE_WIDTH
 9348                } else {
 9349                    size.width - POLE_WIDTH
 9350                },
 9351                size.height - line_height,
 9352            );
 9353
 9354        origin.x = origin.x.max(content_origin.x);
 9355
 9356        element.prepaint_at(origin, window, cx);
 9357
 9358        Some((element, origin))
 9359    }
 9360
 9361    fn render_edit_prediction_scroll_popover(
 9362        &mut self,
 9363        to_y: impl Fn(Size<Pixels>) -> Pixels,
 9364        scroll_icon: IconName,
 9365        visible_row_range: Range<DisplayRow>,
 9366        line_layouts: &[LineWithInvisibles],
 9367        newest_selection_head: Option<DisplayPoint>,
 9368        scrolled_content_origin: gpui::Point<Pixels>,
 9369        window: &mut Window,
 9370        cx: &mut App,
 9371    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9372        let mut element = self
 9373            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9374            .into_any();
 9375
 9376        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9377
 9378        let cursor = newest_selection_head?;
 9379        let cursor_row_layout =
 9380            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9381        let cursor_column = cursor.column() as usize;
 9382
 9383        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9384
 9385        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9386
 9387        element.prepaint_at(origin, window, cx);
 9388        Some((element, origin))
 9389    }
 9390
 9391    fn render_edit_prediction_eager_jump_popover(
 9392        &mut self,
 9393        text_bounds: &Bounds<Pixels>,
 9394        content_origin: gpui::Point<Pixels>,
 9395        editor_snapshot: &EditorSnapshot,
 9396        visible_row_range: Range<DisplayRow>,
 9397        scroll_top: ScrollOffset,
 9398        scroll_bottom: ScrollOffset,
 9399        line_height: Pixels,
 9400        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9401        target_display_point: DisplayPoint,
 9402        editor_width: Pixels,
 9403        window: &mut Window,
 9404        cx: &mut App,
 9405    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9406        if target_display_point.row().as_f64() < scroll_top {
 9407            let mut element = self
 9408                .render_edit_prediction_line_popover(
 9409                    "Jump to Edit",
 9410                    Some(IconName::ArrowUp),
 9411                    window,
 9412                    cx,
 9413                )
 9414                .into_any();
 9415
 9416            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9417            let offset = point(
 9418                (text_bounds.size.width - size.width) / 2.,
 9419                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9420            );
 9421
 9422            let origin = text_bounds.origin + offset;
 9423            element.prepaint_at(origin, window, cx);
 9424            Some((element, origin))
 9425        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9426            let mut element = self
 9427                .render_edit_prediction_line_popover(
 9428                    "Jump to Edit",
 9429                    Some(IconName::ArrowDown),
 9430                    window,
 9431                    cx,
 9432                )
 9433                .into_any();
 9434
 9435            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9436            let offset = point(
 9437                (text_bounds.size.width - size.width) / 2.,
 9438                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9439            );
 9440
 9441            let origin = text_bounds.origin + offset;
 9442            element.prepaint_at(origin, window, cx);
 9443            Some((element, origin))
 9444        } else {
 9445            self.render_edit_prediction_end_of_line_popover(
 9446                "Jump to Edit",
 9447                editor_snapshot,
 9448                visible_row_range,
 9449                target_display_point,
 9450                line_height,
 9451                scroll_pixel_position,
 9452                content_origin,
 9453                editor_width,
 9454                window,
 9455                cx,
 9456            )
 9457        }
 9458    }
 9459
 9460    fn render_edit_prediction_end_of_line_popover(
 9461        self: &mut Editor,
 9462        label: &'static str,
 9463        editor_snapshot: &EditorSnapshot,
 9464        visible_row_range: Range<DisplayRow>,
 9465        target_display_point: DisplayPoint,
 9466        line_height: Pixels,
 9467        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9468        content_origin: gpui::Point<Pixels>,
 9469        editor_width: Pixels,
 9470        window: &mut Window,
 9471        cx: &mut App,
 9472    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9473        let target_line_end = DisplayPoint::new(
 9474            target_display_point.row(),
 9475            editor_snapshot.line_len(target_display_point.row()),
 9476        );
 9477
 9478        let mut element = self
 9479            .render_edit_prediction_line_popover(label, None, window, cx)
 9480            .into_any();
 9481
 9482        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9483
 9484        let line_origin =
 9485            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9486
 9487        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9488        let mut origin = start_point
 9489            + line_origin
 9490            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9491        origin.x = origin.x.max(content_origin.x);
 9492
 9493        let max_x = content_origin.x + editor_width - size.width;
 9494
 9495        if origin.x > max_x {
 9496            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9497
 9498            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9499                origin.y += offset;
 9500                IconName::ArrowUp
 9501            } else {
 9502                origin.y -= offset;
 9503                IconName::ArrowDown
 9504            };
 9505
 9506            element = self
 9507                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9508                .into_any();
 9509
 9510            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9511
 9512            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9513        }
 9514
 9515        element.prepaint_at(origin, window, cx);
 9516        Some((element, origin))
 9517    }
 9518
 9519    fn render_edit_prediction_diff_popover(
 9520        self: &Editor,
 9521        text_bounds: &Bounds<Pixels>,
 9522        content_origin: gpui::Point<Pixels>,
 9523        right_margin: Pixels,
 9524        editor_snapshot: &EditorSnapshot,
 9525        visible_row_range: Range<DisplayRow>,
 9526        line_layouts: &[LineWithInvisibles],
 9527        line_height: Pixels,
 9528        scroll_position: gpui::Point<ScrollOffset>,
 9529        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9530        newest_selection_head: Option<DisplayPoint>,
 9531        editor_width: Pixels,
 9532        style: &EditorStyle,
 9533        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9534        edit_preview: &Option<language::EditPreview>,
 9535        snapshot: &language::BufferSnapshot,
 9536        window: &mut Window,
 9537        cx: &mut App,
 9538    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9539        let edit_start = edits
 9540            .first()
 9541            .unwrap()
 9542            .0
 9543            .start
 9544            .to_display_point(editor_snapshot);
 9545        let edit_end = edits
 9546            .last()
 9547            .unwrap()
 9548            .0
 9549            .end
 9550            .to_display_point(editor_snapshot);
 9551
 9552        let is_visible = visible_row_range.contains(&edit_start.row())
 9553            || visible_row_range.contains(&edit_end.row());
 9554        if !is_visible {
 9555            return None;
 9556        }
 9557
 9558        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9559            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9560        } else {
 9561            // Fallback for providers without edit_preview
 9562            crate::edit_prediction_fallback_text(edits, cx)
 9563        };
 9564
 9565        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9566        let line_count = highlighted_edits.text.lines().count();
 9567
 9568        const BORDER_WIDTH: Pixels = px(1.);
 9569
 9570        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9571        let has_keybind = keybind.is_some();
 9572
 9573        let mut element = h_flex()
 9574            .items_start()
 9575            .child(
 9576                h_flex()
 9577                    .bg(cx.theme().colors().editor_background)
 9578                    .border(BORDER_WIDTH)
 9579                    .shadow_xs()
 9580                    .border_color(cx.theme().colors().border)
 9581                    .rounded_l_lg()
 9582                    .when(line_count > 1, |el| el.rounded_br_lg())
 9583                    .pr_1()
 9584                    .child(styled_text),
 9585            )
 9586            .child(
 9587                h_flex()
 9588                    .h(line_height + BORDER_WIDTH * 2.)
 9589                    .px_1p5()
 9590                    .gap_1()
 9591                    // Workaround: For some reason, there's a gap if we don't do this
 9592                    .ml(-BORDER_WIDTH)
 9593                    .shadow(vec![gpui::BoxShadow {
 9594                        color: gpui::black().opacity(0.05),
 9595                        offset: point(px(1.), px(1.)),
 9596                        blur_radius: px(2.),
 9597                        spread_radius: px(0.),
 9598                    }])
 9599                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9600                    .border(BORDER_WIDTH)
 9601                    .border_color(cx.theme().colors().border)
 9602                    .rounded_r_lg()
 9603                    .id("edit_prediction_diff_popover_keybind")
 9604                    .when(!has_keybind, |el| {
 9605                        let status_colors = cx.theme().status();
 9606
 9607                        el.bg(status_colors.error_background)
 9608                            .border_color(status_colors.error.opacity(0.6))
 9609                            .child(Icon::new(IconName::Info).color(Color::Error))
 9610                            .cursor_default()
 9611                            .hoverable_tooltip(move |_window, cx| {
 9612                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9613                            })
 9614                    })
 9615                    .children(keybind),
 9616            )
 9617            .into_any();
 9618
 9619        let longest_row =
 9620            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9621        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9622            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9623        } else {
 9624            layout_line(
 9625                longest_row,
 9626                editor_snapshot,
 9627                style,
 9628                editor_width,
 9629                |_| false,
 9630                window,
 9631                cx,
 9632            )
 9633            .width
 9634        };
 9635
 9636        let viewport_bounds =
 9637            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9638                right: -right_margin,
 9639                ..Default::default()
 9640            });
 9641
 9642        let x_after_longest = Pixels::from(
 9643            ScrollPixelOffset::from(
 9644                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9645            ) - scroll_pixel_position.x,
 9646        );
 9647
 9648        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9649
 9650        // Fully visible if it can be displayed within the window (allow overlapping other
 9651        // panes). However, this is only allowed if the popover starts within text_bounds.
 9652        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9653            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9654
 9655        let mut origin = if can_position_to_the_right {
 9656            point(
 9657                x_after_longest,
 9658                text_bounds.origin.y
 9659                    + Pixels::from(
 9660                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9661                            - scroll_pixel_position.y,
 9662                    ),
 9663            )
 9664        } else {
 9665            let cursor_row = newest_selection_head.map(|head| head.row());
 9666            let above_edit = edit_start
 9667                .row()
 9668                .0
 9669                .checked_sub(line_count as u32)
 9670                .map(DisplayRow);
 9671            let below_edit = Some(edit_end.row() + 1);
 9672            let above_cursor =
 9673                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9674            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9675
 9676            // Place the edit popover adjacent to the edit if there is a location
 9677            // available that is onscreen and does not obscure the cursor. Otherwise,
 9678            // place it adjacent to the cursor.
 9679            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9680                .into_iter()
 9681                .flatten()
 9682                .find(|&start_row| {
 9683                    let end_row = start_row + line_count as u32;
 9684                    visible_row_range.contains(&start_row)
 9685                        && visible_row_range.contains(&end_row)
 9686                        && cursor_row
 9687                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9688                })?;
 9689
 9690            content_origin
 9691                + point(
 9692                    Pixels::from(-scroll_pixel_position.x),
 9693                    Pixels::from(
 9694                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9695                    ),
 9696                )
 9697        };
 9698
 9699        origin.x -= BORDER_WIDTH;
 9700
 9701        window.defer_draw(element, origin, 1);
 9702
 9703        // Do not return an element, since it will already be drawn due to defer_draw.
 9704        None
 9705    }
 9706
 9707    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9708        px(30.)
 9709    }
 9710
 9711    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9712        if self.read_only(cx) {
 9713            cx.theme().players().read_only()
 9714        } else {
 9715            self.style.as_ref().unwrap().local_player
 9716        }
 9717    }
 9718
 9719    fn render_edit_prediction_accept_keybind(
 9720        &self,
 9721        window: &mut Window,
 9722        cx: &mut App,
 9723    ) -> Option<AnyElement> {
 9724        let accept_binding =
 9725            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9726        let accept_keystroke = accept_binding.keystroke()?;
 9727
 9728        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9729
 9730        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9731            Color::Accent
 9732        } else {
 9733            Color::Muted
 9734        };
 9735
 9736        h_flex()
 9737            .px_0p5()
 9738            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9739            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9740            .text_size(TextSize::XSmall.rems(cx))
 9741            .child(h_flex().children(ui::render_modifiers(
 9742                accept_keystroke.modifiers(),
 9743                PlatformStyle::platform(),
 9744                Some(modifiers_color),
 9745                Some(IconSize::XSmall.rems().into()),
 9746                true,
 9747            )))
 9748            .when(is_platform_style_mac, |parent| {
 9749                parent.child(accept_keystroke.key().to_string())
 9750            })
 9751            .when(!is_platform_style_mac, |parent| {
 9752                parent.child(
 9753                    Key::new(
 9754                        util::capitalize(accept_keystroke.key()),
 9755                        Some(Color::Default),
 9756                    )
 9757                    .size(Some(IconSize::XSmall.rems().into())),
 9758                )
 9759            })
 9760            .into_any()
 9761            .into()
 9762    }
 9763
 9764    fn render_edit_prediction_line_popover(
 9765        &self,
 9766        label: impl Into<SharedString>,
 9767        icon: Option<IconName>,
 9768        window: &mut Window,
 9769        cx: &mut App,
 9770    ) -> Stateful<Div> {
 9771        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9772
 9773        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9774        let has_keybind = keybind.is_some();
 9775        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9776
 9777        h_flex()
 9778            .id("ep-line-popover")
 9779            .py_0p5()
 9780            .pl_1()
 9781            .pr(padding_right)
 9782            .gap_1()
 9783            .rounded_md()
 9784            .border_1()
 9785            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9786            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9787            .shadow_xs()
 9788            .when(!has_keybind, |el| {
 9789                let status_colors = cx.theme().status();
 9790
 9791                el.bg(status_colors.error_background)
 9792                    .border_color(status_colors.error.opacity(0.6))
 9793                    .pl_2()
 9794                    .child(Icon::new(icons.error).color(Color::Error))
 9795                    .cursor_default()
 9796                    .hoverable_tooltip(move |_window, cx| {
 9797                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9798                    })
 9799            })
 9800            .children(keybind)
 9801            .child(
 9802                Label::new(label)
 9803                    .size(LabelSize::Small)
 9804                    .when(!has_keybind, |el| {
 9805                        el.color(cx.theme().status().error.into()).strikethrough()
 9806                    }),
 9807            )
 9808            .when(!has_keybind, |el| {
 9809                el.child(
 9810                    h_flex().ml_1().child(
 9811                        Icon::new(IconName::Info)
 9812                            .size(IconSize::Small)
 9813                            .color(cx.theme().status().error.into()),
 9814                    ),
 9815                )
 9816            })
 9817            .when_some(icon, |element, icon| {
 9818                element.child(
 9819                    div()
 9820                        .mt(px(1.5))
 9821                        .child(Icon::new(icon).size(IconSize::Small)),
 9822                )
 9823            })
 9824    }
 9825
 9826    fn render_edit_prediction_jump_outside_popover(
 9827        &self,
 9828        snapshot: &BufferSnapshot,
 9829        window: &mut Window,
 9830        cx: &mut App,
 9831    ) -> Stateful<Div> {
 9832        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9833        let has_keybind = keybind.is_some();
 9834        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9835
 9836        let file_name = snapshot
 9837            .file()
 9838            .map(|file| SharedString::new(file.file_name(cx)))
 9839            .unwrap_or(SharedString::new_static("untitled"));
 9840
 9841        h_flex()
 9842            .id("ep-jump-outside-popover")
 9843            .py_1()
 9844            .px_2()
 9845            .gap_1()
 9846            .rounded_md()
 9847            .border_1()
 9848            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9849            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9850            .shadow_xs()
 9851            .when(!has_keybind, |el| {
 9852                let status_colors = cx.theme().status();
 9853
 9854                el.bg(status_colors.error_background)
 9855                    .border_color(status_colors.error.opacity(0.6))
 9856                    .pl_2()
 9857                    .child(Icon::new(icons.error).color(Color::Error))
 9858                    .cursor_default()
 9859                    .hoverable_tooltip(move |_window, cx| {
 9860                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9861                    })
 9862            })
 9863            .children(keybind)
 9864            .child(
 9865                Label::new(file_name)
 9866                    .size(LabelSize::Small)
 9867                    .buffer_font(cx)
 9868                    .when(!has_keybind, |el| {
 9869                        el.color(cx.theme().status().error.into()).strikethrough()
 9870                    }),
 9871            )
 9872            .when(!has_keybind, |el| {
 9873                el.child(
 9874                    h_flex().ml_1().child(
 9875                        Icon::new(IconName::Info)
 9876                            .size(IconSize::Small)
 9877                            .color(cx.theme().status().error.into()),
 9878                    ),
 9879                )
 9880            })
 9881            .child(
 9882                div()
 9883                    .mt(px(1.5))
 9884                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
 9885            )
 9886    }
 9887
 9888    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
 9889        let accent_color = cx.theme().colors().text_accent;
 9890        let editor_bg_color = cx.theme().colors().editor_background;
 9891        editor_bg_color.blend(accent_color.opacity(0.1))
 9892    }
 9893
 9894    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
 9895        let accent_color = cx.theme().colors().text_accent;
 9896        let editor_bg_color = cx.theme().colors().editor_background;
 9897        editor_bg_color.blend(accent_color.opacity(0.6))
 9898    }
 9899    fn get_prediction_provider_icons(
 9900        provider: &Option<RegisteredEditPredictionDelegate>,
 9901        cx: &App,
 9902    ) -> edit_prediction_types::EditPredictionIconSet {
 9903        match provider {
 9904            Some(provider) => provider.provider.icons(cx),
 9905            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
 9906        }
 9907    }
 9908
 9909    fn render_edit_prediction_cursor_popover(
 9910        &self,
 9911        min_width: Pixels,
 9912        max_width: Pixels,
 9913        cursor_point: Point,
 9914        style: &EditorStyle,
 9915        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
 9916        _window: &Window,
 9917        cx: &mut Context<Editor>,
 9918    ) -> Option<AnyElement> {
 9919        let provider = self.edit_prediction_provider.as_ref()?;
 9920        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9921
 9922        let is_refreshing = provider.provider.is_refreshing(cx);
 9923
 9924        fn pending_completion_container(icon: IconName) -> Div {
 9925            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
 9926        }
 9927
 9928        let completion = match &self.active_edit_prediction {
 9929            Some(prediction) => {
 9930                if !self.has_visible_completions_menu() {
 9931                    const RADIUS: Pixels = px(6.);
 9932                    const BORDER_WIDTH: Pixels = px(1.);
 9933
 9934                    return Some(
 9935                        h_flex()
 9936                            .elevation_2(cx)
 9937                            .border(BORDER_WIDTH)
 9938                            .border_color(cx.theme().colors().border)
 9939                            .when(accept_keystroke.is_none(), |el| {
 9940                                el.border_color(cx.theme().status().error)
 9941                            })
 9942                            .rounded(RADIUS)
 9943                            .rounded_tl(px(0.))
 9944                            .overflow_hidden()
 9945                            .child(div().px_1p5().child(match &prediction.completion {
 9946                                EditPrediction::MoveWithin { target, snapshot } => {
 9947                                    use text::ToPoint as _;
 9948                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
 9949                                    {
 9950                                        Icon::new(icons.down)
 9951                                    } else {
 9952                                        Icon::new(icons.up)
 9953                                    }
 9954                                }
 9955                                EditPrediction::MoveOutside { .. } => {
 9956                                    // TODO [zeta2] custom icon for external jump?
 9957                                    Icon::new(icons.base)
 9958                                }
 9959                                EditPrediction::Edit { .. } => Icon::new(icons.base),
 9960                            }))
 9961                            .child(
 9962                                h_flex()
 9963                                    .gap_1()
 9964                                    .py_1()
 9965                                    .px_2()
 9966                                    .rounded_r(RADIUS - BORDER_WIDTH)
 9967                                    .border_l_1()
 9968                                    .border_color(cx.theme().colors().border)
 9969                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9970                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
 9971                                        el.child(
 9972                                            Label::new("Hold")
 9973                                                .size(LabelSize::Small)
 9974                                                .when(accept_keystroke.is_none(), |el| {
 9975                                                    el.strikethrough()
 9976                                                })
 9977                                                .line_height_style(LineHeightStyle::UiLabel),
 9978                                        )
 9979                                    })
 9980                                    .id("edit_prediction_cursor_popover_keybind")
 9981                                    .when(accept_keystroke.is_none(), |el| {
 9982                                        let status_colors = cx.theme().status();
 9983
 9984                                        el.bg(status_colors.error_background)
 9985                                            .border_color(status_colors.error.opacity(0.6))
 9986                                            .child(Icon::new(IconName::Info).color(Color::Error))
 9987                                            .cursor_default()
 9988                                            .hoverable_tooltip(move |_window, cx| {
 9989                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
 9990                                                    .into()
 9991                                            })
 9992                                    })
 9993                                    .when_some(
 9994                                        accept_keystroke.as_ref(),
 9995                                        |el, accept_keystroke| {
 9996                                            el.child(h_flex().children(ui::render_modifiers(
 9997                                                accept_keystroke.modifiers(),
 9998                                                PlatformStyle::platform(),
 9999                                                Some(Color::Default),
10000                                                Some(IconSize::XSmall.rems().into()),
10001                                                false,
10002                                            )))
10003                                        },
10004                                    ),
10005                            )
10006                            .into_any(),
10007                    );
10008                }
10009
10010                self.render_edit_prediction_cursor_popover_preview(
10011                    prediction,
10012                    cursor_point,
10013                    style,
10014                    cx,
10015                )?
10016            }
10017
10018            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10019                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10020                    stale_completion,
10021                    cursor_point,
10022                    style,
10023                    cx,
10024                )?,
10025
10026                None => pending_completion_container(icons.base)
10027                    .child(Label::new("...").size(LabelSize::Small)),
10028            },
10029
10030            None => pending_completion_container(icons.base)
10031                .child(Label::new("...").size(LabelSize::Small)),
10032        };
10033
10034        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10035            completion
10036                .with_animation(
10037                    "loading-completion",
10038                    Animation::new(Duration::from_secs(2))
10039                        .repeat()
10040                        .with_easing(pulsating_between(0.4, 0.8)),
10041                    |label, delta| label.opacity(delta),
10042                )
10043                .into_any_element()
10044        } else {
10045            completion.into_any_element()
10046        };
10047
10048        let has_completion = self.active_edit_prediction.is_some();
10049
10050        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10051        Some(
10052            h_flex()
10053                .min_w(min_width)
10054                .max_w(max_width)
10055                .flex_1()
10056                .elevation_2(cx)
10057                .border_color(cx.theme().colors().border)
10058                .child(
10059                    div()
10060                        .flex_1()
10061                        .py_1()
10062                        .px_2()
10063                        .overflow_hidden()
10064                        .child(completion),
10065                )
10066                .when_some(accept_keystroke, |el, accept_keystroke| {
10067                    if !accept_keystroke.modifiers().modified() {
10068                        return el;
10069                    }
10070
10071                    el.child(
10072                        h_flex()
10073                            .h_full()
10074                            .border_l_1()
10075                            .rounded_r_lg()
10076                            .border_color(cx.theme().colors().border)
10077                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10078                            .gap_1()
10079                            .py_1()
10080                            .px_2()
10081                            .child(
10082                                h_flex()
10083                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10084                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10085                                    .child(h_flex().children(ui::render_modifiers(
10086                                        accept_keystroke.modifiers(),
10087                                        PlatformStyle::platform(),
10088                                        Some(if !has_completion {
10089                                            Color::Muted
10090                                        } else {
10091                                            Color::Default
10092                                        }),
10093                                        None,
10094                                        false,
10095                                    ))),
10096                            )
10097                            .child(Label::new("Preview").into_any_element())
10098                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10099                    )
10100                })
10101                .into_any(),
10102        )
10103    }
10104
10105    fn render_edit_prediction_cursor_popover_preview(
10106        &self,
10107        completion: &EditPredictionState,
10108        cursor_point: Point,
10109        style: &EditorStyle,
10110        cx: &mut Context<Editor>,
10111    ) -> Option<Div> {
10112        use text::ToPoint as _;
10113
10114        fn render_relative_row_jump(
10115            prefix: impl Into<String>,
10116            current_row: u32,
10117            target_row: u32,
10118        ) -> Div {
10119            let (row_diff, arrow) = if target_row < current_row {
10120                (current_row - target_row, IconName::ArrowUp)
10121            } else {
10122                (target_row - current_row, IconName::ArrowDown)
10123            };
10124
10125            h_flex()
10126                .child(
10127                    Label::new(format!("{}{}", prefix.into(), row_diff))
10128                        .color(Color::Muted)
10129                        .size(LabelSize::Small),
10130                )
10131                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10132        }
10133
10134        let supports_jump = self
10135            .edit_prediction_provider
10136            .as_ref()
10137            .map(|provider| provider.provider.supports_jump_to_edit())
10138            .unwrap_or(true);
10139
10140        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10141
10142        match &completion.completion {
10143            EditPrediction::MoveWithin {
10144                target, snapshot, ..
10145            } => {
10146                if !supports_jump {
10147                    return None;
10148                }
10149
10150                Some(
10151                    h_flex()
10152                        .px_2()
10153                        .gap_2()
10154                        .flex_1()
10155                        .child(
10156                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10157                                Icon::new(icons.down)
10158                            } else {
10159                                Icon::new(icons.up)
10160                            },
10161                        )
10162                        .child(Label::new("Jump to Edit")),
10163                )
10164            }
10165            EditPrediction::MoveOutside { snapshot, .. } => {
10166                let file_name = snapshot
10167                    .file()
10168                    .map(|file| file.file_name(cx))
10169                    .unwrap_or("untitled");
10170                Some(
10171                    h_flex()
10172                        .px_2()
10173                        .gap_2()
10174                        .flex_1()
10175                        .child(Icon::new(icons.base))
10176                        .child(Label::new(format!("Jump to {file_name}"))),
10177                )
10178            }
10179            EditPrediction::Edit {
10180                edits,
10181                edit_preview,
10182                snapshot,
10183                ..
10184            } => {
10185                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10186
10187                let (highlighted_edits, has_more_lines) =
10188                    if let Some(edit_preview) = edit_preview.as_ref() {
10189                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10190                            .first_line_preview()
10191                    } else {
10192                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10193                    };
10194
10195                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10196                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10197
10198                let preview = h_flex()
10199                    .gap_1()
10200                    .min_w_16()
10201                    .child(styled_text)
10202                    .when(has_more_lines, |parent| parent.child(""));
10203
10204                let left = if supports_jump && first_edit_row != cursor_point.row {
10205                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10206                        .into_any_element()
10207                } else {
10208                    Icon::new(icons.base).into_any_element()
10209                };
10210
10211                Some(
10212                    h_flex()
10213                        .h_full()
10214                        .flex_1()
10215                        .gap_2()
10216                        .pr_1()
10217                        .overflow_x_hidden()
10218                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10219                        .child(left)
10220                        .child(preview),
10221                )
10222            }
10223        }
10224    }
10225
10226    pub fn render_context_menu(
10227        &mut self,
10228        max_height_in_lines: u32,
10229        window: &mut Window,
10230        cx: &mut Context<Editor>,
10231    ) -> Option<AnyElement> {
10232        let menu = self.context_menu.borrow();
10233        let menu = menu.as_ref()?;
10234        if !menu.visible() {
10235            return None;
10236        };
10237        self.style
10238            .as_ref()
10239            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10240    }
10241
10242    fn render_context_menu_aside(
10243        &mut self,
10244        max_size: Size<Pixels>,
10245        window: &mut Window,
10246        cx: &mut Context<Editor>,
10247    ) -> Option<AnyElement> {
10248        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10249            if menu.visible() {
10250                menu.render_aside(max_size, window, cx)
10251            } else {
10252                None
10253            }
10254        })
10255    }
10256
10257    fn hide_context_menu(
10258        &mut self,
10259        window: &mut Window,
10260        cx: &mut Context<Self>,
10261    ) -> Option<CodeContextMenu> {
10262        cx.notify();
10263        self.completion_tasks.clear();
10264        let context_menu = self.context_menu.borrow_mut().take();
10265        self.stale_edit_prediction_in_menu.take();
10266        self.update_visible_edit_prediction(window, cx);
10267        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10268            && let Some(completion_provider) = &self.completion_provider
10269        {
10270            completion_provider.selection_changed(None, window, cx);
10271        }
10272        context_menu
10273    }
10274
10275    fn show_snippet_choices(
10276        &mut self,
10277        choices: &Vec<String>,
10278        selection: Range<Anchor>,
10279        cx: &mut Context<Self>,
10280    ) {
10281        let Some((_, buffer, _)) = self
10282            .buffer()
10283            .read(cx)
10284            .excerpt_containing(selection.start, cx)
10285        else {
10286            return;
10287        };
10288        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10289        else {
10290            return;
10291        };
10292        if buffer != end_buffer {
10293            log::error!("expected anchor range to have matching buffer IDs");
10294            return;
10295        }
10296
10297        let id = post_inc(&mut self.next_completion_id);
10298        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10299        let mut context_menu = self.context_menu.borrow_mut();
10300        let old_menu = context_menu.take();
10301        *context_menu = Some(CodeContextMenu::Completions(
10302            CompletionsMenu::new_snippet_choices(
10303                id,
10304                true,
10305                choices,
10306                selection,
10307                buffer,
10308                old_menu.map(|menu| menu.primary_scroll_handle()),
10309                snippet_sort_order,
10310            ),
10311        ));
10312    }
10313
10314    pub fn insert_snippet(
10315        &mut self,
10316        insertion_ranges: &[Range<MultiBufferOffset>],
10317        snippet: Snippet,
10318        window: &mut Window,
10319        cx: &mut Context<Self>,
10320    ) -> Result<()> {
10321        struct Tabstop<T> {
10322            is_end_tabstop: bool,
10323            ranges: Vec<Range<T>>,
10324            choices: Option<Vec<String>>,
10325        }
10326
10327        let tabstops = self.buffer.update(cx, |buffer, cx| {
10328            let snippet_text: Arc<str> = snippet.text.clone().into();
10329            let edits = insertion_ranges
10330                .iter()
10331                .cloned()
10332                .map(|range| (range, snippet_text.clone()));
10333            let autoindent_mode = AutoindentMode::Block {
10334                original_indent_columns: Vec::new(),
10335            };
10336            buffer.edit(edits, Some(autoindent_mode), cx);
10337
10338            let snapshot = &*buffer.read(cx);
10339            let snippet = &snippet;
10340            snippet
10341                .tabstops
10342                .iter()
10343                .map(|tabstop| {
10344                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10345                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10346                    });
10347                    let mut tabstop_ranges = tabstop
10348                        .ranges
10349                        .iter()
10350                        .flat_map(|tabstop_range| {
10351                            let mut delta = 0_isize;
10352                            insertion_ranges.iter().map(move |insertion_range| {
10353                                let insertion_start = insertion_range.start + delta;
10354                                delta += snippet.text.len() as isize
10355                                    - (insertion_range.end - insertion_range.start) as isize;
10356
10357                                let start =
10358                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10359                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10360                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10361                            })
10362                        })
10363                        .collect::<Vec<_>>();
10364                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10365
10366                    Tabstop {
10367                        is_end_tabstop,
10368                        ranges: tabstop_ranges,
10369                        choices: tabstop.choices.clone(),
10370                    }
10371                })
10372                .collect::<Vec<_>>()
10373        });
10374        if let Some(tabstop) = tabstops.first() {
10375            self.change_selections(Default::default(), window, cx, |s| {
10376                // Reverse order so that the first range is the newest created selection.
10377                // Completions will use it and autoscroll will prioritize it.
10378                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10379            });
10380
10381            if let Some(choices) = &tabstop.choices
10382                && let Some(selection) = tabstop.ranges.first()
10383            {
10384                self.show_snippet_choices(choices, selection.clone(), cx)
10385            }
10386
10387            // If we're already at the last tabstop and it's at the end of the snippet,
10388            // we're done, we don't need to keep the state around.
10389            if !tabstop.is_end_tabstop {
10390                let choices = tabstops
10391                    .iter()
10392                    .map(|tabstop| tabstop.choices.clone())
10393                    .collect();
10394
10395                let ranges = tabstops
10396                    .into_iter()
10397                    .map(|tabstop| tabstop.ranges)
10398                    .collect::<Vec<_>>();
10399
10400                self.snippet_stack.push(SnippetState {
10401                    active_index: 0,
10402                    ranges,
10403                    choices,
10404                });
10405            }
10406
10407            // Check whether the just-entered snippet ends with an auto-closable bracket.
10408            if self.autoclose_regions.is_empty() {
10409                let snapshot = self.buffer.read(cx).snapshot(cx);
10410                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10411                    let selection_head = selection.head();
10412                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10413                        continue;
10414                    };
10415
10416                    let mut bracket_pair = None;
10417                    let max_lookup_length = scope
10418                        .brackets()
10419                        .map(|(pair, _)| {
10420                            pair.start
10421                                .as_str()
10422                                .chars()
10423                                .count()
10424                                .max(pair.end.as_str().chars().count())
10425                        })
10426                        .max();
10427                    if let Some(max_lookup_length) = max_lookup_length {
10428                        let next_text = snapshot
10429                            .chars_at(selection_head)
10430                            .take(max_lookup_length)
10431                            .collect::<String>();
10432                        let prev_text = snapshot
10433                            .reversed_chars_at(selection_head)
10434                            .take(max_lookup_length)
10435                            .collect::<String>();
10436
10437                        for (pair, enabled) in scope.brackets() {
10438                            if enabled
10439                                && pair.close
10440                                && prev_text.starts_with(pair.start.as_str())
10441                                && next_text.starts_with(pair.end.as_str())
10442                            {
10443                                bracket_pair = Some(pair.clone());
10444                                break;
10445                            }
10446                        }
10447                    }
10448
10449                    if let Some(pair) = bracket_pair {
10450                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10451                        let autoclose_enabled =
10452                            self.use_autoclose && snapshot_settings.use_autoclose;
10453                        if autoclose_enabled {
10454                            let start = snapshot.anchor_after(selection_head);
10455                            let end = snapshot.anchor_after(selection_head);
10456                            self.autoclose_regions.push(AutocloseRegion {
10457                                selection_id: selection.id,
10458                                range: start..end,
10459                                pair,
10460                            });
10461                        }
10462                    }
10463                }
10464            }
10465        }
10466        Ok(())
10467    }
10468
10469    pub fn move_to_next_snippet_tabstop(
10470        &mut self,
10471        window: &mut Window,
10472        cx: &mut Context<Self>,
10473    ) -> bool {
10474        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10475    }
10476
10477    pub fn move_to_prev_snippet_tabstop(
10478        &mut self,
10479        window: &mut Window,
10480        cx: &mut Context<Self>,
10481    ) -> bool {
10482        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10483    }
10484
10485    pub fn move_to_snippet_tabstop(
10486        &mut self,
10487        bias: Bias,
10488        window: &mut Window,
10489        cx: &mut Context<Self>,
10490    ) -> bool {
10491        if let Some(mut snippet) = self.snippet_stack.pop() {
10492            match bias {
10493                Bias::Left => {
10494                    if snippet.active_index > 0 {
10495                        snippet.active_index -= 1;
10496                    } else {
10497                        self.snippet_stack.push(snippet);
10498                        return false;
10499                    }
10500                }
10501                Bias::Right => {
10502                    if snippet.active_index + 1 < snippet.ranges.len() {
10503                        snippet.active_index += 1;
10504                    } else {
10505                        self.snippet_stack.push(snippet);
10506                        return false;
10507                    }
10508                }
10509            }
10510            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10511                self.change_selections(Default::default(), window, cx, |s| {
10512                    // Reverse order so that the first range is the newest created selection.
10513                    // Completions will use it and autoscroll will prioritize it.
10514                    s.select_ranges(current_ranges.iter().rev().cloned())
10515                });
10516
10517                if let Some(choices) = &snippet.choices[snippet.active_index]
10518                    && let Some(selection) = current_ranges.first()
10519                {
10520                    self.show_snippet_choices(choices, selection.clone(), cx);
10521                }
10522
10523                // If snippet state is not at the last tabstop, push it back on the stack
10524                if snippet.active_index + 1 < snippet.ranges.len() {
10525                    self.snippet_stack.push(snippet);
10526                }
10527                return true;
10528            }
10529        }
10530
10531        false
10532    }
10533
10534    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10535        self.transact(window, cx, |this, window, cx| {
10536            this.select_all(&SelectAll, window, cx);
10537            this.insert("", window, cx);
10538        });
10539    }
10540
10541    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10542        if self.read_only(cx) {
10543            return;
10544        }
10545        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10546        self.transact(window, cx, |this, window, cx| {
10547            this.select_autoclose_pair(window, cx);
10548
10549            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10550
10551            let mut linked_ranges = HashMap::<_, Vec<_>>::default();
10552            if !this.linked_edit_ranges.is_empty() {
10553                let selections = this.selections.all::<MultiBufferPoint>(&display_map);
10554                let snapshot = this.buffer.read(cx).snapshot(cx);
10555
10556                for selection in selections.iter() {
10557                    let selection_start = snapshot.anchor_before(selection.start).text_anchor;
10558                    let selection_end = snapshot.anchor_after(selection.end).text_anchor;
10559                    if selection_start.buffer_id != selection_end.buffer_id {
10560                        continue;
10561                    }
10562                    if let Some(ranges) =
10563                        this.linked_editing_ranges_for(selection_start..selection_end, cx)
10564                    {
10565                        for (buffer, entries) in ranges {
10566                            linked_ranges.entry(buffer).or_default().extend(entries);
10567                        }
10568                    }
10569                }
10570            }
10571
10572            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10573            for selection in &mut selections {
10574                if selection.is_empty() {
10575                    let old_head = selection.head();
10576                    let mut new_head =
10577                        movement::left(&display_map, old_head.to_display_point(&display_map))
10578                            .to_point(&display_map);
10579                    if let Some((buffer, line_buffer_range)) = display_map
10580                        .buffer_snapshot()
10581                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10582                    {
10583                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10584                        let indent_len = match indent_size.kind {
10585                            IndentKind::Space => {
10586                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10587                            }
10588                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10589                        };
10590                        if old_head.column <= indent_size.len && old_head.column > 0 {
10591                            let indent_len = indent_len.get();
10592                            new_head = cmp::min(
10593                                new_head,
10594                                MultiBufferPoint::new(
10595                                    old_head.row,
10596                                    ((old_head.column - 1) / indent_len) * indent_len,
10597                                ),
10598                            );
10599                        }
10600                    }
10601
10602                    selection.set_head(new_head, SelectionGoal::None);
10603                }
10604            }
10605
10606            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10607            this.insert("", window, cx);
10608            let empty_str: Arc<str> = Arc::from("");
10609            for (buffer, edits) in linked_ranges {
10610                let snapshot = buffer.read(cx).snapshot();
10611                use text::ToPoint as TP;
10612
10613                let edits = edits
10614                    .into_iter()
10615                    .map(|range| {
10616                        let end_point = TP::to_point(&range.end, &snapshot);
10617                        let mut start_point = TP::to_point(&range.start, &snapshot);
10618
10619                        if end_point == start_point {
10620                            let offset = text::ToOffset::to_offset(&range.start, &snapshot)
10621                                .saturating_sub(1);
10622                            start_point =
10623                                snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
10624                        };
10625
10626                        (start_point..end_point, empty_str.clone())
10627                    })
10628                    .sorted_by_key(|(range, _)| range.start)
10629                    .collect::<Vec<_>>();
10630                buffer.update(cx, |this, cx| {
10631                    this.edit(edits, None, cx);
10632                })
10633            }
10634            this.refresh_edit_prediction(true, false, window, cx);
10635            refresh_linked_ranges(this, window, cx);
10636        });
10637    }
10638
10639    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10640        if self.read_only(cx) {
10641            return;
10642        }
10643        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10644        self.transact(window, cx, |this, window, cx| {
10645            this.change_selections(Default::default(), window, cx, |s| {
10646                s.move_with(|map, selection| {
10647                    if selection.is_empty() {
10648                        let cursor = movement::right(map, selection.head());
10649                        selection.end = cursor;
10650                        selection.reversed = true;
10651                        selection.goal = SelectionGoal::None;
10652                    }
10653                })
10654            });
10655            this.insert("", window, cx);
10656            this.refresh_edit_prediction(true, false, window, cx);
10657        });
10658    }
10659
10660    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10661        if self.mode.is_single_line() {
10662            cx.propagate();
10663            return;
10664        }
10665
10666        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10667        if self.move_to_prev_snippet_tabstop(window, cx) {
10668            return;
10669        }
10670        self.outdent(&Outdent, window, cx);
10671    }
10672
10673    pub fn next_snippet_tabstop(
10674        &mut self,
10675        _: &NextSnippetTabstop,
10676        window: &mut Window,
10677        cx: &mut Context<Self>,
10678    ) {
10679        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10680            cx.propagate();
10681            return;
10682        }
10683
10684        if self.move_to_next_snippet_tabstop(window, cx) {
10685            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10686            return;
10687        }
10688        cx.propagate();
10689    }
10690
10691    pub fn previous_snippet_tabstop(
10692        &mut self,
10693        _: &PreviousSnippetTabstop,
10694        window: &mut Window,
10695        cx: &mut Context<Self>,
10696    ) {
10697        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10698            cx.propagate();
10699            return;
10700        }
10701
10702        if self.move_to_prev_snippet_tabstop(window, cx) {
10703            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10704            return;
10705        }
10706        cx.propagate();
10707    }
10708
10709    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10710        if self.mode.is_single_line() {
10711            cx.propagate();
10712            return;
10713        }
10714
10715        if self.move_to_next_snippet_tabstop(window, cx) {
10716            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10717            return;
10718        }
10719        if self.read_only(cx) {
10720            return;
10721        }
10722        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10723        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10724        let buffer = self.buffer.read(cx);
10725        let snapshot = buffer.snapshot(cx);
10726        let rows_iter = selections.iter().map(|s| s.head().row);
10727        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10728
10729        let has_some_cursor_in_whitespace = selections
10730            .iter()
10731            .filter(|selection| selection.is_empty())
10732            .any(|selection| {
10733                let cursor = selection.head();
10734                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10735                cursor.column < current_indent.len
10736            });
10737
10738        let mut edits = Vec::new();
10739        let mut prev_edited_row = 0;
10740        let mut row_delta = 0;
10741        for selection in &mut selections {
10742            if selection.start.row != prev_edited_row {
10743                row_delta = 0;
10744            }
10745            prev_edited_row = selection.end.row;
10746
10747            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10748            if selection.is_empty() {
10749                let cursor = selection.head();
10750                let settings = buffer.language_settings_at(cursor, cx);
10751                if settings.indent_list_on_tab {
10752                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10753                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10754                            row_delta = Self::indent_selection(
10755                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10756                            );
10757                            continue;
10758                        }
10759                    }
10760                }
10761            }
10762
10763            // If the selection is non-empty, then increase the indentation of the selected lines.
10764            if !selection.is_empty() {
10765                row_delta =
10766                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10767                continue;
10768            }
10769
10770            let cursor = selection.head();
10771            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10772            if let Some(suggested_indent) =
10773                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10774            {
10775                // Don't do anything if already at suggested indent
10776                // and there is any other cursor which is not
10777                if has_some_cursor_in_whitespace
10778                    && cursor.column == current_indent.len
10779                    && current_indent.len == suggested_indent.len
10780                {
10781                    continue;
10782                }
10783
10784                // Adjust line and move cursor to suggested indent
10785                // if cursor is not at suggested indent
10786                if cursor.column < suggested_indent.len
10787                    && cursor.column <= current_indent.len
10788                    && current_indent.len <= suggested_indent.len
10789                {
10790                    selection.start = Point::new(cursor.row, suggested_indent.len);
10791                    selection.end = selection.start;
10792                    if row_delta == 0 {
10793                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10794                            cursor.row,
10795                            current_indent,
10796                            suggested_indent,
10797                        ));
10798                        row_delta = suggested_indent.len - current_indent.len;
10799                    }
10800                    continue;
10801                }
10802
10803                // If current indent is more than suggested indent
10804                // only move cursor to current indent and skip indent
10805                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10806                    selection.start = Point::new(cursor.row, current_indent.len);
10807                    selection.end = selection.start;
10808                    continue;
10809                }
10810            }
10811
10812            // Otherwise, insert a hard or soft tab.
10813            let settings = buffer.language_settings_at(cursor, cx);
10814            let tab_size = if settings.hard_tabs {
10815                IndentSize::tab()
10816            } else {
10817                let tab_size = settings.tab_size.get();
10818                let indent_remainder = snapshot
10819                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10820                    .flat_map(str::chars)
10821                    .fold(row_delta % tab_size, |counter: u32, c| {
10822                        if c == '\t' {
10823                            0
10824                        } else {
10825                            (counter + 1) % tab_size
10826                        }
10827                    });
10828
10829                let chars_to_next_tab_stop = tab_size - indent_remainder;
10830                IndentSize::spaces(chars_to_next_tab_stop)
10831            };
10832            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10833            selection.end = selection.start;
10834            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10835            row_delta += tab_size.len;
10836        }
10837
10838        self.transact(window, cx, |this, window, cx| {
10839            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10840            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10841            this.refresh_edit_prediction(true, false, window, cx);
10842        });
10843    }
10844
10845    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10846        if self.read_only(cx) {
10847            return;
10848        }
10849        if self.mode.is_single_line() {
10850            cx.propagate();
10851            return;
10852        }
10853
10854        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10855        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
10856        let mut prev_edited_row = 0;
10857        let mut row_delta = 0;
10858        let mut edits = Vec::new();
10859        let buffer = self.buffer.read(cx);
10860        let snapshot = buffer.snapshot(cx);
10861        for selection in &mut selections {
10862            if selection.start.row != prev_edited_row {
10863                row_delta = 0;
10864            }
10865            prev_edited_row = selection.end.row;
10866
10867            row_delta =
10868                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10869        }
10870
10871        self.transact(window, cx, |this, window, cx| {
10872            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10873            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10874        });
10875    }
10876
10877    fn indent_selection(
10878        buffer: &MultiBuffer,
10879        snapshot: &MultiBufferSnapshot,
10880        selection: &mut Selection<Point>,
10881        edits: &mut Vec<(Range<Point>, String)>,
10882        delta_for_start_row: u32,
10883        cx: &App,
10884    ) -> u32 {
10885        let settings = buffer.language_settings_at(selection.start, cx);
10886        let tab_size = settings.tab_size.get();
10887        let indent_kind = if settings.hard_tabs {
10888            IndentKind::Tab
10889        } else {
10890            IndentKind::Space
10891        };
10892        let mut start_row = selection.start.row;
10893        let mut end_row = selection.end.row + 1;
10894
10895        // If a selection ends at the beginning of a line, don't indent
10896        // that last line.
10897        if selection.end.column == 0 && selection.end.row > selection.start.row {
10898            end_row -= 1;
10899        }
10900
10901        // Avoid re-indenting a row that has already been indented by a
10902        // previous selection, but still update this selection's column
10903        // to reflect that indentation.
10904        if delta_for_start_row > 0 {
10905            start_row += 1;
10906            selection.start.column += delta_for_start_row;
10907            if selection.end.row == selection.start.row {
10908                selection.end.column += delta_for_start_row;
10909            }
10910        }
10911
10912        let mut delta_for_end_row = 0;
10913        let has_multiple_rows = start_row + 1 != end_row;
10914        for row in start_row..end_row {
10915            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
10916            let indent_delta = match (current_indent.kind, indent_kind) {
10917                (IndentKind::Space, IndentKind::Space) => {
10918                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
10919                    IndentSize::spaces(columns_to_next_tab_stop)
10920                }
10921                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
10922                (_, IndentKind::Tab) => IndentSize::tab(),
10923            };
10924
10925            let start = if has_multiple_rows || current_indent.len < selection.start.column {
10926                0
10927            } else {
10928                selection.start.column
10929            };
10930            let row_start = Point::new(row, start);
10931            edits.push((
10932                row_start..row_start,
10933                indent_delta.chars().collect::<String>(),
10934            ));
10935
10936            // Update this selection's endpoints to reflect the indentation.
10937            if row == selection.start.row {
10938                selection.start.column += indent_delta.len;
10939            }
10940            if row == selection.end.row {
10941                selection.end.column += indent_delta.len;
10942                delta_for_end_row = indent_delta.len;
10943            }
10944        }
10945
10946        if selection.start.row == selection.end.row {
10947            delta_for_start_row + delta_for_end_row
10948        } else {
10949            delta_for_end_row
10950        }
10951    }
10952
10953    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
10954        if self.read_only(cx) {
10955            return;
10956        }
10957        if self.mode.is_single_line() {
10958            cx.propagate();
10959            return;
10960        }
10961
10962        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10963        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10964        let selections = self.selections.all::<Point>(&display_map);
10965        let mut deletion_ranges = Vec::new();
10966        let mut last_outdent = None;
10967        {
10968            let buffer = self.buffer.read(cx);
10969            let snapshot = buffer.snapshot(cx);
10970            for selection in &selections {
10971                let settings = buffer.language_settings_at(selection.start, cx);
10972                let tab_size = settings.tab_size.get();
10973                let mut rows = selection.spanned_rows(false, &display_map);
10974
10975                // Avoid re-outdenting a row that has already been outdented by a
10976                // previous selection.
10977                if let Some(last_row) = last_outdent
10978                    && last_row == rows.start
10979                {
10980                    rows.start = rows.start.next_row();
10981                }
10982                let has_multiple_rows = rows.len() > 1;
10983                for row in rows.iter_rows() {
10984                    let indent_size = snapshot.indent_size_for_line(row);
10985                    if indent_size.len > 0 {
10986                        let deletion_len = match indent_size.kind {
10987                            IndentKind::Space => {
10988                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
10989                                if columns_to_prev_tab_stop == 0 {
10990                                    tab_size
10991                                } else {
10992                                    columns_to_prev_tab_stop
10993                                }
10994                            }
10995                            IndentKind::Tab => 1,
10996                        };
10997                        let start = if has_multiple_rows
10998                            || deletion_len > selection.start.column
10999                            || indent_size.len < selection.start.column
11000                        {
11001                            0
11002                        } else {
11003                            selection.start.column - deletion_len
11004                        };
11005                        deletion_ranges.push(
11006                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11007                        );
11008                        last_outdent = Some(row);
11009                    }
11010                }
11011            }
11012        }
11013
11014        self.transact(window, cx, |this, window, cx| {
11015            this.buffer.update(cx, |buffer, cx| {
11016                let empty_str: Arc<str> = Arc::default();
11017                buffer.edit(
11018                    deletion_ranges
11019                        .into_iter()
11020                        .map(|range| (range, empty_str.clone())),
11021                    None,
11022                    cx,
11023                );
11024            });
11025            let selections = this
11026                .selections
11027                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11028            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11029        });
11030    }
11031
11032    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11033        if self.read_only(cx) {
11034            return;
11035        }
11036        if self.mode.is_single_line() {
11037            cx.propagate();
11038            return;
11039        }
11040
11041        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11042        let selections = self
11043            .selections
11044            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11045            .into_iter()
11046            .map(|s| s.range());
11047
11048        self.transact(window, cx, |this, window, cx| {
11049            this.buffer.update(cx, |buffer, cx| {
11050                buffer.autoindent_ranges(selections, cx);
11051            });
11052            let selections = this
11053                .selections
11054                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11055            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11056        });
11057    }
11058
11059    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11060        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11061        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11062        let selections = self.selections.all::<Point>(&display_map);
11063
11064        let mut new_cursors = Vec::new();
11065        let mut edit_ranges = Vec::new();
11066        let mut selections = selections.iter().peekable();
11067        while let Some(selection) = selections.next() {
11068            let mut rows = selection.spanned_rows(false, &display_map);
11069
11070            // Accumulate contiguous regions of rows that we want to delete.
11071            while let Some(next_selection) = selections.peek() {
11072                let next_rows = next_selection.spanned_rows(false, &display_map);
11073                if next_rows.start <= rows.end {
11074                    rows.end = next_rows.end;
11075                    selections.next().unwrap();
11076                } else {
11077                    break;
11078                }
11079            }
11080
11081            let buffer = display_map.buffer_snapshot();
11082            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11083            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11084                // If there's a line after the range, delete the \n from the end of the row range
11085                (
11086                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11087                    rows.end,
11088                )
11089            } else {
11090                // If there isn't a line after the range, delete the \n from the line before the
11091                // start of the row range
11092                edit_start = edit_start.saturating_sub_usize(1);
11093                (buffer.len(), rows.start.previous_row())
11094            };
11095
11096            let text_layout_details = self.text_layout_details(window, cx);
11097            let x = display_map.x_for_display_point(
11098                selection.head().to_display_point(&display_map),
11099                &text_layout_details,
11100            );
11101            let row = Point::new(target_row.0, 0)
11102                .to_display_point(&display_map)
11103                .row();
11104            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11105
11106            new_cursors.push((
11107                selection.id,
11108                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11109                SelectionGoal::None,
11110            ));
11111            edit_ranges.push(edit_start..edit_end);
11112        }
11113
11114        self.transact(window, cx, |this, window, cx| {
11115            let buffer = this.buffer.update(cx, |buffer, cx| {
11116                let empty_str: Arc<str> = Arc::default();
11117                buffer.edit(
11118                    edit_ranges
11119                        .into_iter()
11120                        .map(|range| (range, empty_str.clone())),
11121                    None,
11122                    cx,
11123                );
11124                buffer.snapshot(cx)
11125            });
11126            let new_selections = new_cursors
11127                .into_iter()
11128                .map(|(id, cursor, goal)| {
11129                    let cursor = cursor.to_point(&buffer);
11130                    Selection {
11131                        id,
11132                        start: cursor,
11133                        end: cursor,
11134                        reversed: false,
11135                        goal,
11136                    }
11137                })
11138                .collect();
11139
11140            this.change_selections(Default::default(), window, cx, |s| {
11141                s.select(new_selections);
11142            });
11143        });
11144    }
11145
11146    pub fn join_lines_impl(
11147        &mut self,
11148        insert_whitespace: bool,
11149        window: &mut Window,
11150        cx: &mut Context<Self>,
11151    ) {
11152        if self.read_only(cx) {
11153            return;
11154        }
11155        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11156        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11157            let start = MultiBufferRow(selection.start.row);
11158            // Treat single line selections as if they include the next line. Otherwise this action
11159            // would do nothing for single line selections individual cursors.
11160            let end = if selection.start.row == selection.end.row {
11161                MultiBufferRow(selection.start.row + 1)
11162            } else {
11163                MultiBufferRow(selection.end.row)
11164            };
11165
11166            if let Some(last_row_range) = row_ranges.last_mut()
11167                && start <= last_row_range.end
11168            {
11169                last_row_range.end = end;
11170                continue;
11171            }
11172            row_ranges.push(start..end);
11173        }
11174
11175        let snapshot = self.buffer.read(cx).snapshot(cx);
11176        let mut cursor_positions = Vec::new();
11177        for row_range in &row_ranges {
11178            let anchor = snapshot.anchor_before(Point::new(
11179                row_range.end.previous_row().0,
11180                snapshot.line_len(row_range.end.previous_row()),
11181            ));
11182            cursor_positions.push(anchor..anchor);
11183        }
11184
11185        self.transact(window, cx, |this, window, cx| {
11186            for row_range in row_ranges.into_iter().rev() {
11187                for row in row_range.iter_rows().rev() {
11188                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11189                    let next_line_row = row.next_row();
11190                    let indent = snapshot.indent_size_for_line(next_line_row);
11191                    let start_of_next_line = Point::new(next_line_row.0, indent.len);
11192
11193                    let replace =
11194                        if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
11195                            " "
11196                        } else {
11197                            ""
11198                        };
11199
11200                    this.buffer.update(cx, |buffer, cx| {
11201                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11202                    });
11203                }
11204            }
11205
11206            this.change_selections(Default::default(), window, cx, |s| {
11207                s.select_anchor_ranges(cursor_positions)
11208            });
11209        });
11210    }
11211
11212    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11213        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11214        self.join_lines_impl(true, window, cx);
11215    }
11216
11217    pub fn sort_lines_case_sensitive(
11218        &mut self,
11219        _: &SortLinesCaseSensitive,
11220        window: &mut Window,
11221        cx: &mut Context<Self>,
11222    ) {
11223        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11224    }
11225
11226    pub fn sort_lines_by_length(
11227        &mut self,
11228        _: &SortLinesByLength,
11229        window: &mut Window,
11230        cx: &mut Context<Self>,
11231    ) {
11232        self.manipulate_immutable_lines(window, cx, |lines| {
11233            lines.sort_by_key(|&line| line.chars().count())
11234        })
11235    }
11236
11237    pub fn sort_lines_case_insensitive(
11238        &mut self,
11239        _: &SortLinesCaseInsensitive,
11240        window: &mut Window,
11241        cx: &mut Context<Self>,
11242    ) {
11243        self.manipulate_immutable_lines(window, cx, |lines| {
11244            lines.sort_by_key(|line| line.to_lowercase())
11245        })
11246    }
11247
11248    pub fn unique_lines_case_insensitive(
11249        &mut self,
11250        _: &UniqueLinesCaseInsensitive,
11251        window: &mut Window,
11252        cx: &mut Context<Self>,
11253    ) {
11254        self.manipulate_immutable_lines(window, cx, |lines| {
11255            let mut seen = HashSet::default();
11256            lines.retain(|line| seen.insert(line.to_lowercase()));
11257        })
11258    }
11259
11260    pub fn unique_lines_case_sensitive(
11261        &mut self,
11262        _: &UniqueLinesCaseSensitive,
11263        window: &mut Window,
11264        cx: &mut Context<Self>,
11265    ) {
11266        self.manipulate_immutable_lines(window, cx, |lines| {
11267            let mut seen = HashSet::default();
11268            lines.retain(|line| seen.insert(*line));
11269        })
11270    }
11271
11272    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11273        let snapshot = self.buffer.read(cx).snapshot(cx);
11274        for selection in self.selections.disjoint_anchors_arc().iter() {
11275            if snapshot
11276                .language_at(selection.start)
11277                .and_then(|lang| lang.config().wrap_characters.as_ref())
11278                .is_some()
11279            {
11280                return true;
11281            }
11282        }
11283        false
11284    }
11285
11286    fn wrap_selections_in_tag(
11287        &mut self,
11288        _: &WrapSelectionsInTag,
11289        window: &mut Window,
11290        cx: &mut Context<Self>,
11291    ) {
11292        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11293
11294        let snapshot = self.buffer.read(cx).snapshot(cx);
11295
11296        let mut edits = Vec::new();
11297        let mut boundaries = Vec::new();
11298
11299        for selection in self
11300            .selections
11301            .all_adjusted(&self.display_snapshot(cx))
11302            .iter()
11303        {
11304            let Some(wrap_config) = snapshot
11305                .language_at(selection.start)
11306                .and_then(|lang| lang.config().wrap_characters.clone())
11307            else {
11308                continue;
11309            };
11310
11311            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11312            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11313
11314            let start_before = snapshot.anchor_before(selection.start);
11315            let end_after = snapshot.anchor_after(selection.end);
11316
11317            edits.push((start_before..start_before, open_tag));
11318            edits.push((end_after..end_after, close_tag));
11319
11320            boundaries.push((
11321                start_before,
11322                end_after,
11323                wrap_config.start_prefix.len(),
11324                wrap_config.end_suffix.len(),
11325            ));
11326        }
11327
11328        if edits.is_empty() {
11329            return;
11330        }
11331
11332        self.transact(window, cx, |this, window, cx| {
11333            let buffer = this.buffer.update(cx, |buffer, cx| {
11334                buffer.edit(edits, None, cx);
11335                buffer.snapshot(cx)
11336            });
11337
11338            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11339            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11340                boundaries.into_iter()
11341            {
11342                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11343                let close_offset = end_after
11344                    .to_offset(&buffer)
11345                    .saturating_sub_usize(end_suffix_len);
11346                new_selections.push(open_offset..open_offset);
11347                new_selections.push(close_offset..close_offset);
11348            }
11349
11350            this.change_selections(Default::default(), window, cx, |s| {
11351                s.select_ranges(new_selections);
11352            });
11353
11354            this.request_autoscroll(Autoscroll::fit(), cx);
11355        });
11356    }
11357
11358    pub fn toggle_read_only(
11359        &mut self,
11360        _: &workspace::ToggleReadOnlyFile,
11361        _: &mut Window,
11362        cx: &mut Context<Self>,
11363    ) {
11364        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11365            buffer.update(cx, |buffer, cx| {
11366                buffer.set_capability(
11367                    match buffer.capability() {
11368                        Capability::ReadWrite => Capability::Read,
11369                        Capability::Read => Capability::ReadWrite,
11370                        Capability::ReadOnly => Capability::ReadOnly,
11371                    },
11372                    cx,
11373                );
11374            })
11375        }
11376    }
11377
11378    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11379        let Some(project) = self.project.clone() else {
11380            return;
11381        };
11382        self.reload(project, window, cx)
11383            .detach_and_notify_err(window, cx);
11384    }
11385
11386    pub fn restore_file(
11387        &mut self,
11388        _: &::git::RestoreFile,
11389        window: &mut Window,
11390        cx: &mut Context<Self>,
11391    ) {
11392        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11393        let mut buffer_ids = HashSet::default();
11394        let snapshot = self.buffer().read(cx).snapshot(cx);
11395        for selection in self
11396            .selections
11397            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11398        {
11399            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11400        }
11401
11402        let buffer = self.buffer().read(cx);
11403        let ranges = buffer_ids
11404            .into_iter()
11405            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11406            .collect::<Vec<_>>();
11407
11408        self.restore_hunks_in_ranges(ranges, window, cx);
11409    }
11410
11411    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11412        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11413        let selections = self
11414            .selections
11415            .all(&self.display_snapshot(cx))
11416            .into_iter()
11417            .map(|s| s.range())
11418            .collect();
11419        self.restore_hunks_in_ranges(selections, window, cx);
11420    }
11421
11422    pub fn restore_hunks_in_ranges(
11423        &mut self,
11424        ranges: Vec<Range<Point>>,
11425        window: &mut Window,
11426        cx: &mut Context<Editor>,
11427    ) {
11428        if self.delegate_stage_and_restore {
11429            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11430            if !hunks.is_empty() {
11431                cx.emit(EditorEvent::RestoreRequested { hunks });
11432            }
11433            return;
11434        }
11435        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11436        self.transact(window, cx, |editor, window, cx| {
11437            editor.restore_diff_hunks(hunks, cx);
11438            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11439                selections.refresh()
11440            });
11441        });
11442    }
11443
11444    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11445        let mut revert_changes = HashMap::default();
11446        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11447        for (buffer_id, hunks) in &chunk_by {
11448            let hunks = hunks.collect::<Vec<_>>();
11449            for hunk in &hunks {
11450                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11451            }
11452            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11453        }
11454        if !revert_changes.is_empty() {
11455            self.buffer().update(cx, |multi_buffer, cx| {
11456                for (buffer_id, changes) in revert_changes {
11457                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11458                        buffer.update(cx, |buffer, cx| {
11459                            buffer.edit(
11460                                changes
11461                                    .into_iter()
11462                                    .map(|(range, text)| (range, text.to_string())),
11463                                None,
11464                                cx,
11465                            );
11466                        });
11467                    }
11468                }
11469            });
11470        }
11471    }
11472
11473    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11474        if let Some(status) = self
11475            .addons
11476            .iter()
11477            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11478        {
11479            return Some(status);
11480        }
11481        self.project
11482            .as_ref()?
11483            .read(cx)
11484            .status_for_buffer_id(buffer_id, cx)
11485    }
11486
11487    pub fn open_active_item_in_terminal(
11488        &mut self,
11489        _: &OpenInTerminal,
11490        window: &mut Window,
11491        cx: &mut Context<Self>,
11492    ) {
11493        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11494            let project_path = buffer.read(cx).project_path(cx)?;
11495            let project = self.project()?.read(cx);
11496            let entry = project.entry_for_path(&project_path, cx)?;
11497            let parent = match &entry.canonical_path {
11498                Some(canonical_path) => canonical_path.to_path_buf(),
11499                None => project.absolute_path(&project_path, cx)?,
11500            }
11501            .parent()?
11502            .to_path_buf();
11503            Some(parent)
11504        }) {
11505            window.dispatch_action(
11506                OpenTerminal {
11507                    working_directory,
11508                    local: false,
11509                }
11510                .boxed_clone(),
11511                cx,
11512            );
11513        }
11514    }
11515
11516    fn set_breakpoint_context_menu(
11517        &mut self,
11518        display_row: DisplayRow,
11519        position: Option<Anchor>,
11520        clicked_point: gpui::Point<Pixels>,
11521        window: &mut Window,
11522        cx: &mut Context<Self>,
11523    ) {
11524        let source = self
11525            .buffer
11526            .read(cx)
11527            .snapshot(cx)
11528            .anchor_before(Point::new(display_row.0, 0u32));
11529
11530        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11531
11532        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11533            self,
11534            source,
11535            clicked_point,
11536            context_menu,
11537            window,
11538            cx,
11539        );
11540    }
11541
11542    fn add_edit_breakpoint_block(
11543        &mut self,
11544        anchor: Anchor,
11545        breakpoint: &Breakpoint,
11546        edit_action: BreakpointPromptEditAction,
11547        window: &mut Window,
11548        cx: &mut Context<Self>,
11549    ) {
11550        let weak_editor = cx.weak_entity();
11551        let bp_prompt = cx.new(|cx| {
11552            BreakpointPromptEditor::new(
11553                weak_editor,
11554                anchor,
11555                breakpoint.clone(),
11556                edit_action,
11557                window,
11558                cx,
11559            )
11560        });
11561
11562        let height = bp_prompt.update(cx, |this, cx| {
11563            this.prompt
11564                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11565        });
11566        let cloned_prompt = bp_prompt.clone();
11567        let blocks = vec![BlockProperties {
11568            style: BlockStyle::Sticky,
11569            placement: BlockPlacement::Above(anchor),
11570            height: Some(height),
11571            render: Arc::new(move |cx| {
11572                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11573                cloned_prompt.clone().into_any_element()
11574            }),
11575            priority: 0,
11576        }];
11577
11578        let focus_handle = bp_prompt.focus_handle(cx);
11579        window.focus(&focus_handle, cx);
11580
11581        let block_ids = self.insert_blocks(blocks, None, cx);
11582        bp_prompt.update(cx, |prompt, _| {
11583            prompt.add_block_ids(block_ids);
11584        });
11585    }
11586
11587    pub(crate) fn breakpoint_at_row(
11588        &self,
11589        row: u32,
11590        window: &mut Window,
11591        cx: &mut Context<Self>,
11592    ) -> Option<(Anchor, Breakpoint)> {
11593        let snapshot = self.snapshot(window, cx);
11594        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11595
11596        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11597    }
11598
11599    pub(crate) fn breakpoint_at_anchor(
11600        &self,
11601        breakpoint_position: Anchor,
11602        snapshot: &EditorSnapshot,
11603        cx: &mut Context<Self>,
11604    ) -> Option<(Anchor, Breakpoint)> {
11605        let buffer = self
11606            .buffer
11607            .read(cx)
11608            .buffer_for_anchor(breakpoint_position, cx)?;
11609
11610        let enclosing_excerpt = breakpoint_position.excerpt_id;
11611        let buffer_snapshot = buffer.read(cx).snapshot();
11612
11613        let row = buffer_snapshot
11614            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11615            .row;
11616
11617        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11618        let anchor_end = snapshot
11619            .buffer_snapshot()
11620            .anchor_after(Point::new(row, line_len));
11621
11622        self.breakpoint_store
11623            .as_ref()?
11624            .read_with(cx, |breakpoint_store, cx| {
11625                breakpoint_store
11626                    .breakpoints(
11627                        &buffer,
11628                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11629                        &buffer_snapshot,
11630                        cx,
11631                    )
11632                    .next()
11633                    .and_then(|(bp, _)| {
11634                        let breakpoint_row = buffer_snapshot
11635                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11636                            .row;
11637
11638                        if breakpoint_row == row {
11639                            snapshot
11640                                .buffer_snapshot()
11641                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11642                                .map(|position| (position, bp.bp.clone()))
11643                        } else {
11644                            None
11645                        }
11646                    })
11647            })
11648    }
11649
11650    pub fn edit_log_breakpoint(
11651        &mut self,
11652        _: &EditLogBreakpoint,
11653        window: &mut Window,
11654        cx: &mut Context<Self>,
11655    ) {
11656        if self.breakpoint_store.is_none() {
11657            return;
11658        }
11659
11660        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11661            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11662                message: None,
11663                state: BreakpointState::Enabled,
11664                condition: None,
11665                hit_condition: None,
11666            });
11667
11668            self.add_edit_breakpoint_block(
11669                anchor,
11670                &breakpoint,
11671                BreakpointPromptEditAction::Log,
11672                window,
11673                cx,
11674            );
11675        }
11676    }
11677
11678    fn breakpoints_at_cursors(
11679        &self,
11680        window: &mut Window,
11681        cx: &mut Context<Self>,
11682    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11683        let snapshot = self.snapshot(window, cx);
11684        let cursors = self
11685            .selections
11686            .disjoint_anchors_arc()
11687            .iter()
11688            .map(|selection| {
11689                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11690
11691                let breakpoint_position = self
11692                    .breakpoint_at_row(cursor_position.row, window, cx)
11693                    .map(|bp| bp.0)
11694                    .unwrap_or_else(|| {
11695                        snapshot
11696                            .display_snapshot
11697                            .buffer_snapshot()
11698                            .anchor_after(Point::new(cursor_position.row, 0))
11699                    });
11700
11701                let breakpoint = self
11702                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11703                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11704
11705                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11706            })
11707            // 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.
11708            .collect::<HashMap<Anchor, _>>();
11709
11710        cursors.into_iter().collect()
11711    }
11712
11713    pub fn enable_breakpoint(
11714        &mut self,
11715        _: &crate::actions::EnableBreakpoint,
11716        window: &mut Window,
11717        cx: &mut Context<Self>,
11718    ) {
11719        if self.breakpoint_store.is_none() {
11720            return;
11721        }
11722
11723        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11724            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11725                continue;
11726            };
11727            self.edit_breakpoint_at_anchor(
11728                anchor,
11729                breakpoint,
11730                BreakpointEditAction::InvertState,
11731                cx,
11732            );
11733        }
11734    }
11735
11736    pub fn disable_breakpoint(
11737        &mut self,
11738        _: &crate::actions::DisableBreakpoint,
11739        window: &mut Window,
11740        cx: &mut Context<Self>,
11741    ) {
11742        if self.breakpoint_store.is_none() {
11743            return;
11744        }
11745
11746        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11747            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11748                continue;
11749            };
11750            self.edit_breakpoint_at_anchor(
11751                anchor,
11752                breakpoint,
11753                BreakpointEditAction::InvertState,
11754                cx,
11755            );
11756        }
11757    }
11758
11759    pub fn toggle_breakpoint(
11760        &mut self,
11761        _: &crate::actions::ToggleBreakpoint,
11762        window: &mut Window,
11763        cx: &mut Context<Self>,
11764    ) {
11765        if self.breakpoint_store.is_none() {
11766            return;
11767        }
11768
11769        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11770            if let Some(breakpoint) = breakpoint {
11771                self.edit_breakpoint_at_anchor(
11772                    anchor,
11773                    breakpoint,
11774                    BreakpointEditAction::Toggle,
11775                    cx,
11776                );
11777            } else {
11778                self.edit_breakpoint_at_anchor(
11779                    anchor,
11780                    Breakpoint::new_standard(),
11781                    BreakpointEditAction::Toggle,
11782                    cx,
11783                );
11784            }
11785        }
11786    }
11787
11788    pub fn edit_breakpoint_at_anchor(
11789        &mut self,
11790        breakpoint_position: Anchor,
11791        breakpoint: Breakpoint,
11792        edit_action: BreakpointEditAction,
11793        cx: &mut Context<Self>,
11794    ) {
11795        let Some(breakpoint_store) = &self.breakpoint_store else {
11796            return;
11797        };
11798
11799        let Some(buffer) = self
11800            .buffer
11801            .read(cx)
11802            .buffer_for_anchor(breakpoint_position, cx)
11803        else {
11804            return;
11805        };
11806
11807        breakpoint_store.update(cx, |breakpoint_store, cx| {
11808            breakpoint_store.toggle_breakpoint(
11809                buffer,
11810                BreakpointWithPosition {
11811                    position: breakpoint_position.text_anchor,
11812                    bp: breakpoint,
11813                },
11814                edit_action,
11815                cx,
11816            );
11817        });
11818
11819        cx.notify();
11820    }
11821
11822    #[cfg(any(test, feature = "test-support"))]
11823    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
11824        self.breakpoint_store.clone()
11825    }
11826
11827    pub fn prepare_restore_change(
11828        &self,
11829        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11830        hunk: &MultiBufferDiffHunk,
11831        cx: &mut App,
11832    ) -> Option<()> {
11833        if hunk.is_created_file() {
11834            return None;
11835        }
11836        let buffer = self.buffer.read(cx);
11837        let diff = buffer.diff_for(hunk.buffer_id)?;
11838        let buffer = buffer.buffer(hunk.buffer_id)?;
11839        let buffer = buffer.read(cx);
11840        let original_text = diff
11841            .read(cx)
11842            .base_text(cx)
11843            .as_rope()
11844            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
11845        let buffer_snapshot = buffer.snapshot();
11846        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
11847        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
11848            probe
11849                .0
11850                .start
11851                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
11852                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
11853        }) {
11854            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
11855            Some(())
11856        } else {
11857            None
11858        }
11859    }
11860
11861    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
11862        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
11863    }
11864
11865    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
11866        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
11867    }
11868
11869    pub fn rotate_selections_forward(
11870        &mut self,
11871        _: &RotateSelectionsForward,
11872        window: &mut Window,
11873        cx: &mut Context<Self>,
11874    ) {
11875        self.rotate_selections(window, cx, false)
11876    }
11877
11878    pub fn rotate_selections_backward(
11879        &mut self,
11880        _: &RotateSelectionsBackward,
11881        window: &mut Window,
11882        cx: &mut Context<Self>,
11883    ) {
11884        self.rotate_selections(window, cx, true)
11885    }
11886
11887    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
11888        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11889        let display_snapshot = self.display_snapshot(cx);
11890        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
11891
11892        if selections.len() < 2 {
11893            return;
11894        }
11895
11896        let (edits, new_selections) = {
11897            let buffer = self.buffer.read(cx).read(cx);
11898            let has_selections = selections.iter().any(|s| !s.is_empty());
11899            if has_selections {
11900                let mut selected_texts: Vec<String> = selections
11901                    .iter()
11902                    .map(|selection| {
11903                        buffer
11904                            .text_for_range(selection.start..selection.end)
11905                            .collect()
11906                    })
11907                    .collect();
11908
11909                if reverse {
11910                    selected_texts.rotate_left(1);
11911                } else {
11912                    selected_texts.rotate_right(1);
11913                }
11914
11915                let mut offset_delta: i64 = 0;
11916                let mut new_selections = Vec::new();
11917                let edits: Vec<_> = selections
11918                    .iter()
11919                    .zip(selected_texts.iter())
11920                    .map(|(selection, new_text)| {
11921                        let old_len = (selection.end.0 - selection.start.0) as i64;
11922                        let new_len = new_text.len() as i64;
11923                        let adjusted_start =
11924                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
11925                        let adjusted_end =
11926                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
11927
11928                        new_selections.push(Selection {
11929                            id: selection.id,
11930                            start: adjusted_start,
11931                            end: adjusted_end,
11932                            reversed: selection.reversed,
11933                            goal: selection.goal,
11934                        });
11935
11936                        offset_delta += new_len - old_len;
11937                        (selection.start..selection.end, new_text.clone())
11938                    })
11939                    .collect();
11940                (edits, new_selections)
11941            } else {
11942                let mut all_rows: Vec<u32> = selections
11943                    .iter()
11944                    .map(|selection| buffer.offset_to_point(selection.start).row)
11945                    .collect();
11946                all_rows.sort_unstable();
11947                all_rows.dedup();
11948
11949                if all_rows.len() < 2 {
11950                    return;
11951                }
11952
11953                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
11954                    .iter()
11955                    .map(|&row| {
11956                        let start = Point::new(row, 0);
11957                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11958                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
11959                    })
11960                    .collect();
11961
11962                let mut line_texts: Vec<String> = line_ranges
11963                    .iter()
11964                    .map(|range| buffer.text_for_range(range.clone()).collect())
11965                    .collect();
11966
11967                if reverse {
11968                    line_texts.rotate_left(1);
11969                } else {
11970                    line_texts.rotate_right(1);
11971                }
11972
11973                let edits = line_ranges
11974                    .iter()
11975                    .zip(line_texts.iter())
11976                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
11977                    .collect();
11978
11979                let num_rows = all_rows.len();
11980                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
11981                    .iter()
11982                    .enumerate()
11983                    .map(|(i, &row)| (row, i))
11984                    .collect();
11985
11986                // Compute new line start offsets after rotation (handles CRLF)
11987                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
11988                let first_line_start = line_ranges[0].start.0;
11989                let mut new_line_starts: Vec<usize> = vec![first_line_start];
11990                for text in line_texts.iter().take(num_rows - 1) {
11991                    let prev_start = *new_line_starts.last().unwrap();
11992                    new_line_starts.push(prev_start + text.len() + newline_len);
11993                }
11994
11995                let new_selections = selections
11996                    .iter()
11997                    .map(|selection| {
11998                        let point = buffer.offset_to_point(selection.start);
11999                        let old_index = row_to_index[&point.row];
12000                        let new_index = if reverse {
12001                            (old_index + num_rows - 1) % num_rows
12002                        } else {
12003                            (old_index + 1) % num_rows
12004                        };
12005                        let new_offset =
12006                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12007                        Selection {
12008                            id: selection.id,
12009                            start: new_offset,
12010                            end: new_offset,
12011                            reversed: selection.reversed,
12012                            goal: selection.goal,
12013                        }
12014                    })
12015                    .collect();
12016
12017                (edits, new_selections)
12018            }
12019        };
12020
12021        self.transact(window, cx, |this, window, cx| {
12022            this.buffer.update(cx, |buffer, cx| {
12023                buffer.edit(edits, None, cx);
12024            });
12025            this.change_selections(Default::default(), window, cx, |s| {
12026                s.select(new_selections);
12027            });
12028        });
12029    }
12030
12031    fn manipulate_lines<M>(
12032        &mut self,
12033        window: &mut Window,
12034        cx: &mut Context<Self>,
12035        mut manipulate: M,
12036    ) where
12037        M: FnMut(&str) -> LineManipulationResult,
12038    {
12039        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12040
12041        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12042        let buffer = self.buffer.read(cx).snapshot(cx);
12043
12044        let mut edits = Vec::new();
12045
12046        let selections = self.selections.all::<Point>(&display_map);
12047        let mut selections = selections.iter().peekable();
12048        let mut contiguous_row_selections = Vec::new();
12049        let mut new_selections = Vec::new();
12050        let mut added_lines = 0;
12051        let mut removed_lines = 0;
12052
12053        while let Some(selection) = selections.next() {
12054            let (start_row, end_row) = consume_contiguous_rows(
12055                &mut contiguous_row_selections,
12056                selection,
12057                &display_map,
12058                &mut selections,
12059            );
12060
12061            let start_point = Point::new(start_row.0, 0);
12062            let end_point = Point::new(
12063                end_row.previous_row().0,
12064                buffer.line_len(end_row.previous_row()),
12065            );
12066            let text = buffer
12067                .text_for_range(start_point..end_point)
12068                .collect::<String>();
12069
12070            let LineManipulationResult {
12071                new_text,
12072                line_count_before,
12073                line_count_after,
12074            } = manipulate(&text);
12075
12076            edits.push((start_point..end_point, new_text));
12077
12078            // Selections must change based on added and removed line count
12079            let start_row =
12080                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12081            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12082            new_selections.push(Selection {
12083                id: selection.id,
12084                start: start_row,
12085                end: end_row,
12086                goal: SelectionGoal::None,
12087                reversed: selection.reversed,
12088            });
12089
12090            if line_count_after > line_count_before {
12091                added_lines += line_count_after - line_count_before;
12092            } else if line_count_before > line_count_after {
12093                removed_lines += line_count_before - line_count_after;
12094            }
12095        }
12096
12097        self.transact(window, cx, |this, window, cx| {
12098            let buffer = this.buffer.update(cx, |buffer, cx| {
12099                buffer.edit(edits, None, cx);
12100                buffer.snapshot(cx)
12101            });
12102
12103            // Recalculate offsets on newly edited buffer
12104            let new_selections = new_selections
12105                .iter()
12106                .map(|s| {
12107                    let start_point = Point::new(s.start.0, 0);
12108                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12109                    Selection {
12110                        id: s.id,
12111                        start: buffer.point_to_offset(start_point),
12112                        end: buffer.point_to_offset(end_point),
12113                        goal: s.goal,
12114                        reversed: s.reversed,
12115                    }
12116                })
12117                .collect();
12118
12119            this.change_selections(Default::default(), window, cx, |s| {
12120                s.select(new_selections);
12121            });
12122
12123            this.request_autoscroll(Autoscroll::fit(), cx);
12124        });
12125    }
12126
12127    fn manipulate_immutable_lines<Fn>(
12128        &mut self,
12129        window: &mut Window,
12130        cx: &mut Context<Self>,
12131        mut callback: Fn,
12132    ) where
12133        Fn: FnMut(&mut Vec<&str>),
12134    {
12135        self.manipulate_lines(window, cx, |text| {
12136            let mut lines: Vec<&str> = text.split('\n').collect();
12137            let line_count_before = lines.len();
12138
12139            callback(&mut lines);
12140
12141            LineManipulationResult {
12142                new_text: lines.join("\n"),
12143                line_count_before,
12144                line_count_after: lines.len(),
12145            }
12146        });
12147    }
12148
12149    fn manipulate_mutable_lines<Fn>(
12150        &mut self,
12151        window: &mut Window,
12152        cx: &mut Context<Self>,
12153        mut callback: Fn,
12154    ) where
12155        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12156    {
12157        self.manipulate_lines(window, cx, |text| {
12158            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12159            let line_count_before = lines.len();
12160
12161            callback(&mut lines);
12162
12163            LineManipulationResult {
12164                new_text: lines.join("\n"),
12165                line_count_before,
12166                line_count_after: lines.len(),
12167            }
12168        });
12169    }
12170
12171    pub fn convert_indentation_to_spaces(
12172        &mut self,
12173        _: &ConvertIndentationToSpaces,
12174        window: &mut Window,
12175        cx: &mut Context<Self>,
12176    ) {
12177        let settings = self.buffer.read(cx).language_settings(cx);
12178        let tab_size = settings.tab_size.get() as usize;
12179
12180        self.manipulate_mutable_lines(window, cx, |lines| {
12181            // Allocates a reasonably sized scratch buffer once for the whole loop
12182            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12183            // Avoids recomputing spaces that could be inserted many times
12184            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12185                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12186                .collect();
12187
12188            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12189                let mut chars = line.as_ref().chars();
12190                let mut col = 0;
12191                let mut changed = false;
12192
12193                for ch in chars.by_ref() {
12194                    match ch {
12195                        ' ' => {
12196                            reindented_line.push(' ');
12197                            col += 1;
12198                        }
12199                        '\t' => {
12200                            // \t are converted to spaces depending on the current column
12201                            let spaces_len = tab_size - (col % tab_size);
12202                            reindented_line.extend(&space_cache[spaces_len - 1]);
12203                            col += spaces_len;
12204                            changed = true;
12205                        }
12206                        _ => {
12207                            // If we dont append before break, the character is consumed
12208                            reindented_line.push(ch);
12209                            break;
12210                        }
12211                    }
12212                }
12213
12214                if !changed {
12215                    reindented_line.clear();
12216                    continue;
12217                }
12218                // Append the rest of the line and replace old reference with new one
12219                reindented_line.extend(chars);
12220                *line = Cow::Owned(reindented_line.clone());
12221                reindented_line.clear();
12222            }
12223        });
12224    }
12225
12226    pub fn convert_indentation_to_tabs(
12227        &mut self,
12228        _: &ConvertIndentationToTabs,
12229        window: &mut Window,
12230        cx: &mut Context<Self>,
12231    ) {
12232        let settings = self.buffer.read(cx).language_settings(cx);
12233        let tab_size = settings.tab_size.get() as usize;
12234
12235        self.manipulate_mutable_lines(window, cx, |lines| {
12236            // Allocates a reasonably sized buffer once for the whole loop
12237            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12238            // Avoids recomputing spaces that could be inserted many times
12239            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12240                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12241                .collect();
12242
12243            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12244                let mut chars = line.chars();
12245                let mut spaces_count = 0;
12246                let mut first_non_indent_char = None;
12247                let mut changed = false;
12248
12249                for ch in chars.by_ref() {
12250                    match ch {
12251                        ' ' => {
12252                            // Keep track of spaces. Append \t when we reach tab_size
12253                            spaces_count += 1;
12254                            changed = true;
12255                            if spaces_count == tab_size {
12256                                reindented_line.push('\t');
12257                                spaces_count = 0;
12258                            }
12259                        }
12260                        '\t' => {
12261                            reindented_line.push('\t');
12262                            spaces_count = 0;
12263                        }
12264                        _ => {
12265                            // Dont append it yet, we might have remaining spaces
12266                            first_non_indent_char = Some(ch);
12267                            break;
12268                        }
12269                    }
12270                }
12271
12272                if !changed {
12273                    reindented_line.clear();
12274                    continue;
12275                }
12276                // Remaining spaces that didn't make a full tab stop
12277                if spaces_count > 0 {
12278                    reindented_line.extend(&space_cache[spaces_count - 1]);
12279                }
12280                // If we consume an extra character that was not indentation, add it back
12281                if let Some(extra_char) = first_non_indent_char {
12282                    reindented_line.push(extra_char);
12283                }
12284                // Append the rest of the line and replace old reference with new one
12285                reindented_line.extend(chars);
12286                *line = Cow::Owned(reindented_line.clone());
12287                reindented_line.clear();
12288            }
12289        });
12290    }
12291
12292    pub fn convert_to_upper_case(
12293        &mut self,
12294        _: &ConvertToUpperCase,
12295        window: &mut Window,
12296        cx: &mut Context<Self>,
12297    ) {
12298        self.manipulate_text(window, cx, |text| text.to_uppercase())
12299    }
12300
12301    pub fn convert_to_lower_case(
12302        &mut self,
12303        _: &ConvertToLowerCase,
12304        window: &mut Window,
12305        cx: &mut Context<Self>,
12306    ) {
12307        self.manipulate_text(window, cx, |text| text.to_lowercase())
12308    }
12309
12310    pub fn convert_to_title_case(
12311        &mut self,
12312        _: &ConvertToTitleCase,
12313        window: &mut Window,
12314        cx: &mut Context<Self>,
12315    ) {
12316        self.manipulate_text(window, cx, |text| {
12317            text.split('\n')
12318                .map(|line| line.to_case(Case::Title))
12319                .join("\n")
12320        })
12321    }
12322
12323    pub fn convert_to_snake_case(
12324        &mut self,
12325        _: &ConvertToSnakeCase,
12326        window: &mut Window,
12327        cx: &mut Context<Self>,
12328    ) {
12329        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12330    }
12331
12332    pub fn convert_to_kebab_case(
12333        &mut self,
12334        _: &ConvertToKebabCase,
12335        window: &mut Window,
12336        cx: &mut Context<Self>,
12337    ) {
12338        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12339    }
12340
12341    pub fn convert_to_upper_camel_case(
12342        &mut self,
12343        _: &ConvertToUpperCamelCase,
12344        window: &mut Window,
12345        cx: &mut Context<Self>,
12346    ) {
12347        self.manipulate_text(window, cx, |text| {
12348            text.split('\n')
12349                .map(|line| line.to_case(Case::UpperCamel))
12350                .join("\n")
12351        })
12352    }
12353
12354    pub fn convert_to_lower_camel_case(
12355        &mut self,
12356        _: &ConvertToLowerCamelCase,
12357        window: &mut Window,
12358        cx: &mut Context<Self>,
12359    ) {
12360        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12361    }
12362
12363    pub fn convert_to_opposite_case(
12364        &mut self,
12365        _: &ConvertToOppositeCase,
12366        window: &mut Window,
12367        cx: &mut Context<Self>,
12368    ) {
12369        self.manipulate_text(window, cx, |text| {
12370            text.chars()
12371                .fold(String::with_capacity(text.len()), |mut t, c| {
12372                    if c.is_uppercase() {
12373                        t.extend(c.to_lowercase());
12374                    } else {
12375                        t.extend(c.to_uppercase());
12376                    }
12377                    t
12378                })
12379        })
12380    }
12381
12382    pub fn convert_to_sentence_case(
12383        &mut self,
12384        _: &ConvertToSentenceCase,
12385        window: &mut Window,
12386        cx: &mut Context<Self>,
12387    ) {
12388        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12389    }
12390
12391    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12392        self.manipulate_text(window, cx, |text| {
12393            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12394            if has_upper_case_characters {
12395                text.to_lowercase()
12396            } else {
12397                text.to_uppercase()
12398            }
12399        })
12400    }
12401
12402    pub fn convert_to_rot13(
12403        &mut self,
12404        _: &ConvertToRot13,
12405        window: &mut Window,
12406        cx: &mut Context<Self>,
12407    ) {
12408        self.manipulate_text(window, cx, |text| {
12409            text.chars()
12410                .map(|c| match c {
12411                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12412                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12413                    _ => c,
12414                })
12415                .collect()
12416        })
12417    }
12418
12419    pub fn convert_to_rot47(
12420        &mut self,
12421        _: &ConvertToRot47,
12422        window: &mut Window,
12423        cx: &mut Context<Self>,
12424    ) {
12425        self.manipulate_text(window, cx, |text| {
12426            text.chars()
12427                .map(|c| {
12428                    let code_point = c as u32;
12429                    if code_point >= 33 && code_point <= 126 {
12430                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12431                    }
12432                    c
12433                })
12434                .collect()
12435        })
12436    }
12437
12438    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12439    where
12440        Fn: FnMut(&str) -> String,
12441    {
12442        let buffer = self.buffer.read(cx).snapshot(cx);
12443
12444        let mut new_selections = Vec::new();
12445        let mut edits = Vec::new();
12446        let mut selection_adjustment = 0isize;
12447
12448        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12449            let selection_is_empty = selection.is_empty();
12450
12451            let (start, end) = if selection_is_empty {
12452                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12453                (word_range.start, word_range.end)
12454            } else {
12455                (
12456                    buffer.point_to_offset(selection.start),
12457                    buffer.point_to_offset(selection.end),
12458                )
12459            };
12460
12461            let text = buffer.text_for_range(start..end).collect::<String>();
12462            let old_length = text.len() as isize;
12463            let text = callback(&text);
12464
12465            new_selections.push(Selection {
12466                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12467                end: MultiBufferOffset(
12468                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12469                ),
12470                goal: SelectionGoal::None,
12471                id: selection.id,
12472                reversed: selection.reversed,
12473            });
12474
12475            selection_adjustment += old_length - text.len() as isize;
12476
12477            edits.push((start..end, text));
12478        }
12479
12480        self.transact(window, cx, |this, window, cx| {
12481            this.buffer.update(cx, |buffer, cx| {
12482                buffer.edit(edits, None, cx);
12483            });
12484
12485            this.change_selections(Default::default(), window, cx, |s| {
12486                s.select(new_selections);
12487            });
12488
12489            this.request_autoscroll(Autoscroll::fit(), cx);
12490        });
12491    }
12492
12493    pub fn move_selection_on_drop(
12494        &mut self,
12495        selection: &Selection<Anchor>,
12496        target: DisplayPoint,
12497        is_cut: bool,
12498        window: &mut Window,
12499        cx: &mut Context<Self>,
12500    ) {
12501        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12502        let buffer = display_map.buffer_snapshot();
12503        let mut edits = Vec::new();
12504        let insert_point = display_map
12505            .clip_point(target, Bias::Left)
12506            .to_point(&display_map);
12507        let text = buffer
12508            .text_for_range(selection.start..selection.end)
12509            .collect::<String>();
12510        if is_cut {
12511            edits.push(((selection.start..selection.end), String::new()));
12512        }
12513        let insert_anchor = buffer.anchor_before(insert_point);
12514        edits.push(((insert_anchor..insert_anchor), text));
12515        let last_edit_start = insert_anchor.bias_left(buffer);
12516        let last_edit_end = insert_anchor.bias_right(buffer);
12517        self.transact(window, cx, |this, window, cx| {
12518            this.buffer.update(cx, |buffer, cx| {
12519                buffer.edit(edits, None, cx);
12520            });
12521            this.change_selections(Default::default(), window, cx, |s| {
12522                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12523            });
12524        });
12525    }
12526
12527    pub fn clear_selection_drag_state(&mut self) {
12528        self.selection_drag_state = SelectionDragState::None;
12529    }
12530
12531    pub fn duplicate(
12532        &mut self,
12533        upwards: bool,
12534        whole_lines: bool,
12535        window: &mut Window,
12536        cx: &mut Context<Self>,
12537    ) {
12538        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12539
12540        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12541        let buffer = display_map.buffer_snapshot();
12542        let selections = self.selections.all::<Point>(&display_map);
12543
12544        let mut edits = Vec::new();
12545        let mut selections_iter = selections.iter().peekable();
12546        while let Some(selection) = selections_iter.next() {
12547            let mut rows = selection.spanned_rows(false, &display_map);
12548            // duplicate line-wise
12549            if whole_lines || selection.start == selection.end {
12550                // Avoid duplicating the same lines twice.
12551                while let Some(next_selection) = selections_iter.peek() {
12552                    let next_rows = next_selection.spanned_rows(false, &display_map);
12553                    if next_rows.start < rows.end {
12554                        rows.end = next_rows.end;
12555                        selections_iter.next().unwrap();
12556                    } else {
12557                        break;
12558                    }
12559                }
12560
12561                // Copy the text from the selected row region and splice it either at the start
12562                // or end of the region.
12563                let start = Point::new(rows.start.0, 0);
12564                let end = Point::new(
12565                    rows.end.previous_row().0,
12566                    buffer.line_len(rows.end.previous_row()),
12567                );
12568
12569                let mut text = buffer.text_for_range(start..end).collect::<String>();
12570
12571                let insert_location = if upwards {
12572                    // When duplicating upward, we need to insert before the current line.
12573                    // If we're on the last line and it doesn't end with a newline,
12574                    // we need to add a newline before the duplicated content.
12575                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12576                        && buffer.max_point().column > 0
12577                        && !text.ends_with('\n');
12578
12579                    if needs_leading_newline {
12580                        text.insert(0, '\n');
12581                        end
12582                    } else {
12583                        text.push('\n');
12584                        Point::new(rows.start.0, 0)
12585                    }
12586                } else {
12587                    text.push('\n');
12588                    start
12589                };
12590                edits.push((insert_location..insert_location, text));
12591            } else {
12592                // duplicate character-wise
12593                let start = selection.start;
12594                let end = selection.end;
12595                let text = buffer.text_for_range(start..end).collect::<String>();
12596                edits.push((selection.end..selection.end, text));
12597            }
12598        }
12599
12600        self.transact(window, cx, |this, window, cx| {
12601            this.buffer.update(cx, |buffer, cx| {
12602                buffer.edit(edits, None, cx);
12603            });
12604
12605            // When duplicating upward with whole lines, move the cursor to the duplicated line
12606            if upwards && whole_lines {
12607                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12608
12609                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12610                    let mut new_ranges = Vec::new();
12611                    let selections = s.all::<Point>(&display_map);
12612                    let mut selections_iter = selections.iter().peekable();
12613
12614                    while let Some(first_selection) = selections_iter.next() {
12615                        // Group contiguous selections together to find the total row span
12616                        let mut group_selections = vec![first_selection];
12617                        let mut rows = first_selection.spanned_rows(false, &display_map);
12618
12619                        while let Some(next_selection) = selections_iter.peek() {
12620                            let next_rows = next_selection.spanned_rows(false, &display_map);
12621                            if next_rows.start < rows.end {
12622                                rows.end = next_rows.end;
12623                                group_selections.push(selections_iter.next().unwrap());
12624                            } else {
12625                                break;
12626                            }
12627                        }
12628
12629                        let row_count = rows.end.0 - rows.start.0;
12630
12631                        // Move all selections in this group up by the total number of duplicated rows
12632                        for selection in group_selections {
12633                            let new_start = Point::new(
12634                                selection.start.row.saturating_sub(row_count),
12635                                selection.start.column,
12636                            );
12637
12638                            let new_end = Point::new(
12639                                selection.end.row.saturating_sub(row_count),
12640                                selection.end.column,
12641                            );
12642
12643                            new_ranges.push(new_start..new_end);
12644                        }
12645                    }
12646
12647                    s.select_ranges(new_ranges);
12648                });
12649            }
12650
12651            this.request_autoscroll(Autoscroll::fit(), cx);
12652        });
12653    }
12654
12655    pub fn duplicate_line_up(
12656        &mut self,
12657        _: &DuplicateLineUp,
12658        window: &mut Window,
12659        cx: &mut Context<Self>,
12660    ) {
12661        self.duplicate(true, true, window, cx);
12662    }
12663
12664    pub fn duplicate_line_down(
12665        &mut self,
12666        _: &DuplicateLineDown,
12667        window: &mut Window,
12668        cx: &mut Context<Self>,
12669    ) {
12670        self.duplicate(false, true, window, cx);
12671    }
12672
12673    pub fn duplicate_selection(
12674        &mut self,
12675        _: &DuplicateSelection,
12676        window: &mut Window,
12677        cx: &mut Context<Self>,
12678    ) {
12679        self.duplicate(false, false, window, cx);
12680    }
12681
12682    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12683        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12684        if self.mode.is_single_line() {
12685            cx.propagate();
12686            return;
12687        }
12688
12689        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12690        let buffer = self.buffer.read(cx).snapshot(cx);
12691
12692        let mut edits = Vec::new();
12693        let mut unfold_ranges = Vec::new();
12694        let mut refold_creases = Vec::new();
12695
12696        let selections = self.selections.all::<Point>(&display_map);
12697        let mut selections = selections.iter().peekable();
12698        let mut contiguous_row_selections = Vec::new();
12699        let mut new_selections = Vec::new();
12700
12701        while let Some(selection) = selections.next() {
12702            // Find all the selections that span a contiguous row range
12703            let (start_row, end_row) = consume_contiguous_rows(
12704                &mut contiguous_row_selections,
12705                selection,
12706                &display_map,
12707                &mut selections,
12708            );
12709
12710            // Move the text spanned by the row range to be before the line preceding the row range
12711            if start_row.0 > 0 {
12712                let range_to_move = Point::new(
12713                    start_row.previous_row().0,
12714                    buffer.line_len(start_row.previous_row()),
12715                )
12716                    ..Point::new(
12717                        end_row.previous_row().0,
12718                        buffer.line_len(end_row.previous_row()),
12719                    );
12720                let insertion_point = display_map
12721                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12722                    .0;
12723
12724                // Don't move lines across excerpts
12725                if buffer
12726                    .excerpt_containing(insertion_point..range_to_move.end)
12727                    .is_some()
12728                {
12729                    let text = buffer
12730                        .text_for_range(range_to_move.clone())
12731                        .flat_map(|s| s.chars())
12732                        .skip(1)
12733                        .chain(['\n'])
12734                        .collect::<String>();
12735
12736                    edits.push((
12737                        buffer.anchor_after(range_to_move.start)
12738                            ..buffer.anchor_before(range_to_move.end),
12739                        String::new(),
12740                    ));
12741                    let insertion_anchor = buffer.anchor_after(insertion_point);
12742                    edits.push((insertion_anchor..insertion_anchor, text));
12743
12744                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12745
12746                    // Move selections up
12747                    new_selections.extend(contiguous_row_selections.drain(..).map(
12748                        |mut selection| {
12749                            selection.start.row -= row_delta;
12750                            selection.end.row -= row_delta;
12751                            selection
12752                        },
12753                    ));
12754
12755                    // Move folds up
12756                    unfold_ranges.push(range_to_move.clone());
12757                    for fold in display_map.folds_in_range(
12758                        buffer.anchor_before(range_to_move.start)
12759                            ..buffer.anchor_after(range_to_move.end),
12760                    ) {
12761                        let mut start = fold.range.start.to_point(&buffer);
12762                        let mut end = fold.range.end.to_point(&buffer);
12763                        start.row -= row_delta;
12764                        end.row -= row_delta;
12765                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12766                    }
12767                }
12768            }
12769
12770            // If we didn't move line(s), preserve the existing selections
12771            new_selections.append(&mut contiguous_row_selections);
12772        }
12773
12774        self.transact(window, cx, |this, window, cx| {
12775            this.unfold_ranges(&unfold_ranges, true, true, cx);
12776            this.buffer.update(cx, |buffer, cx| {
12777                for (range, text) in edits {
12778                    buffer.edit([(range, text)], None, cx);
12779                }
12780            });
12781            this.fold_creases(refold_creases, true, window, cx);
12782            this.change_selections(Default::default(), window, cx, |s| {
12783                s.select(new_selections);
12784            })
12785        });
12786    }
12787
12788    pub fn move_line_down(
12789        &mut self,
12790        _: &MoveLineDown,
12791        window: &mut Window,
12792        cx: &mut Context<Self>,
12793    ) {
12794        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12795        if self.mode.is_single_line() {
12796            cx.propagate();
12797            return;
12798        }
12799
12800        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12801        let buffer = self.buffer.read(cx).snapshot(cx);
12802
12803        let mut edits = Vec::new();
12804        let mut unfold_ranges = Vec::new();
12805        let mut refold_creases = Vec::new();
12806
12807        let selections = self.selections.all::<Point>(&display_map);
12808        let mut selections = selections.iter().peekable();
12809        let mut contiguous_row_selections = Vec::new();
12810        let mut new_selections = Vec::new();
12811
12812        while let Some(selection) = selections.next() {
12813            // Find all the selections that span a contiguous row range
12814            let (start_row, end_row) = consume_contiguous_rows(
12815                &mut contiguous_row_selections,
12816                selection,
12817                &display_map,
12818                &mut selections,
12819            );
12820
12821            // Move the text spanned by the row range to be after the last line of the row range
12822            if end_row.0 <= buffer.max_point().row {
12823                let range_to_move =
12824                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
12825                let insertion_point = display_map
12826                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
12827                    .0;
12828
12829                // Don't move lines across excerpt boundaries
12830                if buffer
12831                    .excerpt_containing(range_to_move.start..insertion_point)
12832                    .is_some()
12833                {
12834                    let mut text = String::from("\n");
12835                    text.extend(buffer.text_for_range(range_to_move.clone()));
12836                    text.pop(); // Drop trailing newline
12837                    edits.push((
12838                        buffer.anchor_after(range_to_move.start)
12839                            ..buffer.anchor_before(range_to_move.end),
12840                        String::new(),
12841                    ));
12842                    let insertion_anchor = buffer.anchor_after(insertion_point);
12843                    edits.push((insertion_anchor..insertion_anchor, text));
12844
12845                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
12846
12847                    // Move selections down
12848                    new_selections.extend(contiguous_row_selections.drain(..).map(
12849                        |mut selection| {
12850                            selection.start.row += row_delta;
12851                            selection.end.row += row_delta;
12852                            selection
12853                        },
12854                    ));
12855
12856                    // Move folds down
12857                    unfold_ranges.push(range_to_move.clone());
12858                    for fold in display_map.folds_in_range(
12859                        buffer.anchor_before(range_to_move.start)
12860                            ..buffer.anchor_after(range_to_move.end),
12861                    ) {
12862                        let mut start = fold.range.start.to_point(&buffer);
12863                        let mut end = fold.range.end.to_point(&buffer);
12864                        start.row += row_delta;
12865                        end.row += row_delta;
12866                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12867                    }
12868                }
12869            }
12870
12871            // If we didn't move line(s), preserve the existing selections
12872            new_selections.append(&mut contiguous_row_selections);
12873        }
12874
12875        self.transact(window, cx, |this, window, cx| {
12876            this.unfold_ranges(&unfold_ranges, true, true, cx);
12877            this.buffer.update(cx, |buffer, cx| {
12878                for (range, text) in edits {
12879                    buffer.edit([(range, text)], None, cx);
12880                }
12881            });
12882            this.fold_creases(refold_creases, true, window, cx);
12883            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
12884        });
12885    }
12886
12887    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
12888        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12889        let text_layout_details = &self.text_layout_details(window, cx);
12890        self.transact(window, cx, |this, window, cx| {
12891            let edits = this.change_selections(Default::default(), window, cx, |s| {
12892                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
12893                s.move_with(|display_map, selection| {
12894                    if !selection.is_empty() {
12895                        return;
12896                    }
12897
12898                    let mut head = selection.head();
12899                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
12900                    if head.column() == display_map.line_len(head.row()) {
12901                        transpose_offset = display_map
12902                            .buffer_snapshot()
12903                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
12904                    }
12905
12906                    if transpose_offset == MultiBufferOffset(0) {
12907                        return;
12908                    }
12909
12910                    *head.column_mut() += 1;
12911                    head = display_map.clip_point(head, Bias::Right);
12912                    let goal = SelectionGoal::HorizontalPosition(
12913                        display_map
12914                            .x_for_display_point(head, text_layout_details)
12915                            .into(),
12916                    );
12917                    selection.collapse_to(head, goal);
12918
12919                    let transpose_start = display_map
12920                        .buffer_snapshot()
12921                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
12922                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
12923                        let transpose_end = display_map
12924                            .buffer_snapshot()
12925                            .clip_offset(transpose_offset + 1usize, Bias::Right);
12926                        if let Some(ch) = display_map
12927                            .buffer_snapshot()
12928                            .chars_at(transpose_start)
12929                            .next()
12930                        {
12931                            edits.push((transpose_start..transpose_offset, String::new()));
12932                            edits.push((transpose_end..transpose_end, ch.to_string()));
12933                        }
12934                    }
12935                });
12936                edits
12937            });
12938            this.buffer
12939                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
12940            let selections = this
12941                .selections
12942                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
12943            this.change_selections(Default::default(), window, cx, |s| {
12944                s.select(selections);
12945            });
12946        });
12947    }
12948
12949    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
12950        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12951        if self.mode.is_single_line() {
12952            cx.propagate();
12953            return;
12954        }
12955
12956        self.rewrap_impl(RewrapOptions::default(), cx)
12957    }
12958
12959    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
12960        let buffer = self.buffer.read(cx).snapshot(cx);
12961        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
12962
12963        #[derive(Clone, Debug, PartialEq)]
12964        enum CommentFormat {
12965            /// single line comment, with prefix for line
12966            Line(String),
12967            /// single line within a block comment, with prefix for line
12968            BlockLine(String),
12969            /// a single line of a block comment that includes the initial delimiter
12970            BlockCommentWithStart(BlockCommentConfig),
12971            /// a single line of a block comment that includes the ending delimiter
12972            BlockCommentWithEnd(BlockCommentConfig),
12973        }
12974
12975        // Split selections to respect paragraph, indent, and comment prefix boundaries.
12976        let wrap_ranges = selections.into_iter().flat_map(|selection| {
12977            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
12978                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
12979                .peekable();
12980
12981            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
12982                row
12983            } else {
12984                return Vec::new();
12985            };
12986
12987            let language_settings = buffer.language_settings_at(selection.head(), cx);
12988            let language_scope = buffer.language_scope_at(selection.head());
12989
12990            let indent_and_prefix_for_row =
12991                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
12992                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
12993                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
12994                        &language_scope
12995                    {
12996                        let indent_end = Point::new(row, indent.len);
12997                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12998                        let line_text_after_indent = buffer
12999                            .text_for_range(indent_end..line_end)
13000                            .collect::<String>();
13001
13002                        let is_within_comment_override = buffer
13003                            .language_scope_at(indent_end)
13004                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13005                        let comment_delimiters = if is_within_comment_override {
13006                            // we are within a comment syntax node, but we don't
13007                            // yet know what kind of comment: block, doc or line
13008                            match (
13009                                language_scope.documentation_comment(),
13010                                language_scope.block_comment(),
13011                            ) {
13012                                (Some(config), _) | (_, Some(config))
13013                                    if buffer.contains_str_at(indent_end, &config.start) =>
13014                                {
13015                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13016                                }
13017                                (Some(config), _) | (_, Some(config))
13018                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13019                                {
13020                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13021                                }
13022                                (Some(config), _) | (_, Some(config))
13023                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13024                                {
13025                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13026                                }
13027                                (_, _) => language_scope
13028                                    .line_comment_prefixes()
13029                                    .iter()
13030                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13031                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13032                            }
13033                        } else {
13034                            // we not in an overridden comment node, but we may
13035                            // be within a non-overridden line comment node
13036                            language_scope
13037                                .line_comment_prefixes()
13038                                .iter()
13039                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13040                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13041                        };
13042
13043                        let rewrap_prefix = language_scope
13044                            .rewrap_prefixes()
13045                            .iter()
13046                            .find_map(|prefix_regex| {
13047                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13048                                    if mat.start() == 0 {
13049                                        Some(mat.as_str().to_string())
13050                                    } else {
13051                                        None
13052                                    }
13053                                })
13054                            })
13055                            .flatten();
13056                        (comment_delimiters, rewrap_prefix)
13057                    } else {
13058                        (None, None)
13059                    };
13060                    (indent, comment_prefix, rewrap_prefix)
13061                };
13062
13063            let mut ranges = Vec::new();
13064            let from_empty_selection = selection.is_empty();
13065
13066            let mut current_range_start = first_row;
13067            let mut prev_row = first_row;
13068            let (
13069                mut current_range_indent,
13070                mut current_range_comment_delimiters,
13071                mut current_range_rewrap_prefix,
13072            ) = indent_and_prefix_for_row(first_row);
13073
13074            for row in non_blank_rows_iter.skip(1) {
13075                let has_paragraph_break = row > prev_row + 1;
13076
13077                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13078                    indent_and_prefix_for_row(row);
13079
13080                let has_indent_change = row_indent != current_range_indent;
13081                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13082
13083                let has_boundary_change = has_comment_change
13084                    || row_rewrap_prefix.is_some()
13085                    || (has_indent_change && current_range_comment_delimiters.is_some());
13086
13087                if has_paragraph_break || has_boundary_change {
13088                    ranges.push((
13089                        language_settings.clone(),
13090                        Point::new(current_range_start, 0)
13091                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13092                        current_range_indent,
13093                        current_range_comment_delimiters.clone(),
13094                        current_range_rewrap_prefix.clone(),
13095                        from_empty_selection,
13096                    ));
13097                    current_range_start = row;
13098                    current_range_indent = row_indent;
13099                    current_range_comment_delimiters = row_comment_delimiters;
13100                    current_range_rewrap_prefix = row_rewrap_prefix;
13101                }
13102                prev_row = row;
13103            }
13104
13105            ranges.push((
13106                language_settings.clone(),
13107                Point::new(current_range_start, 0)
13108                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13109                current_range_indent,
13110                current_range_comment_delimiters,
13111                current_range_rewrap_prefix,
13112                from_empty_selection,
13113            ));
13114
13115            ranges
13116        });
13117
13118        let mut edits = Vec::new();
13119        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13120
13121        for (
13122            language_settings,
13123            wrap_range,
13124            mut indent_size,
13125            comment_prefix,
13126            rewrap_prefix,
13127            from_empty_selection,
13128        ) in wrap_ranges
13129        {
13130            let mut start_row = wrap_range.start.row;
13131            let mut end_row = wrap_range.end.row;
13132
13133            // Skip selections that overlap with a range that has already been rewrapped.
13134            let selection_range = start_row..end_row;
13135            if rewrapped_row_ranges
13136                .iter()
13137                .any(|range| range.overlaps(&selection_range))
13138            {
13139                continue;
13140            }
13141
13142            let tab_size = language_settings.tab_size;
13143
13144            let (line_prefix, inside_comment) = match &comment_prefix {
13145                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13146                    (Some(prefix.as_str()), true)
13147                }
13148                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13149                    (Some(prefix.as_ref()), true)
13150                }
13151                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13152                    start: _,
13153                    end: _,
13154                    prefix,
13155                    tab_size,
13156                })) => {
13157                    indent_size.len += tab_size;
13158                    (Some(prefix.as_ref()), true)
13159                }
13160                None => (None, false),
13161            };
13162            let indent_prefix = indent_size.chars().collect::<String>();
13163            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13164
13165            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13166                RewrapBehavior::InComments => inside_comment,
13167                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13168                RewrapBehavior::Anywhere => true,
13169            };
13170
13171            let should_rewrap = options.override_language_settings
13172                || allow_rewrap_based_on_language
13173                || self.hard_wrap.is_some();
13174            if !should_rewrap {
13175                continue;
13176            }
13177
13178            if from_empty_selection {
13179                'expand_upwards: while start_row > 0 {
13180                    let prev_row = start_row - 1;
13181                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13182                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13183                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13184                    {
13185                        start_row = prev_row;
13186                    } else {
13187                        break 'expand_upwards;
13188                    }
13189                }
13190
13191                'expand_downwards: while end_row < buffer.max_point().row {
13192                    let next_row = end_row + 1;
13193                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13194                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13195                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13196                    {
13197                        end_row = next_row;
13198                    } else {
13199                        break 'expand_downwards;
13200                    }
13201                }
13202            }
13203
13204            let start = Point::new(start_row, 0);
13205            let start_offset = ToOffset::to_offset(&start, &buffer);
13206            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13207            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13208            let mut first_line_delimiter = None;
13209            let mut last_line_delimiter = None;
13210            let Some(lines_without_prefixes) = selection_text
13211                .lines()
13212                .enumerate()
13213                .map(|(ix, line)| {
13214                    let line_trimmed = line.trim_start();
13215                    if rewrap_prefix.is_some() && ix > 0 {
13216                        Ok(line_trimmed)
13217                    } else if let Some(
13218                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13219                            start,
13220                            prefix,
13221                            end,
13222                            tab_size,
13223                        })
13224                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13225                            start,
13226                            prefix,
13227                            end,
13228                            tab_size,
13229                        }),
13230                    ) = &comment_prefix
13231                    {
13232                        let line_trimmed = line_trimmed
13233                            .strip_prefix(start.as_ref())
13234                            .map(|s| {
13235                                let mut indent_size = indent_size;
13236                                indent_size.len -= tab_size;
13237                                let indent_prefix: String = indent_size.chars().collect();
13238                                first_line_delimiter = Some((indent_prefix, start));
13239                                s.trim_start()
13240                            })
13241                            .unwrap_or(line_trimmed);
13242                        let line_trimmed = line_trimmed
13243                            .strip_suffix(end.as_ref())
13244                            .map(|s| {
13245                                last_line_delimiter = Some(end);
13246                                s.trim_end()
13247                            })
13248                            .unwrap_or(line_trimmed);
13249                        let line_trimmed = line_trimmed
13250                            .strip_prefix(prefix.as_ref())
13251                            .unwrap_or(line_trimmed);
13252                        Ok(line_trimmed)
13253                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13254                        line_trimmed.strip_prefix(prefix).with_context(|| {
13255                            format!("line did not start with prefix {prefix:?}: {line:?}")
13256                        })
13257                    } else {
13258                        line_trimmed
13259                            .strip_prefix(&line_prefix.trim_start())
13260                            .with_context(|| {
13261                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13262                            })
13263                    }
13264                })
13265                .collect::<Result<Vec<_>, _>>()
13266                .log_err()
13267            else {
13268                continue;
13269            };
13270
13271            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13272                buffer
13273                    .language_settings_at(Point::new(start_row, 0), cx)
13274                    .preferred_line_length as usize
13275            });
13276
13277            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13278                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13279            } else {
13280                line_prefix.clone()
13281            };
13282
13283            let wrapped_text = {
13284                let mut wrapped_text = wrap_with_prefix(
13285                    line_prefix,
13286                    subsequent_lines_prefix,
13287                    lines_without_prefixes.join("\n"),
13288                    wrap_column,
13289                    tab_size,
13290                    options.preserve_existing_whitespace,
13291                );
13292
13293                if let Some((indent, delimiter)) = first_line_delimiter {
13294                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13295                }
13296                if let Some(last_line) = last_line_delimiter {
13297                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13298                }
13299
13300                wrapped_text
13301            };
13302
13303            // TODO: should always use char-based diff while still supporting cursor behavior that
13304            // matches vim.
13305            let mut diff_options = DiffOptions::default();
13306            if options.override_language_settings {
13307                diff_options.max_word_diff_len = 0;
13308                diff_options.max_word_diff_line_count = 0;
13309            } else {
13310                diff_options.max_word_diff_len = usize::MAX;
13311                diff_options.max_word_diff_line_count = usize::MAX;
13312            }
13313
13314            for (old_range, new_text) in
13315                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13316            {
13317                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13318                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13319                edits.push((edit_start..edit_end, new_text));
13320            }
13321
13322            rewrapped_row_ranges.push(start_row..=end_row);
13323        }
13324
13325        self.buffer
13326            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13327    }
13328
13329    pub fn cut_common(
13330        &mut self,
13331        cut_no_selection_line: bool,
13332        window: &mut Window,
13333        cx: &mut Context<Self>,
13334    ) -> ClipboardItem {
13335        let mut text = String::new();
13336        let buffer = self.buffer.read(cx).snapshot(cx);
13337        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13338        let mut clipboard_selections = Vec::with_capacity(selections.len());
13339        {
13340            let max_point = buffer.max_point();
13341            let mut is_first = true;
13342            let mut prev_selection_was_entire_line = false;
13343            for selection in &mut selections {
13344                let is_entire_line =
13345                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13346                if is_entire_line {
13347                    selection.start = Point::new(selection.start.row, 0);
13348                    if !selection.is_empty() && selection.end.column == 0 {
13349                        selection.end = cmp::min(max_point, selection.end);
13350                    } else {
13351                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13352                    }
13353                    selection.goal = SelectionGoal::None;
13354                }
13355                if is_first {
13356                    is_first = false;
13357                } else if !prev_selection_was_entire_line {
13358                    text += "\n";
13359                }
13360                prev_selection_was_entire_line = is_entire_line;
13361                let mut len = 0;
13362                for chunk in buffer.text_for_range(selection.start..selection.end) {
13363                    text.push_str(chunk);
13364                    len += chunk.len();
13365                }
13366
13367                clipboard_selections.push(ClipboardSelection::for_buffer(
13368                    len,
13369                    is_entire_line,
13370                    selection.range(),
13371                    &buffer,
13372                    self.project.as_ref(),
13373                    cx,
13374                ));
13375            }
13376        }
13377
13378        self.transact(window, cx, |this, window, cx| {
13379            this.change_selections(Default::default(), window, cx, |s| {
13380                s.select(selections);
13381            });
13382            this.insert("", window, cx);
13383        });
13384        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13385    }
13386
13387    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13388        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13389        let item = self.cut_common(true, window, cx);
13390        cx.write_to_clipboard(item);
13391    }
13392
13393    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13394        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13395        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13396            s.move_with(|snapshot, sel| {
13397                if sel.is_empty() {
13398                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13399                }
13400                if sel.is_empty() {
13401                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13402                }
13403            });
13404        });
13405        let item = self.cut_common(false, window, cx);
13406        cx.set_global(KillRing(item))
13407    }
13408
13409    pub fn kill_ring_yank(
13410        &mut self,
13411        _: &KillRingYank,
13412        window: &mut Window,
13413        cx: &mut Context<Self>,
13414    ) {
13415        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13416        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13417            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13418                (kill_ring.text().to_string(), kill_ring.metadata_json())
13419            } else {
13420                return;
13421            }
13422        } else {
13423            return;
13424        };
13425        self.do_paste(&text, metadata, false, window, cx);
13426    }
13427
13428    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13429        self.do_copy(true, cx);
13430    }
13431
13432    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13433        self.do_copy(false, cx);
13434    }
13435
13436    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13437        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13438        let buffer = self.buffer.read(cx).read(cx);
13439        let mut text = String::new();
13440
13441        let mut clipboard_selections = Vec::with_capacity(selections.len());
13442        {
13443            let max_point = buffer.max_point();
13444            let mut is_first = true;
13445            let mut prev_selection_was_entire_line = false;
13446            for selection in &selections {
13447                let mut start = selection.start;
13448                let mut end = selection.end;
13449                let is_entire_line = selection.is_empty() || self.selections.line_mode();
13450                let mut add_trailing_newline = false;
13451                if is_entire_line {
13452                    start = Point::new(start.row, 0);
13453                    let next_line_start = Point::new(end.row + 1, 0);
13454                    if next_line_start <= max_point {
13455                        end = next_line_start;
13456                    } else {
13457                        // We're on the last line without a trailing newline.
13458                        // Copy to the end of the line and add a newline afterwards.
13459                        end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13460                        add_trailing_newline = true;
13461                    }
13462                }
13463
13464                let mut trimmed_selections = Vec::new();
13465                if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13466                    let row = MultiBufferRow(start.row);
13467                    let first_indent = buffer.indent_size_for_line(row);
13468                    if first_indent.len == 0 || start.column > first_indent.len {
13469                        trimmed_selections.push(start..end);
13470                    } else {
13471                        trimmed_selections.push(
13472                            Point::new(row.0, first_indent.len)
13473                                ..Point::new(row.0, buffer.line_len(row)),
13474                        );
13475                        for row in start.row + 1..=end.row {
13476                            let mut line_len = buffer.line_len(MultiBufferRow(row));
13477                            if row == end.row {
13478                                line_len = end.column;
13479                            }
13480                            if line_len == 0 {
13481                                trimmed_selections
13482                                    .push(Point::new(row, 0)..Point::new(row, line_len));
13483                                continue;
13484                            }
13485                            let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13486                            if row_indent_size.len >= first_indent.len {
13487                                trimmed_selections.push(
13488                                    Point::new(row, first_indent.len)..Point::new(row, line_len),
13489                                );
13490                            } else {
13491                                trimmed_selections.clear();
13492                                trimmed_selections.push(start..end);
13493                                break;
13494                            }
13495                        }
13496                    }
13497                } else {
13498                    trimmed_selections.push(start..end);
13499                }
13500
13501                let is_multiline_trim = trimmed_selections.len() > 1;
13502                for trimmed_range in trimmed_selections {
13503                    if is_first {
13504                        is_first = false;
13505                    } else if is_multiline_trim || !prev_selection_was_entire_line {
13506                        text += "\n";
13507                    }
13508                    prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13509                    let mut len = 0;
13510                    for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13511                        text.push_str(chunk);
13512                        len += chunk.len();
13513                    }
13514                    if add_trailing_newline {
13515                        text.push('\n');
13516                        len += 1;
13517                    }
13518                    clipboard_selections.push(ClipboardSelection::for_buffer(
13519                        len,
13520                        is_entire_line,
13521                        trimmed_range,
13522                        &buffer,
13523                        self.project.as_ref(),
13524                        cx,
13525                    ));
13526                }
13527            }
13528        }
13529
13530        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13531            text,
13532            clipboard_selections,
13533        ));
13534    }
13535
13536    pub fn do_paste(
13537        &mut self,
13538        text: &String,
13539        clipboard_selections: Option<Vec<ClipboardSelection>>,
13540        handle_entire_lines: bool,
13541        window: &mut Window,
13542        cx: &mut Context<Self>,
13543    ) {
13544        if self.read_only(cx) {
13545            return;
13546        }
13547
13548        let clipboard_text = Cow::Borrowed(text.as_str());
13549
13550        self.transact(window, cx, |this, window, cx| {
13551            let had_active_edit_prediction = this.has_active_edit_prediction();
13552            let display_map = this.display_snapshot(cx);
13553            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13554            let cursor_offset = this
13555                .selections
13556                .last::<MultiBufferOffset>(&display_map)
13557                .head();
13558
13559            if let Some(mut clipboard_selections) = clipboard_selections {
13560                let all_selections_were_entire_line =
13561                    clipboard_selections.iter().all(|s| s.is_entire_line);
13562                let first_selection_indent_column =
13563                    clipboard_selections.first().map(|s| s.first_line_indent);
13564                if clipboard_selections.len() != old_selections.len() {
13565                    clipboard_selections.drain(..);
13566                }
13567                let mut auto_indent_on_paste = true;
13568
13569                this.buffer.update(cx, |buffer, cx| {
13570                    let snapshot = buffer.read(cx);
13571                    auto_indent_on_paste = snapshot
13572                        .language_settings_at(cursor_offset, cx)
13573                        .auto_indent_on_paste;
13574
13575                    let mut start_offset = 0;
13576                    let mut edits = Vec::new();
13577                    let mut original_indent_columns = Vec::new();
13578                    for (ix, selection) in old_selections.iter().enumerate() {
13579                        let to_insert;
13580                        let entire_line;
13581                        let original_indent_column;
13582                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13583                            let end_offset = start_offset + clipboard_selection.len;
13584                            to_insert = &clipboard_text[start_offset..end_offset];
13585                            entire_line = clipboard_selection.is_entire_line;
13586                            start_offset = if entire_line {
13587                                end_offset
13588                            } else {
13589                                end_offset + 1
13590                            };
13591                            original_indent_column = Some(clipboard_selection.first_line_indent);
13592                        } else {
13593                            to_insert = &*clipboard_text;
13594                            entire_line = all_selections_were_entire_line;
13595                            original_indent_column = first_selection_indent_column
13596                        }
13597
13598                        let (range, to_insert) =
13599                            if selection.is_empty() && handle_entire_lines && entire_line {
13600                                // If the corresponding selection was empty when this slice of the
13601                                // clipboard text was written, then the entire line containing the
13602                                // selection was copied. If this selection is also currently empty,
13603                                // then paste the line before the current line of the buffer.
13604                                let column = selection.start.to_point(&snapshot).column as usize;
13605                                let line_start = selection.start - column;
13606                                (line_start..line_start, Cow::Borrowed(to_insert))
13607                            } else {
13608                                let language = snapshot.language_at(selection.head());
13609                                let range = selection.range();
13610                                if let Some(language) = language
13611                                    && language.name() == "Markdown".into()
13612                                {
13613                                    edit_for_markdown_paste(
13614                                        &snapshot,
13615                                        range,
13616                                        to_insert,
13617                                        url::Url::parse(to_insert).ok(),
13618                                    )
13619                                } else {
13620                                    (range, Cow::Borrowed(to_insert))
13621                                }
13622                            };
13623
13624                        edits.push((range, to_insert));
13625                        original_indent_columns.push(original_indent_column);
13626                    }
13627                    drop(snapshot);
13628
13629                    buffer.edit(
13630                        edits,
13631                        if auto_indent_on_paste {
13632                            Some(AutoindentMode::Block {
13633                                original_indent_columns,
13634                            })
13635                        } else {
13636                            None
13637                        },
13638                        cx,
13639                    );
13640                });
13641
13642                let selections = this
13643                    .selections
13644                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13645                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13646            } else {
13647                let url = url::Url::parse(&clipboard_text).ok();
13648
13649                let auto_indent_mode = if !clipboard_text.is_empty() {
13650                    Some(AutoindentMode::Block {
13651                        original_indent_columns: Vec::new(),
13652                    })
13653                } else {
13654                    None
13655                };
13656
13657                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13658                    let snapshot = buffer.snapshot(cx);
13659
13660                    let anchors = old_selections
13661                        .iter()
13662                        .map(|s| {
13663                            let anchor = snapshot.anchor_after(s.head());
13664                            s.map(|_| anchor)
13665                        })
13666                        .collect::<Vec<_>>();
13667
13668                    let mut edits = Vec::new();
13669
13670                    for selection in old_selections.iter() {
13671                        let language = snapshot.language_at(selection.head());
13672                        let range = selection.range();
13673
13674                        let (edit_range, edit_text) = if let Some(language) = language
13675                            && language.name() == "Markdown".into()
13676                        {
13677                            edit_for_markdown_paste(&snapshot, range, &clipboard_text, url.clone())
13678                        } else {
13679                            (range, clipboard_text.clone())
13680                        };
13681
13682                        edits.push((edit_range, edit_text));
13683                    }
13684
13685                    drop(snapshot);
13686                    buffer.edit(edits, auto_indent_mode, cx);
13687
13688                    anchors
13689                });
13690
13691                this.change_selections(Default::default(), window, cx, |s| {
13692                    s.select_anchors(selection_anchors);
13693                });
13694            }
13695
13696            //   🤔                 |    ..     | show_in_menu |
13697            // | ..                  |   true        true
13698            // | had_edit_prediction |   false       true
13699
13700            let trigger_in_words =
13701                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13702
13703            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13704        });
13705    }
13706
13707    pub fn diff_clipboard_with_selection(
13708        &mut self,
13709        _: &DiffClipboardWithSelection,
13710        window: &mut Window,
13711        cx: &mut Context<Self>,
13712    ) {
13713        let selections = self
13714            .selections
13715            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13716
13717        if selections.is_empty() {
13718            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13719            return;
13720        };
13721
13722        let clipboard_text = match cx.read_from_clipboard() {
13723            Some(item) => match item.entries().first() {
13724                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13725                _ => None,
13726            },
13727            None => None,
13728        };
13729
13730        let Some(clipboard_text) = clipboard_text else {
13731            log::warn!("Clipboard doesn't contain text.");
13732            return;
13733        };
13734
13735        window.dispatch_action(
13736            Box::new(DiffClipboardWithSelectionData {
13737                clipboard_text,
13738                editor: cx.entity(),
13739            }),
13740            cx,
13741        );
13742    }
13743
13744    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13745        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13746        if let Some(item) = cx.read_from_clipboard() {
13747            let entries = item.entries();
13748
13749            match entries.first() {
13750                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13751                // of all the pasted entries.
13752                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13753                    .do_paste(
13754                        clipboard_string.text(),
13755                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13756                        true,
13757                        window,
13758                        cx,
13759                    ),
13760                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
13761            }
13762        }
13763    }
13764
13765    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
13766        if self.read_only(cx) {
13767            return;
13768        }
13769
13770        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13771
13772        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
13773            if let Some((selections, _)) =
13774                self.selection_history.transaction(transaction_id).cloned()
13775            {
13776                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13777                    s.select_anchors(selections.to_vec());
13778                });
13779            } else {
13780                log::error!(
13781                    "No entry in selection_history found for undo. \
13782                     This may correspond to a bug where undo does not update the selection. \
13783                     If this is occurring, please add details to \
13784                     https://github.com/zed-industries/zed/issues/22692"
13785                );
13786            }
13787            self.request_autoscroll(Autoscroll::fit(), cx);
13788            self.unmark_text(window, cx);
13789            self.refresh_edit_prediction(true, false, window, cx);
13790            cx.emit(EditorEvent::Edited { transaction_id });
13791            cx.emit(EditorEvent::TransactionUndone { transaction_id });
13792        }
13793    }
13794
13795    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
13796        if self.read_only(cx) {
13797            return;
13798        }
13799
13800        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13801
13802        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
13803            if let Some((_, Some(selections))) =
13804                self.selection_history.transaction(transaction_id).cloned()
13805            {
13806                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13807                    s.select_anchors(selections.to_vec());
13808                });
13809            } else {
13810                log::error!(
13811                    "No entry in selection_history found for redo. \
13812                     This may correspond to a bug where undo does not update the selection. \
13813                     If this is occurring, please add details to \
13814                     https://github.com/zed-industries/zed/issues/22692"
13815                );
13816            }
13817            self.request_autoscroll(Autoscroll::fit(), cx);
13818            self.unmark_text(window, cx);
13819            self.refresh_edit_prediction(true, false, window, cx);
13820            cx.emit(EditorEvent::Edited { transaction_id });
13821        }
13822    }
13823
13824    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
13825        self.buffer
13826            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
13827    }
13828
13829    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
13830        self.buffer
13831            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
13832    }
13833
13834    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
13835        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13836        self.change_selections(Default::default(), window, cx, |s| {
13837            s.move_with(|map, selection| {
13838                let cursor = if selection.is_empty() {
13839                    movement::left(map, selection.start)
13840                } else {
13841                    selection.start
13842                };
13843                selection.collapse_to(cursor, SelectionGoal::None);
13844            });
13845        })
13846    }
13847
13848    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
13849        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13850        self.change_selections(Default::default(), window, cx, |s| {
13851            s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
13852        })
13853    }
13854
13855    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
13856        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13857        self.change_selections(Default::default(), window, cx, |s| {
13858            s.move_with(|map, selection| {
13859                let cursor = if selection.is_empty() {
13860                    movement::right(map, selection.end)
13861                } else {
13862                    selection.end
13863                };
13864                selection.collapse_to(cursor, SelectionGoal::None)
13865            });
13866        })
13867    }
13868
13869    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
13870        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13871        self.change_selections(Default::default(), window, cx, |s| {
13872            s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
13873        });
13874    }
13875
13876    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
13877        if self.take_rename(true, window, cx).is_some() {
13878            return;
13879        }
13880
13881        if self.mode.is_single_line() {
13882            cx.propagate();
13883            return;
13884        }
13885
13886        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13887
13888        let text_layout_details = &self.text_layout_details(window, cx);
13889        let selection_count = self.selections.count();
13890        let first_selection = self.selections.first_anchor();
13891
13892        self.change_selections(Default::default(), window, cx, |s| {
13893            s.move_with(|map, selection| {
13894                if !selection.is_empty() {
13895                    selection.goal = SelectionGoal::None;
13896                }
13897                let (cursor, goal) = movement::up(
13898                    map,
13899                    selection.start,
13900                    selection.goal,
13901                    false,
13902                    text_layout_details,
13903                );
13904                selection.collapse_to(cursor, goal);
13905            });
13906        });
13907
13908        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
13909        {
13910            cx.propagate();
13911        }
13912    }
13913
13914    pub fn move_up_by_lines(
13915        &mut self,
13916        action: &MoveUpByLines,
13917        window: &mut Window,
13918        cx: &mut Context<Self>,
13919    ) {
13920        if self.take_rename(true, window, cx).is_some() {
13921            return;
13922        }
13923
13924        if self.mode.is_single_line() {
13925            cx.propagate();
13926            return;
13927        }
13928
13929        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13930
13931        let text_layout_details = &self.text_layout_details(window, cx);
13932
13933        self.change_selections(Default::default(), window, cx, |s| {
13934            s.move_with(|map, selection| {
13935                if !selection.is_empty() {
13936                    selection.goal = SelectionGoal::None;
13937                }
13938                let (cursor, goal) = movement::up_by_rows(
13939                    map,
13940                    selection.start,
13941                    action.lines,
13942                    selection.goal,
13943                    false,
13944                    text_layout_details,
13945                );
13946                selection.collapse_to(cursor, goal);
13947            });
13948        })
13949    }
13950
13951    pub fn move_down_by_lines(
13952        &mut self,
13953        action: &MoveDownByLines,
13954        window: &mut Window,
13955        cx: &mut Context<Self>,
13956    ) {
13957        if self.take_rename(true, window, cx).is_some() {
13958            return;
13959        }
13960
13961        if self.mode.is_single_line() {
13962            cx.propagate();
13963            return;
13964        }
13965
13966        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13967
13968        let text_layout_details = &self.text_layout_details(window, cx);
13969
13970        self.change_selections(Default::default(), window, cx, |s| {
13971            s.move_with(|map, selection| {
13972                if !selection.is_empty() {
13973                    selection.goal = SelectionGoal::None;
13974                }
13975                let (cursor, goal) = movement::down_by_rows(
13976                    map,
13977                    selection.start,
13978                    action.lines,
13979                    selection.goal,
13980                    false,
13981                    text_layout_details,
13982                );
13983                selection.collapse_to(cursor, goal);
13984            });
13985        })
13986    }
13987
13988    pub fn select_down_by_lines(
13989        &mut self,
13990        action: &SelectDownByLines,
13991        window: &mut Window,
13992        cx: &mut Context<Self>,
13993    ) {
13994        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13995        let text_layout_details = &self.text_layout_details(window, cx);
13996        self.change_selections(Default::default(), window, cx, |s| {
13997            s.move_heads_with(|map, head, goal| {
13998                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
13999            })
14000        })
14001    }
14002
14003    pub fn select_up_by_lines(
14004        &mut self,
14005        action: &SelectUpByLines,
14006        window: &mut Window,
14007        cx: &mut Context<Self>,
14008    ) {
14009        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14010        let text_layout_details = &self.text_layout_details(window, cx);
14011        self.change_selections(Default::default(), window, cx, |s| {
14012            s.move_heads_with(|map, head, goal| {
14013                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14014            })
14015        })
14016    }
14017
14018    pub fn select_page_up(
14019        &mut self,
14020        _: &SelectPageUp,
14021        window: &mut Window,
14022        cx: &mut Context<Self>,
14023    ) {
14024        let Some(row_count) = self.visible_row_count() else {
14025            return;
14026        };
14027
14028        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14029
14030        let text_layout_details = &self.text_layout_details(window, cx);
14031
14032        self.change_selections(Default::default(), window, cx, |s| {
14033            s.move_heads_with(|map, head, goal| {
14034                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14035            })
14036        })
14037    }
14038
14039    pub fn move_page_up(
14040        &mut self,
14041        action: &MovePageUp,
14042        window: &mut Window,
14043        cx: &mut Context<Self>,
14044    ) {
14045        if self.take_rename(true, window, cx).is_some() {
14046            return;
14047        }
14048
14049        if self
14050            .context_menu
14051            .borrow_mut()
14052            .as_mut()
14053            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14054            .unwrap_or(false)
14055        {
14056            return;
14057        }
14058
14059        if matches!(self.mode, EditorMode::SingleLine) {
14060            cx.propagate();
14061            return;
14062        }
14063
14064        let Some(row_count) = self.visible_row_count() else {
14065            return;
14066        };
14067
14068        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14069
14070        let effects = if action.center_cursor {
14071            SelectionEffects::scroll(Autoscroll::center())
14072        } else {
14073            SelectionEffects::default()
14074        };
14075
14076        let text_layout_details = &self.text_layout_details(window, cx);
14077
14078        self.change_selections(effects, window, cx, |s| {
14079            s.move_with(|map, selection| {
14080                if !selection.is_empty() {
14081                    selection.goal = SelectionGoal::None;
14082                }
14083                let (cursor, goal) = movement::up_by_rows(
14084                    map,
14085                    selection.end,
14086                    row_count,
14087                    selection.goal,
14088                    false,
14089                    text_layout_details,
14090                );
14091                selection.collapse_to(cursor, goal);
14092            });
14093        });
14094    }
14095
14096    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14097        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14098        let text_layout_details = &self.text_layout_details(window, cx);
14099        self.change_selections(Default::default(), window, cx, |s| {
14100            s.move_heads_with(|map, head, goal| {
14101                movement::up(map, head, goal, false, text_layout_details)
14102            })
14103        })
14104    }
14105
14106    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14107        self.take_rename(true, window, cx);
14108
14109        if self.mode.is_single_line() {
14110            cx.propagate();
14111            return;
14112        }
14113
14114        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14115
14116        let text_layout_details = &self.text_layout_details(window, cx);
14117        let selection_count = self.selections.count();
14118        let first_selection = self.selections.first_anchor();
14119
14120        self.change_selections(Default::default(), window, cx, |s| {
14121            s.move_with(|map, selection| {
14122                if !selection.is_empty() {
14123                    selection.goal = SelectionGoal::None;
14124                }
14125                let (cursor, goal) = movement::down(
14126                    map,
14127                    selection.end,
14128                    selection.goal,
14129                    false,
14130                    text_layout_details,
14131                );
14132                selection.collapse_to(cursor, goal);
14133            });
14134        });
14135
14136        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14137        {
14138            cx.propagate();
14139        }
14140    }
14141
14142    pub fn select_page_down(
14143        &mut self,
14144        _: &SelectPageDown,
14145        window: &mut Window,
14146        cx: &mut Context<Self>,
14147    ) {
14148        let Some(row_count) = self.visible_row_count() else {
14149            return;
14150        };
14151
14152        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14153
14154        let text_layout_details = &self.text_layout_details(window, cx);
14155
14156        self.change_selections(Default::default(), window, cx, |s| {
14157            s.move_heads_with(|map, head, goal| {
14158                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14159            })
14160        })
14161    }
14162
14163    pub fn move_page_down(
14164        &mut self,
14165        action: &MovePageDown,
14166        window: &mut Window,
14167        cx: &mut Context<Self>,
14168    ) {
14169        if self.take_rename(true, window, cx).is_some() {
14170            return;
14171        }
14172
14173        if self
14174            .context_menu
14175            .borrow_mut()
14176            .as_mut()
14177            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14178            .unwrap_or(false)
14179        {
14180            return;
14181        }
14182
14183        if matches!(self.mode, EditorMode::SingleLine) {
14184            cx.propagate();
14185            return;
14186        }
14187
14188        let Some(row_count) = self.visible_row_count() else {
14189            return;
14190        };
14191
14192        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14193
14194        let effects = if action.center_cursor {
14195            SelectionEffects::scroll(Autoscroll::center())
14196        } else {
14197            SelectionEffects::default()
14198        };
14199
14200        let text_layout_details = &self.text_layout_details(window, cx);
14201        self.change_selections(effects, window, cx, |s| {
14202            s.move_with(|map, selection| {
14203                if !selection.is_empty() {
14204                    selection.goal = SelectionGoal::None;
14205                }
14206                let (cursor, goal) = movement::down_by_rows(
14207                    map,
14208                    selection.end,
14209                    row_count,
14210                    selection.goal,
14211                    false,
14212                    text_layout_details,
14213                );
14214                selection.collapse_to(cursor, goal);
14215            });
14216        });
14217    }
14218
14219    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14220        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14221        let text_layout_details = &self.text_layout_details(window, cx);
14222        self.change_selections(Default::default(), window, cx, |s| {
14223            s.move_heads_with(|map, head, goal| {
14224                movement::down(map, head, goal, false, text_layout_details)
14225            })
14226        });
14227    }
14228
14229    pub fn context_menu_first(
14230        &mut self,
14231        _: &ContextMenuFirst,
14232        window: &mut Window,
14233        cx: &mut Context<Self>,
14234    ) {
14235        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14236            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14237        }
14238    }
14239
14240    pub fn context_menu_prev(
14241        &mut self,
14242        _: &ContextMenuPrevious,
14243        window: &mut Window,
14244        cx: &mut Context<Self>,
14245    ) {
14246        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14247            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14248        }
14249    }
14250
14251    pub fn context_menu_next(
14252        &mut self,
14253        _: &ContextMenuNext,
14254        window: &mut Window,
14255        cx: &mut Context<Self>,
14256    ) {
14257        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14258            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14259        }
14260    }
14261
14262    pub fn context_menu_last(
14263        &mut self,
14264        _: &ContextMenuLast,
14265        window: &mut Window,
14266        cx: &mut Context<Self>,
14267    ) {
14268        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14269            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14270        }
14271    }
14272
14273    pub fn signature_help_prev(
14274        &mut self,
14275        _: &SignatureHelpPrevious,
14276        _: &mut Window,
14277        cx: &mut Context<Self>,
14278    ) {
14279        if let Some(popover) = self.signature_help_state.popover_mut() {
14280            if popover.current_signature == 0 {
14281                popover.current_signature = popover.signatures.len() - 1;
14282            } else {
14283                popover.current_signature -= 1;
14284            }
14285            cx.notify();
14286        }
14287    }
14288
14289    pub fn signature_help_next(
14290        &mut self,
14291        _: &SignatureHelpNext,
14292        _: &mut Window,
14293        cx: &mut Context<Self>,
14294    ) {
14295        if let Some(popover) = self.signature_help_state.popover_mut() {
14296            if popover.current_signature + 1 == popover.signatures.len() {
14297                popover.current_signature = 0;
14298            } else {
14299                popover.current_signature += 1;
14300            }
14301            cx.notify();
14302        }
14303    }
14304
14305    pub fn move_to_previous_word_start(
14306        &mut self,
14307        _: &MoveToPreviousWordStart,
14308        window: &mut Window,
14309        cx: &mut Context<Self>,
14310    ) {
14311        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14312        self.change_selections(Default::default(), window, cx, |s| {
14313            s.move_cursors_with(|map, head, _| {
14314                (
14315                    movement::previous_word_start(map, head),
14316                    SelectionGoal::None,
14317                )
14318            });
14319        })
14320    }
14321
14322    pub fn move_to_previous_subword_start(
14323        &mut self,
14324        _: &MoveToPreviousSubwordStart,
14325        window: &mut Window,
14326        cx: &mut Context<Self>,
14327    ) {
14328        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14329        self.change_selections(Default::default(), window, cx, |s| {
14330            s.move_cursors_with(|map, head, _| {
14331                (
14332                    movement::previous_subword_start(map, head),
14333                    SelectionGoal::None,
14334                )
14335            });
14336        })
14337    }
14338
14339    pub fn select_to_previous_word_start(
14340        &mut self,
14341        _: &SelectToPreviousWordStart,
14342        window: &mut Window,
14343        cx: &mut Context<Self>,
14344    ) {
14345        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14346        self.change_selections(Default::default(), window, cx, |s| {
14347            s.move_heads_with(|map, head, _| {
14348                (
14349                    movement::previous_word_start(map, head),
14350                    SelectionGoal::None,
14351                )
14352            });
14353        })
14354    }
14355
14356    pub fn select_to_previous_subword_start(
14357        &mut self,
14358        _: &SelectToPreviousSubwordStart,
14359        window: &mut Window,
14360        cx: &mut Context<Self>,
14361    ) {
14362        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14363        self.change_selections(Default::default(), window, cx, |s| {
14364            s.move_heads_with(|map, head, _| {
14365                (
14366                    movement::previous_subword_start(map, head),
14367                    SelectionGoal::None,
14368                )
14369            });
14370        })
14371    }
14372
14373    pub fn delete_to_previous_word_start(
14374        &mut self,
14375        action: &DeleteToPreviousWordStart,
14376        window: &mut Window,
14377        cx: &mut Context<Self>,
14378    ) {
14379        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14380        self.transact(window, cx, |this, window, cx| {
14381            this.select_autoclose_pair(window, cx);
14382            this.change_selections(Default::default(), window, cx, |s| {
14383                s.move_with(|map, selection| {
14384                    if selection.is_empty() {
14385                        let mut cursor = if action.ignore_newlines {
14386                            movement::previous_word_start(map, selection.head())
14387                        } else {
14388                            movement::previous_word_start_or_newline(map, selection.head())
14389                        };
14390                        cursor = movement::adjust_greedy_deletion(
14391                            map,
14392                            selection.head(),
14393                            cursor,
14394                            action.ignore_brackets,
14395                        );
14396                        selection.set_head(cursor, SelectionGoal::None);
14397                    }
14398                });
14399            });
14400            this.insert("", window, cx);
14401        });
14402    }
14403
14404    pub fn delete_to_previous_subword_start(
14405        &mut self,
14406        action: &DeleteToPreviousSubwordStart,
14407        window: &mut Window,
14408        cx: &mut Context<Self>,
14409    ) {
14410        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14411        self.transact(window, cx, |this, window, cx| {
14412            this.select_autoclose_pair(window, cx);
14413            this.change_selections(Default::default(), window, cx, |s| {
14414                s.move_with(|map, selection| {
14415                    if selection.is_empty() {
14416                        let mut cursor = if action.ignore_newlines {
14417                            movement::previous_subword_start(map, selection.head())
14418                        } else {
14419                            movement::previous_subword_start_or_newline(map, selection.head())
14420                        };
14421                        cursor = movement::adjust_greedy_deletion(
14422                            map,
14423                            selection.head(),
14424                            cursor,
14425                            action.ignore_brackets,
14426                        );
14427                        selection.set_head(cursor, SelectionGoal::None);
14428                    }
14429                });
14430            });
14431            this.insert("", window, cx);
14432        });
14433    }
14434
14435    pub fn move_to_next_word_end(
14436        &mut self,
14437        _: &MoveToNextWordEnd,
14438        window: &mut Window,
14439        cx: &mut Context<Self>,
14440    ) {
14441        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14442        self.change_selections(Default::default(), window, cx, |s| {
14443            s.move_cursors_with(|map, head, _| {
14444                (movement::next_word_end(map, head), SelectionGoal::None)
14445            });
14446        })
14447    }
14448
14449    pub fn move_to_next_subword_end(
14450        &mut self,
14451        _: &MoveToNextSubwordEnd,
14452        window: &mut Window,
14453        cx: &mut Context<Self>,
14454    ) {
14455        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14456        self.change_selections(Default::default(), window, cx, |s| {
14457            s.move_cursors_with(|map, head, _| {
14458                (movement::next_subword_end(map, head), SelectionGoal::None)
14459            });
14460        })
14461    }
14462
14463    pub fn select_to_next_word_end(
14464        &mut self,
14465        _: &SelectToNextWordEnd,
14466        window: &mut Window,
14467        cx: &mut Context<Self>,
14468    ) {
14469        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14470        self.change_selections(Default::default(), window, cx, |s| {
14471            s.move_heads_with(|map, head, _| {
14472                (movement::next_word_end(map, head), SelectionGoal::None)
14473            });
14474        })
14475    }
14476
14477    pub fn select_to_next_subword_end(
14478        &mut self,
14479        _: &SelectToNextSubwordEnd,
14480        window: &mut Window,
14481        cx: &mut Context<Self>,
14482    ) {
14483        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14484        self.change_selections(Default::default(), window, cx, |s| {
14485            s.move_heads_with(|map, head, _| {
14486                (movement::next_subword_end(map, head), SelectionGoal::None)
14487            });
14488        })
14489    }
14490
14491    pub fn delete_to_next_word_end(
14492        &mut self,
14493        action: &DeleteToNextWordEnd,
14494        window: &mut Window,
14495        cx: &mut Context<Self>,
14496    ) {
14497        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14498        self.transact(window, cx, |this, window, cx| {
14499            this.change_selections(Default::default(), window, cx, |s| {
14500                s.move_with(|map, selection| {
14501                    if selection.is_empty() {
14502                        let mut cursor = if action.ignore_newlines {
14503                            movement::next_word_end(map, selection.head())
14504                        } else {
14505                            movement::next_word_end_or_newline(map, selection.head())
14506                        };
14507                        cursor = movement::adjust_greedy_deletion(
14508                            map,
14509                            selection.head(),
14510                            cursor,
14511                            action.ignore_brackets,
14512                        );
14513                        selection.set_head(cursor, SelectionGoal::None);
14514                    }
14515                });
14516            });
14517            this.insert("", window, cx);
14518        });
14519    }
14520
14521    pub fn delete_to_next_subword_end(
14522        &mut self,
14523        action: &DeleteToNextSubwordEnd,
14524        window: &mut Window,
14525        cx: &mut Context<Self>,
14526    ) {
14527        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14528        self.transact(window, cx, |this, window, cx| {
14529            this.change_selections(Default::default(), window, cx, |s| {
14530                s.move_with(|map, selection| {
14531                    if selection.is_empty() {
14532                        let mut cursor = if action.ignore_newlines {
14533                            movement::next_subword_end(map, selection.head())
14534                        } else {
14535                            movement::next_subword_end_or_newline(map, selection.head())
14536                        };
14537                        cursor = movement::adjust_greedy_deletion(
14538                            map,
14539                            selection.head(),
14540                            cursor,
14541                            action.ignore_brackets,
14542                        );
14543                        selection.set_head(cursor, SelectionGoal::None);
14544                    }
14545                });
14546            });
14547            this.insert("", window, cx);
14548        });
14549    }
14550
14551    pub fn move_to_beginning_of_line(
14552        &mut self,
14553        action: &MoveToBeginningOfLine,
14554        window: &mut Window,
14555        cx: &mut Context<Self>,
14556    ) {
14557        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14558        self.change_selections(Default::default(), window, cx, |s| {
14559            s.move_cursors_with(|map, head, _| {
14560                (
14561                    movement::indented_line_beginning(
14562                        map,
14563                        head,
14564                        action.stop_at_soft_wraps,
14565                        action.stop_at_indent,
14566                    ),
14567                    SelectionGoal::None,
14568                )
14569            });
14570        })
14571    }
14572
14573    pub fn select_to_beginning_of_line(
14574        &mut self,
14575        action: &SelectToBeginningOfLine,
14576        window: &mut Window,
14577        cx: &mut Context<Self>,
14578    ) {
14579        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14580        self.change_selections(Default::default(), window, cx, |s| {
14581            s.move_heads_with(|map, head, _| {
14582                (
14583                    movement::indented_line_beginning(
14584                        map,
14585                        head,
14586                        action.stop_at_soft_wraps,
14587                        action.stop_at_indent,
14588                    ),
14589                    SelectionGoal::None,
14590                )
14591            });
14592        });
14593    }
14594
14595    pub fn delete_to_beginning_of_line(
14596        &mut self,
14597        action: &DeleteToBeginningOfLine,
14598        window: &mut Window,
14599        cx: &mut Context<Self>,
14600    ) {
14601        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14602        self.transact(window, cx, |this, window, cx| {
14603            this.change_selections(Default::default(), window, cx, |s| {
14604                s.move_with(|_, selection| {
14605                    selection.reversed = true;
14606                });
14607            });
14608
14609            this.select_to_beginning_of_line(
14610                &SelectToBeginningOfLine {
14611                    stop_at_soft_wraps: false,
14612                    stop_at_indent: action.stop_at_indent,
14613                },
14614                window,
14615                cx,
14616            );
14617            this.backspace(&Backspace, window, cx);
14618        });
14619    }
14620
14621    pub fn move_to_end_of_line(
14622        &mut self,
14623        action: &MoveToEndOfLine,
14624        window: &mut Window,
14625        cx: &mut Context<Self>,
14626    ) {
14627        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14628        self.change_selections(Default::default(), window, cx, |s| {
14629            s.move_cursors_with(|map, head, _| {
14630                (
14631                    movement::line_end(map, head, action.stop_at_soft_wraps),
14632                    SelectionGoal::None,
14633                )
14634            });
14635        })
14636    }
14637
14638    pub fn select_to_end_of_line(
14639        &mut self,
14640        action: &SelectToEndOfLine,
14641        window: &mut Window,
14642        cx: &mut Context<Self>,
14643    ) {
14644        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14645        self.change_selections(Default::default(), window, cx, |s| {
14646            s.move_heads_with(|map, head, _| {
14647                (
14648                    movement::line_end(map, head, action.stop_at_soft_wraps),
14649                    SelectionGoal::None,
14650                )
14651            });
14652        })
14653    }
14654
14655    pub fn delete_to_end_of_line(
14656        &mut self,
14657        _: &DeleteToEndOfLine,
14658        window: &mut Window,
14659        cx: &mut Context<Self>,
14660    ) {
14661        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14662        self.transact(window, cx, |this, window, cx| {
14663            this.select_to_end_of_line(
14664                &SelectToEndOfLine {
14665                    stop_at_soft_wraps: false,
14666                },
14667                window,
14668                cx,
14669            );
14670            this.delete(&Delete, window, cx);
14671        });
14672    }
14673
14674    pub fn cut_to_end_of_line(
14675        &mut self,
14676        action: &CutToEndOfLine,
14677        window: &mut Window,
14678        cx: &mut Context<Self>,
14679    ) {
14680        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14681        self.transact(window, cx, |this, window, cx| {
14682            this.select_to_end_of_line(
14683                &SelectToEndOfLine {
14684                    stop_at_soft_wraps: false,
14685                },
14686                window,
14687                cx,
14688            );
14689            if !action.stop_at_newlines {
14690                this.change_selections(Default::default(), window, cx, |s| {
14691                    s.move_with(|_, sel| {
14692                        if sel.is_empty() {
14693                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14694                        }
14695                    });
14696                });
14697            }
14698            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14699            let item = this.cut_common(false, window, cx);
14700            cx.write_to_clipboard(item);
14701        });
14702    }
14703
14704    pub fn move_to_start_of_paragraph(
14705        &mut self,
14706        _: &MoveToStartOfParagraph,
14707        window: &mut Window,
14708        cx: &mut Context<Self>,
14709    ) {
14710        if matches!(self.mode, EditorMode::SingleLine) {
14711            cx.propagate();
14712            return;
14713        }
14714        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14715        self.change_selections(Default::default(), window, cx, |s| {
14716            s.move_with(|map, selection| {
14717                selection.collapse_to(
14718                    movement::start_of_paragraph(map, selection.head(), 1),
14719                    SelectionGoal::None,
14720                )
14721            });
14722        })
14723    }
14724
14725    pub fn move_to_end_of_paragraph(
14726        &mut self,
14727        _: &MoveToEndOfParagraph,
14728        window: &mut Window,
14729        cx: &mut Context<Self>,
14730    ) {
14731        if matches!(self.mode, EditorMode::SingleLine) {
14732            cx.propagate();
14733            return;
14734        }
14735        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14736        self.change_selections(Default::default(), window, cx, |s| {
14737            s.move_with(|map, selection| {
14738                selection.collapse_to(
14739                    movement::end_of_paragraph(map, selection.head(), 1),
14740                    SelectionGoal::None,
14741                )
14742            });
14743        })
14744    }
14745
14746    pub fn select_to_start_of_paragraph(
14747        &mut self,
14748        _: &SelectToStartOfParagraph,
14749        window: &mut Window,
14750        cx: &mut Context<Self>,
14751    ) {
14752        if matches!(self.mode, EditorMode::SingleLine) {
14753            cx.propagate();
14754            return;
14755        }
14756        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14757        self.change_selections(Default::default(), window, cx, |s| {
14758            s.move_heads_with(|map, head, _| {
14759                (
14760                    movement::start_of_paragraph(map, head, 1),
14761                    SelectionGoal::None,
14762                )
14763            });
14764        })
14765    }
14766
14767    pub fn select_to_end_of_paragraph(
14768        &mut self,
14769        _: &SelectToEndOfParagraph,
14770        window: &mut Window,
14771        cx: &mut Context<Self>,
14772    ) {
14773        if matches!(self.mode, EditorMode::SingleLine) {
14774            cx.propagate();
14775            return;
14776        }
14777        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14778        self.change_selections(Default::default(), window, cx, |s| {
14779            s.move_heads_with(|map, head, _| {
14780                (
14781                    movement::end_of_paragraph(map, head, 1),
14782                    SelectionGoal::None,
14783                )
14784            });
14785        })
14786    }
14787
14788    pub fn move_to_start_of_excerpt(
14789        &mut self,
14790        _: &MoveToStartOfExcerpt,
14791        window: &mut Window,
14792        cx: &mut Context<Self>,
14793    ) {
14794        if matches!(self.mode, EditorMode::SingleLine) {
14795            cx.propagate();
14796            return;
14797        }
14798        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14799        self.change_selections(Default::default(), window, cx, |s| {
14800            s.move_with(|map, selection| {
14801                selection.collapse_to(
14802                    movement::start_of_excerpt(
14803                        map,
14804                        selection.head(),
14805                        workspace::searchable::Direction::Prev,
14806                    ),
14807                    SelectionGoal::None,
14808                )
14809            });
14810        })
14811    }
14812
14813    pub fn move_to_start_of_next_excerpt(
14814        &mut self,
14815        _: &MoveToStartOfNextExcerpt,
14816        window: &mut Window,
14817        cx: &mut Context<Self>,
14818    ) {
14819        if matches!(self.mode, EditorMode::SingleLine) {
14820            cx.propagate();
14821            return;
14822        }
14823
14824        self.change_selections(Default::default(), window, cx, |s| {
14825            s.move_with(|map, selection| {
14826                selection.collapse_to(
14827                    movement::start_of_excerpt(
14828                        map,
14829                        selection.head(),
14830                        workspace::searchable::Direction::Next,
14831                    ),
14832                    SelectionGoal::None,
14833                )
14834            });
14835        })
14836    }
14837
14838    pub fn move_to_end_of_excerpt(
14839        &mut self,
14840        _: &MoveToEndOfExcerpt,
14841        window: &mut Window,
14842        cx: &mut Context<Self>,
14843    ) {
14844        if matches!(self.mode, EditorMode::SingleLine) {
14845            cx.propagate();
14846            return;
14847        }
14848        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14849        self.change_selections(Default::default(), window, cx, |s| {
14850            s.move_with(|map, selection| {
14851                selection.collapse_to(
14852                    movement::end_of_excerpt(
14853                        map,
14854                        selection.head(),
14855                        workspace::searchable::Direction::Next,
14856                    ),
14857                    SelectionGoal::None,
14858                )
14859            });
14860        })
14861    }
14862
14863    pub fn move_to_end_of_previous_excerpt(
14864        &mut self,
14865        _: &MoveToEndOfPreviousExcerpt,
14866        window: &mut Window,
14867        cx: &mut Context<Self>,
14868    ) {
14869        if matches!(self.mode, EditorMode::SingleLine) {
14870            cx.propagate();
14871            return;
14872        }
14873        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14874        self.change_selections(Default::default(), window, cx, |s| {
14875            s.move_with(|map, selection| {
14876                selection.collapse_to(
14877                    movement::end_of_excerpt(
14878                        map,
14879                        selection.head(),
14880                        workspace::searchable::Direction::Prev,
14881                    ),
14882                    SelectionGoal::None,
14883                )
14884            });
14885        })
14886    }
14887
14888    pub fn select_to_start_of_excerpt(
14889        &mut self,
14890        _: &SelectToStartOfExcerpt,
14891        window: &mut Window,
14892        cx: &mut Context<Self>,
14893    ) {
14894        if matches!(self.mode, EditorMode::SingleLine) {
14895            cx.propagate();
14896            return;
14897        }
14898        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14899        self.change_selections(Default::default(), window, cx, |s| {
14900            s.move_heads_with(|map, head, _| {
14901                (
14902                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
14903                    SelectionGoal::None,
14904                )
14905            });
14906        })
14907    }
14908
14909    pub fn select_to_start_of_next_excerpt(
14910        &mut self,
14911        _: &SelectToStartOfNextExcerpt,
14912        window: &mut Window,
14913        cx: &mut Context<Self>,
14914    ) {
14915        if matches!(self.mode, EditorMode::SingleLine) {
14916            cx.propagate();
14917            return;
14918        }
14919        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14920        self.change_selections(Default::default(), window, cx, |s| {
14921            s.move_heads_with(|map, head, _| {
14922                (
14923                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
14924                    SelectionGoal::None,
14925                )
14926            });
14927        })
14928    }
14929
14930    pub fn select_to_end_of_excerpt(
14931        &mut self,
14932        _: &SelectToEndOfExcerpt,
14933        window: &mut Window,
14934        cx: &mut Context<Self>,
14935    ) {
14936        if matches!(self.mode, EditorMode::SingleLine) {
14937            cx.propagate();
14938            return;
14939        }
14940        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14941        self.change_selections(Default::default(), window, cx, |s| {
14942            s.move_heads_with(|map, head, _| {
14943                (
14944                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
14945                    SelectionGoal::None,
14946                )
14947            });
14948        })
14949    }
14950
14951    pub fn select_to_end_of_previous_excerpt(
14952        &mut self,
14953        _: &SelectToEndOfPreviousExcerpt,
14954        window: &mut Window,
14955        cx: &mut Context<Self>,
14956    ) {
14957        if matches!(self.mode, EditorMode::SingleLine) {
14958            cx.propagate();
14959            return;
14960        }
14961        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14962        self.change_selections(Default::default(), window, cx, |s| {
14963            s.move_heads_with(|map, head, _| {
14964                (
14965                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
14966                    SelectionGoal::None,
14967                )
14968            });
14969        })
14970    }
14971
14972    pub fn move_to_beginning(
14973        &mut self,
14974        _: &MoveToBeginning,
14975        window: &mut Window,
14976        cx: &mut Context<Self>,
14977    ) {
14978        if matches!(self.mode, EditorMode::SingleLine) {
14979            cx.propagate();
14980            return;
14981        }
14982        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14983        self.change_selections(Default::default(), window, cx, |s| {
14984            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
14985        });
14986    }
14987
14988    pub fn select_to_beginning(
14989        &mut self,
14990        _: &SelectToBeginning,
14991        window: &mut Window,
14992        cx: &mut Context<Self>,
14993    ) {
14994        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
14995        selection.set_head(Point::zero(), SelectionGoal::None);
14996        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14997        self.change_selections(Default::default(), window, cx, |s| {
14998            s.select(vec![selection]);
14999        });
15000    }
15001
15002    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15003        if matches!(self.mode, EditorMode::SingleLine) {
15004            cx.propagate();
15005            return;
15006        }
15007        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15008        let cursor = self.buffer.read(cx).read(cx).len();
15009        self.change_selections(Default::default(), window, cx, |s| {
15010            s.select_ranges(vec![cursor..cursor])
15011        });
15012    }
15013
15014    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15015        self.nav_history = nav_history;
15016    }
15017
15018    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15019        self.nav_history.as_ref()
15020    }
15021
15022    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15023        self.push_to_nav_history(
15024            self.selections.newest_anchor().head(),
15025            None,
15026            false,
15027            true,
15028            cx,
15029        );
15030    }
15031
15032    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15033        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15034        let buffer = self.buffer.read(cx).read(cx);
15035        let cursor_position = cursor_anchor.to_point(&buffer);
15036        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15037        let scroll_top_row = scroll_anchor.top_row(&buffer);
15038        drop(buffer);
15039
15040        NavigationData {
15041            cursor_anchor,
15042            cursor_position,
15043            scroll_anchor,
15044            scroll_top_row,
15045        }
15046    }
15047
15048    fn navigation_entry(
15049        &self,
15050        cursor_anchor: Anchor,
15051        cx: &mut Context<Self>,
15052    ) -> Option<NavigationEntry> {
15053        let Some(history) = self.nav_history.clone() else {
15054            return None;
15055        };
15056        let data = self.navigation_data(cursor_anchor, cx);
15057        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15058    }
15059
15060    fn push_to_nav_history(
15061        &mut self,
15062        cursor_anchor: Anchor,
15063        new_position: Option<Point>,
15064        is_deactivate: bool,
15065        always: bool,
15066        cx: &mut Context<Self>,
15067    ) {
15068        let data = self.navigation_data(cursor_anchor, cx);
15069        if let Some(nav_history) = self.nav_history.as_mut() {
15070            if let Some(new_position) = new_position {
15071                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15072                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15073                    return;
15074                }
15075            }
15076
15077            nav_history.push(Some(data), cx);
15078            cx.emit(EditorEvent::PushedToNavHistory {
15079                anchor: cursor_anchor,
15080                is_deactivate,
15081            })
15082        }
15083    }
15084
15085    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15086        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15087        let buffer = self.buffer.read(cx).snapshot(cx);
15088        let mut selection = self
15089            .selections
15090            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15091        selection.set_head(buffer.len(), SelectionGoal::None);
15092        self.change_selections(Default::default(), window, cx, |s| {
15093            s.select(vec![selection]);
15094        });
15095    }
15096
15097    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15098        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15099        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15100            s.select_ranges(vec![Anchor::min()..Anchor::max()]);
15101        });
15102    }
15103
15104    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15105        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15106        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15107        let mut selections = self.selections.all::<Point>(&display_map);
15108        let max_point = display_map.buffer_snapshot().max_point();
15109        for selection in &mut selections {
15110            let rows = selection.spanned_rows(true, &display_map);
15111            selection.start = Point::new(rows.start.0, 0);
15112            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15113            selection.reversed = false;
15114        }
15115        self.change_selections(Default::default(), window, cx, |s| {
15116            s.select(selections);
15117        });
15118    }
15119
15120    pub fn split_selection_into_lines(
15121        &mut self,
15122        action: &SplitSelectionIntoLines,
15123        window: &mut Window,
15124        cx: &mut Context<Self>,
15125    ) {
15126        let selections = self
15127            .selections
15128            .all::<Point>(&self.display_snapshot(cx))
15129            .into_iter()
15130            .map(|selection| selection.start..selection.end)
15131            .collect::<Vec<_>>();
15132        self.unfold_ranges(&selections, true, true, cx);
15133
15134        let mut new_selection_ranges = Vec::new();
15135        {
15136            let buffer = self.buffer.read(cx).read(cx);
15137            for selection in selections {
15138                for row in selection.start.row..selection.end.row {
15139                    let line_start = Point::new(row, 0);
15140                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15141
15142                    if action.keep_selections {
15143                        // Keep the selection range for each line
15144                        let selection_start = if row == selection.start.row {
15145                            selection.start
15146                        } else {
15147                            line_start
15148                        };
15149                        new_selection_ranges.push(selection_start..line_end);
15150                    } else {
15151                        // Collapse to cursor at end of line
15152                        new_selection_ranges.push(line_end..line_end);
15153                    }
15154                }
15155
15156                let is_multiline_selection = selection.start.row != selection.end.row;
15157                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15158                // so this action feels more ergonomic when paired with other selection operations
15159                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15160                if !should_skip_last {
15161                    if action.keep_selections {
15162                        if is_multiline_selection {
15163                            let line_start = Point::new(selection.end.row, 0);
15164                            new_selection_ranges.push(line_start..selection.end);
15165                        } else {
15166                            new_selection_ranges.push(selection.start..selection.end);
15167                        }
15168                    } else {
15169                        new_selection_ranges.push(selection.end..selection.end);
15170                    }
15171                }
15172            }
15173        }
15174        self.change_selections(Default::default(), window, cx, |s| {
15175            s.select_ranges(new_selection_ranges);
15176        });
15177    }
15178
15179    pub fn add_selection_above(
15180        &mut self,
15181        action: &AddSelectionAbove,
15182        window: &mut Window,
15183        cx: &mut Context<Self>,
15184    ) {
15185        self.add_selection(true, action.skip_soft_wrap, window, cx);
15186    }
15187
15188    pub fn add_selection_below(
15189        &mut self,
15190        action: &AddSelectionBelow,
15191        window: &mut Window,
15192        cx: &mut Context<Self>,
15193    ) {
15194        self.add_selection(false, action.skip_soft_wrap, window, cx);
15195    }
15196
15197    fn add_selection(
15198        &mut self,
15199        above: bool,
15200        skip_soft_wrap: bool,
15201        window: &mut Window,
15202        cx: &mut Context<Self>,
15203    ) {
15204        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15205
15206        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15207        let all_selections = self.selections.all::<Point>(&display_map);
15208        let text_layout_details = self.text_layout_details(window, cx);
15209
15210        let (mut columnar_selections, new_selections_to_columnarize) = {
15211            if let Some(state) = self.add_selections_state.as_ref() {
15212                let columnar_selection_ids: HashSet<_> = state
15213                    .groups
15214                    .iter()
15215                    .flat_map(|group| group.stack.iter())
15216                    .copied()
15217                    .collect();
15218
15219                all_selections
15220                    .into_iter()
15221                    .partition(|s| columnar_selection_ids.contains(&s.id))
15222            } else {
15223                (Vec::new(), all_selections)
15224            }
15225        };
15226
15227        let mut state = self
15228            .add_selections_state
15229            .take()
15230            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15231
15232        for selection in new_selections_to_columnarize {
15233            let range = selection.display_range(&display_map).sorted();
15234            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15235            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15236            let positions = start_x.min(end_x)..start_x.max(end_x);
15237            let mut stack = Vec::new();
15238            for row in range.start.row().0..=range.end.row().0 {
15239                if let Some(selection) = self.selections.build_columnar_selection(
15240                    &display_map,
15241                    DisplayRow(row),
15242                    &positions,
15243                    selection.reversed,
15244                    &text_layout_details,
15245                ) {
15246                    stack.push(selection.id);
15247                    columnar_selections.push(selection);
15248                }
15249            }
15250            if !stack.is_empty() {
15251                if above {
15252                    stack.reverse();
15253                }
15254                state.groups.push(AddSelectionsGroup { above, stack });
15255            }
15256        }
15257
15258        let mut final_selections = Vec::new();
15259        let end_row = if above {
15260            DisplayRow(0)
15261        } else {
15262            display_map.max_point().row()
15263        };
15264
15265        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15266        // positions to place new selections, so we need to keep track of the
15267        // column range of the oldest selection in each group, because
15268        // intermediate selections may have been clamped to shorter lines.
15269        // selections may have been clamped to shorter lines.
15270        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15271            let mut map = HashMap::default();
15272            for group in state.groups.iter() {
15273                if let Some(oldest_id) = group.stack.first() {
15274                    if let Some(oldest_selection) =
15275                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15276                    {
15277                        let start_col = oldest_selection.start.column;
15278                        let end_col = oldest_selection.end.column;
15279                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15280                        for id in &group.stack {
15281                            map.insert(*id, goal_columns.clone());
15282                        }
15283                    }
15284                }
15285            }
15286            map
15287        } else {
15288            HashMap::default()
15289        };
15290
15291        let mut last_added_item_per_group = HashMap::default();
15292        for group in state.groups.iter_mut() {
15293            if let Some(last_id) = group.stack.last() {
15294                last_added_item_per_group.insert(*last_id, group);
15295            }
15296        }
15297
15298        for selection in columnar_selections {
15299            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15300                if above == group.above {
15301                    let range = selection.display_range(&display_map).sorted();
15302                    debug_assert_eq!(range.start.row(), range.end.row());
15303                    let row = range.start.row();
15304                    let positions =
15305                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15306                            Pixels::from(start)..Pixels::from(end)
15307                        } else {
15308                            let start_x =
15309                                display_map.x_for_display_point(range.start, &text_layout_details);
15310                            let end_x =
15311                                display_map.x_for_display_point(range.end, &text_layout_details);
15312                            start_x.min(end_x)..start_x.max(end_x)
15313                        };
15314
15315                    let maybe_new_selection = if skip_soft_wrap {
15316                        let goal_columns = goal_columns_by_selection_id
15317                            .remove(&selection.id)
15318                            .unwrap_or_else(|| {
15319                                let start_col = selection.start.column;
15320                                let end_col = selection.end.column;
15321                                start_col.min(end_col)..start_col.max(end_col)
15322                            });
15323                        self.selections.find_next_columnar_selection_by_buffer_row(
15324                            &display_map,
15325                            row,
15326                            end_row,
15327                            above,
15328                            &goal_columns,
15329                            selection.reversed,
15330                            &text_layout_details,
15331                        )
15332                    } else {
15333                        self.selections.find_next_columnar_selection_by_display_row(
15334                            &display_map,
15335                            row,
15336                            end_row,
15337                            above,
15338                            &positions,
15339                            selection.reversed,
15340                            &text_layout_details,
15341                        )
15342                    };
15343
15344                    if let Some(new_selection) = maybe_new_selection {
15345                        group.stack.push(new_selection.id);
15346                        if above {
15347                            final_selections.push(new_selection);
15348                            final_selections.push(selection);
15349                        } else {
15350                            final_selections.push(selection);
15351                            final_selections.push(new_selection);
15352                        }
15353                    } else {
15354                        final_selections.push(selection);
15355                    }
15356                } else {
15357                    group.stack.pop();
15358                }
15359            } else {
15360                final_selections.push(selection);
15361            }
15362        }
15363
15364        self.change_selections(Default::default(), window, cx, |s| {
15365            s.select(final_selections);
15366        });
15367
15368        let final_selection_ids: HashSet<_> = self
15369            .selections
15370            .all::<Point>(&display_map)
15371            .iter()
15372            .map(|s| s.id)
15373            .collect();
15374        state.groups.retain_mut(|group| {
15375            // selections might get merged above so we remove invalid items from stacks
15376            group.stack.retain(|id| final_selection_ids.contains(id));
15377
15378            // single selection in stack can be treated as initial state
15379            group.stack.len() > 1
15380        });
15381
15382        if !state.groups.is_empty() {
15383            self.add_selections_state = Some(state);
15384        }
15385    }
15386
15387    pub fn insert_snippet_at_selections(
15388        &mut self,
15389        action: &InsertSnippet,
15390        window: &mut Window,
15391        cx: &mut Context<Self>,
15392    ) {
15393        self.try_insert_snippet_at_selections(action, window, cx)
15394            .log_err();
15395    }
15396
15397    fn try_insert_snippet_at_selections(
15398        &mut self,
15399        action: &InsertSnippet,
15400        window: &mut Window,
15401        cx: &mut Context<Self>,
15402    ) -> Result<()> {
15403        let insertion_ranges = self
15404            .selections
15405            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15406            .into_iter()
15407            .map(|selection| selection.range())
15408            .collect_vec();
15409
15410        let snippet = if let Some(snippet_body) = &action.snippet {
15411            if action.language.is_none() && action.name.is_none() {
15412                Snippet::parse(snippet_body)?
15413            } else {
15414                bail!("`snippet` is mutually exclusive with `language` and `name`")
15415            }
15416        } else if let Some(name) = &action.name {
15417            let project = self.project().context("no project")?;
15418            let snippet_store = project.read(cx).snippets().read(cx);
15419            let snippet = snippet_store
15420                .snippets_for(action.language.clone(), cx)
15421                .into_iter()
15422                .find(|snippet| snippet.name == *name)
15423                .context("snippet not found")?;
15424            Snippet::parse(&snippet.body)?
15425        } else {
15426            // todo(andrew): open modal to select snippet
15427            bail!("`name` or `snippet` is required")
15428        };
15429
15430        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15431    }
15432
15433    fn select_match_ranges(
15434        &mut self,
15435        range: Range<MultiBufferOffset>,
15436        reversed: bool,
15437        replace_newest: bool,
15438        auto_scroll: Option<Autoscroll>,
15439        window: &mut Window,
15440        cx: &mut Context<Editor>,
15441    ) {
15442        self.unfold_ranges(
15443            std::slice::from_ref(&range),
15444            false,
15445            auto_scroll.is_some(),
15446            cx,
15447        );
15448        let effects = if let Some(scroll) = auto_scroll {
15449            SelectionEffects::scroll(scroll)
15450        } else {
15451            SelectionEffects::no_scroll()
15452        };
15453        self.change_selections(effects, window, cx, |s| {
15454            if replace_newest {
15455                s.delete(s.newest_anchor().id);
15456            }
15457            if reversed {
15458                s.insert_range(range.end..range.start);
15459            } else {
15460                s.insert_range(range);
15461            }
15462        });
15463    }
15464
15465    pub fn select_next_match_internal(
15466        &mut self,
15467        display_map: &DisplaySnapshot,
15468        replace_newest: bool,
15469        autoscroll: Option<Autoscroll>,
15470        window: &mut Window,
15471        cx: &mut Context<Self>,
15472    ) -> Result<()> {
15473        let buffer = display_map.buffer_snapshot();
15474        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15475        if let Some(mut select_next_state) = self.select_next_state.take() {
15476            let query = &select_next_state.query;
15477            if !select_next_state.done {
15478                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15479                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15480                let mut next_selected_range = None;
15481
15482                let bytes_after_last_selection =
15483                    buffer.bytes_in_range(last_selection.end..buffer.len());
15484                let bytes_before_first_selection =
15485                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15486                let query_matches = query
15487                    .stream_find_iter(bytes_after_last_selection)
15488                    .map(|result| (last_selection.end, result))
15489                    .chain(
15490                        query
15491                            .stream_find_iter(bytes_before_first_selection)
15492                            .map(|result| (MultiBufferOffset(0), result)),
15493                    );
15494
15495                for (start_offset, query_match) in query_matches {
15496                    let query_match = query_match.unwrap(); // can only fail due to I/O
15497                    let offset_range =
15498                        start_offset + query_match.start()..start_offset + query_match.end();
15499
15500                    if !select_next_state.wordwise
15501                        || (!buffer.is_inside_word(offset_range.start, None)
15502                            && !buffer.is_inside_word(offset_range.end, None))
15503                    {
15504                        let idx = selections
15505                            .partition_point(|selection| selection.end <= offset_range.start);
15506                        let overlaps = selections
15507                            .get(idx)
15508                            .map_or(false, |selection| selection.start < offset_range.end);
15509
15510                        if !overlaps {
15511                            next_selected_range = Some(offset_range);
15512                            break;
15513                        }
15514                    }
15515                }
15516
15517                if let Some(next_selected_range) = next_selected_range {
15518                    self.select_match_ranges(
15519                        next_selected_range,
15520                        last_selection.reversed,
15521                        replace_newest,
15522                        autoscroll,
15523                        window,
15524                        cx,
15525                    );
15526                } else {
15527                    select_next_state.done = true;
15528                }
15529            }
15530
15531            self.select_next_state = Some(select_next_state);
15532        } else {
15533            let mut only_carets = true;
15534            let mut same_text_selected = true;
15535            let mut selected_text = None;
15536
15537            let mut selections_iter = selections.iter().peekable();
15538            while let Some(selection) = selections_iter.next() {
15539                if selection.start != selection.end {
15540                    only_carets = false;
15541                }
15542
15543                if same_text_selected {
15544                    if selected_text.is_none() {
15545                        selected_text =
15546                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15547                    }
15548
15549                    if let Some(next_selection) = selections_iter.peek() {
15550                        if next_selection.len() == selection.len() {
15551                            let next_selected_text = buffer
15552                                .text_for_range(next_selection.range())
15553                                .collect::<String>();
15554                            if Some(next_selected_text) != selected_text {
15555                                same_text_selected = false;
15556                                selected_text = None;
15557                            }
15558                        } else {
15559                            same_text_selected = false;
15560                            selected_text = None;
15561                        }
15562                    }
15563                }
15564            }
15565
15566            if only_carets {
15567                for selection in &mut selections {
15568                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15569                    selection.start = word_range.start;
15570                    selection.end = word_range.end;
15571                    selection.goal = SelectionGoal::None;
15572                    selection.reversed = false;
15573                    self.select_match_ranges(
15574                        selection.start..selection.end,
15575                        selection.reversed,
15576                        replace_newest,
15577                        autoscroll,
15578                        window,
15579                        cx,
15580                    );
15581                }
15582
15583                if selections.len() == 1 {
15584                    let selection = selections
15585                        .last()
15586                        .expect("ensured that there's only one selection");
15587                    let query = buffer
15588                        .text_for_range(selection.start..selection.end)
15589                        .collect::<String>();
15590                    let is_empty = query.is_empty();
15591                    let select_state = SelectNextState {
15592                        query: self.build_query(&[query], cx)?,
15593                        wordwise: true,
15594                        done: is_empty,
15595                    };
15596                    self.select_next_state = Some(select_state);
15597                } else {
15598                    self.select_next_state = None;
15599                }
15600            } else if let Some(selected_text) = selected_text {
15601                self.select_next_state = Some(SelectNextState {
15602                    query: self.build_query(&[selected_text], cx)?,
15603                    wordwise: false,
15604                    done: false,
15605                });
15606                self.select_next_match_internal(
15607                    display_map,
15608                    replace_newest,
15609                    autoscroll,
15610                    window,
15611                    cx,
15612                )?;
15613            }
15614        }
15615        Ok(())
15616    }
15617
15618    pub fn select_all_matches(
15619        &mut self,
15620        _action: &SelectAllMatches,
15621        window: &mut Window,
15622        cx: &mut Context<Self>,
15623    ) -> Result<()> {
15624        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15625
15626        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15627
15628        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15629        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15630        else {
15631            return Ok(());
15632        };
15633
15634        let mut new_selections = Vec::new();
15635
15636        let reversed = self
15637            .selections
15638            .oldest::<MultiBufferOffset>(&display_map)
15639            .reversed;
15640        let buffer = display_map.buffer_snapshot();
15641        let query_matches = select_next_state
15642            .query
15643            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15644
15645        for query_match in query_matches.into_iter() {
15646            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15647            let offset_range = if reversed {
15648                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15649            } else {
15650                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15651            };
15652
15653            if !select_next_state.wordwise
15654                || (!buffer.is_inside_word(offset_range.start, None)
15655                    && !buffer.is_inside_word(offset_range.end, None))
15656            {
15657                new_selections.push(offset_range.start..offset_range.end);
15658            }
15659        }
15660
15661        select_next_state.done = true;
15662
15663        if new_selections.is_empty() {
15664            log::error!("bug: new_selections is empty in select_all_matches");
15665            return Ok(());
15666        }
15667
15668        self.unfold_ranges(&new_selections, false, false, cx);
15669        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15670            selections.select_ranges(new_selections)
15671        });
15672
15673        Ok(())
15674    }
15675
15676    pub fn select_next(
15677        &mut self,
15678        action: &SelectNext,
15679        window: &mut Window,
15680        cx: &mut Context<Self>,
15681    ) -> Result<()> {
15682        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15683        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15684        self.select_next_match_internal(
15685            &display_map,
15686            action.replace_newest,
15687            Some(Autoscroll::newest()),
15688            window,
15689            cx,
15690        )
15691    }
15692
15693    pub fn select_previous(
15694        &mut self,
15695        action: &SelectPrevious,
15696        window: &mut Window,
15697        cx: &mut Context<Self>,
15698    ) -> Result<()> {
15699        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15700        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15701        let buffer = display_map.buffer_snapshot();
15702        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15703        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15704            let query = &select_prev_state.query;
15705            if !select_prev_state.done {
15706                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15707                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15708                let mut next_selected_range = None;
15709                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15710                let bytes_before_last_selection =
15711                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15712                let bytes_after_first_selection =
15713                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15714                let query_matches = query
15715                    .stream_find_iter(bytes_before_last_selection)
15716                    .map(|result| (last_selection.start, result))
15717                    .chain(
15718                        query
15719                            .stream_find_iter(bytes_after_first_selection)
15720                            .map(|result| (buffer.len(), result)),
15721                    );
15722                for (end_offset, query_match) in query_matches {
15723                    let query_match = query_match.unwrap(); // can only fail due to I/O
15724                    let offset_range =
15725                        end_offset - query_match.end()..end_offset - query_match.start();
15726
15727                    if !select_prev_state.wordwise
15728                        || (!buffer.is_inside_word(offset_range.start, None)
15729                            && !buffer.is_inside_word(offset_range.end, None))
15730                    {
15731                        next_selected_range = Some(offset_range);
15732                        break;
15733                    }
15734                }
15735
15736                if let Some(next_selected_range) = next_selected_range {
15737                    self.select_match_ranges(
15738                        next_selected_range,
15739                        last_selection.reversed,
15740                        action.replace_newest,
15741                        Some(Autoscroll::newest()),
15742                        window,
15743                        cx,
15744                    );
15745                } else {
15746                    select_prev_state.done = true;
15747                }
15748            }
15749
15750            self.select_prev_state = Some(select_prev_state);
15751        } else {
15752            let mut only_carets = true;
15753            let mut same_text_selected = true;
15754            let mut selected_text = None;
15755
15756            let mut selections_iter = selections.iter().peekable();
15757            while let Some(selection) = selections_iter.next() {
15758                if selection.start != selection.end {
15759                    only_carets = false;
15760                }
15761
15762                if same_text_selected {
15763                    if selected_text.is_none() {
15764                        selected_text =
15765                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15766                    }
15767
15768                    if let Some(next_selection) = selections_iter.peek() {
15769                        if next_selection.len() == selection.len() {
15770                            let next_selected_text = buffer
15771                                .text_for_range(next_selection.range())
15772                                .collect::<String>();
15773                            if Some(next_selected_text) != selected_text {
15774                                same_text_selected = false;
15775                                selected_text = None;
15776                            }
15777                        } else {
15778                            same_text_selected = false;
15779                            selected_text = None;
15780                        }
15781                    }
15782                }
15783            }
15784
15785            if only_carets {
15786                for selection in &mut selections {
15787                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15788                    selection.start = word_range.start;
15789                    selection.end = word_range.end;
15790                    selection.goal = SelectionGoal::None;
15791                    selection.reversed = false;
15792                    self.select_match_ranges(
15793                        selection.start..selection.end,
15794                        selection.reversed,
15795                        action.replace_newest,
15796                        Some(Autoscroll::newest()),
15797                        window,
15798                        cx,
15799                    );
15800                }
15801                if selections.len() == 1 {
15802                    let selection = selections
15803                        .last()
15804                        .expect("ensured that there's only one selection");
15805                    let query = buffer
15806                        .text_for_range(selection.start..selection.end)
15807                        .collect::<String>();
15808                    let is_empty = query.is_empty();
15809                    let select_state = SelectNextState {
15810                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
15811                        wordwise: true,
15812                        done: is_empty,
15813                    };
15814                    self.select_prev_state = Some(select_state);
15815                } else {
15816                    self.select_prev_state = None;
15817                }
15818            } else if let Some(selected_text) = selected_text {
15819                self.select_prev_state = Some(SelectNextState {
15820                    query: self
15821                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
15822                    wordwise: false,
15823                    done: false,
15824                });
15825                self.select_previous(action, window, cx)?;
15826            }
15827        }
15828        Ok(())
15829    }
15830
15831    /// Builds an `AhoCorasick` automaton from the provided patterns, while
15832    /// setting the case sensitivity based on the global
15833    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
15834    /// editor's settings.
15835    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
15836    where
15837        I: IntoIterator<Item = P>,
15838        P: AsRef<[u8]>,
15839    {
15840        let case_sensitive = self
15841            .select_next_is_case_sensitive
15842            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
15843
15844        let mut builder = AhoCorasickBuilder::new();
15845        builder.ascii_case_insensitive(!case_sensitive);
15846        builder.build(patterns)
15847    }
15848
15849    pub fn find_next_match(
15850        &mut self,
15851        _: &FindNextMatch,
15852        window: &mut Window,
15853        cx: &mut Context<Self>,
15854    ) -> Result<()> {
15855        let selections = self.selections.disjoint_anchors_arc();
15856        match selections.first() {
15857            Some(first) if selections.len() >= 2 => {
15858                self.change_selections(Default::default(), window, cx, |s| {
15859                    s.select_ranges([first.range()]);
15860                });
15861            }
15862            _ => self.select_next(
15863                &SelectNext {
15864                    replace_newest: true,
15865                },
15866                window,
15867                cx,
15868            )?,
15869        }
15870        Ok(())
15871    }
15872
15873    pub fn find_previous_match(
15874        &mut self,
15875        _: &FindPreviousMatch,
15876        window: &mut Window,
15877        cx: &mut Context<Self>,
15878    ) -> Result<()> {
15879        let selections = self.selections.disjoint_anchors_arc();
15880        match selections.last() {
15881            Some(last) if selections.len() >= 2 => {
15882                self.change_selections(Default::default(), window, cx, |s| {
15883                    s.select_ranges([last.range()]);
15884                });
15885            }
15886            _ => self.select_previous(
15887                &SelectPrevious {
15888                    replace_newest: true,
15889                },
15890                window,
15891                cx,
15892            )?,
15893        }
15894        Ok(())
15895    }
15896
15897    pub fn toggle_comments(
15898        &mut self,
15899        action: &ToggleComments,
15900        window: &mut Window,
15901        cx: &mut Context<Self>,
15902    ) {
15903        if self.read_only(cx) {
15904            return;
15905        }
15906        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15907        let text_layout_details = &self.text_layout_details(window, cx);
15908        self.transact(window, cx, |this, window, cx| {
15909            let mut selections = this
15910                .selections
15911                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
15912            let mut edits = Vec::new();
15913            let mut selection_edit_ranges = Vec::new();
15914            let mut last_toggled_row = None;
15915            let snapshot = this.buffer.read(cx).read(cx);
15916            let empty_str: Arc<str> = Arc::default();
15917            let mut suffixes_inserted = Vec::new();
15918            let ignore_indent = action.ignore_indent;
15919
15920            fn comment_prefix_range(
15921                snapshot: &MultiBufferSnapshot,
15922                row: MultiBufferRow,
15923                comment_prefix: &str,
15924                comment_prefix_whitespace: &str,
15925                ignore_indent: bool,
15926            ) -> Range<Point> {
15927                let indent_size = if ignore_indent {
15928                    0
15929                } else {
15930                    snapshot.indent_size_for_line(row).len
15931                };
15932
15933                let start = Point::new(row.0, indent_size);
15934
15935                let mut line_bytes = snapshot
15936                    .bytes_in_range(start..snapshot.max_point())
15937                    .flatten()
15938                    .copied();
15939
15940                // If this line currently begins with the line comment prefix, then record
15941                // the range containing the prefix.
15942                if line_bytes
15943                    .by_ref()
15944                    .take(comment_prefix.len())
15945                    .eq(comment_prefix.bytes())
15946                {
15947                    // Include any whitespace that matches the comment prefix.
15948                    let matching_whitespace_len = line_bytes
15949                        .zip(comment_prefix_whitespace.bytes())
15950                        .take_while(|(a, b)| a == b)
15951                        .count() as u32;
15952                    let end = Point::new(
15953                        start.row,
15954                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
15955                    );
15956                    start..end
15957                } else {
15958                    start..start
15959                }
15960            }
15961
15962            fn comment_suffix_range(
15963                snapshot: &MultiBufferSnapshot,
15964                row: MultiBufferRow,
15965                comment_suffix: &str,
15966                comment_suffix_has_leading_space: bool,
15967            ) -> Range<Point> {
15968                let end = Point::new(row.0, snapshot.line_len(row));
15969                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
15970
15971                let mut line_end_bytes = snapshot
15972                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
15973                    .flatten()
15974                    .copied();
15975
15976                let leading_space_len = if suffix_start_column > 0
15977                    && line_end_bytes.next() == Some(b' ')
15978                    && comment_suffix_has_leading_space
15979                {
15980                    1
15981                } else {
15982                    0
15983                };
15984
15985                // If this line currently begins with the line comment prefix, then record
15986                // the range containing the prefix.
15987                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
15988                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
15989                    start..end
15990                } else {
15991                    end..end
15992                }
15993            }
15994
15995            // TODO: Handle selections that cross excerpts
15996            for selection in &mut selections {
15997                let start_column = snapshot
15998                    .indent_size_for_line(MultiBufferRow(selection.start.row))
15999                    .len;
16000                let language = if let Some(language) =
16001                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16002                {
16003                    language
16004                } else {
16005                    continue;
16006                };
16007
16008                selection_edit_ranges.clear();
16009
16010                // If multiple selections contain a given row, avoid processing that
16011                // row more than once.
16012                let mut start_row = MultiBufferRow(selection.start.row);
16013                if last_toggled_row == Some(start_row) {
16014                    start_row = start_row.next_row();
16015                }
16016                let end_row =
16017                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16018                        MultiBufferRow(selection.end.row - 1)
16019                    } else {
16020                        MultiBufferRow(selection.end.row)
16021                    };
16022                last_toggled_row = Some(end_row);
16023
16024                if start_row > end_row {
16025                    continue;
16026                }
16027
16028                // If the language has line comments, toggle those.
16029                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16030
16031                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16032                if ignore_indent {
16033                    full_comment_prefixes = full_comment_prefixes
16034                        .into_iter()
16035                        .map(|s| Arc::from(s.trim_end()))
16036                        .collect();
16037                }
16038
16039                if !full_comment_prefixes.is_empty() {
16040                    let first_prefix = full_comment_prefixes
16041                        .first()
16042                        .expect("prefixes is non-empty");
16043                    let prefix_trimmed_lengths = full_comment_prefixes
16044                        .iter()
16045                        .map(|p| p.trim_end_matches(' ').len())
16046                        .collect::<SmallVec<[usize; 4]>>();
16047
16048                    let mut all_selection_lines_are_comments = true;
16049
16050                    for row in start_row.0..=end_row.0 {
16051                        let row = MultiBufferRow(row);
16052                        if start_row < end_row && snapshot.is_line_blank(row) {
16053                            continue;
16054                        }
16055
16056                        let prefix_range = full_comment_prefixes
16057                            .iter()
16058                            .zip(prefix_trimmed_lengths.iter().copied())
16059                            .map(|(prefix, trimmed_prefix_len)| {
16060                                comment_prefix_range(
16061                                    snapshot.deref(),
16062                                    row,
16063                                    &prefix[..trimmed_prefix_len],
16064                                    &prefix[trimmed_prefix_len..],
16065                                    ignore_indent,
16066                                )
16067                            })
16068                            .max_by_key(|range| range.end.column - range.start.column)
16069                            .expect("prefixes is non-empty");
16070
16071                        if prefix_range.is_empty() {
16072                            all_selection_lines_are_comments = false;
16073                        }
16074
16075                        selection_edit_ranges.push(prefix_range);
16076                    }
16077
16078                    if all_selection_lines_are_comments {
16079                        edits.extend(
16080                            selection_edit_ranges
16081                                .iter()
16082                                .cloned()
16083                                .map(|range| (range, empty_str.clone())),
16084                        );
16085                    } else {
16086                        let min_column = selection_edit_ranges
16087                            .iter()
16088                            .map(|range| range.start.column)
16089                            .min()
16090                            .unwrap_or(0);
16091                        edits.extend(selection_edit_ranges.iter().map(|range| {
16092                            let position = Point::new(range.start.row, min_column);
16093                            (position..position, first_prefix.clone())
16094                        }));
16095                    }
16096                } else if let Some(BlockCommentConfig {
16097                    start: full_comment_prefix,
16098                    end: comment_suffix,
16099                    ..
16100                }) = language.block_comment()
16101                {
16102                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16103                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16104                    let prefix_range = comment_prefix_range(
16105                        snapshot.deref(),
16106                        start_row,
16107                        comment_prefix,
16108                        comment_prefix_whitespace,
16109                        ignore_indent,
16110                    );
16111                    let suffix_range = comment_suffix_range(
16112                        snapshot.deref(),
16113                        end_row,
16114                        comment_suffix.trim_start_matches(' '),
16115                        comment_suffix.starts_with(' '),
16116                    );
16117
16118                    if prefix_range.is_empty() || suffix_range.is_empty() {
16119                        edits.push((
16120                            prefix_range.start..prefix_range.start,
16121                            full_comment_prefix.clone(),
16122                        ));
16123                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16124                        suffixes_inserted.push((end_row, comment_suffix.len()));
16125                    } else {
16126                        edits.push((prefix_range, empty_str.clone()));
16127                        edits.push((suffix_range, empty_str.clone()));
16128                    }
16129                } else {
16130                    continue;
16131                }
16132            }
16133
16134            drop(snapshot);
16135            this.buffer.update(cx, |buffer, cx| {
16136                buffer.edit(edits, None, cx);
16137            });
16138
16139            // Adjust selections so that they end before any comment suffixes that
16140            // were inserted.
16141            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16142            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16143            let snapshot = this.buffer.read(cx).read(cx);
16144            for selection in &mut selections {
16145                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16146                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16147                        Ordering::Less => {
16148                            suffixes_inserted.next();
16149                            continue;
16150                        }
16151                        Ordering::Greater => break,
16152                        Ordering::Equal => {
16153                            if selection.end.column == snapshot.line_len(row) {
16154                                if selection.is_empty() {
16155                                    selection.start.column -= suffix_len as u32;
16156                                }
16157                                selection.end.column -= suffix_len as u32;
16158                            }
16159                            break;
16160                        }
16161                    }
16162                }
16163            }
16164
16165            drop(snapshot);
16166            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16167
16168            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16169            let selections_on_single_row = selections.windows(2).all(|selections| {
16170                selections[0].start.row == selections[1].start.row
16171                    && selections[0].end.row == selections[1].end.row
16172                    && selections[0].start.row == selections[0].end.row
16173            });
16174            let selections_selecting = selections
16175                .iter()
16176                .any(|selection| selection.start != selection.end);
16177            let advance_downwards = action.advance_downwards
16178                && selections_on_single_row
16179                && !selections_selecting
16180                && !matches!(this.mode, EditorMode::SingleLine);
16181
16182            if advance_downwards {
16183                let snapshot = this.buffer.read(cx).snapshot(cx);
16184
16185                this.change_selections(Default::default(), window, cx, |s| {
16186                    s.move_cursors_with(|display_snapshot, display_point, _| {
16187                        let mut point = display_point.to_point(display_snapshot);
16188                        point.row += 1;
16189                        point = snapshot.clip_point(point, Bias::Left);
16190                        let display_point = point.to_display_point(display_snapshot);
16191                        let goal = SelectionGoal::HorizontalPosition(
16192                            display_snapshot
16193                                .x_for_display_point(display_point, text_layout_details)
16194                                .into(),
16195                        );
16196                        (display_point, goal)
16197                    })
16198                });
16199            }
16200        });
16201    }
16202
16203    pub fn select_enclosing_symbol(
16204        &mut self,
16205        _: &SelectEnclosingSymbol,
16206        window: &mut Window,
16207        cx: &mut Context<Self>,
16208    ) {
16209        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16210
16211        let buffer = self.buffer.read(cx).snapshot(cx);
16212        let old_selections = self
16213            .selections
16214            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16215            .into_boxed_slice();
16216
16217        fn update_selection(
16218            selection: &Selection<MultiBufferOffset>,
16219            buffer_snap: &MultiBufferSnapshot,
16220        ) -> Option<Selection<MultiBufferOffset>> {
16221            let cursor = selection.head();
16222            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16223            for symbol in symbols.iter().rev() {
16224                let start = symbol.range.start.to_offset(buffer_snap);
16225                let end = symbol.range.end.to_offset(buffer_snap);
16226                let new_range = start..end;
16227                if start < selection.start || end > selection.end {
16228                    return Some(Selection {
16229                        id: selection.id,
16230                        start: new_range.start,
16231                        end: new_range.end,
16232                        goal: SelectionGoal::None,
16233                        reversed: selection.reversed,
16234                    });
16235                }
16236            }
16237            None
16238        }
16239
16240        let mut selected_larger_symbol = false;
16241        let new_selections = old_selections
16242            .iter()
16243            .map(|selection| match update_selection(selection, &buffer) {
16244                Some(new_selection) => {
16245                    if new_selection.range() != selection.range() {
16246                        selected_larger_symbol = true;
16247                    }
16248                    new_selection
16249                }
16250                None => selection.clone(),
16251            })
16252            .collect::<Vec<_>>();
16253
16254        if selected_larger_symbol {
16255            self.change_selections(Default::default(), window, cx, |s| {
16256                s.select(new_selections);
16257            });
16258        }
16259    }
16260
16261    pub fn select_larger_syntax_node(
16262        &mut self,
16263        _: &SelectLargerSyntaxNode,
16264        window: &mut Window,
16265        cx: &mut Context<Self>,
16266    ) {
16267        let Some(visible_row_count) = self.visible_row_count() else {
16268            return;
16269        };
16270        let old_selections: Box<[_]> = self
16271            .selections
16272            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16273            .into();
16274        if old_selections.is_empty() {
16275            return;
16276        }
16277
16278        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16279
16280        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16281        let buffer = self.buffer.read(cx).snapshot(cx);
16282
16283        let mut selected_larger_node = false;
16284        let mut new_selections = old_selections
16285            .iter()
16286            .map(|selection| {
16287                let old_range = selection.start..selection.end;
16288
16289                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16290                    // manually select word at selection
16291                    if ["string_content", "inline"].contains(&node.kind()) {
16292                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16293                        // ignore if word is already selected
16294                        if !word_range.is_empty() && old_range != word_range {
16295                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16296                            // only select word if start and end point belongs to same word
16297                            if word_range == last_word_range {
16298                                selected_larger_node = true;
16299                                return Selection {
16300                                    id: selection.id,
16301                                    start: word_range.start,
16302                                    end: word_range.end,
16303                                    goal: SelectionGoal::None,
16304                                    reversed: selection.reversed,
16305                                };
16306                            }
16307                        }
16308                    }
16309                }
16310
16311                let mut new_range = old_range.clone();
16312                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16313                    new_range = range;
16314                    if !node.is_named() {
16315                        continue;
16316                    }
16317                    if !display_map.intersects_fold(new_range.start)
16318                        && !display_map.intersects_fold(new_range.end)
16319                    {
16320                        break;
16321                    }
16322                }
16323
16324                selected_larger_node |= new_range != old_range;
16325                Selection {
16326                    id: selection.id,
16327                    start: new_range.start,
16328                    end: new_range.end,
16329                    goal: SelectionGoal::None,
16330                    reversed: selection.reversed,
16331                }
16332            })
16333            .collect::<Vec<_>>();
16334
16335        if !selected_larger_node {
16336            return; // don't put this call in the history
16337        }
16338
16339        // scroll based on transformation done to the last selection created by the user
16340        let (last_old, last_new) = old_selections
16341            .last()
16342            .zip(new_selections.last().cloned())
16343            .expect("old_selections isn't empty");
16344
16345        // revert selection
16346        let is_selection_reversed = {
16347            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16348            new_selections.last_mut().expect("checked above").reversed =
16349                should_newest_selection_be_reversed;
16350            should_newest_selection_be_reversed
16351        };
16352
16353        if selected_larger_node {
16354            self.select_syntax_node_history.disable_clearing = true;
16355            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16356                s.select(new_selections.clone());
16357            });
16358            self.select_syntax_node_history.disable_clearing = false;
16359        }
16360
16361        let start_row = last_new.start.to_display_point(&display_map).row().0;
16362        let end_row = last_new.end.to_display_point(&display_map).row().0;
16363        let selection_height = end_row - start_row + 1;
16364        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16365
16366        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16367        let scroll_behavior = if fits_on_the_screen {
16368            self.request_autoscroll(Autoscroll::fit(), cx);
16369            SelectSyntaxNodeScrollBehavior::FitSelection
16370        } else if is_selection_reversed {
16371            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16372            SelectSyntaxNodeScrollBehavior::CursorTop
16373        } else {
16374            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16375            SelectSyntaxNodeScrollBehavior::CursorBottom
16376        };
16377
16378        self.select_syntax_node_history.push((
16379            old_selections,
16380            scroll_behavior,
16381            is_selection_reversed,
16382        ));
16383    }
16384
16385    pub fn select_smaller_syntax_node(
16386        &mut self,
16387        _: &SelectSmallerSyntaxNode,
16388        window: &mut Window,
16389        cx: &mut Context<Self>,
16390    ) {
16391        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16392
16393        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16394            self.select_syntax_node_history.pop()
16395        {
16396            if let Some(selection) = selections.last_mut() {
16397                selection.reversed = is_selection_reversed;
16398            }
16399
16400            self.select_syntax_node_history.disable_clearing = true;
16401            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16402                s.select(selections.to_vec());
16403            });
16404            self.select_syntax_node_history.disable_clearing = false;
16405
16406            match scroll_behavior {
16407                SelectSyntaxNodeScrollBehavior::CursorTop => {
16408                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16409                }
16410                SelectSyntaxNodeScrollBehavior::FitSelection => {
16411                    self.request_autoscroll(Autoscroll::fit(), cx);
16412                }
16413                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16414                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16415                }
16416            }
16417        }
16418    }
16419
16420    pub fn unwrap_syntax_node(
16421        &mut self,
16422        _: &UnwrapSyntaxNode,
16423        window: &mut Window,
16424        cx: &mut Context<Self>,
16425    ) {
16426        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16427
16428        let buffer = self.buffer.read(cx).snapshot(cx);
16429        let selections = self
16430            .selections
16431            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16432            .into_iter()
16433            // subtracting the offset requires sorting
16434            .sorted_by_key(|i| i.start);
16435
16436        let full_edits = selections
16437            .into_iter()
16438            .filter_map(|selection| {
16439                let child = if selection.is_empty()
16440                    && let Some((_, ancestor_range)) =
16441                        buffer.syntax_ancestor(selection.start..selection.end)
16442                {
16443                    ancestor_range
16444                } else {
16445                    selection.range()
16446                };
16447
16448                let mut parent = child.clone();
16449                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16450                    parent = ancestor_range;
16451                    if parent.start < child.start || parent.end > child.end {
16452                        break;
16453                    }
16454                }
16455
16456                if parent == child {
16457                    return None;
16458                }
16459                let text = buffer.text_for_range(child).collect::<String>();
16460                Some((selection.id, parent, text))
16461            })
16462            .collect::<Vec<_>>();
16463        if full_edits.is_empty() {
16464            return;
16465        }
16466
16467        self.transact(window, cx, |this, window, cx| {
16468            this.buffer.update(cx, |buffer, cx| {
16469                buffer.edit(
16470                    full_edits
16471                        .iter()
16472                        .map(|(_, p, t)| (p.clone(), t.clone()))
16473                        .collect::<Vec<_>>(),
16474                    None,
16475                    cx,
16476                );
16477            });
16478            this.change_selections(Default::default(), window, cx, |s| {
16479                let mut offset = 0;
16480                let mut selections = vec![];
16481                for (id, parent, text) in full_edits {
16482                    let start = parent.start - offset;
16483                    offset += (parent.end - parent.start) - text.len();
16484                    selections.push(Selection {
16485                        id,
16486                        start,
16487                        end: start + text.len(),
16488                        reversed: false,
16489                        goal: Default::default(),
16490                    });
16491                }
16492                s.select(selections);
16493            });
16494        });
16495    }
16496
16497    pub fn select_next_syntax_node(
16498        &mut self,
16499        _: &SelectNextSyntaxNode,
16500        window: &mut Window,
16501        cx: &mut Context<Self>,
16502    ) {
16503        let old_selections: Box<[_]> = self
16504            .selections
16505            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16506            .into();
16507        if old_selections.is_empty() {
16508            return;
16509        }
16510
16511        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16512
16513        let buffer = self.buffer.read(cx).snapshot(cx);
16514        let mut selected_sibling = false;
16515
16516        let new_selections = old_selections
16517            .iter()
16518            .map(|selection| {
16519                let old_range = selection.start..selection.end;
16520
16521                let old_range =
16522                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16523                let excerpt = buffer.excerpt_containing(old_range.clone());
16524
16525                if let Some(mut excerpt) = excerpt
16526                    && let Some(node) = excerpt
16527                        .buffer()
16528                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16529                {
16530                    let new_range = excerpt.map_range_from_buffer(
16531                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16532                    );
16533                    selected_sibling = true;
16534                    Selection {
16535                        id: selection.id,
16536                        start: new_range.start,
16537                        end: new_range.end,
16538                        goal: SelectionGoal::None,
16539                        reversed: selection.reversed,
16540                    }
16541                } else {
16542                    selection.clone()
16543                }
16544            })
16545            .collect::<Vec<_>>();
16546
16547        if selected_sibling {
16548            self.change_selections(
16549                SelectionEffects::scroll(Autoscroll::fit()),
16550                window,
16551                cx,
16552                |s| {
16553                    s.select(new_selections);
16554                },
16555            );
16556        }
16557    }
16558
16559    pub fn select_prev_syntax_node(
16560        &mut self,
16561        _: &SelectPreviousSyntaxNode,
16562        window: &mut Window,
16563        cx: &mut Context<Self>,
16564    ) {
16565        let old_selections: Box<[_]> = self
16566            .selections
16567            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16568            .into();
16569        if old_selections.is_empty() {
16570            return;
16571        }
16572
16573        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16574
16575        let buffer = self.buffer.read(cx).snapshot(cx);
16576        let mut selected_sibling = false;
16577
16578        let new_selections = old_selections
16579            .iter()
16580            .map(|selection| {
16581                let old_range = selection.start..selection.end;
16582                let old_range =
16583                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16584                let excerpt = buffer.excerpt_containing(old_range.clone());
16585
16586                if let Some(mut excerpt) = excerpt
16587                    && let Some(node) = excerpt
16588                        .buffer()
16589                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16590                {
16591                    let new_range = excerpt.map_range_from_buffer(
16592                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16593                    );
16594                    selected_sibling = true;
16595                    Selection {
16596                        id: selection.id,
16597                        start: new_range.start,
16598                        end: new_range.end,
16599                        goal: SelectionGoal::None,
16600                        reversed: selection.reversed,
16601                    }
16602                } else {
16603                    selection.clone()
16604                }
16605            })
16606            .collect::<Vec<_>>();
16607
16608        if selected_sibling {
16609            self.change_selections(
16610                SelectionEffects::scroll(Autoscroll::fit()),
16611                window,
16612                cx,
16613                |s| {
16614                    s.select(new_selections);
16615                },
16616            );
16617        }
16618    }
16619
16620    pub fn move_to_start_of_larger_syntax_node(
16621        &mut self,
16622        _: &MoveToStartOfLargerSyntaxNode,
16623        window: &mut Window,
16624        cx: &mut Context<Self>,
16625    ) {
16626        self.move_cursors_to_syntax_nodes(window, cx, false);
16627    }
16628
16629    pub fn move_to_end_of_larger_syntax_node(
16630        &mut self,
16631        _: &MoveToEndOfLargerSyntaxNode,
16632        window: &mut Window,
16633        cx: &mut Context<Self>,
16634    ) {
16635        self.move_cursors_to_syntax_nodes(window, cx, true);
16636    }
16637
16638    fn find_syntax_node_boundary(
16639        &self,
16640        selection_pos: MultiBufferOffset,
16641        move_to_end: bool,
16642        display_map: &DisplaySnapshot,
16643        buffer: &MultiBufferSnapshot,
16644    ) -> MultiBufferOffset {
16645        let old_range = selection_pos..selection_pos;
16646        let mut new_pos = selection_pos;
16647        let mut search_range = old_range;
16648        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16649            search_range = range.clone();
16650            if !node.is_named()
16651                || display_map.intersects_fold(range.start)
16652                || display_map.intersects_fold(range.end)
16653                // If cursor is already at the end of the syntax node, continue searching
16654                || (move_to_end && range.end == selection_pos)
16655                // If cursor is already at the start of the syntax node, continue searching
16656                || (!move_to_end && range.start == selection_pos)
16657            {
16658                continue;
16659            }
16660
16661            // If we found a string_content node, find the largest parent that is still string_content
16662            // Enables us to skip to the end of strings without taking multiple steps inside the string
16663            let (_, final_range) = if node.kind() == "string_content" {
16664                let mut current_node = node;
16665                let mut current_range = range;
16666                while let Some((parent, parent_range)) =
16667                    buffer.syntax_ancestor(current_range.clone())
16668                {
16669                    if parent.kind() == "string_content" {
16670                        current_node = parent;
16671                        current_range = parent_range;
16672                    } else {
16673                        break;
16674                    }
16675                }
16676
16677                (current_node, current_range)
16678            } else {
16679                (node, range)
16680            };
16681
16682            new_pos = if move_to_end {
16683                final_range.end
16684            } else {
16685                final_range.start
16686            };
16687
16688            break;
16689        }
16690
16691        new_pos
16692    }
16693
16694    fn move_cursors_to_syntax_nodes(
16695        &mut self,
16696        window: &mut Window,
16697        cx: &mut Context<Self>,
16698        move_to_end: bool,
16699    ) -> bool {
16700        let old_selections: Box<[_]> = self
16701            .selections
16702            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16703            .into();
16704        if old_selections.is_empty() {
16705            return false;
16706        }
16707
16708        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16709
16710        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16711        let buffer = self.buffer.read(cx).snapshot(cx);
16712
16713        let mut any_cursor_moved = false;
16714        let new_selections = old_selections
16715            .iter()
16716            .map(|selection| {
16717                if !selection.is_empty() {
16718                    return selection.clone();
16719                }
16720
16721                let selection_pos = selection.head();
16722                let new_pos = self.find_syntax_node_boundary(
16723                    selection_pos,
16724                    move_to_end,
16725                    &display_map,
16726                    &buffer,
16727                );
16728
16729                any_cursor_moved |= new_pos != selection_pos;
16730
16731                Selection {
16732                    id: selection.id,
16733                    start: new_pos,
16734                    end: new_pos,
16735                    goal: SelectionGoal::None,
16736                    reversed: false,
16737                }
16738            })
16739            .collect::<Vec<_>>();
16740
16741        self.change_selections(Default::default(), window, cx, |s| {
16742            s.select(new_selections);
16743        });
16744        self.request_autoscroll(Autoscroll::newest(), cx);
16745
16746        any_cursor_moved
16747    }
16748
16749    pub fn select_to_start_of_larger_syntax_node(
16750        &mut self,
16751        _: &SelectToStartOfLargerSyntaxNode,
16752        window: &mut Window,
16753        cx: &mut Context<Self>,
16754    ) {
16755        self.select_to_syntax_nodes(window, cx, false);
16756    }
16757
16758    pub fn select_to_end_of_larger_syntax_node(
16759        &mut self,
16760        _: &SelectToEndOfLargerSyntaxNode,
16761        window: &mut Window,
16762        cx: &mut Context<Self>,
16763    ) {
16764        self.select_to_syntax_nodes(window, cx, true);
16765    }
16766
16767    fn select_to_syntax_nodes(
16768        &mut self,
16769        window: &mut Window,
16770        cx: &mut Context<Self>,
16771        move_to_end: bool,
16772    ) {
16773        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16774
16775        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16776        let buffer = self.buffer.read(cx).snapshot(cx);
16777        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
16778
16779        let new_selections = old_selections
16780            .iter()
16781            .map(|selection| {
16782                let new_pos = self.find_syntax_node_boundary(
16783                    selection.head(),
16784                    move_to_end,
16785                    &display_map,
16786                    &buffer,
16787                );
16788
16789                let mut new_selection = selection.clone();
16790                new_selection.set_head(new_pos, SelectionGoal::None);
16791                new_selection
16792            })
16793            .collect::<Vec<_>>();
16794
16795        self.change_selections(Default::default(), window, cx, |s| {
16796            s.select(new_selections);
16797        });
16798    }
16799
16800    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
16801        if !EditorSettings::get_global(cx).gutter.runnables {
16802            self.clear_tasks();
16803            return Task::ready(());
16804        }
16805        let project = self.project().map(Entity::downgrade);
16806        let task_sources = self.lsp_task_sources(cx);
16807        let multi_buffer = self.buffer.downgrade();
16808        cx.spawn_in(window, async move |editor, cx| {
16809            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
16810            let Some(project) = project.and_then(|p| p.upgrade()) else {
16811                return;
16812            };
16813            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
16814                this.display_map.update(cx, |map, cx| map.snapshot(cx))
16815            }) else {
16816                return;
16817            };
16818
16819            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
16820            if hide_runnables {
16821                return;
16822            }
16823            let new_rows =
16824                cx.background_spawn({
16825                    let snapshot = display_snapshot.clone();
16826                    async move {
16827                        Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
16828                    }
16829                })
16830                    .await;
16831            let Ok(lsp_tasks) =
16832                cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
16833            else {
16834                return;
16835            };
16836            let lsp_tasks = lsp_tasks.await;
16837
16838            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
16839                lsp_tasks
16840                    .into_iter()
16841                    .flat_map(|(kind, tasks)| {
16842                        tasks.into_iter().filter_map(move |(location, task)| {
16843                            Some((kind.clone(), location?, task))
16844                        })
16845                    })
16846                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
16847                        let buffer = location.target.buffer;
16848                        let buffer_snapshot = buffer.read(cx).snapshot();
16849                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
16850                            |(excerpt_id, snapshot, _)| {
16851                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
16852                                    display_snapshot
16853                                        .buffer_snapshot()
16854                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
16855                                } else {
16856                                    None
16857                                }
16858                            },
16859                        );
16860                        if let Some(offset) = offset {
16861                            let task_buffer_range =
16862                                location.target.range.to_point(&buffer_snapshot);
16863                            let context_buffer_range =
16864                                task_buffer_range.to_offset(&buffer_snapshot);
16865                            let context_range = BufferOffset(context_buffer_range.start)
16866                                ..BufferOffset(context_buffer_range.end);
16867
16868                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
16869                                .or_insert_with(|| RunnableTasks {
16870                                    templates: Vec::new(),
16871                                    offset,
16872                                    column: task_buffer_range.start.column,
16873                                    extra_variables: HashMap::default(),
16874                                    context_range,
16875                                })
16876                                .templates
16877                                .push((kind, task.original_task().clone()));
16878                        }
16879
16880                        acc
16881                    })
16882            }) else {
16883                return;
16884            };
16885
16886            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
16887                buffer.language_settings(cx).tasks.prefer_lsp
16888            }) else {
16889                return;
16890            };
16891
16892            let rows = Self::runnable_rows(
16893                project,
16894                display_snapshot,
16895                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
16896                new_rows,
16897                cx.clone(),
16898            )
16899            .await;
16900            editor
16901                .update(cx, |editor, _| {
16902                    editor.clear_tasks();
16903                    for (key, mut value) in rows {
16904                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
16905                            value.templates.extend(lsp_tasks.templates);
16906                        }
16907
16908                        editor.insert_tasks(key, value);
16909                    }
16910                    for (key, value) in lsp_tasks_by_rows {
16911                        editor.insert_tasks(key, value);
16912                    }
16913                })
16914                .ok();
16915        })
16916    }
16917    fn fetch_runnable_ranges(
16918        snapshot: &DisplaySnapshot,
16919        range: Range<Anchor>,
16920    ) -> Vec<(Range<MultiBufferOffset>, language::RunnableRange)> {
16921        snapshot.buffer_snapshot().runnable_ranges(range).collect()
16922    }
16923
16924    fn runnable_rows(
16925        project: Entity<Project>,
16926        snapshot: DisplaySnapshot,
16927        prefer_lsp: bool,
16928        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
16929        cx: AsyncWindowContext,
16930    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
16931        cx.spawn(async move |cx| {
16932            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
16933            for (run_range, mut runnable) in runnable_ranges {
16934                let Some(tasks) = cx
16935                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
16936                    .ok()
16937                else {
16938                    continue;
16939                };
16940                let mut tasks = tasks.await;
16941
16942                if prefer_lsp {
16943                    tasks.retain(|(task_kind, _)| {
16944                        !matches!(task_kind, TaskSourceKind::Language { .. })
16945                    });
16946                }
16947                if tasks.is_empty() {
16948                    continue;
16949                }
16950
16951                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
16952                let Some(row) = snapshot
16953                    .buffer_snapshot()
16954                    .buffer_line_for_row(MultiBufferRow(point.row))
16955                    .map(|(_, range)| range.start.row)
16956                else {
16957                    continue;
16958                };
16959
16960                let context_range =
16961                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
16962                runnable_rows.push((
16963                    (runnable.buffer_id, row),
16964                    RunnableTasks {
16965                        templates: tasks,
16966                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
16967                        context_range,
16968                        column: point.column,
16969                        extra_variables: runnable.extra_captures,
16970                    },
16971                ));
16972            }
16973            runnable_rows
16974        })
16975    }
16976
16977    fn templates_with_tags(
16978        project: &Entity<Project>,
16979        runnable: &mut Runnable,
16980        cx: &mut App,
16981    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
16982        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
16983            let (worktree_id, file) = project
16984                .buffer_for_id(runnable.buffer, cx)
16985                .and_then(|buffer| buffer.read(cx).file())
16986                .map(|file| (file.worktree_id(cx), file.clone()))
16987                .unzip();
16988
16989            (
16990                project.task_store().read(cx).task_inventory().cloned(),
16991                worktree_id,
16992                file,
16993            )
16994        });
16995
16996        let tags = mem::take(&mut runnable.tags);
16997        let language = runnable.language.clone();
16998        cx.spawn(async move |cx| {
16999            let mut templates_with_tags = Vec::new();
17000            if let Some(inventory) = inventory {
17001                for RunnableTag(tag) in tags {
17002                    let new_tasks = inventory.update(cx, |inventory, cx| {
17003                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
17004                    });
17005                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
17006                        move |(_, template)| {
17007                            template.tags.iter().any(|source_tag| source_tag == &tag)
17008                        },
17009                    ));
17010                }
17011            }
17012            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
17013
17014            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
17015                // Strongest source wins; if we have worktree tag binding, prefer that to
17016                // global and language bindings;
17017                // if we have a global binding, prefer that to language binding.
17018                let first_mismatch = templates_with_tags
17019                    .iter()
17020                    .position(|(tag_source, _)| tag_source != leading_tag_source);
17021                if let Some(index) = first_mismatch {
17022                    templates_with_tags.truncate(index);
17023                }
17024            }
17025
17026            templates_with_tags
17027        })
17028    }
17029
17030    pub fn move_to_enclosing_bracket(
17031        &mut self,
17032        _: &MoveToEnclosingBracket,
17033        window: &mut Window,
17034        cx: &mut Context<Self>,
17035    ) {
17036        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17037        self.change_selections(Default::default(), window, cx, |s| {
17038            s.move_offsets_with(|snapshot, selection| {
17039                let Some(enclosing_bracket_ranges) =
17040                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17041                else {
17042                    return;
17043                };
17044
17045                let mut best_length = usize::MAX;
17046                let mut best_inside = false;
17047                let mut best_in_bracket_range = false;
17048                let mut best_destination = None;
17049                for (open, close) in enclosing_bracket_ranges {
17050                    let close = close.to_inclusive();
17051                    let length = *close.end() - open.start;
17052                    let inside = selection.start >= open.end && selection.end <= *close.start();
17053                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17054                        || close.contains(&selection.head());
17055
17056                    // If best is next to a bracket and current isn't, skip
17057                    if !in_bracket_range && best_in_bracket_range {
17058                        continue;
17059                    }
17060
17061                    // Prefer smaller lengths unless best is inside and current isn't
17062                    if length > best_length && (best_inside || !inside) {
17063                        continue;
17064                    }
17065
17066                    best_length = length;
17067                    best_inside = inside;
17068                    best_in_bracket_range = in_bracket_range;
17069                    best_destination = Some(
17070                        if close.contains(&selection.start) && close.contains(&selection.end) {
17071                            if inside { open.end } else { open.start }
17072                        } else if inside {
17073                            *close.start()
17074                        } else {
17075                            *close.end()
17076                        },
17077                    );
17078                }
17079
17080                if let Some(destination) = best_destination {
17081                    selection.collapse_to(destination, SelectionGoal::None);
17082                }
17083            })
17084        });
17085    }
17086
17087    pub fn undo_selection(
17088        &mut self,
17089        _: &UndoSelection,
17090        window: &mut Window,
17091        cx: &mut Context<Self>,
17092    ) {
17093        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17094        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17095            self.selection_history.mode = SelectionHistoryMode::Undoing;
17096            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17097                this.end_selection(window, cx);
17098                this.change_selections(
17099                    SelectionEffects::scroll(Autoscroll::newest()),
17100                    window,
17101                    cx,
17102                    |s| s.select_anchors(entry.selections.to_vec()),
17103                );
17104            });
17105            self.selection_history.mode = SelectionHistoryMode::Normal;
17106
17107            self.select_next_state = entry.select_next_state;
17108            self.select_prev_state = entry.select_prev_state;
17109            self.add_selections_state = entry.add_selections_state;
17110        }
17111    }
17112
17113    pub fn redo_selection(
17114        &mut self,
17115        _: &RedoSelection,
17116        window: &mut Window,
17117        cx: &mut Context<Self>,
17118    ) {
17119        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17120        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17121            self.selection_history.mode = SelectionHistoryMode::Redoing;
17122            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17123                this.end_selection(window, cx);
17124                this.change_selections(
17125                    SelectionEffects::scroll(Autoscroll::newest()),
17126                    window,
17127                    cx,
17128                    |s| s.select_anchors(entry.selections.to_vec()),
17129                );
17130            });
17131            self.selection_history.mode = SelectionHistoryMode::Normal;
17132
17133            self.select_next_state = entry.select_next_state;
17134            self.select_prev_state = entry.select_prev_state;
17135            self.add_selections_state = entry.add_selections_state;
17136        }
17137    }
17138
17139    pub fn expand_excerpts(
17140        &mut self,
17141        action: &ExpandExcerpts,
17142        _: &mut Window,
17143        cx: &mut Context<Self>,
17144    ) {
17145        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17146    }
17147
17148    pub fn expand_excerpts_down(
17149        &mut self,
17150        action: &ExpandExcerptsDown,
17151        _: &mut Window,
17152        cx: &mut Context<Self>,
17153    ) {
17154        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17155    }
17156
17157    pub fn expand_excerpts_up(
17158        &mut self,
17159        action: &ExpandExcerptsUp,
17160        _: &mut Window,
17161        cx: &mut Context<Self>,
17162    ) {
17163        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17164    }
17165
17166    pub fn expand_excerpts_for_direction(
17167        &mut self,
17168        lines: u32,
17169        direction: ExpandExcerptDirection,
17170        cx: &mut Context<Self>,
17171    ) {
17172        let selections = self.selections.disjoint_anchors_arc();
17173
17174        let lines = if lines == 0 {
17175            EditorSettings::get_global(cx).expand_excerpt_lines
17176        } else {
17177            lines
17178        };
17179
17180        let snapshot = self.buffer.read(cx).snapshot(cx);
17181        let mut excerpt_ids = selections
17182            .iter()
17183            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17184            .collect::<Vec<_>>();
17185        excerpt_ids.sort();
17186        excerpt_ids.dedup();
17187
17188        if self.delegate_expand_excerpts {
17189            cx.emit(EditorEvent::ExpandExcerptsRequested {
17190                excerpt_ids,
17191                lines,
17192                direction,
17193            });
17194            return;
17195        }
17196
17197        self.buffer.update(cx, |buffer, cx| {
17198            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17199        })
17200    }
17201
17202    pub fn expand_excerpt(
17203        &mut self,
17204        excerpt: ExcerptId,
17205        direction: ExpandExcerptDirection,
17206        window: &mut Window,
17207        cx: &mut Context<Self>,
17208    ) {
17209        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17210
17211        if self.delegate_expand_excerpts {
17212            cx.emit(EditorEvent::ExpandExcerptsRequested {
17213                excerpt_ids: vec![excerpt],
17214                lines: lines_to_expand,
17215                direction,
17216            });
17217            return;
17218        }
17219
17220        let current_scroll_position = self.scroll_position(cx);
17221        let mut scroll = None;
17222
17223        if direction == ExpandExcerptDirection::Down {
17224            let multi_buffer = self.buffer.read(cx);
17225            let snapshot = multi_buffer.snapshot(cx);
17226            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17227                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17228                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17229            {
17230                let buffer_snapshot = buffer.read(cx).snapshot();
17231                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17232                let last_row = buffer_snapshot.max_point().row;
17233                let lines_below = last_row.saturating_sub(excerpt_end_row);
17234                if lines_below >= lines_to_expand {
17235                    scroll = Some(
17236                        current_scroll_position
17237                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17238                    );
17239                }
17240            }
17241        }
17242        if direction == ExpandExcerptDirection::Up
17243            && self
17244                .buffer
17245                .read(cx)
17246                .snapshot(cx)
17247                .excerpt_before(excerpt)
17248                .is_none()
17249        {
17250            scroll = Some(current_scroll_position);
17251        }
17252
17253        self.buffer.update(cx, |buffer, cx| {
17254            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17255        });
17256
17257        if let Some(new_scroll_position) = scroll {
17258            self.set_scroll_position(new_scroll_position, window, cx);
17259        }
17260    }
17261
17262    pub fn go_to_singleton_buffer_point(
17263        &mut self,
17264        point: Point,
17265        window: &mut Window,
17266        cx: &mut Context<Self>,
17267    ) {
17268        self.go_to_singleton_buffer_range(point..point, window, cx);
17269    }
17270
17271    pub fn go_to_singleton_buffer_range(
17272        &mut self,
17273        range: Range<Point>,
17274        window: &mut Window,
17275        cx: &mut Context<Self>,
17276    ) {
17277        let multibuffer = self.buffer().read(cx);
17278        let Some(buffer) = multibuffer.as_singleton() else {
17279            return;
17280        };
17281        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17282            return;
17283        };
17284        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17285            return;
17286        };
17287        self.change_selections(
17288            SelectionEffects::default().nav_history(true),
17289            window,
17290            cx,
17291            |s| s.select_anchor_ranges([start..end]),
17292        );
17293    }
17294
17295    pub fn go_to_diagnostic(
17296        &mut self,
17297        action: &GoToDiagnostic,
17298        window: &mut Window,
17299        cx: &mut Context<Self>,
17300    ) {
17301        if !self.diagnostics_enabled() {
17302            return;
17303        }
17304        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17305        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17306    }
17307
17308    pub fn go_to_prev_diagnostic(
17309        &mut self,
17310        action: &GoToPreviousDiagnostic,
17311        window: &mut Window,
17312        cx: &mut Context<Self>,
17313    ) {
17314        if !self.diagnostics_enabled() {
17315            return;
17316        }
17317        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17318        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17319    }
17320
17321    pub fn go_to_diagnostic_impl(
17322        &mut self,
17323        direction: Direction,
17324        severity: GoToDiagnosticSeverityFilter,
17325        window: &mut Window,
17326        cx: &mut Context<Self>,
17327    ) {
17328        let buffer = self.buffer.read(cx).snapshot(cx);
17329        let selection = self
17330            .selections
17331            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17332
17333        let mut active_group_id = None;
17334        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17335            && active_group.active_range.start.to_offset(&buffer) == selection.start
17336        {
17337            active_group_id = Some(active_group.group_id);
17338        }
17339
17340        fn filtered<'a>(
17341            severity: GoToDiagnosticSeverityFilter,
17342            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17343        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17344            diagnostics
17345                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17346                .filter(|entry| entry.range.start != entry.range.end)
17347                .filter(|entry| !entry.diagnostic.is_unnecessary)
17348        }
17349
17350        let before = filtered(
17351            severity,
17352            buffer
17353                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17354                .filter(|entry| entry.range.start <= selection.start),
17355        );
17356        let after = filtered(
17357            severity,
17358            buffer
17359                .diagnostics_in_range(selection.start..buffer.len())
17360                .filter(|entry| entry.range.start >= selection.start),
17361        );
17362
17363        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17364        if direction == Direction::Prev {
17365            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17366            {
17367                for diagnostic in prev_diagnostics.into_iter().rev() {
17368                    if diagnostic.range.start != selection.start
17369                        || active_group_id
17370                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17371                    {
17372                        found = Some(diagnostic);
17373                        break 'outer;
17374                    }
17375                }
17376            }
17377        } else {
17378            for diagnostic in after.chain(before) {
17379                if diagnostic.range.start != selection.start
17380                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17381                {
17382                    found = Some(diagnostic);
17383                    break;
17384                }
17385            }
17386        }
17387        let Some(next_diagnostic) = found else {
17388            return;
17389        };
17390
17391        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17392        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17393            return;
17394        };
17395        let snapshot = self.snapshot(window, cx);
17396        if snapshot.intersects_fold(next_diagnostic.range.start) {
17397            self.unfold_ranges(
17398                std::slice::from_ref(&next_diagnostic.range),
17399                true,
17400                false,
17401                cx,
17402            );
17403        }
17404        self.change_selections(Default::default(), window, cx, |s| {
17405            s.select_ranges(vec![
17406                next_diagnostic.range.start..next_diagnostic.range.start,
17407            ])
17408        });
17409        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17410        self.refresh_edit_prediction(false, true, window, cx);
17411    }
17412
17413    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17414        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17415        let snapshot = self.snapshot(window, cx);
17416        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17417        self.go_to_hunk_before_or_after_position(
17418            &snapshot,
17419            selection.head(),
17420            Direction::Next,
17421            window,
17422            cx,
17423        );
17424    }
17425
17426    pub fn go_to_hunk_before_or_after_position(
17427        &mut self,
17428        snapshot: &EditorSnapshot,
17429        position: Point,
17430        direction: Direction,
17431        window: &mut Window,
17432        cx: &mut Context<Editor>,
17433    ) {
17434        let row = if direction == Direction::Next {
17435            self.hunk_after_position(snapshot, position)
17436                .map(|hunk| hunk.row_range.start)
17437        } else {
17438            self.hunk_before_position(snapshot, position)
17439        };
17440
17441        if let Some(row) = row {
17442            let destination = Point::new(row.0, 0);
17443            let autoscroll = Autoscroll::center();
17444
17445            self.unfold_ranges(&[destination..destination], false, false, cx);
17446            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17447                s.select_ranges([destination..destination]);
17448            });
17449        }
17450    }
17451
17452    fn hunk_after_position(
17453        &mut self,
17454        snapshot: &EditorSnapshot,
17455        position: Point,
17456    ) -> Option<MultiBufferDiffHunk> {
17457        snapshot
17458            .buffer_snapshot()
17459            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17460            .find(|hunk| hunk.row_range.start.0 > position.row)
17461            .or_else(|| {
17462                snapshot
17463                    .buffer_snapshot()
17464                    .diff_hunks_in_range(Point::zero()..position)
17465                    .find(|hunk| hunk.row_range.end.0 < position.row)
17466            })
17467    }
17468
17469    fn go_to_prev_hunk(
17470        &mut self,
17471        _: &GoToPreviousHunk,
17472        window: &mut Window,
17473        cx: &mut Context<Self>,
17474    ) {
17475        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17476        let snapshot = self.snapshot(window, cx);
17477        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17478        self.go_to_hunk_before_or_after_position(
17479            &snapshot,
17480            selection.head(),
17481            Direction::Prev,
17482            window,
17483            cx,
17484        );
17485    }
17486
17487    fn hunk_before_position(
17488        &mut self,
17489        snapshot: &EditorSnapshot,
17490        position: Point,
17491    ) -> Option<MultiBufferRow> {
17492        snapshot
17493            .buffer_snapshot()
17494            .diff_hunk_before(position)
17495            .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17496    }
17497
17498    fn go_to_next_change(
17499        &mut self,
17500        _: &GoToNextChange,
17501        window: &mut Window,
17502        cx: &mut Context<Self>,
17503    ) {
17504        if let Some(selections) = self
17505            .change_list
17506            .next_change(1, Direction::Next)
17507            .map(|s| s.to_vec())
17508        {
17509            self.change_selections(Default::default(), window, cx, |s| {
17510                let map = s.display_snapshot();
17511                s.select_display_ranges(selections.iter().map(|a| {
17512                    let point = a.to_display_point(&map);
17513                    point..point
17514                }))
17515            })
17516        }
17517    }
17518
17519    fn go_to_previous_change(
17520        &mut self,
17521        _: &GoToPreviousChange,
17522        window: &mut Window,
17523        cx: &mut Context<Self>,
17524    ) {
17525        if let Some(selections) = self
17526            .change_list
17527            .next_change(1, Direction::Prev)
17528            .map(|s| s.to_vec())
17529        {
17530            self.change_selections(Default::default(), window, cx, |s| {
17531                let map = s.display_snapshot();
17532                s.select_display_ranges(selections.iter().map(|a| {
17533                    let point = a.to_display_point(&map);
17534                    point..point
17535                }))
17536            })
17537        }
17538    }
17539
17540    pub fn go_to_next_document_highlight(
17541        &mut self,
17542        _: &GoToNextDocumentHighlight,
17543        window: &mut Window,
17544        cx: &mut Context<Self>,
17545    ) {
17546        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17547    }
17548
17549    pub fn go_to_prev_document_highlight(
17550        &mut self,
17551        _: &GoToPreviousDocumentHighlight,
17552        window: &mut Window,
17553        cx: &mut Context<Self>,
17554    ) {
17555        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17556    }
17557
17558    pub fn go_to_document_highlight_before_or_after_position(
17559        &mut self,
17560        direction: Direction,
17561        window: &mut Window,
17562        cx: &mut Context<Editor>,
17563    ) {
17564        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17565        let snapshot = self.snapshot(window, cx);
17566        let buffer = &snapshot.buffer_snapshot();
17567        let position = self
17568            .selections
17569            .newest::<Point>(&snapshot.display_snapshot)
17570            .head();
17571        let anchor_position = buffer.anchor_after(position);
17572
17573        // Get all document highlights (both read and write)
17574        let mut all_highlights = Vec::new();
17575
17576        if let Some((_, read_highlights)) = self
17577            .background_highlights
17578            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
17579        {
17580            all_highlights.extend(read_highlights.iter());
17581        }
17582
17583        if let Some((_, write_highlights)) = self
17584            .background_highlights
17585            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
17586        {
17587            all_highlights.extend(write_highlights.iter());
17588        }
17589
17590        if all_highlights.is_empty() {
17591            return;
17592        }
17593
17594        // Sort highlights by position
17595        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17596
17597        let target_highlight = match direction {
17598            Direction::Next => {
17599                // Find the first highlight after the current position
17600                all_highlights
17601                    .iter()
17602                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17603            }
17604            Direction::Prev => {
17605                // Find the last highlight before the current position
17606                all_highlights
17607                    .iter()
17608                    .rev()
17609                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17610            }
17611        };
17612
17613        if let Some(highlight) = target_highlight {
17614            let destination = highlight.start.to_point(buffer);
17615            let autoscroll = Autoscroll::center();
17616
17617            self.unfold_ranges(&[destination..destination], false, false, cx);
17618            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17619                s.select_ranges([destination..destination]);
17620            });
17621        }
17622    }
17623
17624    fn go_to_line<T: 'static>(
17625        &mut self,
17626        position: Anchor,
17627        highlight_color: Option<Hsla>,
17628        window: &mut Window,
17629        cx: &mut Context<Self>,
17630    ) {
17631        let snapshot = self.snapshot(window, cx).display_snapshot;
17632        let position = position.to_point(&snapshot.buffer_snapshot());
17633        let start = snapshot
17634            .buffer_snapshot()
17635            .clip_point(Point::new(position.row, 0), Bias::Left);
17636        let end = start + Point::new(1, 0);
17637        let start = snapshot.buffer_snapshot().anchor_before(start);
17638        let end = snapshot.buffer_snapshot().anchor_before(end);
17639
17640        self.highlight_rows::<T>(
17641            start..end,
17642            highlight_color
17643                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17644            Default::default(),
17645            cx,
17646        );
17647
17648        if self.buffer.read(cx).is_singleton() {
17649            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17650        }
17651    }
17652
17653    pub fn go_to_definition(
17654        &mut self,
17655        _: &GoToDefinition,
17656        window: &mut Window,
17657        cx: &mut Context<Self>,
17658    ) -> Task<Result<Navigated>> {
17659        let definition =
17660            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17661        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17662        cx.spawn_in(window, async move |editor, cx| {
17663            if definition.await? == Navigated::Yes {
17664                return Ok(Navigated::Yes);
17665            }
17666            match fallback_strategy {
17667                GoToDefinitionFallback::None => Ok(Navigated::No),
17668                GoToDefinitionFallback::FindAllReferences => {
17669                    match editor.update_in(cx, |editor, window, cx| {
17670                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17671                    })? {
17672                        Some(references) => references.await,
17673                        None => Ok(Navigated::No),
17674                    }
17675                }
17676            }
17677        })
17678    }
17679
17680    pub fn go_to_declaration(
17681        &mut self,
17682        _: &GoToDeclaration,
17683        window: &mut Window,
17684        cx: &mut Context<Self>,
17685    ) -> Task<Result<Navigated>> {
17686        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17687    }
17688
17689    pub fn go_to_declaration_split(
17690        &mut self,
17691        _: &GoToDeclaration,
17692        window: &mut Window,
17693        cx: &mut Context<Self>,
17694    ) -> Task<Result<Navigated>> {
17695        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17696    }
17697
17698    pub fn go_to_implementation(
17699        &mut self,
17700        _: &GoToImplementation,
17701        window: &mut Window,
17702        cx: &mut Context<Self>,
17703    ) -> Task<Result<Navigated>> {
17704        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17705    }
17706
17707    pub fn go_to_implementation_split(
17708        &mut self,
17709        _: &GoToImplementationSplit,
17710        window: &mut Window,
17711        cx: &mut Context<Self>,
17712    ) -> Task<Result<Navigated>> {
17713        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17714    }
17715
17716    pub fn go_to_type_definition(
17717        &mut self,
17718        _: &GoToTypeDefinition,
17719        window: &mut Window,
17720        cx: &mut Context<Self>,
17721    ) -> Task<Result<Navigated>> {
17722        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17723    }
17724
17725    pub fn go_to_definition_split(
17726        &mut self,
17727        _: &GoToDefinitionSplit,
17728        window: &mut Window,
17729        cx: &mut Context<Self>,
17730    ) -> Task<Result<Navigated>> {
17731        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17732    }
17733
17734    pub fn go_to_type_definition_split(
17735        &mut self,
17736        _: &GoToTypeDefinitionSplit,
17737        window: &mut Window,
17738        cx: &mut Context<Self>,
17739    ) -> Task<Result<Navigated>> {
17740        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17741    }
17742
17743    fn go_to_definition_of_kind(
17744        &mut self,
17745        kind: GotoDefinitionKind,
17746        split: bool,
17747        window: &mut Window,
17748        cx: &mut Context<Self>,
17749    ) -> Task<Result<Navigated>> {
17750        let Some(provider) = self.semantics_provider.clone() else {
17751            return Task::ready(Ok(Navigated::No));
17752        };
17753        let head = self
17754            .selections
17755            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
17756            .head();
17757        let buffer = self.buffer.read(cx);
17758        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
17759            return Task::ready(Ok(Navigated::No));
17760        };
17761        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
17762            return Task::ready(Ok(Navigated::No));
17763        };
17764
17765        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
17766
17767        cx.spawn_in(window, async move |editor, cx| {
17768            let Some(definitions) = definitions.await? else {
17769                return Ok(Navigated::No);
17770            };
17771            let navigated = editor
17772                .update_in(cx, |editor, window, cx| {
17773                    editor.navigate_to_hover_links(
17774                        Some(kind),
17775                        definitions
17776                            .into_iter()
17777                            .filter(|location| {
17778                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
17779                            })
17780                            .map(HoverLink::Text)
17781                            .collect::<Vec<_>>(),
17782                        nav_entry,
17783                        split,
17784                        window,
17785                        cx,
17786                    )
17787                })?
17788                .await?;
17789            anyhow::Ok(navigated)
17790        })
17791    }
17792
17793    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
17794        let selection = self.selections.newest_anchor();
17795        let head = selection.head();
17796        let tail = selection.tail();
17797
17798        let Some((buffer, start_position)) =
17799            self.buffer.read(cx).text_anchor_for_position(head, cx)
17800        else {
17801            return;
17802        };
17803
17804        let end_position = if head != tail {
17805            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
17806                return;
17807            };
17808            Some(pos)
17809        } else {
17810            None
17811        };
17812
17813        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
17814            let url = if let Some(end_pos) = end_position {
17815                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
17816            } else {
17817                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
17818            };
17819
17820            if let Some(url) = url {
17821                cx.update(|window, cx| {
17822                    if parse_zed_link(&url, cx).is_some() {
17823                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
17824                    } else {
17825                        cx.open_url(&url);
17826                    }
17827                })?;
17828            }
17829
17830            anyhow::Ok(())
17831        });
17832
17833        url_finder.detach();
17834    }
17835
17836    pub fn open_selected_filename(
17837        &mut self,
17838        _: &OpenSelectedFilename,
17839        window: &mut Window,
17840        cx: &mut Context<Self>,
17841    ) {
17842        let Some(workspace) = self.workspace() else {
17843            return;
17844        };
17845
17846        let position = self.selections.newest_anchor().head();
17847
17848        let Some((buffer, buffer_position)) =
17849            self.buffer.read(cx).text_anchor_for_position(position, cx)
17850        else {
17851            return;
17852        };
17853
17854        let project = self.project.clone();
17855
17856        cx.spawn_in(window, async move |_, cx| {
17857            let result = find_file(&buffer, project, buffer_position, cx).await;
17858
17859            if let Some((_, path)) = result {
17860                workspace
17861                    .update_in(cx, |workspace, window, cx| {
17862                        workspace.open_resolved_path(path, window, cx)
17863                    })?
17864                    .await?;
17865            }
17866            anyhow::Ok(())
17867        })
17868        .detach();
17869    }
17870
17871    pub(crate) fn navigate_to_hover_links(
17872        &mut self,
17873        kind: Option<GotoDefinitionKind>,
17874        definitions: Vec<HoverLink>,
17875        origin: Option<NavigationEntry>,
17876        split: bool,
17877        window: &mut Window,
17878        cx: &mut Context<Editor>,
17879    ) -> Task<Result<Navigated>> {
17880        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
17881        let mut first_url_or_file = None;
17882        let definitions: Vec<_> = definitions
17883            .into_iter()
17884            .filter_map(|def| match def {
17885                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
17886                HoverLink::InlayHint(lsp_location, server_id) => {
17887                    let computation =
17888                        self.compute_target_location(lsp_location, server_id, window, cx);
17889                    Some(cx.background_spawn(computation))
17890                }
17891                HoverLink::Url(url) => {
17892                    first_url_or_file = Some(Either::Left(url));
17893                    None
17894                }
17895                HoverLink::File(path) => {
17896                    first_url_or_file = Some(Either::Right(path));
17897                    None
17898                }
17899            })
17900            .collect();
17901
17902        let workspace = self.workspace();
17903
17904        cx.spawn_in(window, async move |editor, cx| {
17905            let locations: Vec<Location> = future::join_all(definitions)
17906                .await
17907                .into_iter()
17908                .filter_map(|location| location.transpose())
17909                .collect::<Result<_>>()
17910                .context("location tasks")?;
17911            let mut locations = cx.update(|_, cx| {
17912                locations
17913                    .into_iter()
17914                    .map(|location| {
17915                        let buffer = location.buffer.read(cx);
17916                        (location.buffer, location.range.to_point(buffer))
17917                    })
17918                    .into_group_map()
17919            })?;
17920            let mut num_locations = 0;
17921            for ranges in locations.values_mut() {
17922                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
17923                ranges.dedup();
17924                num_locations += ranges.len();
17925            }
17926
17927            if num_locations > 1 {
17928                let tab_kind = match kind {
17929                    Some(GotoDefinitionKind::Implementation) => "Implementations",
17930                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
17931                    Some(GotoDefinitionKind::Declaration) => "Declarations",
17932                    Some(GotoDefinitionKind::Type) => "Types",
17933                };
17934                let title = editor
17935                    .update_in(cx, |_, _, cx| {
17936                        let target = locations
17937                            .iter()
17938                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
17939                            .map(|(buffer, location)| {
17940                                buffer
17941                                    .read(cx)
17942                                    .text_for_range(location.clone())
17943                                    .collect::<String>()
17944                            })
17945                            .filter(|text| !text.contains('\n'))
17946                            .unique()
17947                            .take(3)
17948                            .join(", ");
17949                        if target.is_empty() {
17950                            tab_kind.to_owned()
17951                        } else {
17952                            format!("{tab_kind} for {target}")
17953                        }
17954                    })
17955                    .context("buffer title")?;
17956
17957                let Some(workspace) = workspace else {
17958                    return Ok(Navigated::No);
17959                };
17960
17961                let opened = workspace
17962                    .update_in(cx, |workspace, window, cx| {
17963                        let allow_preview = PreviewTabsSettings::get_global(cx)
17964                            .enable_preview_multibuffer_from_code_navigation;
17965                        if let Some((target_editor, target_pane)) =
17966                            Self::open_locations_in_multibuffer(
17967                                workspace,
17968                                locations,
17969                                title,
17970                                split,
17971                                allow_preview,
17972                                MultibufferSelectionMode::First,
17973                                window,
17974                                cx,
17975                            )
17976                        {
17977                            // We create our own nav history instead of using
17978                            // `target_editor.nav_history` because `nav_history`
17979                            // seems to be populated asynchronously when an item
17980                            // is added to a pane
17981                            let mut nav_history = target_pane
17982                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
17983                            target_editor.update(cx, |editor, cx| {
17984                                let nav_data = editor
17985                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
17986                                let target =
17987                                    Some(nav_history.navigation_entry(Some(
17988                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
17989                                    )));
17990                                nav_history.push_tag(origin, target);
17991                            })
17992                        }
17993                    })
17994                    .is_ok();
17995
17996                anyhow::Ok(Navigated::from_bool(opened))
17997            } else if num_locations == 0 {
17998                // If there is one url or file, open it directly
17999                match first_url_or_file {
18000                    Some(Either::Left(url)) => {
18001                        cx.update(|window, cx| {
18002                            if parse_zed_link(&url, cx).is_some() {
18003                                window
18004                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18005                            } else {
18006                                cx.open_url(&url);
18007                            }
18008                        })?;
18009                        Ok(Navigated::Yes)
18010                    }
18011                    Some(Either::Right(path)) => {
18012                        // TODO(andrew): respect preview tab settings
18013                        //               `enable_keep_preview_on_code_navigation` and
18014                        //               `enable_preview_file_from_code_navigation`
18015                        let Some(workspace) = workspace else {
18016                            return Ok(Navigated::No);
18017                        };
18018                        workspace
18019                            .update_in(cx, |workspace, window, cx| {
18020                                workspace.open_resolved_path(path, window, cx)
18021                            })?
18022                            .await?;
18023                        Ok(Navigated::Yes)
18024                    }
18025                    None => Ok(Navigated::No),
18026                }
18027            } else {
18028                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18029                let target_range = target_ranges.first().unwrap().clone();
18030
18031                editor.update_in(cx, |editor, window, cx| {
18032                    let range = editor.range_for_match(&target_range);
18033                    let range = collapse_multiline_range(range);
18034
18035                    if !split
18036                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18037                    {
18038                        editor.go_to_singleton_buffer_range(range, window, cx);
18039
18040                        let target =
18041                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18042                        if let Some(mut nav_history) = editor.nav_history.clone() {
18043                            nav_history.push_tag(origin, target);
18044                        }
18045                    } else {
18046                        let Some(workspace) = workspace else {
18047                            return Navigated::No;
18048                        };
18049                        let pane = workspace.read(cx).active_pane().clone();
18050                        window.defer(cx, move |window, cx| {
18051                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18052                                workspace.update(cx, |workspace, cx| {
18053                                    let pane = if split {
18054                                        workspace.adjacent_pane(window, cx)
18055                                    } else {
18056                                        workspace.active_pane().clone()
18057                                    };
18058
18059                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18060                                    let keep_old_preview = preview_tabs_settings
18061                                        .enable_keep_preview_on_code_navigation;
18062                                    let allow_new_preview = preview_tabs_settings
18063                                        .enable_preview_file_from_code_navigation;
18064
18065                                    let editor = workspace.open_project_item(
18066                                        pane.clone(),
18067                                        target_buffer.clone(),
18068                                        true,
18069                                        true,
18070                                        keep_old_preview,
18071                                        allow_new_preview,
18072                                        window,
18073                                        cx,
18074                                    );
18075                                    (editor, pane)
18076                                });
18077                            // We create our own nav history instead of using
18078                            // `target_editor.nav_history` because `nav_history`
18079                            // seems to be populated asynchronously when an item
18080                            // is added to a pane
18081                            let mut nav_history = target_pane
18082                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18083                            target_editor.update(cx, |target_editor, cx| {
18084                                // When selecting a definition in a different buffer, disable the nav history
18085                                // to avoid creating a history entry at the previous cursor location.
18086                                pane.update(cx, |pane, _| pane.disable_history());
18087                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18088
18089                                let nav_data = target_editor.navigation_data(
18090                                    target_editor.selections.newest_anchor().head(),
18091                                    cx,
18092                                );
18093                                let target =
18094                                    Some(nav_history.navigation_entry(Some(
18095                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18096                                    )));
18097                                nav_history.push_tag(origin, target);
18098                                pane.update(cx, |pane, _| pane.enable_history());
18099                            });
18100                        });
18101                    }
18102                    Navigated::Yes
18103                })
18104            }
18105        })
18106    }
18107
18108    fn compute_target_location(
18109        &self,
18110        lsp_location: lsp::Location,
18111        server_id: LanguageServerId,
18112        window: &mut Window,
18113        cx: &mut Context<Self>,
18114    ) -> Task<anyhow::Result<Option<Location>>> {
18115        let Some(project) = self.project.clone() else {
18116            return Task::ready(Ok(None));
18117        };
18118
18119        cx.spawn_in(window, async move |editor, cx| {
18120            let location_task = editor.update(cx, |_, cx| {
18121                project.update(cx, |project, cx| {
18122                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18123                })
18124            })?;
18125            let location = Some({
18126                let target_buffer_handle = location_task.await.context("open local buffer")?;
18127                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18128                    let target_start = target_buffer
18129                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18130                    let target_end = target_buffer
18131                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18132                    target_buffer.anchor_after(target_start)
18133                        ..target_buffer.anchor_before(target_end)
18134                });
18135                Location {
18136                    buffer: target_buffer_handle,
18137                    range,
18138                }
18139            });
18140            Ok(location)
18141        })
18142    }
18143
18144    fn go_to_next_reference(
18145        &mut self,
18146        _: &GoToNextReference,
18147        window: &mut Window,
18148        cx: &mut Context<Self>,
18149    ) {
18150        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18151        if let Some(task) = task {
18152            task.detach();
18153        };
18154    }
18155
18156    fn go_to_prev_reference(
18157        &mut self,
18158        _: &GoToPreviousReference,
18159        window: &mut Window,
18160        cx: &mut Context<Self>,
18161    ) {
18162        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18163        if let Some(task) = task {
18164            task.detach();
18165        };
18166    }
18167
18168    pub fn go_to_reference_before_or_after_position(
18169        &mut self,
18170        direction: Direction,
18171        count: usize,
18172        window: &mut Window,
18173        cx: &mut Context<Self>,
18174    ) -> Option<Task<Result<()>>> {
18175        let selection = self.selections.newest_anchor();
18176        let head = selection.head();
18177
18178        let multi_buffer = self.buffer.read(cx);
18179
18180        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18181        let workspace = self.workspace()?;
18182        let project = workspace.read(cx).project().clone();
18183        let references =
18184            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18185        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18186            let Some(locations) = references.await? else {
18187                return Ok(());
18188            };
18189
18190            if locations.is_empty() {
18191                // totally normal - the cursor may be on something which is not
18192                // a symbol (e.g. a keyword)
18193                log::info!("no references found under cursor");
18194                return Ok(());
18195            }
18196
18197            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18198
18199            let (locations, current_location_index) =
18200                multi_buffer.update(cx, |multi_buffer, cx| {
18201                    let mut locations = locations
18202                        .into_iter()
18203                        .filter_map(|loc| {
18204                            let start = multi_buffer.buffer_anchor_to_anchor(
18205                                &loc.buffer,
18206                                loc.range.start,
18207                                cx,
18208                            )?;
18209                            let end = multi_buffer.buffer_anchor_to_anchor(
18210                                &loc.buffer,
18211                                loc.range.end,
18212                                cx,
18213                            )?;
18214                            Some(start..end)
18215                        })
18216                        .collect::<Vec<_>>();
18217
18218                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18219                    // There is an O(n) implementation, but given this list will be
18220                    // small (usually <100 items), the extra O(log(n)) factor isn't
18221                    // worth the (surprisingly large amount of) extra complexity.
18222                    locations
18223                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18224
18225                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18226
18227                    let current_location_index = locations.iter().position(|loc| {
18228                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18229                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18230                    });
18231
18232                    (locations, current_location_index)
18233                });
18234
18235            let Some(current_location_index) = current_location_index else {
18236                // This indicates something has gone wrong, because we already
18237                // handle the "no references" case above
18238                log::error!(
18239                    "failed to find current reference under cursor. Total references: {}",
18240                    locations.len()
18241                );
18242                return Ok(());
18243            };
18244
18245            let destination_location_index = match direction {
18246                Direction::Next => (current_location_index + count) % locations.len(),
18247                Direction::Prev => {
18248                    (current_location_index + locations.len() - count % locations.len())
18249                        % locations.len()
18250                }
18251            };
18252
18253            // TODO(cameron): is this needed?
18254            // the thinking is to avoid "jumping to the current location" (avoid
18255            // polluting "jumplist" in vim terms)
18256            if current_location_index == destination_location_index {
18257                return Ok(());
18258            }
18259
18260            let Range { start, end } = locations[destination_location_index];
18261
18262            editor.update_in(cx, |editor, window, cx| {
18263                let effects = SelectionEffects::default();
18264
18265                editor.unfold_ranges(&[start..end], false, false, cx);
18266                editor.change_selections(effects, window, cx, |s| {
18267                    s.select_ranges([start..start]);
18268                });
18269            })?;
18270
18271            Ok(())
18272        }))
18273    }
18274
18275    pub fn find_all_references(
18276        &mut self,
18277        action: &FindAllReferences,
18278        window: &mut Window,
18279        cx: &mut Context<Self>,
18280    ) -> Option<Task<Result<Navigated>>> {
18281        let always_open_multibuffer = action.always_open_multibuffer;
18282        let selection = self.selections.newest_anchor();
18283        let multi_buffer = self.buffer.read(cx);
18284        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18285        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18286        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18287        let head = selection_offset.head();
18288
18289        let head_anchor = multi_buffer_snapshot.anchor_at(
18290            head,
18291            if head < selection_offset.tail() {
18292                Bias::Right
18293            } else {
18294                Bias::Left
18295            },
18296        );
18297
18298        match self
18299            .find_all_references_task_sources
18300            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18301        {
18302            Ok(_) => {
18303                log::info!(
18304                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18305                );
18306                return None;
18307            }
18308            Err(i) => {
18309                self.find_all_references_task_sources.insert(i, head_anchor);
18310            }
18311        }
18312
18313        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18314        let workspace = self.workspace()?;
18315        let project = workspace.read(cx).project().clone();
18316        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18317        Some(cx.spawn_in(window, async move |editor, cx| {
18318            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18319                if let Ok(i) = editor
18320                    .find_all_references_task_sources
18321                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18322                {
18323                    editor.find_all_references_task_sources.remove(i);
18324                }
18325            });
18326
18327            let Some(locations) = references.await? else {
18328                return anyhow::Ok(Navigated::No);
18329            };
18330            let mut locations = cx.update(|_, cx| {
18331                locations
18332                    .into_iter()
18333                    .map(|location| {
18334                        let buffer = location.buffer.read(cx);
18335                        (location.buffer, location.range.to_point(buffer))
18336                    })
18337                    // if special-casing the single-match case, remove ranges
18338                    // that intersect current selection
18339                    .filter(|(location_buffer, location)| {
18340                        if always_open_multibuffer || &buffer != location_buffer {
18341                            return true;
18342                        }
18343
18344                        !location.contains_inclusive(&selection_point.range())
18345                    })
18346                    .into_group_map()
18347            })?;
18348            if locations.is_empty() {
18349                return anyhow::Ok(Navigated::No);
18350            }
18351            for ranges in locations.values_mut() {
18352                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18353                ranges.dedup();
18354            }
18355            let mut num_locations = 0;
18356            for ranges in locations.values_mut() {
18357                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18358                ranges.dedup();
18359                num_locations += ranges.len();
18360            }
18361
18362            if num_locations == 1 && !always_open_multibuffer {
18363                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18364                let target_range = target_ranges.first().unwrap().clone();
18365
18366                return editor.update_in(cx, |editor, window, cx| {
18367                    let range = target_range.to_point(target_buffer.read(cx));
18368                    let range = editor.range_for_match(&range);
18369                    let range = range.start..range.start;
18370
18371                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18372                        editor.go_to_singleton_buffer_range(range, window, cx);
18373                    } else {
18374                        let pane = workspace.read(cx).active_pane().clone();
18375                        window.defer(cx, move |window, cx| {
18376                            let target_editor: Entity<Self> =
18377                                workspace.update(cx, |workspace, cx| {
18378                                    let pane = workspace.active_pane().clone();
18379
18380                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18381                                    let keep_old_preview = preview_tabs_settings
18382                                        .enable_keep_preview_on_code_navigation;
18383                                    let allow_new_preview = preview_tabs_settings
18384                                        .enable_preview_file_from_code_navigation;
18385
18386                                    workspace.open_project_item(
18387                                        pane,
18388                                        target_buffer.clone(),
18389                                        true,
18390                                        true,
18391                                        keep_old_preview,
18392                                        allow_new_preview,
18393                                        window,
18394                                        cx,
18395                                    )
18396                                });
18397                            target_editor.update(cx, |target_editor, cx| {
18398                                // When selecting a definition in a different buffer, disable the nav history
18399                                // to avoid creating a history entry at the previous cursor location.
18400                                pane.update(cx, |pane, _| pane.disable_history());
18401                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18402                                pane.update(cx, |pane, _| pane.enable_history());
18403                            });
18404                        });
18405                    }
18406                    Navigated::No
18407                });
18408            }
18409
18410            workspace.update_in(cx, |workspace, window, cx| {
18411                let target = locations
18412                    .iter()
18413                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18414                    .map(|(buffer, location)| {
18415                        buffer
18416                            .read(cx)
18417                            .text_for_range(location.clone())
18418                            .collect::<String>()
18419                    })
18420                    .filter(|text| !text.contains('\n'))
18421                    .unique()
18422                    .take(3)
18423                    .join(", ");
18424                let title = if target.is_empty() {
18425                    "References".to_owned()
18426                } else {
18427                    format!("References to {target}")
18428                };
18429                let allow_preview = PreviewTabsSettings::get_global(cx)
18430                    .enable_preview_multibuffer_from_code_navigation;
18431                Self::open_locations_in_multibuffer(
18432                    workspace,
18433                    locations,
18434                    title,
18435                    false,
18436                    allow_preview,
18437                    MultibufferSelectionMode::First,
18438                    window,
18439                    cx,
18440                );
18441                Navigated::Yes
18442            })
18443        }))
18444    }
18445
18446    /// Opens a multibuffer with the given project locations in it.
18447    pub fn open_locations_in_multibuffer(
18448        workspace: &mut Workspace,
18449        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18450        title: String,
18451        split: bool,
18452        allow_preview: bool,
18453        multibuffer_selection_mode: MultibufferSelectionMode,
18454        window: &mut Window,
18455        cx: &mut Context<Workspace>,
18456    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18457        if locations.is_empty() {
18458            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18459            return None;
18460        }
18461
18462        let capability = workspace.project().read(cx).capability();
18463        let mut ranges = <Vec<Range<Anchor>>>::new();
18464
18465        // a key to find existing multibuffer editors with the same set of locations
18466        // to prevent us from opening more and more multibuffer tabs for searches and the like
18467        let mut key = (title.clone(), vec![]);
18468        let excerpt_buffer = cx.new(|cx| {
18469            let key = &mut key.1;
18470            let mut multibuffer = MultiBuffer::new(capability);
18471            for (buffer, mut ranges_for_buffer) in locations {
18472                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18473                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18474                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18475                    PathKey::for_buffer(&buffer, cx),
18476                    buffer.clone(),
18477                    ranges_for_buffer,
18478                    multibuffer_context_lines(cx),
18479                    cx,
18480                );
18481                ranges.extend(new_ranges)
18482            }
18483
18484            multibuffer.with_title(title)
18485        });
18486        let existing = workspace.active_pane().update(cx, |pane, cx| {
18487            pane.items()
18488                .filter_map(|item| item.downcast::<Editor>())
18489                .find(|editor| {
18490                    editor
18491                        .read(cx)
18492                        .lookup_key
18493                        .as_ref()
18494                        .and_then(|it| {
18495                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18496                        })
18497                        .is_some_and(|it| *it == key)
18498                })
18499        });
18500        let was_existing = existing.is_some();
18501        let editor = existing.unwrap_or_else(|| {
18502            cx.new(|cx| {
18503                let mut editor = Editor::for_multibuffer(
18504                    excerpt_buffer,
18505                    Some(workspace.project().clone()),
18506                    window,
18507                    cx,
18508                );
18509                editor.lookup_key = Some(Box::new(key));
18510                editor
18511            })
18512        });
18513        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18514            MultibufferSelectionMode::First => {
18515                if let Some(first_range) = ranges.first() {
18516                    editor.change_selections(
18517                        SelectionEffects::no_scroll(),
18518                        window,
18519                        cx,
18520                        |selections| {
18521                            selections.clear_disjoint();
18522                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18523                        },
18524                    );
18525                }
18526                editor.highlight_background::<Self>(
18527                    &ranges,
18528                    |_, theme| theme.colors().editor_highlighted_line_background,
18529                    cx,
18530                );
18531            }
18532            MultibufferSelectionMode::All => {
18533                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18534                    selections.clear_disjoint();
18535                    selections.select_anchor_ranges(ranges);
18536                });
18537            }
18538        });
18539
18540        let item = Box::new(editor.clone());
18541
18542        let pane = if split {
18543            workspace.adjacent_pane(window, cx)
18544        } else {
18545            workspace.active_pane().clone()
18546        };
18547        let activate_pane = split;
18548
18549        let mut destination_index = None;
18550        pane.update(cx, |pane, cx| {
18551            if allow_preview && !was_existing {
18552                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18553            }
18554            if was_existing && !allow_preview {
18555                pane.unpreview_item_if_preview(item.item_id());
18556            }
18557            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18558        });
18559
18560        Some((editor, pane))
18561    }
18562
18563    pub fn rename(
18564        &mut self,
18565        _: &Rename,
18566        window: &mut Window,
18567        cx: &mut Context<Self>,
18568    ) -> Option<Task<Result<()>>> {
18569        use language::ToOffset as _;
18570
18571        let provider = self.semantics_provider.clone()?;
18572        let selection = self.selections.newest_anchor().clone();
18573        let (cursor_buffer, cursor_buffer_position) = self
18574            .buffer
18575            .read(cx)
18576            .text_anchor_for_position(selection.head(), cx)?;
18577        let (tail_buffer, cursor_buffer_position_end) = self
18578            .buffer
18579            .read(cx)
18580            .text_anchor_for_position(selection.tail(), cx)?;
18581        if tail_buffer != cursor_buffer {
18582            return None;
18583        }
18584
18585        let snapshot = cursor_buffer.read(cx).snapshot();
18586        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18587        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18588        let prepare_rename = provider
18589            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18590            .unwrap_or_else(|| Task::ready(Ok(None)));
18591        drop(snapshot);
18592
18593        Some(cx.spawn_in(window, async move |this, cx| {
18594            let rename_range = if let Some(range) = prepare_rename.await? {
18595                Some(range)
18596            } else {
18597                this.update(cx, |this, cx| {
18598                    let buffer = this.buffer.read(cx).snapshot(cx);
18599                    let mut buffer_highlights = this
18600                        .document_highlights_for_position(selection.head(), &buffer)
18601                        .filter(|highlight| {
18602                            highlight.start.excerpt_id == selection.head().excerpt_id
18603                                && highlight.end.excerpt_id == selection.head().excerpt_id
18604                        });
18605                    buffer_highlights
18606                        .next()
18607                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18608                })?
18609            };
18610            if let Some(rename_range) = rename_range {
18611                this.update_in(cx, |this, window, cx| {
18612                    let snapshot = cursor_buffer.read(cx).snapshot();
18613                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18614                    let cursor_offset_in_rename_range =
18615                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18616                    let cursor_offset_in_rename_range_end =
18617                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18618
18619                    this.take_rename(false, window, cx);
18620                    let buffer = this.buffer.read(cx).read(cx);
18621                    let cursor_offset = selection.head().to_offset(&buffer);
18622                    let rename_start =
18623                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18624                    let rename_end = rename_start + rename_buffer_range.len();
18625                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18626                    let mut old_highlight_id = None;
18627                    let old_name: Arc<str> = buffer
18628                        .chunks(rename_start..rename_end, true)
18629                        .map(|chunk| {
18630                            if old_highlight_id.is_none() {
18631                                old_highlight_id = chunk.syntax_highlight_id;
18632                            }
18633                            chunk.text
18634                        })
18635                        .collect::<String>()
18636                        .into();
18637
18638                    drop(buffer);
18639
18640                    // Position the selection in the rename editor so that it matches the current selection.
18641                    this.show_local_selections = false;
18642                    let rename_editor = cx.new(|cx| {
18643                        let mut editor = Editor::single_line(window, cx);
18644                        editor.buffer.update(cx, |buffer, cx| {
18645                            buffer.edit(
18646                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18647                                None,
18648                                cx,
18649                            )
18650                        });
18651                        let cursor_offset_in_rename_range =
18652                            MultiBufferOffset(cursor_offset_in_rename_range);
18653                        let cursor_offset_in_rename_range_end =
18654                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18655                        let rename_selection_range = match cursor_offset_in_rename_range
18656                            .cmp(&cursor_offset_in_rename_range_end)
18657                        {
18658                            Ordering::Equal => {
18659                                editor.select_all(&SelectAll, window, cx);
18660                                return editor;
18661                            }
18662                            Ordering::Less => {
18663                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18664                            }
18665                            Ordering::Greater => {
18666                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18667                            }
18668                        };
18669                        if rename_selection_range.end.0 > old_name.len() {
18670                            editor.select_all(&SelectAll, window, cx);
18671                        } else {
18672                            editor.change_selections(Default::default(), window, cx, |s| {
18673                                s.select_ranges([rename_selection_range]);
18674                            });
18675                        }
18676                        editor
18677                    });
18678                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18679                        if e == &EditorEvent::Focused {
18680                            cx.emit(EditorEvent::FocusedIn)
18681                        }
18682                    })
18683                    .detach();
18684
18685                    let write_highlights =
18686                        this.clear_background_highlights::<DocumentHighlightWrite>(cx);
18687                    let read_highlights =
18688                        this.clear_background_highlights::<DocumentHighlightRead>(cx);
18689                    let ranges = write_highlights
18690                        .iter()
18691                        .flat_map(|(_, ranges)| ranges.iter())
18692                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18693                        .cloned()
18694                        .collect();
18695
18696                    this.highlight_text::<Rename>(
18697                        ranges,
18698                        HighlightStyle {
18699                            fade_out: Some(0.6),
18700                            ..Default::default()
18701                        },
18702                        cx,
18703                    );
18704                    let rename_focus_handle = rename_editor.focus_handle(cx);
18705                    window.focus(&rename_focus_handle, cx);
18706                    let block_id = this.insert_blocks(
18707                        [BlockProperties {
18708                            style: BlockStyle::Flex,
18709                            placement: BlockPlacement::Below(range.start),
18710                            height: Some(1),
18711                            render: Arc::new({
18712                                let rename_editor = rename_editor.clone();
18713                                move |cx: &mut BlockContext| {
18714                                    let mut text_style = cx.editor_style.text.clone();
18715                                    if let Some(highlight_style) = old_highlight_id
18716                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18717                                    {
18718                                        text_style = text_style.highlight(highlight_style);
18719                                    }
18720                                    div()
18721                                        .block_mouse_except_scroll()
18722                                        .pl(cx.anchor_x)
18723                                        .child(EditorElement::new(
18724                                            &rename_editor,
18725                                            EditorStyle {
18726                                                background: cx.theme().system().transparent,
18727                                                local_player: cx.editor_style.local_player,
18728                                                text: text_style,
18729                                                scrollbar_width: cx.editor_style.scrollbar_width,
18730                                                syntax: cx.editor_style.syntax.clone(),
18731                                                status: cx.editor_style.status.clone(),
18732                                                inlay_hints_style: HighlightStyle {
18733                                                    font_weight: Some(FontWeight::BOLD),
18734                                                    ..make_inlay_hints_style(cx.app)
18735                                                },
18736                                                edit_prediction_styles: make_suggestion_styles(
18737                                                    cx.app,
18738                                                ),
18739                                                ..EditorStyle::default()
18740                                            },
18741                                        ))
18742                                        .into_any_element()
18743                                }
18744                            }),
18745                            priority: 0,
18746                        }],
18747                        Some(Autoscroll::fit()),
18748                        cx,
18749                    )[0];
18750                    this.pending_rename = Some(RenameState {
18751                        range,
18752                        old_name,
18753                        editor: rename_editor,
18754                        block_id,
18755                    });
18756                })?;
18757            }
18758
18759            Ok(())
18760        }))
18761    }
18762
18763    pub fn confirm_rename(
18764        &mut self,
18765        _: &ConfirmRename,
18766        window: &mut Window,
18767        cx: &mut Context<Self>,
18768    ) -> Option<Task<Result<()>>> {
18769        let rename = self.take_rename(false, window, cx)?;
18770        let workspace = self.workspace()?.downgrade();
18771        let (buffer, start) = self
18772            .buffer
18773            .read(cx)
18774            .text_anchor_for_position(rename.range.start, cx)?;
18775        let (end_buffer, _) = self
18776            .buffer
18777            .read(cx)
18778            .text_anchor_for_position(rename.range.end, cx)?;
18779        if buffer != end_buffer {
18780            return None;
18781        }
18782
18783        let old_name = rename.old_name;
18784        let new_name = rename.editor.read(cx).text(cx);
18785
18786        let rename = self.semantics_provider.as_ref()?.perform_rename(
18787            &buffer,
18788            start,
18789            new_name.clone(),
18790            cx,
18791        )?;
18792
18793        Some(cx.spawn_in(window, async move |editor, cx| {
18794            let project_transaction = rename.await?;
18795            Self::open_project_transaction(
18796                &editor,
18797                workspace,
18798                project_transaction,
18799                format!("Rename: {}{}", old_name, new_name),
18800                cx,
18801            )
18802            .await?;
18803
18804            editor.update(cx, |editor, cx| {
18805                editor.refresh_document_highlights(cx);
18806            })?;
18807            Ok(())
18808        }))
18809    }
18810
18811    fn take_rename(
18812        &mut self,
18813        moving_cursor: bool,
18814        window: &mut Window,
18815        cx: &mut Context<Self>,
18816    ) -> Option<RenameState> {
18817        let rename = self.pending_rename.take()?;
18818        if rename.editor.focus_handle(cx).is_focused(window) {
18819            window.focus(&self.focus_handle, cx);
18820        }
18821
18822        self.remove_blocks(
18823            [rename.block_id].into_iter().collect(),
18824            Some(Autoscroll::fit()),
18825            cx,
18826        );
18827        self.clear_highlights::<Rename>(cx);
18828        self.show_local_selections = true;
18829
18830        if moving_cursor {
18831            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
18832                editor
18833                    .selections
18834                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
18835                    .head()
18836            });
18837
18838            // Update the selection to match the position of the selection inside
18839            // the rename editor.
18840            let snapshot = self.buffer.read(cx).read(cx);
18841            let rename_range = rename.range.to_offset(&snapshot);
18842            let cursor_in_editor = snapshot
18843                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
18844                .min(rename_range.end);
18845            drop(snapshot);
18846
18847            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
18848                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
18849            });
18850        } else {
18851            self.refresh_document_highlights(cx);
18852        }
18853
18854        Some(rename)
18855    }
18856
18857    pub fn pending_rename(&self) -> Option<&RenameState> {
18858        self.pending_rename.as_ref()
18859    }
18860
18861    fn format(
18862        &mut self,
18863        _: &Format,
18864        window: &mut Window,
18865        cx: &mut Context<Self>,
18866    ) -> Option<Task<Result<()>>> {
18867        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18868
18869        let project = match &self.project {
18870            Some(project) => project.clone(),
18871            None => return None,
18872        };
18873
18874        Some(self.perform_format(
18875            project,
18876            FormatTrigger::Manual,
18877            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
18878            window,
18879            cx,
18880        ))
18881    }
18882
18883    fn format_selections(
18884        &mut self,
18885        _: &FormatSelections,
18886        window: &mut Window,
18887        cx: &mut Context<Self>,
18888    ) -> Option<Task<Result<()>>> {
18889        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18890
18891        let project = match &self.project {
18892            Some(project) => project.clone(),
18893            None => return None,
18894        };
18895
18896        let ranges = self
18897            .selections
18898            .all_adjusted(&self.display_snapshot(cx))
18899            .into_iter()
18900            .map(|selection| selection.range())
18901            .collect_vec();
18902
18903        Some(self.perform_format(
18904            project,
18905            FormatTrigger::Manual,
18906            FormatTarget::Ranges(ranges),
18907            window,
18908            cx,
18909        ))
18910    }
18911
18912    fn perform_format(
18913        &mut self,
18914        project: Entity<Project>,
18915        trigger: FormatTrigger,
18916        target: FormatTarget,
18917        window: &mut Window,
18918        cx: &mut Context<Self>,
18919    ) -> Task<Result<()>> {
18920        let buffer = self.buffer.clone();
18921        let (buffers, target) = match target {
18922            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
18923            FormatTarget::Ranges(selection_ranges) => {
18924                let multi_buffer = buffer.read(cx);
18925                let snapshot = multi_buffer.read(cx);
18926                let mut buffers = HashSet::default();
18927                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
18928                    BTreeMap::new();
18929                for selection_range in selection_ranges {
18930                    for (buffer, buffer_range, _) in
18931                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
18932                    {
18933                        let buffer_id = buffer.remote_id();
18934                        let start = buffer.anchor_before(buffer_range.start);
18935                        let end = buffer.anchor_after(buffer_range.end);
18936                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
18937                        buffer_id_to_ranges
18938                            .entry(buffer_id)
18939                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
18940                            .or_insert_with(|| vec![start..end]);
18941                    }
18942                }
18943                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
18944            }
18945        };
18946
18947        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
18948        let selections_prev = transaction_id_prev
18949            .and_then(|transaction_id_prev| {
18950                // default to selections as they were after the last edit, if we have them,
18951                // instead of how they are now.
18952                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
18953                // will take you back to where you made the last edit, instead of staying where you scrolled
18954                self.selection_history
18955                    .transaction(transaction_id_prev)
18956                    .map(|t| t.0.clone())
18957            })
18958            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
18959
18960        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
18961        let format = project.update(cx, |project, cx| {
18962            project.format(buffers, target, true, trigger, cx)
18963        });
18964
18965        cx.spawn_in(window, async move |editor, cx| {
18966            let transaction = futures::select_biased! {
18967                transaction = format.log_err().fuse() => transaction,
18968                () = timeout => {
18969                    log::warn!("timed out waiting for formatting");
18970                    None
18971                }
18972            };
18973
18974            buffer.update(cx, |buffer, cx| {
18975                if let Some(transaction) = transaction
18976                    && !buffer.is_singleton()
18977                {
18978                    buffer.push_transaction(&transaction.0, cx);
18979                }
18980                cx.notify();
18981            });
18982
18983            if let Some(transaction_id_now) =
18984                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
18985            {
18986                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
18987                if has_new_transaction {
18988                    editor
18989                        .update(cx, |editor, _| {
18990                            editor
18991                                .selection_history
18992                                .insert_transaction(transaction_id_now, selections_prev);
18993                        })
18994                        .ok();
18995                }
18996            }
18997
18998            Ok(())
18999        })
19000    }
19001
19002    fn organize_imports(
19003        &mut self,
19004        _: &OrganizeImports,
19005        window: &mut Window,
19006        cx: &mut Context<Self>,
19007    ) -> Option<Task<Result<()>>> {
19008        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19009        let project = match &self.project {
19010            Some(project) => project.clone(),
19011            None => return None,
19012        };
19013        Some(self.perform_code_action_kind(
19014            project,
19015            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19016            window,
19017            cx,
19018        ))
19019    }
19020
19021    fn perform_code_action_kind(
19022        &mut self,
19023        project: Entity<Project>,
19024        kind: CodeActionKind,
19025        window: &mut Window,
19026        cx: &mut Context<Self>,
19027    ) -> Task<Result<()>> {
19028        let buffer = self.buffer.clone();
19029        let buffers = buffer.read(cx).all_buffers();
19030        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19031        let apply_action = project.update(cx, |project, cx| {
19032            project.apply_code_action_kind(buffers, kind, true, cx)
19033        });
19034        cx.spawn_in(window, async move |_, cx| {
19035            let transaction = futures::select_biased! {
19036                () = timeout => {
19037                    log::warn!("timed out waiting for executing code action");
19038                    None
19039                }
19040                transaction = apply_action.log_err().fuse() => transaction,
19041            };
19042            buffer.update(cx, |buffer, cx| {
19043                // check if we need this
19044                if let Some(transaction) = transaction
19045                    && !buffer.is_singleton()
19046                {
19047                    buffer.push_transaction(&transaction.0, cx);
19048                }
19049                cx.notify();
19050            });
19051            Ok(())
19052        })
19053    }
19054
19055    pub fn restart_language_server(
19056        &mut self,
19057        _: &RestartLanguageServer,
19058        _: &mut Window,
19059        cx: &mut Context<Self>,
19060    ) {
19061        if let Some(project) = self.project.clone() {
19062            self.buffer.update(cx, |multi_buffer, cx| {
19063                project.update(cx, |project, cx| {
19064                    project.restart_language_servers_for_buffers(
19065                        multi_buffer.all_buffers().into_iter().collect(),
19066                        HashSet::default(),
19067                        cx,
19068                    );
19069                });
19070            })
19071        }
19072    }
19073
19074    pub fn stop_language_server(
19075        &mut self,
19076        _: &StopLanguageServer,
19077        _: &mut Window,
19078        cx: &mut Context<Self>,
19079    ) {
19080        if let Some(project) = self.project.clone() {
19081            self.buffer.update(cx, |multi_buffer, cx| {
19082                project.update(cx, |project, cx| {
19083                    project.stop_language_servers_for_buffers(
19084                        multi_buffer.all_buffers().into_iter().collect(),
19085                        HashSet::default(),
19086                        cx,
19087                    );
19088                });
19089            });
19090            self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
19091        }
19092    }
19093
19094    fn cancel_language_server_work(
19095        workspace: &mut Workspace,
19096        _: &actions::CancelLanguageServerWork,
19097        _: &mut Window,
19098        cx: &mut Context<Workspace>,
19099    ) {
19100        let project = workspace.project();
19101        let buffers = workspace
19102            .active_item(cx)
19103            .and_then(|item| item.act_as::<Editor>(cx))
19104            .map_or(HashSet::default(), |editor| {
19105                editor.read(cx).buffer.read(cx).all_buffers()
19106            });
19107        project.update(cx, |project, cx| {
19108            project.cancel_language_server_work_for_buffers(buffers, cx);
19109        });
19110    }
19111
19112    fn show_character_palette(
19113        &mut self,
19114        _: &ShowCharacterPalette,
19115        window: &mut Window,
19116        _: &mut Context<Self>,
19117    ) {
19118        window.show_character_palette();
19119    }
19120
19121    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19122        if !self.diagnostics_enabled() {
19123            return;
19124        }
19125
19126        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19127            let buffer = self.buffer.read(cx).snapshot(cx);
19128            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19129            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19130            let is_valid = buffer
19131                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19132                .any(|entry| {
19133                    entry.diagnostic.is_primary
19134                        && !entry.range.is_empty()
19135                        && entry.range.start == primary_range_start
19136                        && entry.diagnostic.message == active_diagnostics.active_message
19137                });
19138
19139            if !is_valid {
19140                self.dismiss_diagnostics(cx);
19141            }
19142        }
19143    }
19144
19145    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19146        match &self.active_diagnostics {
19147            ActiveDiagnostic::Group(group) => Some(group),
19148            _ => None,
19149        }
19150    }
19151
19152    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19153        if !self.diagnostics_enabled() {
19154            return;
19155        }
19156        self.dismiss_diagnostics(cx);
19157        self.active_diagnostics = ActiveDiagnostic::All;
19158    }
19159
19160    fn activate_diagnostics(
19161        &mut self,
19162        buffer_id: BufferId,
19163        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19164        window: &mut Window,
19165        cx: &mut Context<Self>,
19166    ) {
19167        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19168            return;
19169        }
19170        self.dismiss_diagnostics(cx);
19171        let snapshot = self.snapshot(window, cx);
19172        let buffer = self.buffer.read(cx).snapshot(cx);
19173        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19174            return;
19175        };
19176
19177        let diagnostic_group = buffer
19178            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19179            .collect::<Vec<_>>();
19180
19181        let language_registry = self
19182            .project()
19183            .map(|project| project.read(cx).languages().clone());
19184
19185        let blocks = renderer.render_group(
19186            diagnostic_group,
19187            buffer_id,
19188            snapshot,
19189            cx.weak_entity(),
19190            language_registry,
19191            cx,
19192        );
19193
19194        let blocks = self.display_map.update(cx, |display_map, cx| {
19195            display_map.insert_blocks(blocks, cx).into_iter().collect()
19196        });
19197        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19198            active_range: buffer.anchor_before(diagnostic.range.start)
19199                ..buffer.anchor_after(diagnostic.range.end),
19200            active_message: diagnostic.diagnostic.message.clone(),
19201            group_id: diagnostic.diagnostic.group_id,
19202            blocks,
19203        });
19204        cx.notify();
19205    }
19206
19207    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19208        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19209            return;
19210        };
19211
19212        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19213        if let ActiveDiagnostic::Group(group) = prev {
19214            self.display_map.update(cx, |display_map, cx| {
19215                display_map.remove_blocks(group.blocks, cx);
19216            });
19217            cx.notify();
19218        }
19219    }
19220
19221    /// Disable inline diagnostics rendering for this editor.
19222    pub fn disable_inline_diagnostics(&mut self) {
19223        self.inline_diagnostics_enabled = false;
19224        self.inline_diagnostics_update = Task::ready(());
19225        self.inline_diagnostics.clear();
19226    }
19227
19228    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19229        self.diagnostics_enabled = false;
19230        self.dismiss_diagnostics(cx);
19231        self.inline_diagnostics_update = Task::ready(());
19232        self.inline_diagnostics.clear();
19233    }
19234
19235    pub fn disable_word_completions(&mut self) {
19236        self.word_completions_enabled = false;
19237    }
19238
19239    pub fn diagnostics_enabled(&self) -> bool {
19240        self.diagnostics_enabled && self.mode.is_full()
19241    }
19242
19243    pub fn inline_diagnostics_enabled(&self) -> bool {
19244        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19245    }
19246
19247    pub fn show_inline_diagnostics(&self) -> bool {
19248        self.show_inline_diagnostics
19249    }
19250
19251    pub fn toggle_inline_diagnostics(
19252        &mut self,
19253        _: &ToggleInlineDiagnostics,
19254        window: &mut Window,
19255        cx: &mut Context<Editor>,
19256    ) {
19257        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19258        self.refresh_inline_diagnostics(false, window, cx);
19259    }
19260
19261    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19262        self.diagnostics_max_severity = severity;
19263        self.display_map.update(cx, |display_map, _| {
19264            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19265        });
19266    }
19267
19268    pub fn toggle_diagnostics(
19269        &mut self,
19270        _: &ToggleDiagnostics,
19271        window: &mut Window,
19272        cx: &mut Context<Editor>,
19273    ) {
19274        if !self.diagnostics_enabled() {
19275            return;
19276        }
19277
19278        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19279            EditorSettings::get_global(cx)
19280                .diagnostics_max_severity
19281                .filter(|severity| severity != &DiagnosticSeverity::Off)
19282                .unwrap_or(DiagnosticSeverity::Hint)
19283        } else {
19284            DiagnosticSeverity::Off
19285        };
19286        self.set_max_diagnostics_severity(new_severity, cx);
19287        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19288            self.active_diagnostics = ActiveDiagnostic::None;
19289            self.inline_diagnostics_update = Task::ready(());
19290            self.inline_diagnostics.clear();
19291        } else {
19292            self.refresh_inline_diagnostics(false, window, cx);
19293        }
19294
19295        cx.notify();
19296    }
19297
19298    pub fn toggle_minimap(
19299        &mut self,
19300        _: &ToggleMinimap,
19301        window: &mut Window,
19302        cx: &mut Context<Editor>,
19303    ) {
19304        if self.supports_minimap(cx) {
19305            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19306        }
19307    }
19308
19309    fn refresh_inline_diagnostics(
19310        &mut self,
19311        debounce: bool,
19312        window: &mut Window,
19313        cx: &mut Context<Self>,
19314    ) {
19315        let max_severity = ProjectSettings::get_global(cx)
19316            .diagnostics
19317            .inline
19318            .max_severity
19319            .unwrap_or(self.diagnostics_max_severity);
19320
19321        if !self.inline_diagnostics_enabled()
19322            || !self.diagnostics_enabled()
19323            || !self.show_inline_diagnostics
19324            || max_severity == DiagnosticSeverity::Off
19325        {
19326            self.inline_diagnostics_update = Task::ready(());
19327            self.inline_diagnostics.clear();
19328            return;
19329        }
19330
19331        let debounce_ms = ProjectSettings::get_global(cx)
19332            .diagnostics
19333            .inline
19334            .update_debounce_ms;
19335        let debounce = if debounce && debounce_ms > 0 {
19336            Some(Duration::from_millis(debounce_ms))
19337        } else {
19338            None
19339        };
19340        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19341            if let Some(debounce) = debounce {
19342                cx.background_executor().timer(debounce).await;
19343            }
19344            let Some(snapshot) = editor.upgrade().map(|editor| {
19345                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19346            }) else {
19347                return;
19348            };
19349
19350            let new_inline_diagnostics = cx
19351                .background_spawn(async move {
19352                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19353                    for diagnostic_entry in
19354                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19355                    {
19356                        let message = diagnostic_entry
19357                            .diagnostic
19358                            .message
19359                            .split_once('\n')
19360                            .map(|(line, _)| line)
19361                            .map(SharedString::new)
19362                            .unwrap_or_else(|| {
19363                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19364                            });
19365                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19366                        let (Ok(i) | Err(i)) = inline_diagnostics
19367                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19368                        inline_diagnostics.insert(
19369                            i,
19370                            (
19371                                start_anchor,
19372                                InlineDiagnostic {
19373                                    message,
19374                                    group_id: diagnostic_entry.diagnostic.group_id,
19375                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19376                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19377                                    severity: diagnostic_entry.diagnostic.severity,
19378                                },
19379                            ),
19380                        );
19381                    }
19382                    inline_diagnostics
19383                })
19384                .await;
19385
19386            editor
19387                .update(cx, |editor, cx| {
19388                    editor.inline_diagnostics = new_inline_diagnostics;
19389                    cx.notify();
19390                })
19391                .ok();
19392        });
19393    }
19394
19395    fn pull_diagnostics(
19396        &mut self,
19397        buffer_id: BufferId,
19398        _window: &Window,
19399        cx: &mut Context<Self>,
19400    ) -> Option<()> {
19401        if self.ignore_lsp_data() || !self.diagnostics_enabled() {
19402            return None;
19403        }
19404        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19405            .diagnostics
19406            .lsp_pull_diagnostics;
19407        if !pull_diagnostics_settings.enabled {
19408            return None;
19409        }
19410        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19411        let project = self.project()?.downgrade();
19412        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19413
19414        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19415            cx.background_executor().timer(debounce).await;
19416            if let Ok(task) = project.update(cx, |project, cx| {
19417                project.lsp_store().update(cx, |lsp_store, cx| {
19418                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19419                })
19420            }) {
19421                task.await.log_err();
19422            }
19423            project
19424                .update(cx, |project, cx| {
19425                    project.lsp_store().update(cx, |lsp_store, cx| {
19426                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19427                    })
19428                })
19429                .log_err();
19430        });
19431
19432        Some(())
19433    }
19434
19435    pub fn set_selections_from_remote(
19436        &mut self,
19437        selections: Vec<Selection<Anchor>>,
19438        pending_selection: Option<Selection<Anchor>>,
19439        window: &mut Window,
19440        cx: &mut Context<Self>,
19441    ) {
19442        let old_cursor_position = self.selections.newest_anchor().head();
19443        self.selections
19444            .change_with(&self.display_snapshot(cx), |s| {
19445                s.select_anchors(selections);
19446                if let Some(pending_selection) = pending_selection {
19447                    s.set_pending(pending_selection, SelectMode::Character);
19448                } else {
19449                    s.clear_pending();
19450                }
19451            });
19452        self.selections_did_change(
19453            false,
19454            &old_cursor_position,
19455            SelectionEffects::default(),
19456            window,
19457            cx,
19458        );
19459    }
19460
19461    pub fn transact(
19462        &mut self,
19463        window: &mut Window,
19464        cx: &mut Context<Self>,
19465        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19466    ) -> Option<TransactionId> {
19467        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19468            this.start_transaction_at(Instant::now(), window, cx);
19469            update(this, window, cx);
19470            this.end_transaction_at(Instant::now(), cx)
19471        })
19472    }
19473
19474    pub fn start_transaction_at(
19475        &mut self,
19476        now: Instant,
19477        window: &mut Window,
19478        cx: &mut Context<Self>,
19479    ) -> Option<TransactionId> {
19480        self.end_selection(window, cx);
19481        if let Some(tx_id) = self
19482            .buffer
19483            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19484        {
19485            self.selection_history
19486                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19487            cx.emit(EditorEvent::TransactionBegun {
19488                transaction_id: tx_id,
19489            });
19490            Some(tx_id)
19491        } else {
19492            None
19493        }
19494    }
19495
19496    pub fn end_transaction_at(
19497        &mut self,
19498        now: Instant,
19499        cx: &mut Context<Self>,
19500    ) -> Option<TransactionId> {
19501        if let Some(transaction_id) = self
19502            .buffer
19503            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19504        {
19505            if let Some((_, end_selections)) =
19506                self.selection_history.transaction_mut(transaction_id)
19507            {
19508                *end_selections = Some(self.selections.disjoint_anchors_arc());
19509            } else {
19510                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19511            }
19512
19513            cx.emit(EditorEvent::Edited { transaction_id });
19514            Some(transaction_id)
19515        } else {
19516            None
19517        }
19518    }
19519
19520    pub fn modify_transaction_selection_history(
19521        &mut self,
19522        transaction_id: TransactionId,
19523        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19524    ) -> bool {
19525        self.selection_history
19526            .transaction_mut(transaction_id)
19527            .map(modify)
19528            .is_some()
19529    }
19530
19531    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19532        if self.selection_mark_mode {
19533            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19534                s.move_with(|_, sel| {
19535                    sel.collapse_to(sel.head(), SelectionGoal::None);
19536                });
19537            })
19538        }
19539        self.selection_mark_mode = true;
19540        cx.notify();
19541    }
19542
19543    pub fn swap_selection_ends(
19544        &mut self,
19545        _: &actions::SwapSelectionEnds,
19546        window: &mut Window,
19547        cx: &mut Context<Self>,
19548    ) {
19549        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19550            s.move_with(|_, sel| {
19551                if sel.start != sel.end {
19552                    sel.reversed = !sel.reversed
19553                }
19554            });
19555        });
19556        self.request_autoscroll(Autoscroll::newest(), cx);
19557        cx.notify();
19558    }
19559
19560    pub fn toggle_focus(
19561        workspace: &mut Workspace,
19562        _: &actions::ToggleFocus,
19563        window: &mut Window,
19564        cx: &mut Context<Workspace>,
19565    ) {
19566        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19567            return;
19568        };
19569        workspace.activate_item(&item, true, true, window, cx);
19570    }
19571
19572    pub fn toggle_fold(
19573        &mut self,
19574        _: &actions::ToggleFold,
19575        window: &mut Window,
19576        cx: &mut Context<Self>,
19577    ) {
19578        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19579            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19580            let selection = self.selections.newest::<Point>(&display_map);
19581
19582            let range = if selection.is_empty() {
19583                let point = selection.head().to_display_point(&display_map);
19584                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19585                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19586                    .to_point(&display_map);
19587                start..end
19588            } else {
19589                selection.range()
19590            };
19591            if display_map.folds_in_range(range).next().is_some() {
19592                self.unfold_lines(&Default::default(), window, cx)
19593            } else {
19594                self.fold(&Default::default(), window, cx)
19595            }
19596        } else {
19597            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19598            let buffer_ids: HashSet<_> = self
19599                .selections
19600                .disjoint_anchor_ranges()
19601                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19602                .collect();
19603
19604            let should_unfold = buffer_ids
19605                .iter()
19606                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19607
19608            for buffer_id in buffer_ids {
19609                if should_unfold {
19610                    self.unfold_buffer(buffer_id, cx);
19611                } else {
19612                    self.fold_buffer(buffer_id, cx);
19613                }
19614            }
19615        }
19616    }
19617
19618    pub fn toggle_fold_recursive(
19619        &mut self,
19620        _: &actions::ToggleFoldRecursive,
19621        window: &mut Window,
19622        cx: &mut Context<Self>,
19623    ) {
19624        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19625
19626        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19627        let range = if selection.is_empty() {
19628            let point = selection.head().to_display_point(&display_map);
19629            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19630            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19631                .to_point(&display_map);
19632            start..end
19633        } else {
19634            selection.range()
19635        };
19636        if display_map.folds_in_range(range).next().is_some() {
19637            self.unfold_recursive(&Default::default(), window, cx)
19638        } else {
19639            self.fold_recursive(&Default::default(), window, cx)
19640        }
19641    }
19642
19643    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19644        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19645            let mut to_fold = Vec::new();
19646            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19647            let selections = self.selections.all_adjusted(&display_map);
19648
19649            for selection in selections {
19650                let range = selection.range().sorted();
19651                let buffer_start_row = range.start.row;
19652
19653                if range.start.row != range.end.row {
19654                    let mut found = false;
19655                    let mut row = range.start.row;
19656                    while row <= range.end.row {
19657                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19658                        {
19659                            found = true;
19660                            row = crease.range().end.row + 1;
19661                            to_fold.push(crease);
19662                        } else {
19663                            row += 1
19664                        }
19665                    }
19666                    if found {
19667                        continue;
19668                    }
19669                }
19670
19671                for row in (0..=range.start.row).rev() {
19672                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19673                        && crease.range().end.row >= buffer_start_row
19674                    {
19675                        to_fold.push(crease);
19676                        if row <= range.start.row {
19677                            break;
19678                        }
19679                    }
19680                }
19681            }
19682
19683            self.fold_creases(to_fold, true, window, cx);
19684        } else {
19685            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19686            let buffer_ids = self
19687                .selections
19688                .disjoint_anchor_ranges()
19689                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19690                .collect::<HashSet<_>>();
19691            for buffer_id in buffer_ids {
19692                self.fold_buffer(buffer_id, cx);
19693            }
19694        }
19695    }
19696
19697    pub fn toggle_fold_all(
19698        &mut self,
19699        _: &actions::ToggleFoldAll,
19700        window: &mut Window,
19701        cx: &mut Context<Self>,
19702    ) {
19703        let has_folds = if self.buffer.read(cx).is_singleton() {
19704            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19705            let has_folds = display_map
19706                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19707                .next()
19708                .is_some();
19709            has_folds
19710        } else {
19711            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19712            let has_folds = buffer_ids
19713                .iter()
19714                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19715            has_folds
19716        };
19717
19718        if has_folds {
19719            self.unfold_all(&actions::UnfoldAll, window, cx);
19720        } else {
19721            self.fold_all(&actions::FoldAll, window, cx);
19722        }
19723    }
19724
19725    fn fold_at_level(
19726        &mut self,
19727        fold_at: &FoldAtLevel,
19728        window: &mut Window,
19729        cx: &mut Context<Self>,
19730    ) {
19731        if !self.buffer.read(cx).is_singleton() {
19732            return;
19733        }
19734
19735        let fold_at_level = fold_at.0;
19736        let snapshot = self.buffer.read(cx).snapshot(cx);
19737        let mut to_fold = Vec::new();
19738        let mut stack = vec![(0, snapshot.max_row().0, 1)];
19739
19740        let row_ranges_to_keep: Vec<Range<u32>> = self
19741            .selections
19742            .all::<Point>(&self.display_snapshot(cx))
19743            .into_iter()
19744            .map(|sel| sel.start.row..sel.end.row)
19745            .collect();
19746
19747        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
19748            while start_row < end_row {
19749                match self
19750                    .snapshot(window, cx)
19751                    .crease_for_buffer_row(MultiBufferRow(start_row))
19752                {
19753                    Some(crease) => {
19754                        let nested_start_row = crease.range().start.row + 1;
19755                        let nested_end_row = crease.range().end.row;
19756
19757                        if current_level < fold_at_level {
19758                            stack.push((nested_start_row, nested_end_row, current_level + 1));
19759                        } else if current_level == fold_at_level {
19760                            // Fold iff there is no selection completely contained within the fold region
19761                            if !row_ranges_to_keep.iter().any(|selection| {
19762                                selection.end >= nested_start_row
19763                                    && selection.start <= nested_end_row
19764                            }) {
19765                                to_fold.push(crease);
19766                            }
19767                        }
19768
19769                        start_row = nested_end_row + 1;
19770                    }
19771                    None => start_row += 1,
19772                }
19773            }
19774        }
19775
19776        self.fold_creases(to_fold, true, window, cx);
19777    }
19778
19779    pub fn fold_at_level_1(
19780        &mut self,
19781        _: &actions::FoldAtLevel1,
19782        window: &mut Window,
19783        cx: &mut Context<Self>,
19784    ) {
19785        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
19786    }
19787
19788    pub fn fold_at_level_2(
19789        &mut self,
19790        _: &actions::FoldAtLevel2,
19791        window: &mut Window,
19792        cx: &mut Context<Self>,
19793    ) {
19794        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
19795    }
19796
19797    pub fn fold_at_level_3(
19798        &mut self,
19799        _: &actions::FoldAtLevel3,
19800        window: &mut Window,
19801        cx: &mut Context<Self>,
19802    ) {
19803        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
19804    }
19805
19806    pub fn fold_at_level_4(
19807        &mut self,
19808        _: &actions::FoldAtLevel4,
19809        window: &mut Window,
19810        cx: &mut Context<Self>,
19811    ) {
19812        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
19813    }
19814
19815    pub fn fold_at_level_5(
19816        &mut self,
19817        _: &actions::FoldAtLevel5,
19818        window: &mut Window,
19819        cx: &mut Context<Self>,
19820    ) {
19821        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
19822    }
19823
19824    pub fn fold_at_level_6(
19825        &mut self,
19826        _: &actions::FoldAtLevel6,
19827        window: &mut Window,
19828        cx: &mut Context<Self>,
19829    ) {
19830        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
19831    }
19832
19833    pub fn fold_at_level_7(
19834        &mut self,
19835        _: &actions::FoldAtLevel7,
19836        window: &mut Window,
19837        cx: &mut Context<Self>,
19838    ) {
19839        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
19840    }
19841
19842    pub fn fold_at_level_8(
19843        &mut self,
19844        _: &actions::FoldAtLevel8,
19845        window: &mut Window,
19846        cx: &mut Context<Self>,
19847    ) {
19848        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
19849    }
19850
19851    pub fn fold_at_level_9(
19852        &mut self,
19853        _: &actions::FoldAtLevel9,
19854        window: &mut Window,
19855        cx: &mut Context<Self>,
19856    ) {
19857        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
19858    }
19859
19860    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
19861        if self.buffer.read(cx).is_singleton() {
19862            let mut fold_ranges = Vec::new();
19863            let snapshot = self.buffer.read(cx).snapshot(cx);
19864
19865            for row in 0..snapshot.max_row().0 {
19866                if let Some(foldable_range) = self
19867                    .snapshot(window, cx)
19868                    .crease_for_buffer_row(MultiBufferRow(row))
19869                {
19870                    fold_ranges.push(foldable_range);
19871                }
19872            }
19873
19874            self.fold_creases(fold_ranges, true, window, cx);
19875        } else {
19876            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
19877                editor
19878                    .update_in(cx, |editor, _, cx| {
19879                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
19880                            editor.fold_buffer(buffer_id, cx);
19881                        }
19882                    })
19883                    .ok();
19884            });
19885        }
19886        cx.emit(SearchEvent::ResultsCollapsedChanged(
19887            CollapseDirection::Collapsed,
19888        ));
19889    }
19890
19891    pub fn fold_function_bodies(
19892        &mut self,
19893        _: &actions::FoldFunctionBodies,
19894        window: &mut Window,
19895        cx: &mut Context<Self>,
19896    ) {
19897        let snapshot = self.buffer.read(cx).snapshot(cx);
19898
19899        let ranges = snapshot
19900            .text_object_ranges(
19901                MultiBufferOffset(0)..snapshot.len(),
19902                TreeSitterOptions::default(),
19903            )
19904            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
19905            .collect::<Vec<_>>();
19906
19907        let creases = ranges
19908            .into_iter()
19909            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
19910            .collect();
19911
19912        self.fold_creases(creases, true, window, cx);
19913    }
19914
19915    pub fn fold_recursive(
19916        &mut self,
19917        _: &actions::FoldRecursive,
19918        window: &mut Window,
19919        cx: &mut Context<Self>,
19920    ) {
19921        let mut to_fold = Vec::new();
19922        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19923        let selections = self.selections.all_adjusted(&display_map);
19924
19925        for selection in selections {
19926            let range = selection.range().sorted();
19927            let buffer_start_row = range.start.row;
19928
19929            if range.start.row != range.end.row {
19930                let mut found = false;
19931                for row in range.start.row..=range.end.row {
19932                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19933                        found = true;
19934                        to_fold.push(crease);
19935                    }
19936                }
19937                if found {
19938                    continue;
19939                }
19940            }
19941
19942            for row in (0..=range.start.row).rev() {
19943                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19944                    if crease.range().end.row >= buffer_start_row {
19945                        to_fold.push(crease);
19946                    } else {
19947                        break;
19948                    }
19949                }
19950            }
19951        }
19952
19953        self.fold_creases(to_fold, true, window, cx);
19954    }
19955
19956    pub fn fold_at(
19957        &mut self,
19958        buffer_row: MultiBufferRow,
19959        window: &mut Window,
19960        cx: &mut Context<Self>,
19961    ) {
19962        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19963
19964        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
19965            let autoscroll = self
19966                .selections
19967                .all::<Point>(&display_map)
19968                .iter()
19969                .any(|selection| crease.range().overlaps(&selection.range()));
19970
19971            self.fold_creases(vec![crease], autoscroll, window, cx);
19972        }
19973    }
19974
19975    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
19976        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19977            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19978            let buffer = display_map.buffer_snapshot();
19979            let selections = self.selections.all::<Point>(&display_map);
19980            let ranges = selections
19981                .iter()
19982                .map(|s| {
19983                    let range = s.display_range(&display_map).sorted();
19984                    let mut start = range.start.to_point(&display_map);
19985                    let mut end = range.end.to_point(&display_map);
19986                    start.column = 0;
19987                    end.column = buffer.line_len(MultiBufferRow(end.row));
19988                    start..end
19989                })
19990                .collect::<Vec<_>>();
19991
19992            self.unfold_ranges(&ranges, true, true, cx);
19993        } else {
19994            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19995            let buffer_ids = self
19996                .selections
19997                .disjoint_anchor_ranges()
19998                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19999                .collect::<HashSet<_>>();
20000            for buffer_id in buffer_ids {
20001                self.unfold_buffer(buffer_id, cx);
20002            }
20003        }
20004    }
20005
20006    pub fn unfold_recursive(
20007        &mut self,
20008        _: &UnfoldRecursive,
20009        _window: &mut Window,
20010        cx: &mut Context<Self>,
20011    ) {
20012        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20013        let selections = self.selections.all::<Point>(&display_map);
20014        let ranges = selections
20015            .iter()
20016            .map(|s| {
20017                let mut range = s.display_range(&display_map).sorted();
20018                *range.start.column_mut() = 0;
20019                *range.end.column_mut() = display_map.line_len(range.end.row());
20020                let start = range.start.to_point(&display_map);
20021                let end = range.end.to_point(&display_map);
20022                start..end
20023            })
20024            .collect::<Vec<_>>();
20025
20026        self.unfold_ranges(&ranges, true, true, cx);
20027    }
20028
20029    pub fn unfold_at(
20030        &mut self,
20031        buffer_row: MultiBufferRow,
20032        _window: &mut Window,
20033        cx: &mut Context<Self>,
20034    ) {
20035        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20036
20037        let intersection_range = Point::new(buffer_row.0, 0)
20038            ..Point::new(
20039                buffer_row.0,
20040                display_map.buffer_snapshot().line_len(buffer_row),
20041            );
20042
20043        let autoscroll = self
20044            .selections
20045            .all::<Point>(&display_map)
20046            .iter()
20047            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20048
20049        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20050    }
20051
20052    pub fn unfold_all(
20053        &mut self,
20054        _: &actions::UnfoldAll,
20055        _window: &mut Window,
20056        cx: &mut Context<Self>,
20057    ) {
20058        if self.buffer.read(cx).is_singleton() {
20059            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20060            self.unfold_ranges(
20061                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20062                true,
20063                true,
20064                cx,
20065            );
20066        } else {
20067            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20068                editor
20069                    .update(cx, |editor, cx| {
20070                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20071                            editor.unfold_buffer(buffer_id, cx);
20072                        }
20073                    })
20074                    .ok();
20075            });
20076        }
20077        cx.emit(SearchEvent::ResultsCollapsedChanged(
20078            CollapseDirection::Expanded,
20079        ));
20080    }
20081
20082    pub fn fold_selected_ranges(
20083        &mut self,
20084        _: &FoldSelectedRanges,
20085        window: &mut Window,
20086        cx: &mut Context<Self>,
20087    ) {
20088        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20089        let selections = self.selections.all_adjusted(&display_map);
20090        let ranges = selections
20091            .into_iter()
20092            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20093            .collect::<Vec<_>>();
20094        self.fold_creases(ranges, true, window, cx);
20095    }
20096
20097    pub fn fold_ranges<T: ToOffset + Clone>(
20098        &mut self,
20099        ranges: Vec<Range<T>>,
20100        auto_scroll: bool,
20101        window: &mut Window,
20102        cx: &mut Context<Self>,
20103    ) {
20104        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20105        let ranges = ranges
20106            .into_iter()
20107            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20108            .collect::<Vec<_>>();
20109        self.fold_creases(ranges, auto_scroll, window, cx);
20110    }
20111
20112    pub fn fold_creases<T: ToOffset + Clone>(
20113        &mut self,
20114        creases: Vec<Crease<T>>,
20115        auto_scroll: bool,
20116        _window: &mut Window,
20117        cx: &mut Context<Self>,
20118    ) {
20119        if creases.is_empty() {
20120            return;
20121        }
20122
20123        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20124
20125        if auto_scroll {
20126            self.request_autoscroll(Autoscroll::fit(), cx);
20127        }
20128
20129        cx.notify();
20130
20131        self.scrollbar_marker_state.dirty = true;
20132        self.folds_did_change(cx);
20133    }
20134
20135    /// Removes any folds whose ranges intersect any of the given ranges.
20136    pub fn unfold_ranges<T: ToOffset + Clone>(
20137        &mut self,
20138        ranges: &[Range<T>],
20139        inclusive: bool,
20140        auto_scroll: bool,
20141        cx: &mut Context<Self>,
20142    ) {
20143        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20144            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20145        });
20146        self.folds_did_change(cx);
20147    }
20148
20149    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20150        if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
20151            return;
20152        }
20153
20154        let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20155        self.display_map.update(cx, |display_map, cx| {
20156            display_map.fold_buffers([buffer_id], cx)
20157        });
20158
20159        let snapshot = self.display_snapshot(cx);
20160        self.selections.change_with(&snapshot, |selections| {
20161            selections.remove_selections_from_buffer(buffer_id);
20162        });
20163
20164        cx.emit(EditorEvent::BufferFoldToggled {
20165            ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
20166            folded: true,
20167        });
20168        cx.notify();
20169    }
20170
20171    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20172        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20173            return;
20174        }
20175        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20176        self.display_map.update(cx, |display_map, cx| {
20177            display_map.unfold_buffers([buffer_id], cx);
20178        });
20179        cx.emit(EditorEvent::BufferFoldToggled {
20180            ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
20181            folded: false,
20182        });
20183        cx.notify();
20184    }
20185
20186    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20187        self.display_map.read(cx).is_buffer_folded(buffer)
20188    }
20189
20190    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20191        self.display_map.read(cx).folded_buffers()
20192    }
20193
20194    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20195        self.display_map.update(cx, |display_map, cx| {
20196            display_map.disable_header_for_buffer(buffer_id, cx);
20197        });
20198        cx.notify();
20199    }
20200
20201    /// Removes any folds with the given ranges.
20202    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20203        &mut self,
20204        ranges: &[Range<T>],
20205        type_id: TypeId,
20206        auto_scroll: bool,
20207        cx: &mut Context<Self>,
20208    ) {
20209        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20210            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20211        });
20212        self.folds_did_change(cx);
20213    }
20214
20215    fn remove_folds_with<T: ToOffset + Clone>(
20216        &mut self,
20217        ranges: &[Range<T>],
20218        auto_scroll: bool,
20219        cx: &mut Context<Self>,
20220        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20221    ) {
20222        if ranges.is_empty() {
20223            return;
20224        }
20225
20226        let mut buffers_affected = HashSet::default();
20227        let multi_buffer = self.buffer().read(cx);
20228        for range in ranges {
20229            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20230                buffers_affected.insert(buffer.read(cx).remote_id());
20231            };
20232        }
20233
20234        self.display_map.update(cx, update);
20235
20236        if auto_scroll {
20237            self.request_autoscroll(Autoscroll::fit(), cx);
20238        }
20239
20240        cx.notify();
20241        self.scrollbar_marker_state.dirty = true;
20242        self.active_indent_guides_state.dirty = true;
20243    }
20244
20245    pub fn update_renderer_widths(
20246        &mut self,
20247        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20248        cx: &mut Context<Self>,
20249    ) -> bool {
20250        self.display_map
20251            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20252    }
20253
20254    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20255        self.display_map.read(cx).fold_placeholder.clone()
20256    }
20257
20258    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20259        self.buffer.update(cx, |buffer, cx| {
20260            buffer.set_all_diff_hunks_expanded(cx);
20261        });
20262    }
20263
20264    pub fn expand_all_diff_hunks(
20265        &mut self,
20266        _: &ExpandAllDiffHunks,
20267        _window: &mut Window,
20268        cx: &mut Context<Self>,
20269    ) {
20270        self.buffer.update(cx, |buffer, cx| {
20271            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20272        });
20273    }
20274
20275    pub fn collapse_all_diff_hunks(
20276        &mut self,
20277        _: &CollapseAllDiffHunks,
20278        _window: &mut Window,
20279        cx: &mut Context<Self>,
20280    ) {
20281        self.buffer.update(cx, |buffer, cx| {
20282            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20283        });
20284    }
20285
20286    pub fn toggle_selected_diff_hunks(
20287        &mut self,
20288        _: &ToggleSelectedDiffHunks,
20289        _window: &mut Window,
20290        cx: &mut Context<Self>,
20291    ) {
20292        let ranges: Vec<_> = self
20293            .selections
20294            .disjoint_anchors()
20295            .iter()
20296            .map(|s| s.range())
20297            .collect();
20298        self.toggle_diff_hunks_in_ranges(ranges, cx);
20299    }
20300
20301    pub fn diff_hunks_in_ranges<'a>(
20302        &'a self,
20303        ranges: &'a [Range<Anchor>],
20304        buffer: &'a MultiBufferSnapshot,
20305    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20306        ranges.iter().flat_map(move |range| {
20307            let end_excerpt_id = range.end.excerpt_id;
20308            let range = range.to_point(buffer);
20309            let mut peek_end = range.end;
20310            if range.end.row < buffer.max_row().0 {
20311                peek_end = Point::new(range.end.row + 1, 0);
20312            }
20313            buffer
20314                .diff_hunks_in_range(range.start..peek_end)
20315                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20316        })
20317    }
20318
20319    pub fn has_stageable_diff_hunks_in_ranges(
20320        &self,
20321        ranges: &[Range<Anchor>],
20322        snapshot: &MultiBufferSnapshot,
20323    ) -> bool {
20324        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20325        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20326    }
20327
20328    pub fn toggle_staged_selected_diff_hunks(
20329        &mut self,
20330        _: &::git::ToggleStaged,
20331        _: &mut Window,
20332        cx: &mut Context<Self>,
20333    ) {
20334        let snapshot = self.buffer.read(cx).snapshot(cx);
20335        let ranges: Vec<_> = self
20336            .selections
20337            .disjoint_anchors()
20338            .iter()
20339            .map(|s| s.range())
20340            .collect();
20341        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20342        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20343    }
20344
20345    pub fn set_render_diff_hunk_controls(
20346        &mut self,
20347        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20348        cx: &mut Context<Self>,
20349    ) {
20350        self.render_diff_hunk_controls = render_diff_hunk_controls;
20351        cx.notify();
20352    }
20353
20354    pub fn stage_and_next(
20355        &mut self,
20356        _: &::git::StageAndNext,
20357        window: &mut Window,
20358        cx: &mut Context<Self>,
20359    ) {
20360        self.do_stage_or_unstage_and_next(true, window, cx);
20361    }
20362
20363    pub fn unstage_and_next(
20364        &mut self,
20365        _: &::git::UnstageAndNext,
20366        window: &mut Window,
20367        cx: &mut Context<Self>,
20368    ) {
20369        self.do_stage_or_unstage_and_next(false, window, cx);
20370    }
20371
20372    pub fn stage_or_unstage_diff_hunks(
20373        &mut self,
20374        stage: bool,
20375        ranges: Vec<Range<Anchor>>,
20376        cx: &mut Context<Self>,
20377    ) {
20378        if self.delegate_stage_and_restore {
20379            let snapshot = self.buffer.read(cx).snapshot(cx);
20380            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20381            if !hunks.is_empty() {
20382                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20383            }
20384            return;
20385        }
20386        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20387        cx.spawn(async move |this, cx| {
20388            task.await?;
20389            this.update(cx, |this, cx| {
20390                let snapshot = this.buffer.read(cx).snapshot(cx);
20391                let chunk_by = this
20392                    .diff_hunks_in_ranges(&ranges, &snapshot)
20393                    .chunk_by(|hunk| hunk.buffer_id);
20394                for (buffer_id, hunks) in &chunk_by {
20395                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20396                }
20397            })
20398        })
20399        .detach_and_log_err(cx);
20400    }
20401
20402    fn save_buffers_for_ranges_if_needed(
20403        &mut self,
20404        ranges: &[Range<Anchor>],
20405        cx: &mut Context<Editor>,
20406    ) -> Task<Result<()>> {
20407        let multibuffer = self.buffer.read(cx);
20408        let snapshot = multibuffer.read(cx);
20409        let buffer_ids: HashSet<_> = ranges
20410            .iter()
20411            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20412            .collect();
20413        drop(snapshot);
20414
20415        let mut buffers = HashSet::default();
20416        for buffer_id in buffer_ids {
20417            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20418                let buffer = buffer_entity.read(cx);
20419                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20420                {
20421                    buffers.insert(buffer_entity);
20422                }
20423            }
20424        }
20425
20426        if let Some(project) = &self.project {
20427            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20428        } else {
20429            Task::ready(Ok(()))
20430        }
20431    }
20432
20433    fn do_stage_or_unstage_and_next(
20434        &mut self,
20435        stage: bool,
20436        window: &mut Window,
20437        cx: &mut Context<Self>,
20438    ) {
20439        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20440
20441        if ranges.iter().any(|range| range.start != range.end) {
20442            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20443            return;
20444        }
20445
20446        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20447        let snapshot = self.snapshot(window, cx);
20448        let position = self
20449            .selections
20450            .newest::<Point>(&snapshot.display_snapshot)
20451            .head();
20452        let mut row = snapshot
20453            .buffer_snapshot()
20454            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
20455            .find(|hunk| hunk.row_range.start.0 > position.row)
20456            .map(|hunk| hunk.row_range.start);
20457
20458        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20459        // Outside of the project diff editor, wrap around to the beginning.
20460        if !all_diff_hunks_expanded {
20461            row = row.or_else(|| {
20462                snapshot
20463                    .buffer_snapshot()
20464                    .diff_hunks_in_range(Point::zero()..position)
20465                    .find(|hunk| hunk.row_range.end.0 < position.row)
20466                    .map(|hunk| hunk.row_range.start)
20467            });
20468        }
20469
20470        if let Some(row) = row {
20471            let destination = Point::new(row.0, 0);
20472            let autoscroll = Autoscroll::center();
20473
20474            self.unfold_ranges(&[destination..destination], false, false, cx);
20475            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
20476                s.select_ranges([destination..destination]);
20477            });
20478        }
20479    }
20480
20481    pub(crate) fn do_stage_or_unstage(
20482        &self,
20483        stage: bool,
20484        buffer_id: BufferId,
20485        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20486        cx: &mut App,
20487    ) -> Option<()> {
20488        let project = self.project()?;
20489        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20490        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20491        let buffer_snapshot = buffer.read(cx).snapshot();
20492        let file_exists = buffer_snapshot
20493            .file()
20494            .is_some_and(|file| file.disk_state().exists());
20495        diff.update(cx, |diff, cx| {
20496            diff.stage_or_unstage_hunks(
20497                stage,
20498                &hunks
20499                    .map(|hunk| buffer_diff::DiffHunk {
20500                        buffer_range: hunk.buffer_range,
20501                        // We don't need to pass in word diffs here because they're only used for rendering and
20502                        // this function changes internal state
20503                        base_word_diffs: Vec::default(),
20504                        buffer_word_diffs: Vec::default(),
20505                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20506                            ..hunk.diff_base_byte_range.end.0,
20507                        secondary_status: hunk.status.secondary,
20508                        range: Point::zero()..Point::zero(), // unused
20509                    })
20510                    .collect::<Vec<_>>(),
20511                &buffer_snapshot,
20512                file_exists,
20513                cx,
20514            )
20515        });
20516        None
20517    }
20518
20519    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20520        let ranges: Vec<_> = self
20521            .selections
20522            .disjoint_anchors()
20523            .iter()
20524            .map(|s| s.range())
20525            .collect();
20526        self.buffer
20527            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20528    }
20529
20530    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20531        self.buffer.update(cx, |buffer, cx| {
20532            let ranges = vec![Anchor::min()..Anchor::max()];
20533            if !buffer.all_diff_hunks_expanded()
20534                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20535            {
20536                buffer.collapse_diff_hunks(ranges, cx);
20537                true
20538            } else {
20539                false
20540            }
20541        })
20542    }
20543
20544    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20545        if self.buffer.read(cx).all_diff_hunks_expanded() {
20546            return true;
20547        }
20548        let ranges = vec![Anchor::min()..Anchor::max()];
20549        self.buffer
20550            .read(cx)
20551            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20552    }
20553
20554    fn toggle_diff_hunks_in_ranges(
20555        &mut self,
20556        ranges: Vec<Range<Anchor>>,
20557        cx: &mut Context<Editor>,
20558    ) {
20559        self.buffer.update(cx, |buffer, cx| {
20560            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20561            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20562        })
20563    }
20564
20565    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20566        self.buffer.update(cx, |buffer, cx| {
20567            let snapshot = buffer.snapshot(cx);
20568            let excerpt_id = range.end.excerpt_id;
20569            let point_range = range.to_point(&snapshot);
20570            let expand = !buffer.single_hunk_is_expanded(range, cx);
20571            buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
20572        })
20573    }
20574
20575    pub(crate) fn apply_all_diff_hunks(
20576        &mut self,
20577        _: &ApplyAllDiffHunks,
20578        window: &mut Window,
20579        cx: &mut Context<Self>,
20580    ) {
20581        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20582
20583        let buffers = self.buffer.read(cx).all_buffers();
20584        for branch_buffer in buffers {
20585            branch_buffer.update(cx, |branch_buffer, cx| {
20586                branch_buffer.merge_into_base(Vec::new(), cx);
20587            });
20588        }
20589
20590        if let Some(project) = self.project.clone() {
20591            self.save(
20592                SaveOptions {
20593                    format: true,
20594                    autosave: false,
20595                },
20596                project,
20597                window,
20598                cx,
20599            )
20600            .detach_and_log_err(cx);
20601        }
20602    }
20603
20604    pub(crate) fn apply_selected_diff_hunks(
20605        &mut self,
20606        _: &ApplyDiffHunk,
20607        window: &mut Window,
20608        cx: &mut Context<Self>,
20609    ) {
20610        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20611        let snapshot = self.snapshot(window, cx);
20612        let hunks = snapshot.hunks_for_ranges(
20613            self.selections
20614                .all(&snapshot.display_snapshot)
20615                .into_iter()
20616                .map(|selection| selection.range()),
20617        );
20618        let mut ranges_by_buffer = HashMap::default();
20619        self.transact(window, cx, |editor, _window, cx| {
20620            for hunk in hunks {
20621                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20622                    ranges_by_buffer
20623                        .entry(buffer.clone())
20624                        .or_insert_with(Vec::new)
20625                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20626                }
20627            }
20628
20629            for (buffer, ranges) in ranges_by_buffer {
20630                buffer.update(cx, |buffer, cx| {
20631                    buffer.merge_into_base(ranges, cx);
20632                });
20633            }
20634        });
20635
20636        if let Some(project) = self.project.clone() {
20637            self.save(
20638                SaveOptions {
20639                    format: true,
20640                    autosave: false,
20641                },
20642                project,
20643                window,
20644                cx,
20645            )
20646            .detach_and_log_err(cx);
20647        }
20648    }
20649
20650    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20651        if hovered != self.gutter_hovered {
20652            self.gutter_hovered = hovered;
20653            cx.notify();
20654        }
20655    }
20656
20657    pub fn insert_blocks(
20658        &mut self,
20659        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20660        autoscroll: Option<Autoscroll>,
20661        cx: &mut Context<Self>,
20662    ) -> Vec<CustomBlockId> {
20663        let blocks = self
20664            .display_map
20665            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20666        if let Some(autoscroll) = autoscroll {
20667            self.request_autoscroll(autoscroll, cx);
20668        }
20669        cx.notify();
20670        blocks
20671    }
20672
20673    pub fn resize_blocks(
20674        &mut self,
20675        heights: HashMap<CustomBlockId, u32>,
20676        autoscroll: Option<Autoscroll>,
20677        cx: &mut Context<Self>,
20678    ) {
20679        self.display_map
20680            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20681        if let Some(autoscroll) = autoscroll {
20682            self.request_autoscroll(autoscroll, cx);
20683        }
20684        cx.notify();
20685    }
20686
20687    pub fn replace_blocks(
20688        &mut self,
20689        renderers: HashMap<CustomBlockId, RenderBlock>,
20690        autoscroll: Option<Autoscroll>,
20691        cx: &mut Context<Self>,
20692    ) {
20693        self.display_map
20694            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20695        if let Some(autoscroll) = autoscroll {
20696            self.request_autoscroll(autoscroll, cx);
20697        }
20698        cx.notify();
20699    }
20700
20701    pub fn remove_blocks(
20702        &mut self,
20703        block_ids: HashSet<CustomBlockId>,
20704        autoscroll: Option<Autoscroll>,
20705        cx: &mut Context<Self>,
20706    ) {
20707        self.display_map.update(cx, |display_map, cx| {
20708            display_map.remove_blocks(block_ids, cx)
20709        });
20710        if let Some(autoscroll) = autoscroll {
20711            self.request_autoscroll(autoscroll, cx);
20712        }
20713        cx.notify();
20714    }
20715
20716    pub fn row_for_block(
20717        &self,
20718        block_id: CustomBlockId,
20719        cx: &mut Context<Self>,
20720    ) -> Option<DisplayRow> {
20721        self.display_map
20722            .update(cx, |map, cx| map.row_for_block(block_id, cx))
20723    }
20724
20725    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
20726        self.focused_block = Some(focused_block);
20727    }
20728
20729    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
20730        self.focused_block.take()
20731    }
20732
20733    pub fn insert_creases(
20734        &mut self,
20735        creases: impl IntoIterator<Item = Crease<Anchor>>,
20736        cx: &mut Context<Self>,
20737    ) -> Vec<CreaseId> {
20738        self.display_map
20739            .update(cx, |map, cx| map.insert_creases(creases, cx))
20740    }
20741
20742    pub fn remove_creases(
20743        &mut self,
20744        ids: impl IntoIterator<Item = CreaseId>,
20745        cx: &mut Context<Self>,
20746    ) -> Vec<(CreaseId, Range<Anchor>)> {
20747        self.display_map
20748            .update(cx, |map, cx| map.remove_creases(ids, cx))
20749    }
20750
20751    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
20752        self.display_map
20753            .update(cx, |map, cx| map.snapshot(cx))
20754            .longest_row()
20755    }
20756
20757    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
20758        self.display_map
20759            .update(cx, |map, cx| map.snapshot(cx))
20760            .max_point()
20761    }
20762
20763    pub fn text(&self, cx: &App) -> String {
20764        self.buffer.read(cx).read(cx).text()
20765    }
20766
20767    pub fn is_empty(&self, cx: &App) -> bool {
20768        self.buffer.read(cx).read(cx).is_empty()
20769    }
20770
20771    pub fn text_option(&self, cx: &App) -> Option<String> {
20772        let text = self.text(cx);
20773        let text = text.trim();
20774
20775        if text.is_empty() {
20776            return None;
20777        }
20778
20779        Some(text.to_string())
20780    }
20781
20782    pub fn set_text(
20783        &mut self,
20784        text: impl Into<Arc<str>>,
20785        window: &mut Window,
20786        cx: &mut Context<Self>,
20787    ) {
20788        self.transact(window, cx, |this, _, cx| {
20789            this.buffer
20790                .read(cx)
20791                .as_singleton()
20792                .expect("you can only call set_text on editors for singleton buffers")
20793                .update(cx, |buffer, cx| buffer.set_text(text, cx));
20794        });
20795    }
20796
20797    pub fn display_text(&self, cx: &mut App) -> String {
20798        self.display_map
20799            .update(cx, |map, cx| map.snapshot(cx))
20800            .text()
20801    }
20802
20803    fn create_minimap(
20804        &self,
20805        minimap_settings: MinimapSettings,
20806        window: &mut Window,
20807        cx: &mut Context<Self>,
20808    ) -> Option<Entity<Self>> {
20809        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
20810            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
20811    }
20812
20813    fn initialize_new_minimap(
20814        &self,
20815        minimap_settings: MinimapSettings,
20816        window: &mut Window,
20817        cx: &mut Context<Self>,
20818    ) -> Entity<Self> {
20819        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
20820        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
20821
20822        let mut minimap = Editor::new_internal(
20823            EditorMode::Minimap {
20824                parent: cx.weak_entity(),
20825            },
20826            self.buffer.clone(),
20827            None,
20828            Some(self.display_map.clone()),
20829            window,
20830            cx,
20831        );
20832        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20833        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
20834        minimap.scroll_manager.clone_state(
20835            &self.scroll_manager,
20836            &my_snapshot,
20837            &minimap_snapshot,
20838            cx,
20839        );
20840        minimap.set_text_style_refinement(TextStyleRefinement {
20841            font_size: Some(MINIMAP_FONT_SIZE),
20842            font_weight: Some(MINIMAP_FONT_WEIGHT),
20843            font_family: Some(MINIMAP_FONT_FAMILY),
20844            ..Default::default()
20845        });
20846        minimap.update_minimap_configuration(minimap_settings, cx);
20847        cx.new(|_| minimap)
20848    }
20849
20850    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
20851        let current_line_highlight = minimap_settings
20852            .current_line_highlight
20853            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
20854        self.set_current_line_highlight(Some(current_line_highlight));
20855    }
20856
20857    pub fn minimap(&self) -> Option<&Entity<Self>> {
20858        self.minimap
20859            .as_ref()
20860            .filter(|_| self.minimap_visibility.visible())
20861    }
20862
20863    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
20864        let mut wrap_guides = smallvec![];
20865
20866        if self.show_wrap_guides == Some(false) {
20867            return wrap_guides;
20868        }
20869
20870        let settings = self.buffer.read(cx).language_settings(cx);
20871        if settings.show_wrap_guides {
20872            match self.soft_wrap_mode(cx) {
20873                SoftWrap::Column(soft_wrap) => {
20874                    wrap_guides.push((soft_wrap as usize, true));
20875                }
20876                SoftWrap::Bounded(soft_wrap) => {
20877                    wrap_guides.push((soft_wrap as usize, true));
20878                }
20879                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
20880            }
20881            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
20882        }
20883
20884        wrap_guides
20885    }
20886
20887    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
20888        let settings = self.buffer.read(cx).language_settings(cx);
20889        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
20890        match mode {
20891            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
20892                SoftWrap::None
20893            }
20894            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
20895            language_settings::SoftWrap::PreferredLineLength => {
20896                SoftWrap::Column(settings.preferred_line_length)
20897            }
20898            language_settings::SoftWrap::Bounded => {
20899                SoftWrap::Bounded(settings.preferred_line_length)
20900            }
20901        }
20902    }
20903
20904    pub fn set_soft_wrap_mode(
20905        &mut self,
20906        mode: language_settings::SoftWrap,
20907        cx: &mut Context<Self>,
20908    ) {
20909        self.soft_wrap_mode_override = Some(mode);
20910        cx.notify();
20911    }
20912
20913    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
20914        self.hard_wrap = hard_wrap;
20915        cx.notify();
20916    }
20917
20918    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
20919        self.text_style_refinement = Some(style);
20920    }
20921
20922    /// called by the Element so we know what style we were most recently rendered with.
20923    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
20924        // We intentionally do not inform the display map about the minimap style
20925        // so that wrapping is not recalculated and stays consistent for the editor
20926        // and its linked minimap.
20927        if !self.mode.is_minimap() {
20928            let font = style.text.font();
20929            let font_size = style.text.font_size.to_pixels(window.rem_size());
20930            let display_map = self
20931                .placeholder_display_map
20932                .as_ref()
20933                .filter(|_| self.is_empty(cx))
20934                .unwrap_or(&self.display_map);
20935
20936            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
20937        }
20938        self.style = Some(style);
20939    }
20940
20941    pub fn style(&mut self, cx: &App) -> &EditorStyle {
20942        if self.style.is_none() {
20943            self.style = Some(self.create_style(cx));
20944        }
20945        self.style.as_ref().unwrap()
20946    }
20947
20948    // Called by the element. This method is not designed to be called outside of the editor
20949    // element's layout code because it does not notify when rewrapping is computed synchronously.
20950    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
20951        if self.is_empty(cx) {
20952            self.placeholder_display_map
20953                .as_ref()
20954                .map_or(false, |display_map| {
20955                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
20956                })
20957        } else {
20958            self.display_map
20959                .update(cx, |map, cx| map.set_wrap_width(width, cx))
20960        }
20961    }
20962
20963    pub fn set_soft_wrap(&mut self) {
20964        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
20965    }
20966
20967    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
20968        if self.soft_wrap_mode_override.is_some() {
20969            self.soft_wrap_mode_override.take();
20970        } else {
20971            let soft_wrap = match self.soft_wrap_mode(cx) {
20972                SoftWrap::GitDiff => return,
20973                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
20974                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
20975                    language_settings::SoftWrap::None
20976                }
20977            };
20978            self.soft_wrap_mode_override = Some(soft_wrap);
20979        }
20980        cx.notify();
20981    }
20982
20983    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
20984        let Some(workspace) = self.workspace() else {
20985            return;
20986        };
20987        let fs = workspace.read(cx).app_state().fs.clone();
20988        let current_show = TabBarSettings::get_global(cx).show;
20989        update_settings_file(fs, cx, move |setting, _| {
20990            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
20991        });
20992    }
20993
20994    pub fn toggle_indent_guides(
20995        &mut self,
20996        _: &ToggleIndentGuides,
20997        _: &mut Window,
20998        cx: &mut Context<Self>,
20999    ) {
21000        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21001            self.buffer
21002                .read(cx)
21003                .language_settings(cx)
21004                .indent_guides
21005                .enabled
21006        });
21007        self.show_indent_guides = Some(!currently_enabled);
21008        cx.notify();
21009    }
21010
21011    fn should_show_indent_guides(&self) -> Option<bool> {
21012        self.show_indent_guides
21013    }
21014
21015    pub fn disable_indent_guides_for_buffer(
21016        &mut self,
21017        buffer_id: BufferId,
21018        cx: &mut Context<Self>,
21019    ) {
21020        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21021        cx.notify();
21022    }
21023
21024    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21025        self.buffers_with_disabled_indent_guides
21026            .contains(&buffer_id)
21027    }
21028
21029    pub fn toggle_line_numbers(
21030        &mut self,
21031        _: &ToggleLineNumbers,
21032        _: &mut Window,
21033        cx: &mut Context<Self>,
21034    ) {
21035        let mut editor_settings = EditorSettings::get_global(cx).clone();
21036        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21037        EditorSettings::override_global(editor_settings, cx);
21038    }
21039
21040    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21041        if let Some(show_line_numbers) = self.show_line_numbers {
21042            return show_line_numbers;
21043        }
21044        EditorSettings::get_global(cx).gutter.line_numbers
21045    }
21046
21047    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21048        match (
21049            self.use_relative_line_numbers,
21050            EditorSettings::get_global(cx).relative_line_numbers,
21051        ) {
21052            (None, setting) => setting,
21053            (Some(false), _) => RelativeLineNumbers::Disabled,
21054            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21055            (Some(true), _) => RelativeLineNumbers::Enabled,
21056        }
21057    }
21058
21059    pub fn toggle_relative_line_numbers(
21060        &mut self,
21061        _: &ToggleRelativeLineNumbers,
21062        _: &mut Window,
21063        cx: &mut Context<Self>,
21064    ) {
21065        let is_relative = self.relative_line_numbers(cx);
21066        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21067    }
21068
21069    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21070        self.use_relative_line_numbers = is_relative;
21071        cx.notify();
21072    }
21073
21074    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21075        self.show_gutter = show_gutter;
21076        cx.notify();
21077    }
21078
21079    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21080        self.show_scrollbars = ScrollbarAxes {
21081            horizontal: show,
21082            vertical: show,
21083        };
21084        cx.notify();
21085    }
21086
21087    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21088        self.show_scrollbars.vertical = show;
21089        cx.notify();
21090    }
21091
21092    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21093        self.show_scrollbars.horizontal = show;
21094        cx.notify();
21095    }
21096
21097    pub fn set_minimap_visibility(
21098        &mut self,
21099        minimap_visibility: MinimapVisibility,
21100        window: &mut Window,
21101        cx: &mut Context<Self>,
21102    ) {
21103        if self.minimap_visibility != minimap_visibility {
21104            if minimap_visibility.visible() && self.minimap.is_none() {
21105                let minimap_settings = EditorSettings::get_global(cx).minimap;
21106                self.minimap =
21107                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21108            }
21109            self.minimap_visibility = minimap_visibility;
21110            cx.notify();
21111        }
21112    }
21113
21114    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21115        self.set_show_scrollbars(false, cx);
21116        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21117    }
21118
21119    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21120        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21121    }
21122
21123    /// Normally the text in full mode and auto height editors is padded on the
21124    /// left side by roughly half a character width for improved hit testing.
21125    ///
21126    /// Use this method to disable this for cases where this is not wanted (e.g.
21127    /// if you want to align the editor text with some other text above or below)
21128    /// or if you want to add this padding to single-line editors.
21129    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21130        self.offset_content = offset_content;
21131        cx.notify();
21132    }
21133
21134    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21135        self.show_line_numbers = Some(show_line_numbers);
21136        cx.notify();
21137    }
21138
21139    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21140        self.disable_expand_excerpt_buttons = true;
21141        cx.notify();
21142    }
21143
21144    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21145        self.number_deleted_lines = number;
21146        cx.notify();
21147    }
21148
21149    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21150        self.delegate_expand_excerpts = delegate;
21151    }
21152
21153    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21154        self.delegate_stage_and_restore = delegate;
21155    }
21156
21157    pub fn set_on_local_selections_changed(
21158        &mut self,
21159        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21160    ) {
21161        self.on_local_selections_changed = callback;
21162    }
21163
21164    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21165        self.suppress_selection_callback = suppress;
21166    }
21167
21168    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21169        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21170        cx.notify();
21171    }
21172
21173    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21174        self.show_code_actions = Some(show_code_actions);
21175        cx.notify();
21176    }
21177
21178    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21179        self.show_runnables = Some(show_runnables);
21180        cx.notify();
21181    }
21182
21183    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21184        self.show_breakpoints = Some(show_breakpoints);
21185        cx.notify();
21186    }
21187
21188    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21189        self.show_diff_review_button = show;
21190        cx.notify();
21191    }
21192
21193    pub fn show_diff_review_button(&self) -> bool {
21194        self.show_diff_review_button
21195    }
21196
21197    pub fn render_diff_review_button(
21198        &self,
21199        display_row: DisplayRow,
21200        width: Pixels,
21201        cx: &mut Context<Self>,
21202    ) -> impl IntoElement {
21203        let text_color = cx.theme().colors().text;
21204        let icon_color = cx.theme().colors().icon_accent;
21205
21206        h_flex()
21207            .id("diff_review_button")
21208            .cursor_pointer()
21209            .w(width - px(1.))
21210            .h(relative(0.9))
21211            .justify_center()
21212            .rounded_sm()
21213            .border_1()
21214            .border_color(text_color.opacity(0.1))
21215            .bg(text_color.opacity(0.15))
21216            .hover(|s| {
21217                s.bg(icon_color.opacity(0.4))
21218                    .border_color(icon_color.opacity(0.5))
21219            })
21220            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21221            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21222            .on_mouse_down(
21223                gpui::MouseButton::Left,
21224                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21225                    editor.start_diff_review_drag(display_row, window, cx);
21226                }),
21227            )
21228    }
21229
21230    pub fn start_diff_review_drag(
21231        &mut self,
21232        display_row: DisplayRow,
21233        window: &mut Window,
21234        cx: &mut Context<Self>,
21235    ) {
21236        let snapshot = self.snapshot(window, cx);
21237        let point = snapshot
21238            .display_snapshot
21239            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21240        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21241        self.diff_review_drag_state = Some(DiffReviewDragState {
21242            start_anchor: anchor,
21243            current_anchor: anchor,
21244        });
21245        cx.notify();
21246    }
21247
21248    pub fn update_diff_review_drag(
21249        &mut self,
21250        display_row: DisplayRow,
21251        window: &mut Window,
21252        cx: &mut Context<Self>,
21253    ) {
21254        if self.diff_review_drag_state.is_none() {
21255            return;
21256        }
21257        let snapshot = self.snapshot(window, cx);
21258        let point = snapshot
21259            .display_snapshot
21260            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21261        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21262        if let Some(drag_state) = &mut self.diff_review_drag_state {
21263            drag_state.current_anchor = anchor;
21264            cx.notify();
21265        }
21266    }
21267
21268    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21269        if let Some(drag_state) = self.diff_review_drag_state.take() {
21270            let snapshot = self.snapshot(window, cx);
21271            let range = drag_state.row_range(&snapshot.display_snapshot);
21272            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21273        }
21274        cx.notify();
21275    }
21276
21277    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21278        self.diff_review_drag_state = None;
21279        cx.notify();
21280    }
21281
21282    /// Calculates the appropriate block height for the diff review overlay.
21283    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21284    /// and 2 lines per comment when expanded.
21285    fn calculate_overlay_height(
21286        &self,
21287        hunk_key: &DiffHunkKey,
21288        comments_expanded: bool,
21289        snapshot: &MultiBufferSnapshot,
21290    ) -> u32 {
21291        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21292        let base_height: u32 = 2; // Input row with avatar and buttons
21293
21294        if comment_count == 0 {
21295            base_height
21296        } else if comments_expanded {
21297            // Header (1 line) + 2 lines per comment
21298            base_height + 1 + (comment_count as u32 * 2)
21299        } else {
21300            // Just header when collapsed
21301            base_height + 1
21302        }
21303    }
21304
21305    pub fn show_diff_review_overlay(
21306        &mut self,
21307        display_range: Range<DisplayRow>,
21308        window: &mut Window,
21309        cx: &mut Context<Self>,
21310    ) {
21311        let Range { start, end } = display_range.sorted();
21312
21313        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21314        let editor_snapshot = self.snapshot(window, cx);
21315
21316        // Convert display rows to multibuffer points
21317        let start_point = editor_snapshot
21318            .display_snapshot
21319            .display_point_to_point(start.as_display_point(), Bias::Left);
21320        let end_point = editor_snapshot
21321            .display_snapshot
21322            .display_point_to_point(end.as_display_point(), Bias::Left);
21323        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21324
21325        // Create anchor range for the selected lines (start of first line to end of last line)
21326        let line_end = Point::new(
21327            end_point.row,
21328            buffer_snapshot.line_len(end_multi_buffer_row),
21329        );
21330        let anchor_range =
21331            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21332
21333        // Compute the hunk key for this display row
21334        let file_path = buffer_snapshot
21335            .file_at(start_point)
21336            .map(|file: &Arc<dyn language::File>| file.path().clone())
21337            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21338        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21339        let new_hunk_key = DiffHunkKey {
21340            file_path,
21341            hunk_start_anchor,
21342        };
21343
21344        // Check if we already have an overlay for this hunk
21345        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21346            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21347        }) {
21348            // Just focus the existing overlay's prompt editor
21349            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21350            window.focus(&focus_handle, cx);
21351            return;
21352        }
21353
21354        // Dismiss overlays that have no comments for their hunks
21355        self.dismiss_overlays_without_comments(cx);
21356
21357        // Get the current user's avatar URI from the project's user_store
21358        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21359            let user_store = project.read(cx).user_store();
21360            user_store
21361                .read(cx)
21362                .current_user()
21363                .map(|user| user.avatar_uri.clone())
21364        });
21365
21366        // Create anchor at the end of the last row so the block appears immediately below it
21367        // Use multibuffer coordinates for anchor creation
21368        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21369        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21370
21371        // Use the hunk key we already computed
21372        let hunk_key = new_hunk_key;
21373
21374        // Create the prompt editor for the review input
21375        let prompt_editor = cx.new(|cx| {
21376            let mut editor = Editor::single_line(window, cx);
21377            editor.set_placeholder_text("Add a review comment...", window, cx);
21378            editor
21379        });
21380
21381        // Register the Newline action on the prompt editor to submit the review
21382        let parent_editor = cx.entity().downgrade();
21383        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21384            prompt_editor.register_action({
21385                let parent_editor = parent_editor.clone();
21386                move |_: &crate::actions::Newline, window, cx| {
21387                    if let Some(editor) = parent_editor.upgrade() {
21388                        editor.update(cx, |editor, cx| {
21389                            editor.submit_diff_review_comment(window, cx);
21390                        });
21391                    }
21392                }
21393            })
21394        });
21395
21396        // Calculate initial height based on existing comments for this hunk
21397        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21398
21399        // Create the overlay block
21400        let prompt_editor_for_render = prompt_editor.clone();
21401        let hunk_key_for_render = hunk_key.clone();
21402        let editor_handle = cx.entity().downgrade();
21403        let block = BlockProperties {
21404            style: BlockStyle::Sticky,
21405            placement: BlockPlacement::Below(anchor),
21406            height: Some(initial_height),
21407            render: Arc::new(move |cx| {
21408                Self::render_diff_review_overlay(
21409                    &prompt_editor_for_render,
21410                    &hunk_key_for_render,
21411                    &editor_handle,
21412                    cx,
21413                )
21414            }),
21415            priority: 0,
21416        };
21417
21418        let block_ids = self.insert_blocks([block], None, cx);
21419        let Some(block_id) = block_ids.into_iter().next() else {
21420            log::error!("Failed to insert diff review overlay block");
21421            return;
21422        };
21423
21424        self.diff_review_overlays.push(DiffReviewOverlay {
21425            anchor_range,
21426            block_id,
21427            prompt_editor: prompt_editor.clone(),
21428            hunk_key,
21429            comments_expanded: true,
21430            inline_edit_editors: HashMap::default(),
21431            inline_edit_subscriptions: HashMap::default(),
21432            user_avatar_uri,
21433            _subscription: subscription,
21434        });
21435
21436        // Focus the prompt editor
21437        let focus_handle = prompt_editor.focus_handle(cx);
21438        window.focus(&focus_handle, cx);
21439
21440        cx.notify();
21441    }
21442
21443    /// Dismisses all diff review overlays.
21444    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21445        if self.diff_review_overlays.is_empty() {
21446            return;
21447        }
21448        let block_ids: HashSet<_> = self
21449            .diff_review_overlays
21450            .drain(..)
21451            .map(|overlay| overlay.block_id)
21452            .collect();
21453        self.remove_blocks(block_ids, None, cx);
21454        cx.notify();
21455    }
21456
21457    /// Dismisses overlays that have no comments stored for their hunks.
21458    /// Keeps overlays that have at least one comment.
21459    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21460        let snapshot = self.buffer.read(cx).snapshot(cx);
21461
21462        // First, compute which overlays have comments (to avoid borrow issues with retain)
21463        let overlays_with_comments: Vec<bool> = self
21464            .diff_review_overlays
21465            .iter()
21466            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21467            .collect();
21468
21469        // Now collect block IDs to remove and retain overlays
21470        let mut block_ids_to_remove = HashSet::default();
21471        let mut index = 0;
21472        self.diff_review_overlays.retain(|overlay| {
21473            let has_comments = overlays_with_comments[index];
21474            index += 1;
21475            if !has_comments {
21476                block_ids_to_remove.insert(overlay.block_id);
21477            }
21478            has_comments
21479        });
21480
21481        if !block_ids_to_remove.is_empty() {
21482            self.remove_blocks(block_ids_to_remove, None, cx);
21483            cx.notify();
21484        }
21485    }
21486
21487    /// Refreshes the diff review overlay block to update its height and render function.
21488    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21489    fn refresh_diff_review_overlay_height(
21490        &mut self,
21491        hunk_key: &DiffHunkKey,
21492        _window: &mut Window,
21493        cx: &mut Context<Self>,
21494    ) {
21495        // Extract all needed data from overlay first to avoid borrow conflicts
21496        let snapshot = self.buffer.read(cx).snapshot(cx);
21497        let (comments_expanded, block_id, prompt_editor) = {
21498            let Some(overlay) = self
21499                .diff_review_overlays
21500                .iter()
21501                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21502            else {
21503                return;
21504            };
21505
21506            (
21507                overlay.comments_expanded,
21508                overlay.block_id,
21509                overlay.prompt_editor.clone(),
21510            )
21511        };
21512
21513        // Calculate new height
21514        let snapshot = self.buffer.read(cx).snapshot(cx);
21515        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21516
21517        // Update the block height using resize_blocks (avoids flicker)
21518        let mut heights = HashMap::default();
21519        heights.insert(block_id, new_height);
21520        self.resize_blocks(heights, None, cx);
21521
21522        // Update the render function using replace_blocks (avoids flicker)
21523        let hunk_key_for_render = hunk_key.clone();
21524        let editor_handle = cx.entity().downgrade();
21525        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21526            Arc::new(move |cx| {
21527                Self::render_diff_review_overlay(
21528                    &prompt_editor,
21529                    &hunk_key_for_render,
21530                    &editor_handle,
21531                    cx,
21532                )
21533            });
21534
21535        let mut renderers = HashMap::default();
21536        renderers.insert(block_id, render);
21537        self.replace_blocks(renderers, None, cx);
21538    }
21539
21540    /// Action handler for SubmitDiffReviewComment.
21541    pub fn submit_diff_review_comment_action(
21542        &mut self,
21543        _: &SubmitDiffReviewComment,
21544        window: &mut Window,
21545        cx: &mut Context<Self>,
21546    ) {
21547        self.submit_diff_review_comment(window, cx);
21548    }
21549
21550    /// Stores the diff review comment locally.
21551    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21552    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21553        // Find the overlay that currently has focus
21554        let overlay_index = self
21555            .diff_review_overlays
21556            .iter()
21557            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21558        let Some(overlay_index) = overlay_index else {
21559            return;
21560        };
21561        let overlay = &self.diff_review_overlays[overlay_index];
21562
21563        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21564        if comment_text.is_empty() {
21565            return;
21566        }
21567
21568        let anchor_range = overlay.anchor_range.clone();
21569        let hunk_key = overlay.hunk_key.clone();
21570
21571        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21572
21573        // Clear the prompt editor but keep the overlay open
21574        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21575            overlay.prompt_editor.update(cx, |editor, cx| {
21576                editor.clear(window, cx);
21577            });
21578        }
21579
21580        // Refresh the overlay to update the block height for the new comment
21581        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21582
21583        cx.notify();
21584    }
21585
21586    /// Returns the prompt editor for the diff review overlay, if one is active.
21587    /// This is primarily used for testing.
21588    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21589        self.diff_review_overlays
21590            .first()
21591            .map(|overlay| &overlay.prompt_editor)
21592    }
21593
21594    /// Returns the line range for the first diff review overlay, if one is active.
21595    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21596    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21597        let overlay = self.diff_review_overlays.first()?;
21598        let snapshot = self.buffer.read(cx).snapshot(cx);
21599        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21600        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21601        let start_row = snapshot
21602            .point_to_buffer_point(start_point)
21603            .map(|(_, p, _)| p.row)
21604            .unwrap_or(start_point.row);
21605        let end_row = snapshot
21606            .point_to_buffer_point(end_point)
21607            .map(|(_, p, _)| p.row)
21608            .unwrap_or(end_point.row);
21609        Some((start_row, end_row))
21610    }
21611
21612    /// Sets whether the comments section is expanded in the diff review overlay.
21613    /// This is primarily used for testing.
21614    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21615        for overlay in &mut self.diff_review_overlays {
21616            overlay.comments_expanded = expanded;
21617        }
21618        cx.notify();
21619    }
21620
21621    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21622    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21623        a.file_path == b.file_path
21624            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21625    }
21626
21627    /// Returns comments for a specific hunk, ordered by creation time.
21628    pub fn comments_for_hunk<'a>(
21629        &'a self,
21630        key: &DiffHunkKey,
21631        snapshot: &MultiBufferSnapshot,
21632    ) -> &'a [StoredReviewComment] {
21633        let key_point = key.hunk_start_anchor.to_point(snapshot);
21634        self.stored_review_comments
21635            .iter()
21636            .find(|(k, _)| {
21637                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21638            })
21639            .map(|(_, comments)| comments.as_slice())
21640            .unwrap_or(&[])
21641    }
21642
21643    /// Returns the total count of stored review comments across all hunks.
21644    pub fn total_review_comment_count(&self) -> usize {
21645        self.stored_review_comments
21646            .iter()
21647            .map(|(_, v)| v.len())
21648            .sum()
21649    }
21650
21651    /// Returns the count of comments for a specific hunk.
21652    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21653        let key_point = key.hunk_start_anchor.to_point(snapshot);
21654        self.stored_review_comments
21655            .iter()
21656            .find(|(k, _)| {
21657                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21658            })
21659            .map(|(_, v)| v.len())
21660            .unwrap_or(0)
21661    }
21662
21663    /// Adds a new review comment to a specific hunk.
21664    pub fn add_review_comment(
21665        &mut self,
21666        hunk_key: DiffHunkKey,
21667        comment: String,
21668        anchor_range: Range<Anchor>,
21669        cx: &mut Context<Self>,
21670    ) -> usize {
21671        let id = self.next_review_comment_id;
21672        self.next_review_comment_id += 1;
21673
21674        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21675
21676        let snapshot = self.buffer.read(cx).snapshot(cx);
21677        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21678
21679        // Find existing entry for this hunk or add a new one
21680        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21681            k.file_path == hunk_key.file_path
21682                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21683        }) {
21684            comments.push(stored_comment);
21685        } else {
21686            self.stored_review_comments
21687                .push((hunk_key, vec![stored_comment]));
21688        }
21689
21690        cx.emit(EditorEvent::ReviewCommentsChanged {
21691            total_count: self.total_review_comment_count(),
21692        });
21693        cx.notify();
21694        id
21695    }
21696
21697    /// Removes a review comment by ID from any hunk.
21698    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21699        for (_, comments) in self.stored_review_comments.iter_mut() {
21700            if let Some(index) = comments.iter().position(|c| c.id == id) {
21701                comments.remove(index);
21702                cx.emit(EditorEvent::ReviewCommentsChanged {
21703                    total_count: self.total_review_comment_count(),
21704                });
21705                cx.notify();
21706                return true;
21707            }
21708        }
21709        false
21710    }
21711
21712    /// Updates a review comment's text by ID.
21713    pub fn update_review_comment(
21714        &mut self,
21715        id: usize,
21716        new_comment: String,
21717        cx: &mut Context<Self>,
21718    ) -> bool {
21719        for (_, comments) in self.stored_review_comments.iter_mut() {
21720            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21721                comment.comment = new_comment;
21722                comment.is_editing = false;
21723                cx.emit(EditorEvent::ReviewCommentsChanged {
21724                    total_count: self.total_review_comment_count(),
21725                });
21726                cx.notify();
21727                return true;
21728            }
21729        }
21730        false
21731    }
21732
21733    /// Sets a comment's editing state.
21734    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
21735        for (_, comments) in self.stored_review_comments.iter_mut() {
21736            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21737                comment.is_editing = is_editing;
21738                cx.notify();
21739                return;
21740            }
21741        }
21742    }
21743
21744    /// Takes all stored comments from all hunks, clearing the storage.
21745    /// Returns a Vec of (hunk_key, comments) pairs.
21746    pub fn take_all_review_comments(
21747        &mut self,
21748        cx: &mut Context<Self>,
21749    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
21750        // Dismiss all overlays when taking comments (e.g., when sending to agent)
21751        self.dismiss_all_diff_review_overlays(cx);
21752        let comments = std::mem::take(&mut self.stored_review_comments);
21753        // Reset the ID counter since all comments have been taken
21754        self.next_review_comment_id = 0;
21755        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
21756        cx.notify();
21757        comments
21758    }
21759
21760    /// Removes review comments whose anchors are no longer valid or whose
21761    /// associated diff hunks no longer exist.
21762    ///
21763    /// This should be called when the buffer changes to prevent orphaned comments
21764    /// from accumulating.
21765    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
21766        let snapshot = self.buffer.read(cx).snapshot(cx);
21767        let original_count = self.total_review_comment_count();
21768
21769        // Remove comments with invalid hunk anchors
21770        self.stored_review_comments
21771            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
21772
21773        // Also clean up individual comments with invalid anchor ranges
21774        for (_, comments) in &mut self.stored_review_comments {
21775            comments.retain(|comment| {
21776                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
21777            });
21778        }
21779
21780        // Remove empty hunk entries
21781        self.stored_review_comments
21782            .retain(|(_, comments)| !comments.is_empty());
21783
21784        let new_count = self.total_review_comment_count();
21785        if new_count != original_count {
21786            cx.emit(EditorEvent::ReviewCommentsChanged {
21787                total_count: new_count,
21788            });
21789            cx.notify();
21790        }
21791    }
21792
21793    /// Toggles the expanded state of the comments section in the overlay.
21794    pub fn toggle_review_comments_expanded(
21795        &mut self,
21796        _: &ToggleReviewCommentsExpanded,
21797        window: &mut Window,
21798        cx: &mut Context<Self>,
21799    ) {
21800        // Find the overlay that currently has focus, or use the first one
21801        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
21802            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
21803                overlay.comments_expanded = !overlay.comments_expanded;
21804                Some(overlay.hunk_key.clone())
21805            } else {
21806                None
21807            }
21808        });
21809
21810        // If no focused overlay found, toggle the first one
21811        let hunk_key = overlay_info.or_else(|| {
21812            self.diff_review_overlays.first_mut().map(|overlay| {
21813                overlay.comments_expanded = !overlay.comments_expanded;
21814                overlay.hunk_key.clone()
21815            })
21816        });
21817
21818        if let Some(hunk_key) = hunk_key {
21819            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21820            cx.notify();
21821        }
21822    }
21823
21824    /// Handles the EditReviewComment action - sets a comment into editing mode.
21825    pub fn edit_review_comment(
21826        &mut self,
21827        action: &EditReviewComment,
21828        window: &mut Window,
21829        cx: &mut Context<Self>,
21830    ) {
21831        let comment_id = action.id;
21832
21833        // Set the comment to editing mode
21834        self.set_comment_editing(comment_id, true, cx);
21835
21836        // Find the overlay that contains this comment and create an inline editor if needed
21837        // First, find which hunk this comment belongs to
21838        let hunk_key = self
21839            .stored_review_comments
21840            .iter()
21841            .find_map(|(key, comments)| {
21842                if comments.iter().any(|c| c.id == comment_id) {
21843                    Some(key.clone())
21844                } else {
21845                    None
21846                }
21847            });
21848
21849        let snapshot = self.buffer.read(cx).snapshot(cx);
21850        if let Some(hunk_key) = hunk_key {
21851            if let Some(overlay) = self
21852                .diff_review_overlays
21853                .iter_mut()
21854                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21855            {
21856                if let std::collections::hash_map::Entry::Vacant(entry) =
21857                    overlay.inline_edit_editors.entry(comment_id)
21858                {
21859                    // Find the comment text
21860                    let comment_text = self
21861                        .stored_review_comments
21862                        .iter()
21863                        .flat_map(|(_, comments)| comments)
21864                        .find(|c| c.id == comment_id)
21865                        .map(|c| c.comment.clone())
21866                        .unwrap_or_default();
21867
21868                    // Create inline editor
21869                    let parent_editor = cx.entity().downgrade();
21870                    let inline_editor = cx.new(|cx| {
21871                        let mut editor = Editor::single_line(window, cx);
21872                        editor.set_text(&*comment_text, window, cx);
21873                        // Select all text for easy replacement
21874                        editor.select_all(&crate::actions::SelectAll, window, cx);
21875                        editor
21876                    });
21877
21878                    // Register the Newline action to confirm the edit
21879                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
21880                        inline_editor.register_action({
21881                            let parent_editor = parent_editor.clone();
21882                            move |_: &crate::actions::Newline, window, cx| {
21883                                if let Some(editor) = parent_editor.upgrade() {
21884                                    editor.update(cx, |editor, cx| {
21885                                        editor.confirm_edit_review_comment(comment_id, window, cx);
21886                                    });
21887                                }
21888                            }
21889                        })
21890                    });
21891
21892                    // Store the subscription to keep the action handler alive
21893                    overlay
21894                        .inline_edit_subscriptions
21895                        .insert(comment_id, subscription);
21896
21897                    // Focus the inline editor
21898                    let focus_handle = inline_editor.focus_handle(cx);
21899                    window.focus(&focus_handle, cx);
21900
21901                    entry.insert(inline_editor);
21902                }
21903            }
21904        }
21905
21906        cx.notify();
21907    }
21908
21909    /// Confirms an inline edit of a review comment.
21910    pub fn confirm_edit_review_comment(
21911        &mut self,
21912        comment_id: usize,
21913        _window: &mut Window,
21914        cx: &mut Context<Self>,
21915    ) {
21916        // Get the new text from the inline editor
21917        // Find the overlay containing this comment's inline editor
21918        let snapshot = self.buffer.read(cx).snapshot(cx);
21919        let hunk_key = self
21920            .stored_review_comments
21921            .iter()
21922            .find_map(|(key, comments)| {
21923                if comments.iter().any(|c| c.id == comment_id) {
21924                    Some(key.clone())
21925                } else {
21926                    None
21927                }
21928            });
21929
21930        let new_text = hunk_key
21931            .as_ref()
21932            .and_then(|hunk_key| {
21933                self.diff_review_overlays
21934                    .iter()
21935                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21936            })
21937            .as_ref()
21938            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
21939            .map(|editor| editor.read(cx).text(cx).trim().to_string());
21940
21941        if let Some(new_text) = new_text {
21942            if !new_text.is_empty() {
21943                self.update_review_comment(comment_id, new_text, cx);
21944            }
21945        }
21946
21947        // Remove the inline editor and its subscription
21948        if let Some(hunk_key) = hunk_key {
21949            if let Some(overlay) = self
21950                .diff_review_overlays
21951                .iter_mut()
21952                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21953            {
21954                overlay.inline_edit_editors.remove(&comment_id);
21955                overlay.inline_edit_subscriptions.remove(&comment_id);
21956            }
21957        }
21958
21959        // Clear editing state
21960        self.set_comment_editing(comment_id, false, cx);
21961    }
21962
21963    /// Cancels an inline edit of a review comment.
21964    pub fn cancel_edit_review_comment(
21965        &mut self,
21966        comment_id: usize,
21967        _window: &mut Window,
21968        cx: &mut Context<Self>,
21969    ) {
21970        // Find which hunk this comment belongs to
21971        let hunk_key = self
21972            .stored_review_comments
21973            .iter()
21974            .find_map(|(key, comments)| {
21975                if comments.iter().any(|c| c.id == comment_id) {
21976                    Some(key.clone())
21977                } else {
21978                    None
21979                }
21980            });
21981
21982        // Remove the inline editor and its subscription
21983        if let Some(hunk_key) = hunk_key {
21984            let snapshot = self.buffer.read(cx).snapshot(cx);
21985            if let Some(overlay) = self
21986                .diff_review_overlays
21987                .iter_mut()
21988                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21989            {
21990                overlay.inline_edit_editors.remove(&comment_id);
21991                overlay.inline_edit_subscriptions.remove(&comment_id);
21992            }
21993        }
21994
21995        // Clear editing state
21996        self.set_comment_editing(comment_id, false, cx);
21997    }
21998
21999    /// Action handler for ConfirmEditReviewComment.
22000    pub fn confirm_edit_review_comment_action(
22001        &mut self,
22002        action: &ConfirmEditReviewComment,
22003        window: &mut Window,
22004        cx: &mut Context<Self>,
22005    ) {
22006        self.confirm_edit_review_comment(action.id, window, cx);
22007    }
22008
22009    /// Action handler for CancelEditReviewComment.
22010    pub fn cancel_edit_review_comment_action(
22011        &mut self,
22012        action: &CancelEditReviewComment,
22013        window: &mut Window,
22014        cx: &mut Context<Self>,
22015    ) {
22016        self.cancel_edit_review_comment(action.id, window, cx);
22017    }
22018
22019    /// Handles the DeleteReviewComment action - removes a comment.
22020    pub fn delete_review_comment(
22021        &mut self,
22022        action: &DeleteReviewComment,
22023        window: &mut Window,
22024        cx: &mut Context<Self>,
22025    ) {
22026        // Get the hunk key before removing the comment
22027        // Find the hunk key from the comment itself
22028        let comment_id = action.id;
22029        let hunk_key = self
22030            .stored_review_comments
22031            .iter()
22032            .find_map(|(key, comments)| {
22033                if comments.iter().any(|c| c.id == comment_id) {
22034                    Some(key.clone())
22035                } else {
22036                    None
22037                }
22038            });
22039
22040        // Also get it from the overlay for refresh purposes
22041        let overlay_hunk_key = self
22042            .diff_review_overlays
22043            .first()
22044            .map(|o| o.hunk_key.clone());
22045
22046        self.remove_review_comment(action.id, cx);
22047
22048        // Refresh the overlay height after removing a comment
22049        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22050            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22051        }
22052    }
22053
22054    fn render_diff_review_overlay(
22055        prompt_editor: &Entity<Editor>,
22056        hunk_key: &DiffHunkKey,
22057        editor_handle: &WeakEntity<Editor>,
22058        cx: &mut BlockContext,
22059    ) -> AnyElement {
22060        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22061            if ranges.is_empty() {
22062                return None;
22063            }
22064            let formatted: Vec<String> = ranges
22065                .iter()
22066                .map(|(start, end)| {
22067                    let start_line = start + 1;
22068                    let end_line = end + 1;
22069                    if start_line == end_line {
22070                        format!("Line {start_line}")
22071                    } else {
22072                        format!("Lines {start_line}-{end_line}")
22073                    }
22074                })
22075                .collect();
22076            // Don't show label for single line in single excerpt
22077            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22078                return None;
22079            }
22080            Some(formatted.join(""))
22081        }
22082
22083        let theme = cx.theme();
22084        let colors = theme.colors();
22085
22086        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22087            editor_handle
22088                .upgrade()
22089                .map(|editor| {
22090                    let editor = editor.read(cx);
22091                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22092                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22093                    let (expanded, editors, avatar_uri, line_ranges) = editor
22094                        .diff_review_overlays
22095                        .iter()
22096                        .find(|overlay| {
22097                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22098                        })
22099                        .map(|o| {
22100                            let start_point = o.anchor_range.start.to_point(&snapshot);
22101                            let end_point = o.anchor_range.end.to_point(&snapshot);
22102                            // Get line ranges per excerpt to detect discontinuities
22103                            let buffer_ranges =
22104                                snapshot.range_to_buffer_ranges(start_point..end_point);
22105                            let ranges: Vec<(u32, u32)> = buffer_ranges
22106                                .iter()
22107                                .map(|(buffer, range, _)| {
22108                                    let start = buffer.offset_to_point(range.start.0).row;
22109                                    let end = buffer.offset_to_point(range.end.0).row;
22110                                    (start, end)
22111                                })
22112                                .collect();
22113                            (
22114                                o.comments_expanded,
22115                                o.inline_edit_editors.clone(),
22116                                o.user_avatar_uri.clone(),
22117                                if ranges.is_empty() {
22118                                    None
22119                                } else {
22120                                    Some(ranges)
22121                                },
22122                            )
22123                        })
22124                        .unwrap_or((true, HashMap::default(), None, None));
22125                    (comments, expanded, editors, avatar_uri, line_ranges)
22126                })
22127                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22128
22129        let comment_count = comments.len();
22130        let avatar_size = px(20.);
22131        let action_icon_size = IconSize::XSmall;
22132
22133        v_flex()
22134            .w_full()
22135            .bg(colors.editor_background)
22136            .border_b_1()
22137            .border_color(colors.border)
22138            .px_2()
22139            .pb_2()
22140            .gap_2()
22141            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22142            .when_some(line_ranges, |el, ranges| {
22143                let label = format_line_ranges(&ranges);
22144                if let Some(label) = label {
22145                    el.child(
22146                        h_flex()
22147                            .w_full()
22148                            .px_2()
22149                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22150                    )
22151                } else {
22152                    el
22153                }
22154            })
22155            // Top row: editable input with user's avatar
22156            .child(
22157                h_flex()
22158                    .w_full()
22159                    .items_center()
22160                    .gap_2()
22161                    .px_2()
22162                    .py_1p5()
22163                    .rounded_md()
22164                    .bg(colors.surface_background)
22165                    .child(
22166                        div()
22167                            .size(avatar_size)
22168                            .flex_shrink_0()
22169                            .rounded_full()
22170                            .overflow_hidden()
22171                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22172                                Avatar::new(avatar_uri.clone())
22173                                    .size(avatar_size)
22174                                    .into_any_element()
22175                            } else {
22176                                Icon::new(IconName::Person)
22177                                    .size(IconSize::Small)
22178                                    .color(ui::Color::Muted)
22179                                    .into_any_element()
22180                            }),
22181                    )
22182                    .child(
22183                        div()
22184                            .flex_1()
22185                            .border_1()
22186                            .border_color(colors.border)
22187                            .rounded_md()
22188                            .bg(colors.editor_background)
22189                            .px_2()
22190                            .py_1()
22191                            .child(prompt_editor.clone()),
22192                    )
22193                    .child(
22194                        h_flex()
22195                            .flex_shrink_0()
22196                            .gap_1()
22197                            .child(
22198                                IconButton::new("diff-review-close", IconName::Close)
22199                                    .icon_color(ui::Color::Muted)
22200                                    .icon_size(action_icon_size)
22201                                    .tooltip(Tooltip::text("Close"))
22202                                    .on_click(|_, window, cx| {
22203                                        window
22204                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22205                                    }),
22206                            )
22207                            .child(
22208                                IconButton::new("diff-review-add", IconName::Return)
22209                                    .icon_color(ui::Color::Muted)
22210                                    .icon_size(action_icon_size)
22211                                    .tooltip(Tooltip::text("Add comment"))
22212                                    .on_click(|_, window, cx| {
22213                                        window.dispatch_action(
22214                                            Box::new(crate::actions::SubmitDiffReviewComment),
22215                                            cx,
22216                                        );
22217                                    }),
22218                            ),
22219                    ),
22220            )
22221            // Expandable comments section (only shown when there are comments)
22222            .when(comment_count > 0, |el| {
22223                el.child(Self::render_comments_section(
22224                    comments,
22225                    comments_expanded,
22226                    inline_editors,
22227                    user_avatar_uri,
22228                    avatar_size,
22229                    action_icon_size,
22230                    colors,
22231                ))
22232            })
22233            .into_any_element()
22234    }
22235
22236    fn render_comments_section(
22237        comments: Vec<StoredReviewComment>,
22238        expanded: bool,
22239        inline_editors: HashMap<usize, Entity<Editor>>,
22240        user_avatar_uri: Option<SharedUri>,
22241        avatar_size: Pixels,
22242        action_icon_size: IconSize,
22243        colors: &theme::ThemeColors,
22244    ) -> impl IntoElement {
22245        let comment_count = comments.len();
22246
22247        v_flex()
22248            .w_full()
22249            .gap_1()
22250            // Header with expand/collapse toggle
22251            .child(
22252                h_flex()
22253                    .id("review-comments-header")
22254                    .w_full()
22255                    .items_center()
22256                    .gap_1()
22257                    .px_2()
22258                    .py_1()
22259                    .cursor_pointer()
22260                    .rounded_md()
22261                    .hover(|style| style.bg(colors.ghost_element_hover))
22262                    .on_click(|_, window: &mut Window, cx| {
22263                        window.dispatch_action(
22264                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22265                            cx,
22266                        );
22267                    })
22268                    .child(
22269                        Icon::new(if expanded {
22270                            IconName::ChevronDown
22271                        } else {
22272                            IconName::ChevronRight
22273                        })
22274                        .size(IconSize::Small)
22275                        .color(ui::Color::Muted),
22276                    )
22277                    .child(
22278                        Label::new(format!(
22279                            "{} Comment{}",
22280                            comment_count,
22281                            if comment_count == 1 { "" } else { "s" }
22282                        ))
22283                        .size(LabelSize::Small)
22284                        .color(Color::Muted),
22285                    ),
22286            )
22287            // Comments list (when expanded)
22288            .when(expanded, |el| {
22289                el.children(comments.into_iter().map(|comment| {
22290                    let inline_editor = inline_editors.get(&comment.id).cloned();
22291                    Self::render_comment_row(
22292                        comment,
22293                        inline_editor,
22294                        user_avatar_uri.clone(),
22295                        avatar_size,
22296                        action_icon_size,
22297                        colors,
22298                    )
22299                }))
22300            })
22301    }
22302
22303    fn render_comment_row(
22304        comment: StoredReviewComment,
22305        inline_editor: Option<Entity<Editor>>,
22306        user_avatar_uri: Option<SharedUri>,
22307        avatar_size: Pixels,
22308        action_icon_size: IconSize,
22309        colors: &theme::ThemeColors,
22310    ) -> impl IntoElement {
22311        let comment_id = comment.id;
22312        let is_editing = inline_editor.is_some();
22313
22314        h_flex()
22315            .w_full()
22316            .items_center()
22317            .gap_2()
22318            .px_2()
22319            .py_1p5()
22320            .rounded_md()
22321            .bg(colors.surface_background)
22322            .child(
22323                div()
22324                    .size(avatar_size)
22325                    .flex_shrink_0()
22326                    .rounded_full()
22327                    .overflow_hidden()
22328                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22329                        Avatar::new(avatar_uri.clone())
22330                            .size(avatar_size)
22331                            .into_any_element()
22332                    } else {
22333                        Icon::new(IconName::Person)
22334                            .size(IconSize::Small)
22335                            .color(ui::Color::Muted)
22336                            .into_any_element()
22337                    }),
22338            )
22339            .child(if let Some(editor) = inline_editor {
22340                // Inline edit mode: show an editable text field
22341                div()
22342                    .flex_1()
22343                    .border_1()
22344                    .border_color(colors.border)
22345                    .rounded_md()
22346                    .bg(colors.editor_background)
22347                    .px_2()
22348                    .py_1()
22349                    .child(editor)
22350                    .into_any_element()
22351            } else {
22352                // Display mode: show the comment text
22353                div()
22354                    .flex_1()
22355                    .text_sm()
22356                    .text_color(colors.text)
22357                    .child(comment.comment)
22358                    .into_any_element()
22359            })
22360            .child(if is_editing {
22361                // Editing mode: show close and confirm buttons
22362                h_flex()
22363                    .gap_1()
22364                    .child(
22365                        IconButton::new(
22366                            format!("diff-review-cancel-edit-{comment_id}"),
22367                            IconName::Close,
22368                        )
22369                        .icon_color(ui::Color::Muted)
22370                        .icon_size(action_icon_size)
22371                        .tooltip(Tooltip::text("Cancel"))
22372                        .on_click(move |_, window, cx| {
22373                            window.dispatch_action(
22374                                Box::new(crate::actions::CancelEditReviewComment {
22375                                    id: comment_id,
22376                                }),
22377                                cx,
22378                            );
22379                        }),
22380                    )
22381                    .child(
22382                        IconButton::new(
22383                            format!("diff-review-confirm-edit-{comment_id}"),
22384                            IconName::Return,
22385                        )
22386                        .icon_color(ui::Color::Muted)
22387                        .icon_size(action_icon_size)
22388                        .tooltip(Tooltip::text("Confirm"))
22389                        .on_click(move |_, window, cx| {
22390                            window.dispatch_action(
22391                                Box::new(crate::actions::ConfirmEditReviewComment {
22392                                    id: comment_id,
22393                                }),
22394                                cx,
22395                            );
22396                        }),
22397                    )
22398                    .into_any_element()
22399            } else {
22400                // Display mode: no action buttons for now (edit/delete not yet implemented)
22401                gpui::Empty.into_any_element()
22402            })
22403    }
22404
22405    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22406        if self.display_map.read(cx).masked != masked {
22407            self.display_map.update(cx, |map, _| map.masked = masked);
22408        }
22409        cx.notify()
22410    }
22411
22412    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22413        self.show_wrap_guides = Some(show_wrap_guides);
22414        cx.notify();
22415    }
22416
22417    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22418        self.show_indent_guides = Some(show_indent_guides);
22419        cx.notify();
22420    }
22421
22422    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22423        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22424            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22425                && let Some(dir) = file.abs_path(cx).parent()
22426            {
22427                return Some(dir.to_owned());
22428            }
22429        }
22430
22431        None
22432    }
22433
22434    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22435        self.active_excerpt(cx)?
22436            .1
22437            .read(cx)
22438            .file()
22439            .and_then(|f| f.as_local())
22440    }
22441
22442    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22443        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22444            let buffer = buffer.read(cx);
22445            if let Some(project_path) = buffer.project_path(cx) {
22446                let project = self.project()?.read(cx);
22447                project.absolute_path(&project_path, cx)
22448            } else {
22449                buffer
22450                    .file()
22451                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22452            }
22453        })
22454    }
22455
22456    pub fn reveal_in_finder(
22457        &mut self,
22458        _: &RevealInFileManager,
22459        _window: &mut Window,
22460        cx: &mut Context<Self>,
22461    ) {
22462        if let Some(path) = self.target_file_abs_path(cx) {
22463            if let Some(project) = self.project() {
22464                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22465            } else {
22466                cx.reveal_path(&path);
22467            }
22468        }
22469    }
22470
22471    pub fn copy_path(
22472        &mut self,
22473        _: &zed_actions::workspace::CopyPath,
22474        _window: &mut Window,
22475        cx: &mut Context<Self>,
22476    ) {
22477        if let Some(path) = self.target_file_abs_path(cx)
22478            && let Some(path) = path.to_str()
22479        {
22480            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22481        } else {
22482            cx.propagate();
22483        }
22484    }
22485
22486    pub fn copy_relative_path(
22487        &mut self,
22488        _: &zed_actions::workspace::CopyRelativePath,
22489        _window: &mut Window,
22490        cx: &mut Context<Self>,
22491    ) {
22492        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22493            let project = self.project()?.read(cx);
22494            let path = buffer.read(cx).file()?.path();
22495            let path = path.display(project.path_style(cx));
22496            Some(path)
22497        }) {
22498            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22499        } else {
22500            cx.propagate();
22501        }
22502    }
22503
22504    /// Returns the project path for the editor's buffer, if any buffer is
22505    /// opened in the editor.
22506    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22507        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22508            buffer.read(cx).project_path(cx)
22509        } else {
22510            None
22511        }
22512    }
22513
22514    // Returns true if the editor handled a go-to-line request
22515    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22516        maybe!({
22517            let breakpoint_store = self.breakpoint_store.as_ref()?;
22518
22519            let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
22520            else {
22521                self.clear_row_highlights::<ActiveDebugLine>();
22522                return None;
22523            };
22524
22525            let position = active_stack_frame.position;
22526            let buffer_id = position.buffer_id?;
22527            let snapshot = self
22528                .project
22529                .as_ref()?
22530                .read(cx)
22531                .buffer_for_id(buffer_id, cx)?
22532                .read(cx)
22533                .snapshot();
22534
22535            let mut handled = false;
22536            for (id, ExcerptRange { context, .. }) in
22537                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22538            {
22539                if context.start.cmp(&position, &snapshot).is_ge()
22540                    || context.end.cmp(&position, &snapshot).is_lt()
22541                {
22542                    continue;
22543                }
22544                let snapshot = self.buffer.read(cx).snapshot(cx);
22545                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22546
22547                handled = true;
22548                self.clear_row_highlights::<ActiveDebugLine>();
22549
22550                self.go_to_line::<ActiveDebugLine>(
22551                    multibuffer_anchor,
22552                    Some(cx.theme().colors().editor_debugger_active_line_background),
22553                    window,
22554                    cx,
22555                );
22556
22557                cx.notify();
22558            }
22559
22560            handled.then_some(())
22561        })
22562        .is_some()
22563    }
22564
22565    pub fn copy_file_name_without_extension(
22566        &mut self,
22567        _: &CopyFileNameWithoutExtension,
22568        _: &mut Window,
22569        cx: &mut Context<Self>,
22570    ) {
22571        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22572            let file = buffer.read(cx).file()?;
22573            file.path().file_stem()
22574        }) {
22575            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22576        }
22577    }
22578
22579    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22580        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22581            let file = buffer.read(cx).file()?;
22582            Some(file.file_name(cx))
22583        }) {
22584            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22585        }
22586    }
22587
22588    pub fn toggle_git_blame(
22589        &mut self,
22590        _: &::git::Blame,
22591        window: &mut Window,
22592        cx: &mut Context<Self>,
22593    ) {
22594        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22595
22596        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22597            self.start_git_blame(true, window, cx);
22598        }
22599
22600        cx.notify();
22601    }
22602
22603    pub fn toggle_git_blame_inline(
22604        &mut self,
22605        _: &ToggleGitBlameInline,
22606        window: &mut Window,
22607        cx: &mut Context<Self>,
22608    ) {
22609        self.toggle_git_blame_inline_internal(true, window, cx);
22610        cx.notify();
22611    }
22612
22613    pub fn open_git_blame_commit(
22614        &mut self,
22615        _: &OpenGitBlameCommit,
22616        window: &mut Window,
22617        cx: &mut Context<Self>,
22618    ) {
22619        self.open_git_blame_commit_internal(window, cx);
22620    }
22621
22622    fn open_git_blame_commit_internal(
22623        &mut self,
22624        window: &mut Window,
22625        cx: &mut Context<Self>,
22626    ) -> Option<()> {
22627        let blame = self.blame.as_ref()?;
22628        let snapshot = self.snapshot(window, cx);
22629        let cursor = self
22630            .selections
22631            .newest::<Point>(&snapshot.display_snapshot)
22632            .head();
22633        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22634        let (_, blame_entry) = blame
22635            .update(cx, |blame, cx| {
22636                blame
22637                    .blame_for_rows(
22638                        &[RowInfo {
22639                            buffer_id: Some(buffer.remote_id()),
22640                            buffer_row: Some(point.row),
22641                            ..Default::default()
22642                        }],
22643                        cx,
22644                    )
22645                    .next()
22646            })
22647            .flatten()?;
22648        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22649        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22650        let workspace = self.workspace()?.downgrade();
22651        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22652        None
22653    }
22654
22655    pub fn git_blame_inline_enabled(&self) -> bool {
22656        self.git_blame_inline_enabled
22657    }
22658
22659    pub fn toggle_selection_menu(
22660        &mut self,
22661        _: &ToggleSelectionMenu,
22662        _: &mut Window,
22663        cx: &mut Context<Self>,
22664    ) {
22665        self.show_selection_menu = self
22666            .show_selection_menu
22667            .map(|show_selections_menu| !show_selections_menu)
22668            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22669
22670        cx.notify();
22671    }
22672
22673    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22674        self.show_selection_menu
22675            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22676    }
22677
22678    fn start_git_blame(
22679        &mut self,
22680        user_triggered: bool,
22681        window: &mut Window,
22682        cx: &mut Context<Self>,
22683    ) {
22684        if let Some(project) = self.project() {
22685            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22686                && buffer.read(cx).file().is_none()
22687            {
22688                return;
22689            }
22690
22691            let focused = self.focus_handle(cx).contains_focused(window, cx);
22692
22693            let project = project.clone();
22694            let blame = cx
22695                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
22696            self.blame_subscription =
22697                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
22698            self.blame = Some(blame);
22699        }
22700    }
22701
22702    fn toggle_git_blame_inline_internal(
22703        &mut self,
22704        user_triggered: bool,
22705        window: &mut Window,
22706        cx: &mut Context<Self>,
22707    ) {
22708        if self.git_blame_inline_enabled {
22709            self.git_blame_inline_enabled = false;
22710            self.show_git_blame_inline = false;
22711            self.show_git_blame_inline_delay_task.take();
22712        } else {
22713            self.git_blame_inline_enabled = true;
22714            self.start_git_blame_inline(user_triggered, window, cx);
22715        }
22716
22717        cx.notify();
22718    }
22719
22720    fn start_git_blame_inline(
22721        &mut self,
22722        user_triggered: bool,
22723        window: &mut Window,
22724        cx: &mut Context<Self>,
22725    ) {
22726        self.start_git_blame(user_triggered, window, cx);
22727
22728        if ProjectSettings::get_global(cx)
22729            .git
22730            .inline_blame_delay()
22731            .is_some()
22732        {
22733            self.start_inline_blame_timer(window, cx);
22734        } else {
22735            self.show_git_blame_inline = true
22736        }
22737    }
22738
22739    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
22740        self.blame.as_ref()
22741    }
22742
22743    pub fn show_git_blame_gutter(&self) -> bool {
22744        self.show_git_blame_gutter
22745    }
22746
22747    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
22748        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
22749    }
22750
22751    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
22752        self.show_git_blame_inline
22753            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
22754            && !self.newest_selection_head_on_empty_line(cx)
22755            && self.has_blame_entries(cx)
22756    }
22757
22758    fn has_blame_entries(&self, cx: &App) -> bool {
22759        self.blame()
22760            .is_some_and(|blame| blame.read(cx).has_generated_entries())
22761    }
22762
22763    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
22764        let cursor_anchor = self.selections.newest_anchor().head();
22765
22766        let snapshot = self.buffer.read(cx).snapshot(cx);
22767        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
22768
22769        snapshot.line_len(buffer_row) == 0
22770    }
22771
22772    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
22773        let buffer_and_selection = maybe!({
22774            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
22775            let selection_range = selection.range();
22776
22777            let multi_buffer = self.buffer().read(cx);
22778            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
22779            let buffer_ranges = multi_buffer_snapshot
22780                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
22781
22782            let (buffer, range, _) = if selection.reversed {
22783                buffer_ranges.first()
22784            } else {
22785                buffer_ranges.last()
22786            }?;
22787
22788            let buffer_range = range.to_point(buffer);
22789
22790            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
22791                return Some((
22792                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
22793                    buffer_range.start.row..buffer_range.end.row,
22794                ));
22795            };
22796
22797            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
22798            let start =
22799                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
22800            let end =
22801                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
22802
22803            Some((
22804                multi_buffer.buffer(buffer.remote_id()).unwrap(),
22805                start.row..end.row,
22806            ))
22807        });
22808
22809        let Some((buffer, selection)) = buffer_and_selection else {
22810            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
22811        };
22812
22813        let Some(project) = self.project() else {
22814            return Task::ready(Err(anyhow!("editor does not have project")));
22815        };
22816
22817        project.update(cx, |project, cx| {
22818            project.get_permalink_to_line(&buffer, selection, cx)
22819        })
22820    }
22821
22822    pub fn copy_permalink_to_line(
22823        &mut self,
22824        _: &CopyPermalinkToLine,
22825        window: &mut Window,
22826        cx: &mut Context<Self>,
22827    ) {
22828        let permalink_task = self.get_permalink_to_line(cx);
22829        let workspace = self.workspace();
22830
22831        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22832            Ok(permalink) => {
22833                cx.update(|_, cx| {
22834                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
22835                })
22836                .ok();
22837            }
22838            Err(err) => {
22839                let message = format!("Failed to copy permalink: {err}");
22840
22841                anyhow::Result::<()>::Err(err).log_err();
22842
22843                if let Some(workspace) = workspace {
22844                    workspace
22845                        .update_in(cx, |workspace, _, cx| {
22846                            struct CopyPermalinkToLine;
22847
22848                            workspace.show_toast(
22849                                Toast::new(
22850                                    NotificationId::unique::<CopyPermalinkToLine>(),
22851                                    message,
22852                                ),
22853                                cx,
22854                            )
22855                        })
22856                        .ok();
22857                }
22858            }
22859        })
22860        .detach();
22861    }
22862
22863    pub fn copy_file_location(
22864        &mut self,
22865        _: &CopyFileLocation,
22866        _: &mut Window,
22867        cx: &mut Context<Self>,
22868    ) {
22869        let selection = self
22870            .selections
22871            .newest::<Point>(&self.display_snapshot(cx))
22872            .start
22873            .row
22874            + 1;
22875        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22876            let project = self.project()?.read(cx);
22877            let file = buffer.read(cx).file()?;
22878            let path = file.path().display(project.path_style(cx));
22879
22880            Some(format!("{path}:{selection}"))
22881        }) {
22882            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
22883        }
22884    }
22885
22886    pub fn open_permalink_to_line(
22887        &mut self,
22888        _: &OpenPermalinkToLine,
22889        window: &mut Window,
22890        cx: &mut Context<Self>,
22891    ) {
22892        let permalink_task = self.get_permalink_to_line(cx);
22893        let workspace = self.workspace();
22894
22895        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22896            Ok(permalink) => {
22897                cx.update(|_, cx| {
22898                    cx.open_url(permalink.as_ref());
22899                })
22900                .ok();
22901            }
22902            Err(err) => {
22903                let message = format!("Failed to open permalink: {err}");
22904
22905                anyhow::Result::<()>::Err(err).log_err();
22906
22907                if let Some(workspace) = workspace {
22908                    workspace.update(cx, |workspace, cx| {
22909                        struct OpenPermalinkToLine;
22910
22911                        workspace.show_toast(
22912                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
22913                            cx,
22914                        )
22915                    });
22916                }
22917            }
22918        })
22919        .detach();
22920    }
22921
22922    pub fn insert_uuid_v4(
22923        &mut self,
22924        _: &InsertUuidV4,
22925        window: &mut Window,
22926        cx: &mut Context<Self>,
22927    ) {
22928        self.insert_uuid(UuidVersion::V4, window, cx);
22929    }
22930
22931    pub fn insert_uuid_v7(
22932        &mut self,
22933        _: &InsertUuidV7,
22934        window: &mut Window,
22935        cx: &mut Context<Self>,
22936    ) {
22937        self.insert_uuid(UuidVersion::V7, window, cx);
22938    }
22939
22940    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
22941        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
22942        self.transact(window, cx, |this, window, cx| {
22943            let edits = this
22944                .selections
22945                .all::<Point>(&this.display_snapshot(cx))
22946                .into_iter()
22947                .map(|selection| {
22948                    let uuid = match version {
22949                        UuidVersion::V4 => uuid::Uuid::new_v4(),
22950                        UuidVersion::V7 => uuid::Uuid::now_v7(),
22951                    };
22952
22953                    (selection.range(), uuid.to_string())
22954                });
22955            this.edit(edits, cx);
22956            this.refresh_edit_prediction(true, false, window, cx);
22957        });
22958    }
22959
22960    pub fn open_selections_in_multibuffer(
22961        &mut self,
22962        _: &OpenSelectionsInMultibuffer,
22963        window: &mut Window,
22964        cx: &mut Context<Self>,
22965    ) {
22966        let multibuffer = self.buffer.read(cx);
22967
22968        let Some(buffer) = multibuffer.as_singleton() else {
22969            return;
22970        };
22971
22972        let Some(workspace) = self.workspace() else {
22973            return;
22974        };
22975
22976        let title = multibuffer.title(cx).to_string();
22977
22978        let locations = self
22979            .selections
22980            .all_anchors(&self.display_snapshot(cx))
22981            .iter()
22982            .map(|selection| {
22983                (
22984                    buffer.clone(),
22985                    (selection.start.text_anchor..selection.end.text_anchor)
22986                        .to_point(buffer.read(cx)),
22987                )
22988            })
22989            .into_group_map();
22990
22991        cx.spawn_in(window, async move |_, cx| {
22992            workspace.update_in(cx, |workspace, window, cx| {
22993                Self::open_locations_in_multibuffer(
22994                    workspace,
22995                    locations,
22996                    format!("Selections for '{title}'"),
22997                    false,
22998                    false,
22999                    MultibufferSelectionMode::All,
23000                    window,
23001                    cx,
23002                );
23003            })
23004        })
23005        .detach();
23006    }
23007
23008    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23009    /// last highlight added will be used.
23010    ///
23011    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23012    pub fn highlight_rows<T: 'static>(
23013        &mut self,
23014        range: Range<Anchor>,
23015        color: Hsla,
23016        options: RowHighlightOptions,
23017        cx: &mut Context<Self>,
23018    ) {
23019        let snapshot = self.buffer().read(cx).snapshot(cx);
23020        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23021        let ix = row_highlights.binary_search_by(|highlight| {
23022            Ordering::Equal
23023                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23024                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23025        });
23026
23027        if let Err(mut ix) = ix {
23028            let index = post_inc(&mut self.highlight_order);
23029
23030            // If this range intersects with the preceding highlight, then merge it with
23031            // the preceding highlight. Otherwise insert a new highlight.
23032            let mut merged = false;
23033            if ix > 0 {
23034                let prev_highlight = &mut row_highlights[ix - 1];
23035                if prev_highlight
23036                    .range
23037                    .end
23038                    .cmp(&range.start, &snapshot)
23039                    .is_ge()
23040                {
23041                    ix -= 1;
23042                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23043                        prev_highlight.range.end = range.end;
23044                    }
23045                    merged = true;
23046                    prev_highlight.index = index;
23047                    prev_highlight.color = color;
23048                    prev_highlight.options = options;
23049                }
23050            }
23051
23052            if !merged {
23053                row_highlights.insert(
23054                    ix,
23055                    RowHighlight {
23056                        range,
23057                        index,
23058                        color,
23059                        options,
23060                        type_id: TypeId::of::<T>(),
23061                    },
23062                );
23063            }
23064
23065            // If any of the following highlights intersect with this one, merge them.
23066            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23067                let highlight = &row_highlights[ix];
23068                if next_highlight
23069                    .range
23070                    .start
23071                    .cmp(&highlight.range.end, &snapshot)
23072                    .is_le()
23073                {
23074                    if next_highlight
23075                        .range
23076                        .end
23077                        .cmp(&highlight.range.end, &snapshot)
23078                        .is_gt()
23079                    {
23080                        row_highlights[ix].range.end = next_highlight.range.end;
23081                    }
23082                    row_highlights.remove(ix + 1);
23083                } else {
23084                    break;
23085                }
23086            }
23087        }
23088    }
23089
23090    /// Remove any highlighted row ranges of the given type that intersect the
23091    /// given ranges.
23092    pub fn remove_highlighted_rows<T: 'static>(
23093        &mut self,
23094        ranges_to_remove: Vec<Range<Anchor>>,
23095        cx: &mut Context<Self>,
23096    ) {
23097        let snapshot = self.buffer().read(cx).snapshot(cx);
23098        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23099        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23100        row_highlights.retain(|highlight| {
23101            while let Some(range_to_remove) = ranges_to_remove.peek() {
23102                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23103                    Ordering::Less | Ordering::Equal => {
23104                        ranges_to_remove.next();
23105                    }
23106                    Ordering::Greater => {
23107                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23108                            Ordering::Less | Ordering::Equal => {
23109                                return false;
23110                            }
23111                            Ordering::Greater => break,
23112                        }
23113                    }
23114                }
23115            }
23116
23117            true
23118        })
23119    }
23120
23121    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23122    pub fn clear_row_highlights<T: 'static>(&mut self) {
23123        self.highlighted_rows.remove(&TypeId::of::<T>());
23124    }
23125
23126    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23127    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23128        self.highlighted_rows
23129            .get(&TypeId::of::<T>())
23130            .map_or(&[] as &[_], |vec| vec.as_slice())
23131            .iter()
23132            .map(|highlight| (highlight.range.clone(), highlight.color))
23133    }
23134
23135    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23136    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23137    /// Allows to ignore certain kinds of highlights.
23138    pub fn highlighted_display_rows(
23139        &self,
23140        window: &mut Window,
23141        cx: &mut App,
23142    ) -> BTreeMap<DisplayRow, LineHighlight> {
23143        let snapshot = self.snapshot(window, cx);
23144        let mut used_highlight_orders = HashMap::default();
23145        self.highlighted_rows
23146            .iter()
23147            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23148            .fold(
23149                BTreeMap::<DisplayRow, LineHighlight>::new(),
23150                |mut unique_rows, highlight| {
23151                    let start = highlight.range.start.to_display_point(&snapshot);
23152                    let end = highlight.range.end.to_display_point(&snapshot);
23153                    let start_row = start.row().0;
23154                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23155                    {
23156                        end.row().0.saturating_sub(1)
23157                    } else {
23158                        end.row().0
23159                    };
23160                    for row in start_row..=end_row {
23161                        let used_index =
23162                            used_highlight_orders.entry(row).or_insert(highlight.index);
23163                        if highlight.index >= *used_index {
23164                            *used_index = highlight.index;
23165                            unique_rows.insert(
23166                                DisplayRow(row),
23167                                LineHighlight {
23168                                    include_gutter: highlight.options.include_gutter,
23169                                    border: None,
23170                                    background: highlight.color.into(),
23171                                    type_id: Some(highlight.type_id),
23172                                },
23173                            );
23174                        }
23175                    }
23176                    unique_rows
23177                },
23178            )
23179    }
23180
23181    pub fn highlighted_display_row_for_autoscroll(
23182        &self,
23183        snapshot: &DisplaySnapshot,
23184    ) -> Option<DisplayRow> {
23185        self.highlighted_rows
23186            .values()
23187            .flat_map(|highlighted_rows| highlighted_rows.iter())
23188            .filter_map(|highlight| {
23189                if highlight.options.autoscroll {
23190                    Some(highlight.range.start.to_display_point(snapshot).row())
23191                } else {
23192                    None
23193                }
23194            })
23195            .min()
23196    }
23197
23198    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23199        self.highlight_background::<SearchWithinRange>(
23200            ranges,
23201            |_, colors| colors.colors().editor_document_highlight_read_background,
23202            cx,
23203        )
23204    }
23205
23206    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23207        self.breadcrumb_header = Some(new_header);
23208    }
23209
23210    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23211        self.clear_background_highlights::<SearchWithinRange>(cx);
23212    }
23213
23214    pub fn highlight_background<T: 'static>(
23215        &mut self,
23216        ranges: &[Range<Anchor>],
23217        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23218        cx: &mut Context<Self>,
23219    ) {
23220        self.background_highlights.insert(
23221            HighlightKey::Type(TypeId::of::<T>()),
23222            (Arc::new(color_fetcher), Arc::from(ranges)),
23223        );
23224        self.scrollbar_marker_state.dirty = true;
23225        cx.notify();
23226    }
23227
23228    pub fn highlight_background_key<T: 'static>(
23229        &mut self,
23230        key: usize,
23231        ranges: &[Range<Anchor>],
23232        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23233        cx: &mut Context<Self>,
23234    ) {
23235        self.background_highlights.insert(
23236            HighlightKey::TypePlus(TypeId::of::<T>(), key),
23237            (Arc::new(color_fetcher), Arc::from(ranges)),
23238        );
23239        self.scrollbar_marker_state.dirty = true;
23240        cx.notify();
23241    }
23242
23243    pub fn clear_background_highlights<T: 'static>(
23244        &mut self,
23245        cx: &mut Context<Self>,
23246    ) -> Option<BackgroundHighlight> {
23247        let text_highlights = self
23248            .background_highlights
23249            .remove(&HighlightKey::Type(TypeId::of::<T>()))?;
23250        if !text_highlights.1.is_empty() {
23251            self.scrollbar_marker_state.dirty = true;
23252            cx.notify();
23253        }
23254        Some(text_highlights)
23255    }
23256
23257    pub fn clear_background_highlights_key<T: 'static>(
23258        &mut self,
23259        key: usize,
23260        cx: &mut Context<Self>,
23261    ) -> Option<BackgroundHighlight> {
23262        let text_highlights = self
23263            .background_highlights
23264            .remove(&HighlightKey::TypePlus(TypeId::of::<T>(), key))?;
23265        if !text_highlights.1.is_empty() {
23266            self.scrollbar_marker_state.dirty = true;
23267            cx.notify();
23268        }
23269        Some(text_highlights)
23270    }
23271
23272    pub fn highlight_gutter<T: 'static>(
23273        &mut self,
23274        ranges: impl Into<Vec<Range<Anchor>>>,
23275        color_fetcher: fn(&App) -> Hsla,
23276        cx: &mut Context<Self>,
23277    ) {
23278        self.gutter_highlights
23279            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23280        cx.notify();
23281    }
23282
23283    pub fn clear_gutter_highlights<T: 'static>(
23284        &mut self,
23285        cx: &mut Context<Self>,
23286    ) -> Option<GutterHighlight> {
23287        cx.notify();
23288        self.gutter_highlights.remove(&TypeId::of::<T>())
23289    }
23290
23291    pub fn insert_gutter_highlight<T: 'static>(
23292        &mut self,
23293        range: Range<Anchor>,
23294        color_fetcher: fn(&App) -> Hsla,
23295        cx: &mut Context<Self>,
23296    ) {
23297        let snapshot = self.buffer().read(cx).snapshot(cx);
23298        let mut highlights = self
23299            .gutter_highlights
23300            .remove(&TypeId::of::<T>())
23301            .map(|(_, highlights)| highlights)
23302            .unwrap_or_default();
23303        let ix = highlights.binary_search_by(|highlight| {
23304            Ordering::Equal
23305                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23306                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23307        });
23308        if let Err(ix) = ix {
23309            highlights.insert(ix, range);
23310        }
23311        self.gutter_highlights
23312            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23313    }
23314
23315    pub fn remove_gutter_highlights<T: 'static>(
23316        &mut self,
23317        ranges_to_remove: Vec<Range<Anchor>>,
23318        cx: &mut Context<Self>,
23319    ) {
23320        let snapshot = self.buffer().read(cx).snapshot(cx);
23321        let Some((color_fetcher, mut gutter_highlights)) =
23322            self.gutter_highlights.remove(&TypeId::of::<T>())
23323        else {
23324            return;
23325        };
23326        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23327        gutter_highlights.retain(|highlight| {
23328            while let Some(range_to_remove) = ranges_to_remove.peek() {
23329                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23330                    Ordering::Less | Ordering::Equal => {
23331                        ranges_to_remove.next();
23332                    }
23333                    Ordering::Greater => {
23334                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23335                            Ordering::Less | Ordering::Equal => {
23336                                return false;
23337                            }
23338                            Ordering::Greater => break,
23339                        }
23340                    }
23341                }
23342            }
23343
23344            true
23345        });
23346        self.gutter_highlights
23347            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23348    }
23349
23350    #[cfg(feature = "test-support")]
23351    pub fn all_text_highlights(
23352        &self,
23353        window: &mut Window,
23354        cx: &mut Context<Self>,
23355    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23356        let snapshot = self.snapshot(window, cx);
23357        self.display_map.update(cx, |display_map, _| {
23358            display_map
23359                .all_text_highlights()
23360                .map(|highlight| {
23361                    let (style, ranges) = highlight.as_ref();
23362                    (
23363                        *style,
23364                        ranges
23365                            .iter()
23366                            .map(|range| range.clone().to_display_points(&snapshot))
23367                            .collect(),
23368                    )
23369                })
23370                .collect()
23371        })
23372    }
23373
23374    #[cfg(feature = "test-support")]
23375    pub fn all_text_background_highlights(
23376        &self,
23377        window: &mut Window,
23378        cx: &mut Context<Self>,
23379    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23380        let snapshot = self.snapshot(window, cx);
23381        let buffer = &snapshot.buffer_snapshot();
23382        let start = buffer.anchor_before(MultiBufferOffset(0));
23383        let end = buffer.anchor_after(buffer.len());
23384        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23385    }
23386
23387    #[cfg(any(test, feature = "test-support"))]
23388    pub fn sorted_background_highlights_in_range(
23389        &self,
23390        search_range: Range<Anchor>,
23391        display_snapshot: &DisplaySnapshot,
23392        theme: &Theme,
23393    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23394        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23395        res.sort_by(|a, b| {
23396            a.0.start
23397                .cmp(&b.0.start)
23398                .then_with(|| a.0.end.cmp(&b.0.end))
23399                .then_with(|| a.1.cmp(&b.1))
23400        });
23401        res
23402    }
23403
23404    #[cfg(feature = "test-support")]
23405    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23406        let snapshot = self.buffer().read(cx).snapshot(cx);
23407
23408        let highlights = self
23409            .background_highlights
23410            .get(&HighlightKey::Type(TypeId::of::<
23411                items::BufferSearchHighlights,
23412            >()));
23413
23414        if let Some((_color, ranges)) = highlights {
23415            ranges
23416                .iter()
23417                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23418                .collect_vec()
23419        } else {
23420            vec![]
23421        }
23422    }
23423
23424    fn document_highlights_for_position<'a>(
23425        &'a self,
23426        position: Anchor,
23427        buffer: &'a MultiBufferSnapshot,
23428    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23429        let read_highlights = self
23430            .background_highlights
23431            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
23432            .map(|h| &h.1);
23433        let write_highlights = self
23434            .background_highlights
23435            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
23436            .map(|h| &h.1);
23437        let left_position = position.bias_left(buffer);
23438        let right_position = position.bias_right(buffer);
23439        read_highlights
23440            .into_iter()
23441            .chain(write_highlights)
23442            .flat_map(move |ranges| {
23443                let start_ix = match ranges.binary_search_by(|probe| {
23444                    let cmp = probe.end.cmp(&left_position, buffer);
23445                    if cmp.is_ge() {
23446                        Ordering::Greater
23447                    } else {
23448                        Ordering::Less
23449                    }
23450                }) {
23451                    Ok(i) | Err(i) => i,
23452                };
23453
23454                ranges[start_ix..]
23455                    .iter()
23456                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23457            })
23458    }
23459
23460    pub fn has_background_highlights<T: 'static>(&self) -> bool {
23461        self.background_highlights
23462            .get(&HighlightKey::Type(TypeId::of::<T>()))
23463            .is_some_and(|(_, highlights)| !highlights.is_empty())
23464    }
23465
23466    /// Returns all background highlights for a given range.
23467    ///
23468    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23469    pub fn background_highlights_in_range(
23470        &self,
23471        search_range: Range<Anchor>,
23472        display_snapshot: &DisplaySnapshot,
23473        theme: &Theme,
23474    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23475        let mut results = Vec::new();
23476        for (color_fetcher, ranges) in self.background_highlights.values() {
23477            let start_ix = match ranges.binary_search_by(|probe| {
23478                let cmp = probe
23479                    .end
23480                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23481                if cmp.is_gt() {
23482                    Ordering::Greater
23483                } else {
23484                    Ordering::Less
23485                }
23486            }) {
23487                Ok(i) | Err(i) => i,
23488            };
23489            for (index, range) in ranges[start_ix..].iter().enumerate() {
23490                if range
23491                    .start
23492                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23493                    .is_ge()
23494                {
23495                    break;
23496                }
23497
23498                let color = color_fetcher(&(start_ix + index), theme);
23499                let start = range.start.to_display_point(display_snapshot);
23500                let end = range.end.to_display_point(display_snapshot);
23501                results.push((start..end, color))
23502            }
23503        }
23504        results
23505    }
23506
23507    pub fn gutter_highlights_in_range(
23508        &self,
23509        search_range: Range<Anchor>,
23510        display_snapshot: &DisplaySnapshot,
23511        cx: &App,
23512    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23513        let mut results = Vec::new();
23514        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23515            let color = color_fetcher(cx);
23516            let start_ix = match ranges.binary_search_by(|probe| {
23517                let cmp = probe
23518                    .end
23519                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23520                if cmp.is_gt() {
23521                    Ordering::Greater
23522                } else {
23523                    Ordering::Less
23524                }
23525            }) {
23526                Ok(i) | Err(i) => i,
23527            };
23528            for range in &ranges[start_ix..] {
23529                if range
23530                    .start
23531                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23532                    .is_ge()
23533                {
23534                    break;
23535                }
23536
23537                let start = range.start.to_display_point(display_snapshot);
23538                let end = range.end.to_display_point(display_snapshot);
23539                results.push((start..end, color))
23540            }
23541        }
23542        results
23543    }
23544
23545    /// Get the text ranges corresponding to the redaction query
23546    pub fn redacted_ranges(
23547        &self,
23548        search_range: Range<Anchor>,
23549        display_snapshot: &DisplaySnapshot,
23550        cx: &App,
23551    ) -> Vec<Range<DisplayPoint>> {
23552        display_snapshot
23553            .buffer_snapshot()
23554            .redacted_ranges(search_range, |file| {
23555                if let Some(file) = file {
23556                    file.is_private()
23557                        && EditorSettings::get(
23558                            Some(SettingsLocation {
23559                                worktree_id: file.worktree_id(cx),
23560                                path: file.path().as_ref(),
23561                            }),
23562                            cx,
23563                        )
23564                        .redact_private_values
23565                } else {
23566                    false
23567                }
23568            })
23569            .map(|range| {
23570                range.start.to_display_point(display_snapshot)
23571                    ..range.end.to_display_point(display_snapshot)
23572            })
23573            .collect()
23574    }
23575
23576    pub fn highlight_text_key<T: 'static>(
23577        &mut self,
23578        key: usize,
23579        ranges: Vec<Range<Anchor>>,
23580        style: HighlightStyle,
23581        merge: bool,
23582        cx: &mut Context<Self>,
23583    ) {
23584        self.display_map.update(cx, |map, cx| {
23585            map.highlight_text(
23586                HighlightKey::TypePlus(TypeId::of::<T>(), key),
23587                ranges,
23588                style,
23589                merge,
23590                cx,
23591            );
23592        });
23593        cx.notify();
23594    }
23595
23596    pub fn highlight_text<T: 'static>(
23597        &mut self,
23598        ranges: Vec<Range<Anchor>>,
23599        style: HighlightStyle,
23600        cx: &mut Context<Self>,
23601    ) {
23602        self.display_map.update(cx, |map, cx| {
23603            map.highlight_text(
23604                HighlightKey::Type(TypeId::of::<T>()),
23605                ranges,
23606                style,
23607                false,
23608                cx,
23609            )
23610        });
23611        cx.notify();
23612    }
23613
23614    pub fn text_highlights<'a, T: 'static>(
23615        &'a self,
23616        cx: &'a App,
23617    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23618        self.display_map.read(cx).text_highlights(TypeId::of::<T>())
23619    }
23620
23621    pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
23622        let cleared = self
23623            .display_map
23624            .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
23625        if cleared {
23626            cx.notify();
23627        }
23628    }
23629
23630    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23631        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23632            && self.focus_handle.is_focused(window)
23633    }
23634
23635    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23636        self.show_cursor_when_unfocused = is_enabled;
23637        cx.notify();
23638    }
23639
23640    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23641        cx.notify();
23642    }
23643
23644    fn on_debug_session_event(
23645        &mut self,
23646        _session: Entity<Session>,
23647        event: &SessionEvent,
23648        cx: &mut Context<Self>,
23649    ) {
23650        if let SessionEvent::InvalidateInlineValue = event {
23651            self.refresh_inline_values(cx);
23652        }
23653    }
23654
23655    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23656        let Some(project) = self.project.clone() else {
23657            return;
23658        };
23659
23660        if !self.inline_value_cache.enabled {
23661            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23662            self.splice_inlays(&inlays, Vec::new(), cx);
23663            return;
23664        }
23665
23666        let current_execution_position = self
23667            .highlighted_rows
23668            .get(&TypeId::of::<ActiveDebugLine>())
23669            .and_then(|lines| lines.last().map(|line| line.range.end));
23670
23671        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23672            let inline_values = editor
23673                .update(cx, |editor, cx| {
23674                    let Some(current_execution_position) = current_execution_position else {
23675                        return Some(Task::ready(Ok(Vec::new())));
23676                    };
23677
23678                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23679                        let snapshot = buffer.snapshot(cx);
23680
23681                        let excerpt = snapshot.excerpt_containing(
23682                            current_execution_position..current_execution_position,
23683                        )?;
23684
23685                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23686                    })?;
23687
23688                    let range =
23689                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23690
23691                    project.inline_values(buffer, range, cx)
23692                })
23693                .ok()
23694                .flatten()?
23695                .await
23696                .context("refreshing debugger inlays")
23697                .log_err()?;
23698
23699            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23700
23701            for (buffer_id, inline_value) in inline_values
23702                .into_iter()
23703                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23704            {
23705                buffer_inline_values
23706                    .entry(buffer_id)
23707                    .or_default()
23708                    .push(inline_value);
23709            }
23710
23711            editor
23712                .update(cx, |editor, cx| {
23713                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23714                    let mut new_inlays = Vec::default();
23715
23716                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23717                        let buffer_id = buffer_snapshot.remote_id();
23718                        buffer_inline_values
23719                            .get(&buffer_id)
23720                            .into_iter()
23721                            .flatten()
23722                            .for_each(|hint| {
23723                                let inlay = Inlay::debugger(
23724                                    post_inc(&mut editor.next_inlay_id),
23725                                    Anchor::in_buffer(excerpt_id, hint.position),
23726                                    hint.text(),
23727                                );
23728                                if !inlay.text().chars().contains(&'\n') {
23729                                    new_inlays.push(inlay);
23730                                }
23731                            });
23732                    }
23733
23734                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
23735                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
23736
23737                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
23738                })
23739                .ok()?;
23740            Some(())
23741        });
23742    }
23743
23744    fn on_buffer_event(
23745        &mut self,
23746        multibuffer: &Entity<MultiBuffer>,
23747        event: &multi_buffer::Event,
23748        window: &mut Window,
23749        cx: &mut Context<Self>,
23750    ) {
23751        match event {
23752            multi_buffer::Event::Edited { edited_buffer } => {
23753                self.scrollbar_marker_state.dirty = true;
23754                self.active_indent_guides_state.dirty = true;
23755                self.refresh_active_diagnostics(cx);
23756                self.refresh_code_actions(window, cx);
23757                self.refresh_single_line_folds(window, cx);
23758                self.refresh_matching_bracket_highlights(window, cx);
23759                if self.has_active_edit_prediction() {
23760                    self.update_visible_edit_prediction(window, cx);
23761                }
23762
23763                // Clean up orphaned review comments after edits
23764                self.cleanup_orphaned_review_comments(cx);
23765
23766                if let Some(buffer) = edited_buffer {
23767                    if buffer.read(cx).file().is_none() {
23768                        cx.emit(EditorEvent::TitleChanged);
23769                    }
23770
23771                    if self.project.is_some() {
23772                        let buffer_id = buffer.read(cx).remote_id();
23773                        self.register_buffer(buffer_id, cx);
23774                        self.update_lsp_data(Some(buffer_id), window, cx);
23775                        self.refresh_inlay_hints(
23776                            InlayHintRefreshReason::BufferEdited(buffer_id),
23777                            cx,
23778                        );
23779                    }
23780                }
23781
23782                cx.emit(EditorEvent::BufferEdited);
23783                cx.emit(SearchEvent::MatchesInvalidated);
23784
23785                let Some(project) = &self.project else { return };
23786                let (telemetry, is_via_ssh) = {
23787                    let project = project.read(cx);
23788                    let telemetry = project.client().telemetry().clone();
23789                    let is_via_ssh = project.is_via_remote_server();
23790                    (telemetry, is_via_ssh)
23791                };
23792                telemetry.log_edit_event("editor", is_via_ssh);
23793            }
23794            multi_buffer::Event::ExcerptsAdded {
23795                buffer,
23796                predecessor,
23797                excerpts,
23798            } => {
23799                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23800                let buffer_id = buffer.read(cx).remote_id();
23801                if self.buffer.read(cx).diff_for(buffer_id).is_none()
23802                    && let Some(project) = &self.project
23803                {
23804                    update_uncommitted_diff_for_buffer(
23805                        cx.entity(),
23806                        project,
23807                        [buffer.clone()],
23808                        self.buffer.clone(),
23809                        cx,
23810                    )
23811                    .detach();
23812                }
23813                self.update_lsp_data(Some(buffer_id), window, cx);
23814                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23815                self.colorize_brackets(false, cx);
23816                self.refresh_selected_text_highlights(true, window, cx);
23817                cx.emit(EditorEvent::ExcerptsAdded {
23818                    buffer: buffer.clone(),
23819                    predecessor: *predecessor,
23820                    excerpts: excerpts.clone(),
23821                });
23822            }
23823            multi_buffer::Event::ExcerptsRemoved {
23824                ids,
23825                removed_buffer_ids,
23826            } => {
23827                if let Some(inlay_hints) = &mut self.inlay_hints {
23828                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
23829                }
23830                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
23831                for buffer_id in removed_buffer_ids {
23832                    self.registered_buffers.remove(buffer_id);
23833                }
23834                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23835                cx.emit(EditorEvent::ExcerptsRemoved {
23836                    ids: ids.clone(),
23837                    removed_buffer_ids: removed_buffer_ids.clone(),
23838                });
23839            }
23840            multi_buffer::Event::ExcerptsEdited {
23841                excerpt_ids,
23842                buffer_ids,
23843            } => {
23844                self.display_map.update(cx, |map, cx| {
23845                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
23846                });
23847                cx.emit(EditorEvent::ExcerptsEdited {
23848                    ids: excerpt_ids.clone(),
23849                });
23850            }
23851            multi_buffer::Event::ExcerptsExpanded { ids } => {
23852                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23853                self.refresh_document_highlights(cx);
23854                for id in ids {
23855                    self.fetched_tree_sitter_chunks.remove(id);
23856                }
23857                self.colorize_brackets(false, cx);
23858                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
23859            }
23860            multi_buffer::Event::Reparsed(buffer_id) => {
23861                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23862                self.refresh_selected_text_highlights(true, window, cx);
23863                self.colorize_brackets(true, cx);
23864                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23865
23866                cx.emit(EditorEvent::Reparsed(*buffer_id));
23867            }
23868            multi_buffer::Event::DiffHunksToggled => {
23869                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23870            }
23871            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
23872                if !is_fresh_language {
23873                    self.registered_buffers.remove(&buffer_id);
23874                }
23875                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23876                cx.emit(EditorEvent::Reparsed(*buffer_id));
23877                cx.notify();
23878            }
23879            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
23880            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
23881            multi_buffer::Event::FileHandleChanged
23882            | multi_buffer::Event::Reloaded
23883            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
23884            multi_buffer::Event::DiagnosticsUpdated => {
23885                self.update_diagnostics_state(window, cx);
23886            }
23887            _ => {}
23888        };
23889    }
23890
23891    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
23892        if !self.diagnostics_enabled() {
23893            return;
23894        }
23895        self.refresh_active_diagnostics(cx);
23896        self.refresh_inline_diagnostics(true, window, cx);
23897        self.scrollbar_marker_state.dirty = true;
23898        cx.notify();
23899    }
23900
23901    pub fn start_temporary_diff_override(&mut self) {
23902        self.load_diff_task.take();
23903        self.temporary_diff_override = true;
23904    }
23905
23906    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
23907        self.temporary_diff_override = false;
23908        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
23909        self.buffer.update(cx, |buffer, cx| {
23910            buffer.set_all_diff_hunks_collapsed(cx);
23911        });
23912
23913        if let Some(project) = self.project.clone() {
23914            self.load_diff_task = Some(
23915                update_uncommitted_diff_for_buffer(
23916                    cx.entity(),
23917                    &project,
23918                    self.buffer.read(cx).all_buffers(),
23919                    self.buffer.clone(),
23920                    cx,
23921                )
23922                .shared(),
23923            );
23924        }
23925    }
23926
23927    fn on_display_map_changed(
23928        &mut self,
23929        _: Entity<DisplayMap>,
23930        _: &mut Window,
23931        cx: &mut Context<Self>,
23932    ) {
23933        cx.notify();
23934    }
23935
23936    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
23937        if !self.mode.is_full() {
23938            return None;
23939        }
23940
23941        let theme_settings = theme::ThemeSettings::get_global(cx);
23942        let theme = cx.theme();
23943        let accent_colors = theme.accents().clone();
23944
23945        let accent_overrides = theme_settings
23946            .theme_overrides
23947            .get(theme.name.as_ref())
23948            .map(|theme_style| &theme_style.accents)
23949            .into_iter()
23950            .flatten()
23951            .chain(
23952                theme_settings
23953                    .experimental_theme_overrides
23954                    .as_ref()
23955                    .map(|overrides| &overrides.accents)
23956                    .into_iter()
23957                    .flatten(),
23958            )
23959            .flat_map(|accent| accent.0.clone().map(SharedString::from))
23960            .collect();
23961
23962        Some(AccentData {
23963            colors: accent_colors,
23964            overrides: accent_overrides,
23965        })
23966    }
23967
23968    fn fetch_applicable_language_settings(
23969        &self,
23970        cx: &App,
23971    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
23972        if !self.mode.is_full() {
23973            return HashMap::default();
23974        }
23975
23976        self.buffer().read(cx).all_buffers().into_iter().fold(
23977            HashMap::default(),
23978            |mut acc, buffer| {
23979                let buffer = buffer.read(cx);
23980                let language = buffer.language().map(|language| language.name());
23981                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
23982                    let file = buffer.file();
23983                    v.insert(language_settings(language, file, cx).into_owned());
23984                }
23985                acc
23986            },
23987        )
23988    }
23989
23990    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
23991        let new_language_settings = self.fetch_applicable_language_settings(cx);
23992        let language_settings_changed = new_language_settings != self.applicable_language_settings;
23993        self.applicable_language_settings = new_language_settings;
23994
23995        let new_accents = self.fetch_accent_data(cx);
23996        let accents_changed = new_accents != self.accent_data;
23997        self.accent_data = new_accents;
23998
23999        if self.diagnostics_enabled() {
24000            let new_severity = EditorSettings::get_global(cx)
24001                .diagnostics_max_severity
24002                .unwrap_or(DiagnosticSeverity::Hint);
24003            self.set_max_diagnostics_severity(new_severity, cx);
24004        }
24005        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24006        self.update_edit_prediction_settings(cx);
24007        self.refresh_edit_prediction(true, false, window, cx);
24008        self.refresh_inline_values(cx);
24009        self.refresh_inlay_hints(
24010            InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24011                self.selections.newest_anchor().head(),
24012                &self.buffer.read(cx).snapshot(cx),
24013                cx,
24014            )),
24015            cx,
24016        );
24017
24018        let old_cursor_shape = self.cursor_shape;
24019        let old_show_breadcrumbs = self.show_breadcrumbs;
24020
24021        {
24022            let editor_settings = EditorSettings::get_global(cx);
24023            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24024            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24025            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24026            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24027        }
24028
24029        if old_cursor_shape != self.cursor_shape {
24030            cx.emit(EditorEvent::CursorShapeChanged);
24031        }
24032
24033        if old_show_breadcrumbs != self.show_breadcrumbs {
24034            cx.emit(EditorEvent::BreadcrumbsChanged);
24035        }
24036
24037        let project_settings = ProjectSettings::get_global(cx);
24038        self.buffer_serialization = self
24039            .should_serialize_buffer()
24040            .then(|| BufferSerialization::new(project_settings.session.restore_unsaved_buffers));
24041
24042        if self.mode.is_full() {
24043            let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
24044            let inline_blame_enabled = project_settings.git.inline_blame.enabled;
24045            if self.show_inline_diagnostics != show_inline_diagnostics {
24046                self.show_inline_diagnostics = show_inline_diagnostics;
24047                self.refresh_inline_diagnostics(false, window, cx);
24048            }
24049
24050            if self.git_blame_inline_enabled != inline_blame_enabled {
24051                self.toggle_git_blame_inline_internal(false, window, cx);
24052            }
24053
24054            let minimap_settings = EditorSettings::get_global(cx).minimap;
24055            if self.minimap_visibility != MinimapVisibility::Disabled {
24056                if self.minimap_visibility.settings_visibility()
24057                    != minimap_settings.minimap_enabled()
24058                {
24059                    self.set_minimap_visibility(
24060                        MinimapVisibility::for_mode(self.mode(), cx),
24061                        window,
24062                        cx,
24063                    );
24064                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24065                    minimap_entity.update(cx, |minimap_editor, cx| {
24066                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24067                    })
24068                }
24069            }
24070
24071            if language_settings_changed || accents_changed {
24072                self.colorize_brackets(true, cx);
24073            }
24074
24075            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24076                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24077            }) {
24078                if !inlay_splice.is_empty() {
24079                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24080                }
24081                self.refresh_colors_for_visible_range(None, window, cx);
24082            }
24083        }
24084
24085        cx.notify();
24086    }
24087
24088    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24089        if !self.mode.is_full() {
24090            return;
24091        }
24092
24093        let new_accents = self.fetch_accent_data(cx);
24094        if new_accents != self.accent_data {
24095            self.accent_data = new_accents;
24096            self.colorize_brackets(true, cx);
24097        }
24098    }
24099
24100    pub fn set_searchable(&mut self, searchable: bool) {
24101        self.searchable = searchable;
24102    }
24103
24104    pub fn searchable(&self) -> bool {
24105        self.searchable
24106    }
24107
24108    pub fn open_excerpts_in_split(
24109        &mut self,
24110        _: &OpenExcerptsSplit,
24111        window: &mut Window,
24112        cx: &mut Context<Self>,
24113    ) {
24114        self.open_excerpts_common(None, true, window, cx)
24115    }
24116
24117    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24118        self.open_excerpts_common(None, false, window, cx)
24119    }
24120
24121    pub(crate) fn open_excerpts_common(
24122        &mut self,
24123        jump_data: Option<JumpData>,
24124        split: bool,
24125        window: &mut Window,
24126        cx: &mut Context<Self>,
24127    ) {
24128        let Some(workspace) = self.workspace() else {
24129            cx.propagate();
24130            return;
24131        };
24132
24133        if self.buffer.read(cx).is_singleton() {
24134            cx.propagate();
24135            return;
24136        }
24137
24138        let mut new_selections_by_buffer = HashMap::default();
24139        match &jump_data {
24140            Some(JumpData::MultiBufferPoint {
24141                excerpt_id,
24142                position,
24143                anchor,
24144                line_offset_from_top,
24145            }) => {
24146                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24147                if let Some(buffer) = multi_buffer_snapshot
24148                    .buffer_id_for_excerpt(*excerpt_id)
24149                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24150                {
24151                    let buffer_snapshot = buffer.read(cx).snapshot();
24152                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24153                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24154                    } else {
24155                        buffer_snapshot.clip_point(*position, Bias::Left)
24156                    };
24157                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24158                    new_selections_by_buffer.insert(
24159                        buffer,
24160                        (
24161                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24162                            Some(*line_offset_from_top),
24163                        ),
24164                    );
24165                }
24166            }
24167            Some(JumpData::MultiBufferRow {
24168                row,
24169                line_offset_from_top,
24170            }) => {
24171                let point = MultiBufferPoint::new(row.0, 0);
24172                if let Some((buffer, buffer_point, _)) =
24173                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24174                {
24175                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24176                    new_selections_by_buffer
24177                        .entry(buffer)
24178                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24179                        .0
24180                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24181                }
24182            }
24183            None => {
24184                let selections = self
24185                    .selections
24186                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24187                let multi_buffer = self.buffer.read(cx);
24188                for selection in selections {
24189                    for (snapshot, range, _, anchor) in multi_buffer
24190                        .snapshot(cx)
24191                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24192                    {
24193                        if let Some(anchor) = anchor {
24194                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24195                            else {
24196                                continue;
24197                            };
24198                            let offset = text::ToOffset::to_offset(
24199                                &anchor.text_anchor,
24200                                &buffer_handle.read(cx).snapshot(),
24201                            );
24202                            let range = BufferOffset(offset)..BufferOffset(offset);
24203                            new_selections_by_buffer
24204                                .entry(buffer_handle)
24205                                .or_insert((Vec::new(), None))
24206                                .0
24207                                .push(range)
24208                        } else {
24209                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24210                            else {
24211                                continue;
24212                            };
24213                            new_selections_by_buffer
24214                                .entry(buffer_handle)
24215                                .or_insert((Vec::new(), None))
24216                                .0
24217                                .push(range)
24218                        }
24219                    }
24220                }
24221            }
24222        }
24223
24224        new_selections_by_buffer
24225            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24226
24227        if new_selections_by_buffer.is_empty() {
24228            return;
24229        }
24230
24231        // We defer the pane interaction because we ourselves are a workspace item
24232        // and activating a new item causes the pane to call a method on us reentrantly,
24233        // which panics if we're on the stack.
24234        window.defer(cx, move |window, cx| {
24235            workspace.update(cx, |workspace, cx| {
24236                let pane = if split {
24237                    workspace.adjacent_pane(window, cx)
24238                } else {
24239                    workspace.active_pane().clone()
24240                };
24241
24242                for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24243                    let buffer_read = buffer.read(cx);
24244                    let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24245                        (true, project::File::from_dyn(Some(file)).is_some())
24246                    } else {
24247                        (false, false)
24248                    };
24249
24250                    // If project file is none workspace.open_project_item will fail to open the excerpt
24251                    // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24252                    // so we check if there's a tab match in that case first
24253                    let editor = (!has_file || !is_project_file)
24254                        .then(|| {
24255                            // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24256                            // so `workspace.open_project_item` will never find them, always opening a new editor.
24257                            // Instead, we try to activate the existing editor in the pane first.
24258                            let (editor, pane_item_index, pane_item_id) =
24259                                pane.read(cx).items().enumerate().find_map(|(i, item)| {
24260                                    let editor = item.downcast::<Editor>()?;
24261                                    let singleton_buffer =
24262                                        editor.read(cx).buffer().read(cx).as_singleton()?;
24263                                    if singleton_buffer == buffer {
24264                                        Some((editor, i, item.item_id()))
24265                                    } else {
24266                                        None
24267                                    }
24268                                })?;
24269                            pane.update(cx, |pane, cx| {
24270                                pane.activate_item(pane_item_index, true, true, window, cx);
24271                                if !PreviewTabsSettings::get_global(cx)
24272                                    .enable_preview_from_multibuffer
24273                                {
24274                                    pane.unpreview_item_if_preview(pane_item_id);
24275                                }
24276                            });
24277                            Some(editor)
24278                        })
24279                        .flatten()
24280                        .unwrap_or_else(|| {
24281                            let keep_old_preview = PreviewTabsSettings::get_global(cx)
24282                                .enable_keep_preview_on_code_navigation;
24283                            let allow_new_preview =
24284                                PreviewTabsSettings::get_global(cx).enable_preview_from_multibuffer;
24285                            workspace.open_project_item::<Self>(
24286                                pane.clone(),
24287                                buffer,
24288                                true,
24289                                true,
24290                                keep_old_preview,
24291                                allow_new_preview,
24292                                window,
24293                                cx,
24294                            )
24295                        });
24296
24297                    editor.update(cx, |editor, cx| {
24298                        if has_file && !is_project_file {
24299                            editor.set_read_only(true);
24300                        }
24301                        let autoscroll = match scroll_offset {
24302                            Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
24303                            None => Autoscroll::newest(),
24304                        };
24305                        let nav_history = editor.nav_history.take();
24306                        let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24307                        let Some((&excerpt_id, _, buffer_snapshot)) =
24308                            multibuffer_snapshot.as_singleton()
24309                        else {
24310                            return;
24311                        };
24312                        editor.change_selections(
24313                            SelectionEffects::scroll(autoscroll),
24314                            window,
24315                            cx,
24316                            |s| {
24317                                s.select_ranges(ranges.into_iter().map(|range| {
24318                                    let range = buffer_snapshot.anchor_before(range.start)
24319                                        ..buffer_snapshot.anchor_after(range.end);
24320                                    multibuffer_snapshot
24321                                        .anchor_range_in_excerpt(excerpt_id, range)
24322                                        .unwrap()
24323                                }));
24324                            },
24325                        );
24326                        editor.nav_history = nav_history;
24327                    });
24328                }
24329            })
24330        });
24331    }
24332
24333    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24334        let snapshot = self.buffer.read(cx).read(cx);
24335        let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
24336        Some(
24337            ranges
24338                .iter()
24339                .map(move |range| {
24340                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24341                })
24342                .collect(),
24343        )
24344    }
24345
24346    fn selection_replacement_ranges(
24347        &self,
24348        range: Range<MultiBufferOffsetUtf16>,
24349        cx: &mut App,
24350    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24351        let selections = self
24352            .selections
24353            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24354        let newest_selection = selections
24355            .iter()
24356            .max_by_key(|selection| selection.id)
24357            .unwrap();
24358        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24359        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24360        let snapshot = self.buffer.read(cx).read(cx);
24361        selections
24362            .into_iter()
24363            .map(|mut selection| {
24364                selection.start.0.0 =
24365                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24366                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24367                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24368                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24369            })
24370            .collect()
24371    }
24372
24373    fn report_editor_event(
24374        &self,
24375        reported_event: ReportEditorEvent,
24376        file_extension: Option<String>,
24377        cx: &App,
24378    ) {
24379        if cfg!(any(test, feature = "test-support")) {
24380            return;
24381        }
24382
24383        let Some(project) = &self.project else { return };
24384
24385        // If None, we are in a file without an extension
24386        let file = self
24387            .buffer
24388            .read(cx)
24389            .as_singleton()
24390            .and_then(|b| b.read(cx).file());
24391        let file_extension = file_extension.or(file
24392            .as_ref()
24393            .and_then(|file| Path::new(file.file_name(cx)).extension())
24394            .and_then(|e| e.to_str())
24395            .map(|a| a.to_string()));
24396
24397        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24398            .map(|vim_mode| vim_mode.0)
24399            .unwrap_or(false);
24400
24401        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24402        let copilot_enabled = edit_predictions_provider
24403            == language::language_settings::EditPredictionProvider::Copilot;
24404        let copilot_enabled_for_language = self
24405            .buffer
24406            .read(cx)
24407            .language_settings(cx)
24408            .show_edit_predictions;
24409
24410        let project = project.read(cx);
24411        let event_type = reported_event.event_type();
24412
24413        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24414            telemetry::event!(
24415                event_type,
24416                type = if auto_saved {"autosave"} else {"manual"},
24417                file_extension,
24418                vim_mode,
24419                copilot_enabled,
24420                copilot_enabled_for_language,
24421                edit_predictions_provider,
24422                is_via_ssh = project.is_via_remote_server(),
24423            );
24424        } else {
24425            telemetry::event!(
24426                event_type,
24427                file_extension,
24428                vim_mode,
24429                copilot_enabled,
24430                copilot_enabled_for_language,
24431                edit_predictions_provider,
24432                is_via_ssh = project.is_via_remote_server(),
24433            );
24434        };
24435    }
24436
24437    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24438    /// with each line being an array of {text, highlight} objects.
24439    fn copy_highlight_json(
24440        &mut self,
24441        _: &CopyHighlightJson,
24442        window: &mut Window,
24443        cx: &mut Context<Self>,
24444    ) {
24445        #[derive(Serialize)]
24446        struct Chunk<'a> {
24447            text: String,
24448            highlight: Option<&'a str>,
24449        }
24450
24451        let snapshot = self.buffer.read(cx).snapshot(cx);
24452        let range = self
24453            .selected_text_range(false, window, cx)
24454            .and_then(|selection| {
24455                if selection.range.is_empty() {
24456                    None
24457                } else {
24458                    Some(
24459                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24460                            selection.range.start,
24461                        )))
24462                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24463                                selection.range.end,
24464                            ))),
24465                    )
24466                }
24467            })
24468            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24469
24470        let chunks = snapshot.chunks(range, true);
24471        let mut lines = Vec::new();
24472        let mut line: VecDeque<Chunk> = VecDeque::new();
24473
24474        let Some(style) = self.style.as_ref() else {
24475            return;
24476        };
24477
24478        for chunk in chunks {
24479            let highlight = chunk
24480                .syntax_highlight_id
24481                .and_then(|id| id.name(&style.syntax));
24482            let mut chunk_lines = chunk.text.split('\n').peekable();
24483            while let Some(text) = chunk_lines.next() {
24484                let mut merged_with_last_token = false;
24485                if let Some(last_token) = line.back_mut()
24486                    && last_token.highlight == highlight
24487                {
24488                    last_token.text.push_str(text);
24489                    merged_with_last_token = true;
24490                }
24491
24492                if !merged_with_last_token {
24493                    line.push_back(Chunk {
24494                        text: text.into(),
24495                        highlight,
24496                    });
24497                }
24498
24499                if chunk_lines.peek().is_some() {
24500                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24501                        line.pop_front();
24502                    }
24503                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24504                        line.pop_back();
24505                    }
24506
24507                    lines.push(mem::take(&mut line));
24508                }
24509            }
24510        }
24511
24512        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24513            return;
24514        };
24515        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24516    }
24517
24518    pub fn open_context_menu(
24519        &mut self,
24520        _: &OpenContextMenu,
24521        window: &mut Window,
24522        cx: &mut Context<Self>,
24523    ) {
24524        self.request_autoscroll(Autoscroll::newest(), cx);
24525        let position = self
24526            .selections
24527            .newest_display(&self.display_snapshot(cx))
24528            .start;
24529        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24530    }
24531
24532    pub fn replay_insert_event(
24533        &mut self,
24534        text: &str,
24535        relative_utf16_range: Option<Range<isize>>,
24536        window: &mut Window,
24537        cx: &mut Context<Self>,
24538    ) {
24539        if !self.input_enabled {
24540            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24541            return;
24542        }
24543        if let Some(relative_utf16_range) = relative_utf16_range {
24544            let selections = self
24545                .selections
24546                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24547            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24548                let new_ranges = selections.into_iter().map(|range| {
24549                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24550                        range
24551                            .head()
24552                            .0
24553                            .0
24554                            .saturating_add_signed(relative_utf16_range.start),
24555                    ));
24556                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24557                        range
24558                            .head()
24559                            .0
24560                            .0
24561                            .saturating_add_signed(relative_utf16_range.end),
24562                    ));
24563                    start..end
24564                });
24565                s.select_ranges(new_ranges);
24566            });
24567        }
24568
24569        self.handle_input(text, window, cx);
24570    }
24571
24572    pub fn is_focused(&self, window: &Window) -> bool {
24573        self.focus_handle.is_focused(window)
24574    }
24575
24576    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24577        cx.emit(EditorEvent::Focused);
24578
24579        if let Some(descendant) = self
24580            .last_focused_descendant
24581            .take()
24582            .and_then(|descendant| descendant.upgrade())
24583        {
24584            window.focus(&descendant, cx);
24585        } else {
24586            if let Some(blame) = self.blame.as_ref() {
24587                blame.update(cx, GitBlame::focus)
24588            }
24589
24590            self.blink_manager.update(cx, BlinkManager::enable);
24591            self.show_cursor_names(window, cx);
24592            self.buffer.update(cx, |buffer, cx| {
24593                buffer.finalize_last_transaction(cx);
24594                if self.leader_id.is_none() {
24595                    buffer.set_active_selections(
24596                        &self.selections.disjoint_anchors_arc(),
24597                        self.selections.line_mode(),
24598                        self.cursor_shape,
24599                        cx,
24600                    );
24601                }
24602            });
24603
24604            if let Some(position_map) = self.last_position_map.clone() {
24605                EditorElement::mouse_moved(
24606                    self,
24607                    &MouseMoveEvent {
24608                        position: window.mouse_position(),
24609                        pressed_button: None,
24610                        modifiers: window.modifiers(),
24611                    },
24612                    &position_map,
24613                    None,
24614                    window,
24615                    cx,
24616                );
24617            }
24618        }
24619    }
24620
24621    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24622        cx.emit(EditorEvent::FocusedIn)
24623    }
24624
24625    fn handle_focus_out(
24626        &mut self,
24627        event: FocusOutEvent,
24628        _window: &mut Window,
24629        cx: &mut Context<Self>,
24630    ) {
24631        if event.blurred != self.focus_handle {
24632            self.last_focused_descendant = Some(event.blurred);
24633        }
24634        self.selection_drag_state = SelectionDragState::None;
24635        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24636    }
24637
24638    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24639        self.blink_manager.update(cx, BlinkManager::disable);
24640        self.buffer
24641            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
24642
24643        if let Some(blame) = self.blame.as_ref() {
24644            blame.update(cx, GitBlame::blur)
24645        }
24646        if !self.hover_state.focused(window, cx) {
24647            hide_hover(self, cx);
24648        }
24649        if !self
24650            .context_menu
24651            .borrow()
24652            .as_ref()
24653            .is_some_and(|context_menu| context_menu.focused(window, cx))
24654        {
24655            self.hide_context_menu(window, cx);
24656        }
24657        self.take_active_edit_prediction(cx);
24658        cx.emit(EditorEvent::Blurred);
24659        cx.notify();
24660    }
24661
24662    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24663        let mut pending: String = window
24664            .pending_input_keystrokes()
24665            .into_iter()
24666            .flatten()
24667            .filter_map(|keystroke| keystroke.key_char.clone())
24668            .collect();
24669
24670        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
24671            pending = "".to_string();
24672        }
24673
24674        let existing_pending = self
24675            .text_highlights::<PendingInput>(cx)
24676            .map(|(_, ranges)| ranges.to_vec());
24677        if existing_pending.is_none() && pending.is_empty() {
24678            return;
24679        }
24680        let transaction =
24681            self.transact(window, cx, |this, window, cx| {
24682                let selections = this
24683                    .selections
24684                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
24685                let edits = selections
24686                    .iter()
24687                    .map(|selection| (selection.end..selection.end, pending.clone()));
24688                this.edit(edits, cx);
24689                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24690                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
24691                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
24692                    }));
24693                });
24694                if let Some(existing_ranges) = existing_pending {
24695                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
24696                    this.edit(edits, cx);
24697                }
24698            });
24699
24700        let snapshot = self.snapshot(window, cx);
24701        let ranges = self
24702            .selections
24703            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
24704            .into_iter()
24705            .map(|selection| {
24706                snapshot.buffer_snapshot().anchor_after(selection.end)
24707                    ..snapshot
24708                        .buffer_snapshot()
24709                        .anchor_before(selection.end + pending.len())
24710            })
24711            .collect();
24712
24713        if pending.is_empty() {
24714            self.clear_highlights::<PendingInput>(cx);
24715        } else {
24716            self.highlight_text::<PendingInput>(
24717                ranges,
24718                HighlightStyle {
24719                    underline: Some(UnderlineStyle {
24720                        thickness: px(1.),
24721                        color: None,
24722                        wavy: false,
24723                    }),
24724                    ..Default::default()
24725                },
24726                cx,
24727            );
24728        }
24729
24730        self.ime_transaction = self.ime_transaction.or(transaction);
24731        if let Some(transaction) = self.ime_transaction {
24732            self.buffer.update(cx, |buffer, cx| {
24733                buffer.group_until_transaction(transaction, cx);
24734            });
24735        }
24736
24737        if self.text_highlights::<PendingInput>(cx).is_none() {
24738            self.ime_transaction.take();
24739        }
24740    }
24741
24742    pub fn register_action_renderer(
24743        &mut self,
24744        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
24745    ) -> Subscription {
24746        let id = self.next_editor_action_id.post_inc();
24747        self.editor_actions
24748            .borrow_mut()
24749            .insert(id, Box::new(listener));
24750
24751        let editor_actions = self.editor_actions.clone();
24752        Subscription::new(move || {
24753            editor_actions.borrow_mut().remove(&id);
24754        })
24755    }
24756
24757    pub fn register_action<A: Action>(
24758        &mut self,
24759        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
24760    ) -> Subscription {
24761        let id = self.next_editor_action_id.post_inc();
24762        let listener = Arc::new(listener);
24763        self.editor_actions.borrow_mut().insert(
24764            id,
24765            Box::new(move |_, window, _| {
24766                let listener = listener.clone();
24767                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
24768                    let action = action.downcast_ref().unwrap();
24769                    if phase == DispatchPhase::Bubble {
24770                        listener(action, window, cx)
24771                    }
24772                })
24773            }),
24774        );
24775
24776        let editor_actions = self.editor_actions.clone();
24777        Subscription::new(move || {
24778            editor_actions.borrow_mut().remove(&id);
24779        })
24780    }
24781
24782    pub fn file_header_size(&self) -> u32 {
24783        FILE_HEADER_HEIGHT
24784    }
24785
24786    pub fn restore(
24787        &mut self,
24788        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
24789        window: &mut Window,
24790        cx: &mut Context<Self>,
24791    ) {
24792        self.buffer().update(cx, |multi_buffer, cx| {
24793            for (buffer_id, changes) in revert_changes {
24794                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
24795                    buffer.update(cx, |buffer, cx| {
24796                        buffer.edit(
24797                            changes
24798                                .into_iter()
24799                                .map(|(range, text)| (range, text.to_string())),
24800                            None,
24801                            cx,
24802                        );
24803                    });
24804                }
24805            }
24806        });
24807        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
24808            selections.refresh()
24809        });
24810    }
24811
24812    pub fn to_pixel_point(
24813        &mut self,
24814        source: Anchor,
24815        editor_snapshot: &EditorSnapshot,
24816        window: &mut Window,
24817        cx: &mut App,
24818    ) -> Option<gpui::Point<Pixels>> {
24819        let source_point = source.to_display_point(editor_snapshot);
24820        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
24821    }
24822
24823    pub fn display_to_pixel_point(
24824        &mut self,
24825        source: DisplayPoint,
24826        editor_snapshot: &EditorSnapshot,
24827        window: &mut Window,
24828        cx: &mut App,
24829    ) -> Option<gpui::Point<Pixels>> {
24830        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
24831        let text_layout_details = self.text_layout_details(window, cx);
24832        let scroll_top = text_layout_details
24833            .scroll_anchor
24834            .scroll_position(editor_snapshot)
24835            .y;
24836
24837        if source.row().as_f64() < scroll_top.floor() {
24838            return None;
24839        }
24840        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
24841        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
24842        Some(gpui::Point::new(source_x, source_y))
24843    }
24844
24845    pub fn has_visible_completions_menu(&self) -> bool {
24846        !self.edit_prediction_preview_is_active()
24847            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
24848                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
24849            })
24850    }
24851
24852    pub fn register_addon<T: Addon>(&mut self, instance: T) {
24853        if self.mode.is_minimap() {
24854            return;
24855        }
24856        self.addons
24857            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
24858    }
24859
24860    pub fn unregister_addon<T: Addon>(&mut self) {
24861        self.addons.remove(&std::any::TypeId::of::<T>());
24862    }
24863
24864    pub fn addon<T: Addon>(&self) -> Option<&T> {
24865        let type_id = std::any::TypeId::of::<T>();
24866        self.addons
24867            .get(&type_id)
24868            .and_then(|item| item.to_any().downcast_ref::<T>())
24869    }
24870
24871    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
24872        let type_id = std::any::TypeId::of::<T>();
24873        self.addons
24874            .get_mut(&type_id)
24875            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
24876    }
24877
24878    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
24879        let text_layout_details = self.text_layout_details(window, cx);
24880        let style = &text_layout_details.editor_style;
24881        let font_id = window.text_system().resolve_font(&style.text.font());
24882        let font_size = style.text.font_size.to_pixels(window.rem_size());
24883        let line_height = style.text.line_height_in_pixels(window.rem_size());
24884        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
24885        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
24886
24887        CharacterDimensions {
24888            em_width,
24889            em_advance,
24890            line_height,
24891        }
24892    }
24893
24894    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
24895        self.load_diff_task.clone()
24896    }
24897
24898    fn read_metadata_from_db(
24899        &mut self,
24900        item_id: u64,
24901        workspace_id: WorkspaceId,
24902        window: &mut Window,
24903        cx: &mut Context<Editor>,
24904    ) {
24905        if self.buffer_kind(cx) == ItemBufferKind::Singleton
24906            && !self.mode.is_minimap()
24907            && WorkspaceSettings::get(None, cx).restore_on_startup
24908                != RestoreOnStartupBehavior::EmptyTab
24909        {
24910            let buffer_snapshot = OnceCell::new();
24911
24912            if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
24913                && !folds.is_empty()
24914            {
24915                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
24916                let snapshot_len = snapshot.len().0;
24917
24918                // Helper: search for fingerprint in buffer, return offset if found
24919                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
24920                    // Ensure we start at a character boundary (defensive)
24921                    let search_start = snapshot
24922                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
24923                        .0;
24924                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
24925
24926                    let mut byte_offset = search_start;
24927                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
24928                        if byte_offset > search_end {
24929                            break;
24930                        }
24931                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
24932                            return Some(byte_offset);
24933                        }
24934                        byte_offset += ch.len_utf8();
24935                    }
24936                    None
24937                };
24938
24939                // Track search position to handle duplicate fingerprints correctly.
24940                // Folds are stored in document order, so we advance after each match.
24941                let mut search_start = 0usize;
24942
24943                let valid_folds: Vec<_> = folds
24944                    .into_iter()
24945                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
24946                        // Skip folds without fingerprints (old data before migration)
24947                        let sfp = start_fp?;
24948                        let efp = end_fp?;
24949                        let efp_len = efp.len();
24950
24951                        // Fast path: check if fingerprints match at stored offsets
24952                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
24953                        let start_matches = stored_start < snapshot_len
24954                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
24955                        let efp_check_pos = stored_end.saturating_sub(efp_len);
24956                        let end_matches = efp_check_pos >= stored_start
24957                            && stored_end <= snapshot_len
24958                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
24959
24960                        let (new_start, new_end) = if start_matches && end_matches {
24961                            // Offsets unchanged, use stored values
24962                            (stored_start, stored_end)
24963                        } else if sfp == efp {
24964                            // Short fold: identical fingerprints can only match once per search
24965                            // Use stored fold length to compute new_end
24966                            let new_start = find_fingerprint(&sfp, search_start)?;
24967                            let fold_len = stored_end - stored_start;
24968                            let new_end = new_start + fold_len;
24969                            (new_start, new_end)
24970                        } else {
24971                            // Slow path: search for fingerprints in buffer
24972                            let new_start = find_fingerprint(&sfp, search_start)?;
24973                            // Search for end_fp after start, then add efp_len to get actual fold end
24974                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
24975                            let new_end = efp_pos + efp_len;
24976                            (new_start, new_end)
24977                        };
24978
24979                        // Advance search position for next fold
24980                        search_start = new_end;
24981
24982                        // Validate fold makes sense (end must be after start)
24983                        if new_end <= new_start {
24984                            return None;
24985                        }
24986
24987                        Some(
24988                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
24989                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
24990                        )
24991                    })
24992                    .collect();
24993
24994                if !valid_folds.is_empty() {
24995                    self.fold_ranges(valid_folds, false, window, cx);
24996
24997                    // Migrate folds to current entity_id before workspace cleanup runs.
24998                    // Entity IDs change between sessions, but workspace cleanup deletes
24999                    // old editor rows (cascading to folds) based on current entity IDs.
25000                    let new_editor_id = cx.entity().entity_id().as_u64() as ItemId;
25001                    if new_editor_id != item_id {
25002                        cx.spawn(async move |_, _| {
25003                            DB.migrate_editor_folds(item_id, new_editor_id, workspace_id)
25004                                .await
25005                                .log_err();
25006                        })
25007                        .detach();
25008                    }
25009                }
25010            }
25011
25012            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25013                && !selections.is_empty()
25014            {
25015                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25016                // skip adding the initial selection to selection history
25017                self.selection_history.mode = SelectionHistoryMode::Skipping;
25018                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25019                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25020                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25021                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25022                    }));
25023                });
25024                self.selection_history.mode = SelectionHistoryMode::Normal;
25025            };
25026        }
25027
25028        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25029    }
25030
25031    fn update_lsp_data(
25032        &mut self,
25033        for_buffer: Option<BufferId>,
25034        window: &mut Window,
25035        cx: &mut Context<'_, Self>,
25036    ) {
25037        if let Some(buffer_id) = for_buffer {
25038            self.pull_diagnostics(buffer_id, window, cx);
25039        }
25040        self.refresh_colors_for_visible_range(for_buffer, window, cx);
25041    }
25042
25043    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25044        if self.ignore_lsp_data() {
25045            return;
25046        }
25047        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25048            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25049        }
25050    }
25051
25052    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25053        if self.ignore_lsp_data() {
25054            return;
25055        }
25056
25057        if !self.registered_buffers.contains_key(&buffer_id)
25058            && let Some(project) = self.project.as_ref()
25059        {
25060            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25061                project.update(cx, |project, cx| {
25062                    self.registered_buffers.insert(
25063                        buffer_id,
25064                        project.register_buffer_with_language_servers(&buffer, cx),
25065                    );
25066                });
25067            } else {
25068                self.registered_buffers.remove(&buffer_id);
25069            }
25070        }
25071    }
25072
25073    fn ignore_lsp_data(&self) -> bool {
25074        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
25075        // skip any LSP updates for it.
25076        self.active_diagnostics == ActiveDiagnostic::All || !self.mode().is_full()
25077    }
25078
25079    pub(crate) fn create_style(&self, cx: &App) -> EditorStyle {
25080        let settings = ThemeSettings::get_global(cx);
25081
25082        let mut text_style = match self.mode {
25083            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25084                color: cx.theme().colors().editor_foreground,
25085                font_family: settings.ui_font.family.clone(),
25086                font_features: settings.ui_font.features.clone(),
25087                font_fallbacks: settings.ui_font.fallbacks.clone(),
25088                font_size: rems(0.875).into(),
25089                font_weight: settings.ui_font.weight,
25090                line_height: relative(settings.buffer_line_height.value()),
25091                ..Default::default()
25092            },
25093            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25094                color: cx.theme().colors().editor_foreground,
25095                font_family: settings.buffer_font.family.clone(),
25096                font_features: settings.buffer_font.features.clone(),
25097                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25098                font_size: settings.buffer_font_size(cx).into(),
25099                font_weight: settings.buffer_font.weight,
25100                line_height: relative(settings.buffer_line_height.value()),
25101                ..Default::default()
25102            },
25103        };
25104        if let Some(text_style_refinement) = &self.text_style_refinement {
25105            text_style.refine(text_style_refinement)
25106        }
25107
25108        let background = match self.mode {
25109            EditorMode::SingleLine => cx.theme().system().transparent,
25110            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25111            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25112            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25113        };
25114
25115        EditorStyle {
25116            background,
25117            border: cx.theme().colors().border,
25118            local_player: cx.theme().players().local(),
25119            text: text_style,
25120            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25121            syntax: cx.theme().syntax().clone(),
25122            status: cx.theme().status().clone(),
25123            inlay_hints_style: make_inlay_hints_style(cx),
25124            edit_prediction_styles: make_suggestion_styles(cx),
25125            unnecessary_code_fade: settings.unnecessary_code_fade,
25126            show_underlines: self.diagnostics_enabled(),
25127        }
25128    }
25129    fn breadcrumbs_inner(&self, variant: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
25130        let cursor = self.selections.newest_anchor().head();
25131        let multibuffer = self.buffer().read(cx);
25132        let is_singleton = multibuffer.is_singleton();
25133        let (buffer_id, symbols) = multibuffer
25134            .read(cx)
25135            .symbols_containing(cursor, Some(variant.syntax()))?;
25136        let buffer = multibuffer.buffer(buffer_id)?;
25137
25138        let buffer = buffer.read(cx);
25139        let settings = ThemeSettings::get_global(cx);
25140        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25141        let mut breadcrumbs = if is_singleton {
25142            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25143                buffer
25144                    .snapshot()
25145                    .resolve_file_path(
25146                        self.project
25147                            .as_ref()
25148                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25149                            .unwrap_or_default(),
25150                        cx,
25151                    )
25152                    .unwrap_or_else(|| {
25153                        if multibuffer.is_singleton() {
25154                            multibuffer.title(cx).to_string()
25155                        } else {
25156                            "untitled".to_string()
25157                        }
25158                    })
25159            });
25160            vec![BreadcrumbText {
25161                text,
25162                highlights: None,
25163                font: Some(settings.buffer_font.clone()),
25164            }]
25165        } else {
25166            vec![]
25167        };
25168
25169        breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText {
25170            text: symbol.text,
25171            highlights: Some(symbol.highlight_ranges),
25172            font: Some(settings.buffer_font.clone()),
25173        }));
25174        Some(breadcrumbs)
25175    }
25176}
25177
25178fn edit_for_markdown_paste<'a>(
25179    buffer: &MultiBufferSnapshot,
25180    range: Range<MultiBufferOffset>,
25181    to_insert: &'a str,
25182    url: Option<url::Url>,
25183) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25184    if url.is_none() {
25185        return (range, Cow::Borrowed(to_insert));
25186    };
25187
25188    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25189
25190    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25191        Cow::Borrowed(to_insert)
25192    } else {
25193        Cow::Owned(format!("[{old_text}]({to_insert})"))
25194    };
25195    (range, new_text)
25196}
25197
25198fn process_completion_for_edit(
25199    completion: &Completion,
25200    intent: CompletionIntent,
25201    buffer: &Entity<Buffer>,
25202    cursor_position: &text::Anchor,
25203    cx: &mut Context<Editor>,
25204) -> CompletionEdit {
25205    let buffer = buffer.read(cx);
25206    let buffer_snapshot = buffer.snapshot();
25207    let (snippet, new_text) = if completion.is_snippet() {
25208        let mut snippet_source = completion.new_text.clone();
25209        // Workaround for typescript language server issues so that methods don't expand within
25210        // strings and functions with type expressions. The previous point is used because the query
25211        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25212        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25213        let previous_point = if previous_point.column > 0 {
25214            cursor_position.to_previous_offset(&buffer_snapshot)
25215        } else {
25216            cursor_position.to_offset(&buffer_snapshot)
25217        };
25218        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25219            && scope.prefers_label_for_snippet_in_completion()
25220            && let Some(label) = completion.label()
25221            && matches!(
25222                completion.kind(),
25223                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25224            )
25225        {
25226            snippet_source = label;
25227        }
25228        match Snippet::parse(&snippet_source).log_err() {
25229            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25230            None => (None, completion.new_text.clone()),
25231        }
25232    } else {
25233        (None, completion.new_text.clone())
25234    };
25235
25236    let mut range_to_replace = {
25237        let replace_range = &completion.replace_range;
25238        if let CompletionSource::Lsp {
25239            insert_range: Some(insert_range),
25240            ..
25241        } = &completion.source
25242        {
25243            debug_assert_eq!(
25244                insert_range.start, replace_range.start,
25245                "insert_range and replace_range should start at the same position"
25246            );
25247            debug_assert!(
25248                insert_range
25249                    .start
25250                    .cmp(cursor_position, &buffer_snapshot)
25251                    .is_le(),
25252                "insert_range should start before or at cursor position"
25253            );
25254            debug_assert!(
25255                replace_range
25256                    .start
25257                    .cmp(cursor_position, &buffer_snapshot)
25258                    .is_le(),
25259                "replace_range should start before or at cursor position"
25260            );
25261
25262            let should_replace = match intent {
25263                CompletionIntent::CompleteWithInsert => false,
25264                CompletionIntent::CompleteWithReplace => true,
25265                CompletionIntent::Complete | CompletionIntent::Compose => {
25266                    let insert_mode =
25267                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25268                            .completions
25269                            .lsp_insert_mode;
25270                    match insert_mode {
25271                        LspInsertMode::Insert => false,
25272                        LspInsertMode::Replace => true,
25273                        LspInsertMode::ReplaceSubsequence => {
25274                            let mut text_to_replace = buffer.chars_for_range(
25275                                buffer.anchor_before(replace_range.start)
25276                                    ..buffer.anchor_after(replace_range.end),
25277                            );
25278                            let mut current_needle = text_to_replace.next();
25279                            for haystack_ch in completion.label.text.chars() {
25280                                if let Some(needle_ch) = current_needle
25281                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25282                                {
25283                                    current_needle = text_to_replace.next();
25284                                }
25285                            }
25286                            current_needle.is_none()
25287                        }
25288                        LspInsertMode::ReplaceSuffix => {
25289                            if replace_range
25290                                .end
25291                                .cmp(cursor_position, &buffer_snapshot)
25292                                .is_gt()
25293                            {
25294                                let range_after_cursor = *cursor_position..replace_range.end;
25295                                let text_after_cursor = buffer
25296                                    .text_for_range(
25297                                        buffer.anchor_before(range_after_cursor.start)
25298                                            ..buffer.anchor_after(range_after_cursor.end),
25299                                    )
25300                                    .collect::<String>()
25301                                    .to_ascii_lowercase();
25302                                completion
25303                                    .label
25304                                    .text
25305                                    .to_ascii_lowercase()
25306                                    .ends_with(&text_after_cursor)
25307                            } else {
25308                                true
25309                            }
25310                        }
25311                    }
25312                }
25313            };
25314
25315            if should_replace {
25316                replace_range.clone()
25317            } else {
25318                insert_range.clone()
25319            }
25320        } else {
25321            replace_range.clone()
25322        }
25323    };
25324
25325    if range_to_replace
25326        .end
25327        .cmp(cursor_position, &buffer_snapshot)
25328        .is_lt()
25329    {
25330        range_to_replace.end = *cursor_position;
25331    }
25332
25333    let replace_range = range_to_replace.to_offset(buffer);
25334    CompletionEdit {
25335        new_text,
25336        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25337        snippet,
25338    }
25339}
25340
25341struct CompletionEdit {
25342    new_text: String,
25343    replace_range: Range<BufferOffset>,
25344    snippet: Option<Snippet>,
25345}
25346
25347fn comment_delimiter_for_newline(
25348    start_point: &Point,
25349    buffer: &MultiBufferSnapshot,
25350    language: &LanguageScope,
25351) -> Option<Arc<str>> {
25352    let delimiters = language.line_comment_prefixes();
25353    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25354    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25355
25356    let num_of_whitespaces = snapshot
25357        .chars_for_range(range.clone())
25358        .take_while(|c| c.is_whitespace())
25359        .count();
25360    let comment_candidate = snapshot
25361        .chars_for_range(range.clone())
25362        .skip(num_of_whitespaces)
25363        .take(max_len_of_delimiter)
25364        .collect::<String>();
25365    let (delimiter, trimmed_len) = delimiters
25366        .iter()
25367        .filter_map(|delimiter| {
25368            let prefix = delimiter.trim_end();
25369            if comment_candidate.starts_with(prefix) {
25370                Some((delimiter, prefix.len()))
25371            } else {
25372                None
25373            }
25374        })
25375        .max_by_key(|(_, len)| *len)?;
25376
25377    if let Some(BlockCommentConfig {
25378        start: block_start, ..
25379    }) = language.block_comment()
25380    {
25381        let block_start_trimmed = block_start.trim_end();
25382        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25383            let line_content = snapshot
25384                .chars_for_range(range)
25385                .skip(num_of_whitespaces)
25386                .take(block_start_trimmed.len())
25387                .collect::<String>();
25388
25389            if line_content.starts_with(block_start_trimmed) {
25390                return None;
25391            }
25392        }
25393    }
25394
25395    let cursor_is_placed_after_comment_marker =
25396        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25397    if cursor_is_placed_after_comment_marker {
25398        Some(delimiter.clone())
25399    } else {
25400        None
25401    }
25402}
25403
25404fn documentation_delimiter_for_newline(
25405    start_point: &Point,
25406    buffer: &MultiBufferSnapshot,
25407    language: &LanguageScope,
25408    newline_config: &mut NewlineConfig,
25409) -> Option<Arc<str>> {
25410    let BlockCommentConfig {
25411        start: start_tag,
25412        end: end_tag,
25413        prefix: delimiter,
25414        tab_size: len,
25415    } = language.documentation_comment()?;
25416    let is_within_block_comment = buffer
25417        .language_scope_at(*start_point)
25418        .is_some_and(|scope| scope.override_name() == Some("comment"));
25419    if !is_within_block_comment {
25420        return None;
25421    }
25422
25423    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25424
25425    let num_of_whitespaces = snapshot
25426        .chars_for_range(range.clone())
25427        .take_while(|c| c.is_whitespace())
25428        .count();
25429
25430    // It is safe to use a column from MultiBufferPoint in context of a single buffer ranges, because we're only ever looking at a single line at a time.
25431    let column = start_point.column;
25432    let cursor_is_after_start_tag = {
25433        let start_tag_len = start_tag.len();
25434        let start_tag_line = snapshot
25435            .chars_for_range(range.clone())
25436            .skip(num_of_whitespaces)
25437            .take(start_tag_len)
25438            .collect::<String>();
25439        if start_tag_line.starts_with(start_tag.as_ref()) {
25440            num_of_whitespaces + start_tag_len <= column as usize
25441        } else {
25442            false
25443        }
25444    };
25445
25446    let cursor_is_after_delimiter = {
25447        let delimiter_trim = delimiter.trim_end();
25448        let delimiter_line = snapshot
25449            .chars_for_range(range.clone())
25450            .skip(num_of_whitespaces)
25451            .take(delimiter_trim.len())
25452            .collect::<String>();
25453        if delimiter_line.starts_with(delimiter_trim) {
25454            num_of_whitespaces + delimiter_trim.len() <= column as usize
25455        } else {
25456            false
25457        }
25458    };
25459
25460    let mut needs_extra_line = false;
25461    let mut extra_line_additional_indent = IndentSize::spaces(0);
25462
25463    let cursor_is_before_end_tag_if_exists = {
25464        let mut char_position = 0u32;
25465        let mut end_tag_offset = None;
25466
25467        'outer: for chunk in snapshot.text_for_range(range) {
25468            if let Some(byte_pos) = chunk.find(&**end_tag) {
25469                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25470                end_tag_offset = Some(char_position + chars_before_match);
25471                break 'outer;
25472            }
25473            char_position += chunk.chars().count() as u32;
25474        }
25475
25476        if let Some(end_tag_offset) = end_tag_offset {
25477            let cursor_is_before_end_tag = column <= end_tag_offset;
25478            if cursor_is_after_start_tag {
25479                if cursor_is_before_end_tag {
25480                    needs_extra_line = true;
25481                }
25482                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
25483                if cursor_is_at_start_of_end_tag {
25484                    extra_line_additional_indent.len = *len;
25485                }
25486            }
25487            cursor_is_before_end_tag
25488        } else {
25489            true
25490        }
25491    };
25492
25493    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
25494        && cursor_is_before_end_tag_if_exists
25495    {
25496        let additional_indent = if cursor_is_after_start_tag {
25497            IndentSize::spaces(*len)
25498        } else {
25499            IndentSize::spaces(0)
25500        };
25501
25502        *newline_config = NewlineConfig::Newline {
25503            additional_indent,
25504            extra_line_additional_indent: if needs_extra_line {
25505                Some(extra_line_additional_indent)
25506            } else {
25507                None
25508            },
25509            prevent_auto_indent: true,
25510        };
25511        Some(delimiter.clone())
25512    } else {
25513        None
25514    }
25515}
25516
25517const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
25518
25519fn list_delimiter_for_newline(
25520    start_point: &Point,
25521    buffer: &MultiBufferSnapshot,
25522    language: &LanguageScope,
25523    newline_config: &mut NewlineConfig,
25524) -> Option<Arc<str>> {
25525    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25526
25527    let num_of_whitespaces = snapshot
25528        .chars_for_range(range.clone())
25529        .take_while(|c| c.is_whitespace())
25530        .count();
25531
25532    let task_list_entries: Vec<_> = language
25533        .task_list()
25534        .into_iter()
25535        .flat_map(|config| {
25536            config
25537                .prefixes
25538                .iter()
25539                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
25540        })
25541        .collect();
25542    let unordered_list_entries: Vec<_> = language
25543        .unordered_list()
25544        .iter()
25545        .map(|marker| (marker.as_ref(), marker.as_ref()))
25546        .collect();
25547
25548    let all_entries: Vec<_> = task_list_entries
25549        .into_iter()
25550        .chain(unordered_list_entries)
25551        .collect();
25552
25553    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
25554        let candidate: String = snapshot
25555            .chars_for_range(range.clone())
25556            .skip(num_of_whitespaces)
25557            .take(max_prefix_len)
25558            .collect();
25559
25560        if let Some((prefix, continuation)) = all_entries
25561            .iter()
25562            .filter(|(prefix, _)| candidate.starts_with(*prefix))
25563            .max_by_key(|(prefix, _)| prefix.len())
25564        {
25565            let end_of_prefix = num_of_whitespaces + prefix.len();
25566            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25567            let has_content_after_marker = snapshot
25568                .chars_for_range(range)
25569                .skip(end_of_prefix)
25570                .any(|c| !c.is_whitespace());
25571
25572            if has_content_after_marker && cursor_is_after_prefix {
25573                return Some((*continuation).into());
25574            }
25575
25576            if start_point.column as usize == end_of_prefix {
25577                if num_of_whitespaces == 0 {
25578                    *newline_config = NewlineConfig::ClearCurrentLine;
25579                } else {
25580                    *newline_config = NewlineConfig::UnindentCurrentLine {
25581                        continuation: (*continuation).into(),
25582                    };
25583                }
25584            }
25585
25586            return None;
25587        }
25588    }
25589
25590    let candidate: String = snapshot
25591        .chars_for_range(range.clone())
25592        .skip(num_of_whitespaces)
25593        .take(ORDERED_LIST_MAX_MARKER_LEN)
25594        .collect();
25595
25596    for ordered_config in language.ordered_list() {
25597        let regex = match Regex::new(&ordered_config.pattern) {
25598            Ok(r) => r,
25599            Err(_) => continue,
25600        };
25601
25602        if let Some(captures) = regex.captures(&candidate) {
25603            let full_match = captures.get(0)?;
25604            let marker_len = full_match.len();
25605            let end_of_prefix = num_of_whitespaces + marker_len;
25606            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25607
25608            let has_content_after_marker = snapshot
25609                .chars_for_range(range)
25610                .skip(end_of_prefix)
25611                .any(|c| !c.is_whitespace());
25612
25613            if has_content_after_marker && cursor_is_after_prefix {
25614                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
25615                let continuation = ordered_config
25616                    .format
25617                    .replace("{1}", &(number + 1).to_string());
25618                return Some(continuation.into());
25619            }
25620
25621            if start_point.column as usize == end_of_prefix {
25622                let continuation = ordered_config.format.replace("{1}", "1");
25623                if num_of_whitespaces == 0 {
25624                    *newline_config = NewlineConfig::ClearCurrentLine;
25625                } else {
25626                    *newline_config = NewlineConfig::UnindentCurrentLine {
25627                        continuation: continuation.into(),
25628                    };
25629                }
25630            }
25631
25632            return None;
25633        }
25634    }
25635
25636    None
25637}
25638
25639fn is_list_prefix_row(
25640    row: MultiBufferRow,
25641    buffer: &MultiBufferSnapshot,
25642    language: &LanguageScope,
25643) -> bool {
25644    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
25645        return false;
25646    };
25647
25648    let num_of_whitespaces = snapshot
25649        .chars_for_range(range.clone())
25650        .take_while(|c| c.is_whitespace())
25651        .count();
25652
25653    let task_list_prefixes: Vec<_> = language
25654        .task_list()
25655        .into_iter()
25656        .flat_map(|config| {
25657            config
25658                .prefixes
25659                .iter()
25660                .map(|p| p.as_ref())
25661                .collect::<Vec<_>>()
25662        })
25663        .collect();
25664    let unordered_list_markers: Vec<_> = language
25665        .unordered_list()
25666        .iter()
25667        .map(|marker| marker.as_ref())
25668        .collect();
25669    let all_prefixes: Vec<_> = task_list_prefixes
25670        .into_iter()
25671        .chain(unordered_list_markers)
25672        .collect();
25673    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
25674        let candidate: String = snapshot
25675            .chars_for_range(range.clone())
25676            .skip(num_of_whitespaces)
25677            .take(max_prefix_len)
25678            .collect();
25679        if all_prefixes
25680            .iter()
25681            .any(|prefix| candidate.starts_with(*prefix))
25682        {
25683            return true;
25684        }
25685    }
25686
25687    let ordered_list_candidate: String = snapshot
25688        .chars_for_range(range)
25689        .skip(num_of_whitespaces)
25690        .take(ORDERED_LIST_MAX_MARKER_LEN)
25691        .collect();
25692    for ordered_config in language.ordered_list() {
25693        let regex = match Regex::new(&ordered_config.pattern) {
25694            Ok(r) => r,
25695            Err(_) => continue,
25696        };
25697        if let Some(captures) = regex.captures(&ordered_list_candidate) {
25698            return captures.get(0).is_some();
25699        }
25700    }
25701
25702    false
25703}
25704
25705#[derive(Debug)]
25706enum NewlineConfig {
25707    /// Insert newline with optional additional indent and optional extra blank line
25708    Newline {
25709        additional_indent: IndentSize,
25710        extra_line_additional_indent: Option<IndentSize>,
25711        prevent_auto_indent: bool,
25712    },
25713    /// Clear the current line
25714    ClearCurrentLine,
25715    /// Unindent the current line and add continuation
25716    UnindentCurrentLine { continuation: Arc<str> },
25717}
25718
25719impl NewlineConfig {
25720    fn has_extra_line(&self) -> bool {
25721        matches!(
25722            self,
25723            Self::Newline {
25724                extra_line_additional_indent: Some(_),
25725                ..
25726            }
25727        )
25728    }
25729
25730    fn insert_extra_newline_brackets(
25731        buffer: &MultiBufferSnapshot,
25732        range: Range<MultiBufferOffset>,
25733        language: &language::LanguageScope,
25734    ) -> bool {
25735        let leading_whitespace_len = buffer
25736            .reversed_chars_at(range.start)
25737            .take_while(|c| c.is_whitespace() && *c != '\n')
25738            .map(|c| c.len_utf8())
25739            .sum::<usize>();
25740        let trailing_whitespace_len = buffer
25741            .chars_at(range.end)
25742            .take_while(|c| c.is_whitespace() && *c != '\n')
25743            .map(|c| c.len_utf8())
25744            .sum::<usize>();
25745        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
25746
25747        language.brackets().any(|(pair, enabled)| {
25748            let pair_start = pair.start.trim_end();
25749            let pair_end = pair.end.trim_start();
25750
25751            enabled
25752                && pair.newline
25753                && buffer.contains_str_at(range.end, pair_end)
25754                && buffer.contains_str_at(
25755                    range.start.saturating_sub_usize(pair_start.len()),
25756                    pair_start,
25757                )
25758        })
25759    }
25760
25761    fn insert_extra_newline_tree_sitter(
25762        buffer: &MultiBufferSnapshot,
25763        range: Range<MultiBufferOffset>,
25764    ) -> bool {
25765        let (buffer, range) = match buffer
25766            .range_to_buffer_ranges(range.start..=range.end)
25767            .as_slice()
25768        {
25769            [(buffer, range, _)] => (*buffer, range.clone()),
25770            _ => return false,
25771        };
25772        let pair = {
25773            let mut result: Option<BracketMatch<usize>> = None;
25774
25775            for pair in buffer
25776                .all_bracket_ranges(range.start.0..range.end.0)
25777                .filter(move |pair| {
25778                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
25779                })
25780            {
25781                let len = pair.close_range.end - pair.open_range.start;
25782
25783                if let Some(existing) = &result {
25784                    let existing_len = existing.close_range.end - existing.open_range.start;
25785                    if len > existing_len {
25786                        continue;
25787                    }
25788                }
25789
25790                result = Some(pair);
25791            }
25792
25793            result
25794        };
25795        let Some(pair) = pair else {
25796            return false;
25797        };
25798        pair.newline_only
25799            && buffer
25800                .chars_for_range(pair.open_range.end..range.start.0)
25801                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
25802                .all(|c| c.is_whitespace() && c != '\n')
25803    }
25804}
25805
25806fn update_uncommitted_diff_for_buffer(
25807    editor: Entity<Editor>,
25808    project: &Entity<Project>,
25809    buffers: impl IntoIterator<Item = Entity<Buffer>>,
25810    buffer: Entity<MultiBuffer>,
25811    cx: &mut App,
25812) -> Task<()> {
25813    let mut tasks = Vec::new();
25814    project.update(cx, |project, cx| {
25815        for buffer in buffers {
25816            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
25817                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
25818            }
25819        }
25820    });
25821    cx.spawn(async move |cx| {
25822        let diffs = future::join_all(tasks).await;
25823        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
25824            return;
25825        }
25826
25827        buffer.update(cx, |buffer, cx| {
25828            for diff in diffs.into_iter().flatten() {
25829                buffer.add_diff(diff, cx);
25830            }
25831        });
25832    })
25833}
25834
25835fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
25836    let tab_size = tab_size.get() as usize;
25837    let mut width = offset;
25838
25839    for ch in text.chars() {
25840        width += if ch == '\t' {
25841            tab_size - (width % tab_size)
25842        } else {
25843            1
25844        };
25845    }
25846
25847    width - offset
25848}
25849
25850#[cfg(test)]
25851mod tests {
25852    use super::*;
25853
25854    #[test]
25855    fn test_string_size_with_expanded_tabs() {
25856        let nz = |val| NonZeroU32::new(val).unwrap();
25857        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
25858        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
25859        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
25860        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
25861        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
25862        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
25863        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
25864        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
25865    }
25866}
25867
25868/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
25869struct WordBreakingTokenizer<'a> {
25870    input: &'a str,
25871}
25872
25873impl<'a> WordBreakingTokenizer<'a> {
25874    fn new(input: &'a str) -> Self {
25875        Self { input }
25876    }
25877}
25878
25879fn is_char_ideographic(ch: char) -> bool {
25880    use unicode_script::Script::*;
25881    use unicode_script::UnicodeScript;
25882    matches!(ch.script(), Han | Tangut | Yi)
25883}
25884
25885fn is_grapheme_ideographic(text: &str) -> bool {
25886    text.chars().any(is_char_ideographic)
25887}
25888
25889fn is_grapheme_whitespace(text: &str) -> bool {
25890    text.chars().any(|x| x.is_whitespace())
25891}
25892
25893fn should_stay_with_preceding_ideograph(text: &str) -> bool {
25894    text.chars()
25895        .next()
25896        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
25897}
25898
25899#[derive(PartialEq, Eq, Debug, Clone, Copy)]
25900enum WordBreakToken<'a> {
25901    Word { token: &'a str, grapheme_len: usize },
25902    InlineWhitespace { token: &'a str, grapheme_len: usize },
25903    Newline,
25904}
25905
25906impl<'a> Iterator for WordBreakingTokenizer<'a> {
25907    /// Yields a span, the count of graphemes in the token, and whether it was
25908    /// whitespace. Note that it also breaks at word boundaries.
25909    type Item = WordBreakToken<'a>;
25910
25911    fn next(&mut self) -> Option<Self::Item> {
25912        use unicode_segmentation::UnicodeSegmentation;
25913        if self.input.is_empty() {
25914            return None;
25915        }
25916
25917        let mut iter = self.input.graphemes(true).peekable();
25918        let mut offset = 0;
25919        let mut grapheme_len = 0;
25920        if let Some(first_grapheme) = iter.next() {
25921            let is_newline = first_grapheme == "\n";
25922            let is_whitespace = is_grapheme_whitespace(first_grapheme);
25923            offset += first_grapheme.len();
25924            grapheme_len += 1;
25925            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
25926                if let Some(grapheme) = iter.peek().copied()
25927                    && should_stay_with_preceding_ideograph(grapheme)
25928                {
25929                    offset += grapheme.len();
25930                    grapheme_len += 1;
25931                }
25932            } else {
25933                let mut words = self.input[offset..].split_word_bound_indices().peekable();
25934                let mut next_word_bound = words.peek().copied();
25935                if next_word_bound.is_some_and(|(i, _)| i == 0) {
25936                    next_word_bound = words.next();
25937                }
25938                while let Some(grapheme) = iter.peek().copied() {
25939                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
25940                        break;
25941                    };
25942                    if is_grapheme_whitespace(grapheme) != is_whitespace
25943                        || (grapheme == "\n") != is_newline
25944                    {
25945                        break;
25946                    };
25947                    offset += grapheme.len();
25948                    grapheme_len += 1;
25949                    iter.next();
25950                }
25951            }
25952            let token = &self.input[..offset];
25953            self.input = &self.input[offset..];
25954            if token == "\n" {
25955                Some(WordBreakToken::Newline)
25956            } else if is_whitespace {
25957                Some(WordBreakToken::InlineWhitespace {
25958                    token,
25959                    grapheme_len,
25960                })
25961            } else {
25962                Some(WordBreakToken::Word {
25963                    token,
25964                    grapheme_len,
25965                })
25966            }
25967        } else {
25968            None
25969        }
25970    }
25971}
25972
25973#[test]
25974fn test_word_breaking_tokenizer() {
25975    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
25976        ("", &[]),
25977        ("  ", &[whitespace("  ", 2)]),
25978        ("Ʒ", &[word("Ʒ", 1)]),
25979        ("Ǽ", &[word("Ǽ", 1)]),
25980        ("", &[word("", 1)]),
25981        ("⋑⋑", &[word("⋑⋑", 2)]),
25982        (
25983            "原理,进而",
25984            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
25985        ),
25986        (
25987            "hello world",
25988            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
25989        ),
25990        (
25991            "hello, world",
25992            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
25993        ),
25994        (
25995            "  hello world",
25996            &[
25997                whitespace("  ", 2),
25998                word("hello", 5),
25999                whitespace(" ", 1),
26000                word("world", 5),
26001            ],
26002        ),
26003        (
26004            "这是什么 \n 钢笔",
26005            &[
26006                word("", 1),
26007                word("", 1),
26008                word("", 1),
26009                word("", 1),
26010                whitespace(" ", 1),
26011                newline(),
26012                whitespace(" ", 1),
26013                word("", 1),
26014                word("", 1),
26015            ],
26016        ),
26017        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26018    ];
26019
26020    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26021        WordBreakToken::Word {
26022            token,
26023            grapheme_len,
26024        }
26025    }
26026
26027    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26028        WordBreakToken::InlineWhitespace {
26029            token,
26030            grapheme_len,
26031        }
26032    }
26033
26034    fn newline() -> WordBreakToken<'static> {
26035        WordBreakToken::Newline
26036    }
26037
26038    for (input, result) in tests {
26039        assert_eq!(
26040            WordBreakingTokenizer::new(input)
26041                .collect::<Vec<_>>()
26042                .as_slice(),
26043            *result,
26044        );
26045    }
26046}
26047
26048fn wrap_with_prefix(
26049    first_line_prefix: String,
26050    subsequent_lines_prefix: String,
26051    unwrapped_text: String,
26052    wrap_column: usize,
26053    tab_size: NonZeroU32,
26054    preserve_existing_whitespace: bool,
26055) -> String {
26056    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26057    let subsequent_lines_prefix_len =
26058        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26059    let mut wrapped_text = String::new();
26060    let mut current_line = first_line_prefix;
26061    let mut is_first_line = true;
26062
26063    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26064    let mut current_line_len = first_line_prefix_len;
26065    let mut in_whitespace = false;
26066    for token in tokenizer {
26067        let have_preceding_whitespace = in_whitespace;
26068        match token {
26069            WordBreakToken::Word {
26070                token,
26071                grapheme_len,
26072            } => {
26073                in_whitespace = false;
26074                let current_prefix_len = if is_first_line {
26075                    first_line_prefix_len
26076                } else {
26077                    subsequent_lines_prefix_len
26078                };
26079                if current_line_len + grapheme_len > wrap_column
26080                    && current_line_len != current_prefix_len
26081                {
26082                    wrapped_text.push_str(current_line.trim_end());
26083                    wrapped_text.push('\n');
26084                    is_first_line = false;
26085                    current_line = subsequent_lines_prefix.clone();
26086                    current_line_len = subsequent_lines_prefix_len;
26087                }
26088                current_line.push_str(token);
26089                current_line_len += grapheme_len;
26090            }
26091            WordBreakToken::InlineWhitespace {
26092                mut token,
26093                mut grapheme_len,
26094            } => {
26095                in_whitespace = true;
26096                if have_preceding_whitespace && !preserve_existing_whitespace {
26097                    continue;
26098                }
26099                if !preserve_existing_whitespace {
26100                    // Keep a single whitespace grapheme as-is
26101                    if let Some(first) =
26102                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26103                    {
26104                        token = first;
26105                    } else {
26106                        token = " ";
26107                    }
26108                    grapheme_len = 1;
26109                }
26110                let current_prefix_len = if is_first_line {
26111                    first_line_prefix_len
26112                } else {
26113                    subsequent_lines_prefix_len
26114                };
26115                if current_line_len + grapheme_len > wrap_column {
26116                    wrapped_text.push_str(current_line.trim_end());
26117                    wrapped_text.push('\n');
26118                    is_first_line = false;
26119                    current_line = subsequent_lines_prefix.clone();
26120                    current_line_len = subsequent_lines_prefix_len;
26121                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26122                    current_line.push_str(token);
26123                    current_line_len += grapheme_len;
26124                }
26125            }
26126            WordBreakToken::Newline => {
26127                in_whitespace = true;
26128                let current_prefix_len = if is_first_line {
26129                    first_line_prefix_len
26130                } else {
26131                    subsequent_lines_prefix_len
26132                };
26133                if preserve_existing_whitespace {
26134                    wrapped_text.push_str(current_line.trim_end());
26135                    wrapped_text.push('\n');
26136                    is_first_line = false;
26137                    current_line = subsequent_lines_prefix.clone();
26138                    current_line_len = subsequent_lines_prefix_len;
26139                } else if have_preceding_whitespace {
26140                    continue;
26141                } else if current_line_len + 1 > wrap_column
26142                    && current_line_len != current_prefix_len
26143                {
26144                    wrapped_text.push_str(current_line.trim_end());
26145                    wrapped_text.push('\n');
26146                    is_first_line = false;
26147                    current_line = subsequent_lines_prefix.clone();
26148                    current_line_len = subsequent_lines_prefix_len;
26149                } else if current_line_len != current_prefix_len {
26150                    current_line.push(' ');
26151                    current_line_len += 1;
26152                }
26153            }
26154        }
26155    }
26156
26157    if !current_line.is_empty() {
26158        wrapped_text.push_str(&current_line);
26159    }
26160    wrapped_text
26161}
26162
26163#[test]
26164fn test_wrap_with_prefix() {
26165    assert_eq!(
26166        wrap_with_prefix(
26167            "# ".to_string(),
26168            "# ".to_string(),
26169            "abcdefg".to_string(),
26170            4,
26171            NonZeroU32::new(4).unwrap(),
26172            false,
26173        ),
26174        "# abcdefg"
26175    );
26176    assert_eq!(
26177        wrap_with_prefix(
26178            "".to_string(),
26179            "".to_string(),
26180            "\thello world".to_string(),
26181            8,
26182            NonZeroU32::new(4).unwrap(),
26183            false,
26184        ),
26185        "hello\nworld"
26186    );
26187    assert_eq!(
26188        wrap_with_prefix(
26189            "// ".to_string(),
26190            "// ".to_string(),
26191            "xx \nyy zz aa bb cc".to_string(),
26192            12,
26193            NonZeroU32::new(4).unwrap(),
26194            false,
26195        ),
26196        "// xx yy zz\n// aa bb cc"
26197    );
26198    assert_eq!(
26199        wrap_with_prefix(
26200            String::new(),
26201            String::new(),
26202            "这是什么 \n 钢笔".to_string(),
26203            3,
26204            NonZeroU32::new(4).unwrap(),
26205            false,
26206        ),
26207        "这是什\n么 钢\n"
26208    );
26209    assert_eq!(
26210        wrap_with_prefix(
26211            String::new(),
26212            String::new(),
26213            format!("foo{}bar", '\u{2009}'), // thin space
26214            80,
26215            NonZeroU32::new(4).unwrap(),
26216            false,
26217        ),
26218        format!("foo{}bar", '\u{2009}')
26219    );
26220}
26221
26222pub trait CollaborationHub {
26223    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26224    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26225    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26226}
26227
26228impl CollaborationHub for Entity<Project> {
26229    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26230        self.read(cx).collaborators()
26231    }
26232
26233    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26234        self.read(cx).user_store().read(cx).participant_indices()
26235    }
26236
26237    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26238        let this = self.read(cx);
26239        let user_ids = this.collaborators().values().map(|c| c.user_id);
26240        this.user_store().read(cx).participant_names(user_ids, cx)
26241    }
26242}
26243
26244pub trait SemanticsProvider {
26245    fn hover(
26246        &self,
26247        buffer: &Entity<Buffer>,
26248        position: text::Anchor,
26249        cx: &mut App,
26250    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26251
26252    fn inline_values(
26253        &self,
26254        buffer_handle: Entity<Buffer>,
26255        range: Range<text::Anchor>,
26256        cx: &mut App,
26257    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26258
26259    fn applicable_inlay_chunks(
26260        &self,
26261        buffer: &Entity<Buffer>,
26262        ranges: &[Range<text::Anchor>],
26263        cx: &mut App,
26264    ) -> Vec<Range<BufferRow>>;
26265
26266    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26267
26268    fn inlay_hints(
26269        &self,
26270        invalidate: InvalidationStrategy,
26271        buffer: Entity<Buffer>,
26272        ranges: Vec<Range<text::Anchor>>,
26273        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26274        cx: &mut App,
26275    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26276
26277    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26278
26279    fn document_highlights(
26280        &self,
26281        buffer: &Entity<Buffer>,
26282        position: text::Anchor,
26283        cx: &mut App,
26284    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26285
26286    fn definitions(
26287        &self,
26288        buffer: &Entity<Buffer>,
26289        position: text::Anchor,
26290        kind: GotoDefinitionKind,
26291        cx: &mut App,
26292    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26293
26294    fn range_for_rename(
26295        &self,
26296        buffer: &Entity<Buffer>,
26297        position: text::Anchor,
26298        cx: &mut App,
26299    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26300
26301    fn perform_rename(
26302        &self,
26303        buffer: &Entity<Buffer>,
26304        position: text::Anchor,
26305        new_name: String,
26306        cx: &mut App,
26307    ) -> Option<Task<Result<ProjectTransaction>>>;
26308}
26309
26310pub trait CompletionProvider {
26311    fn completions(
26312        &self,
26313        excerpt_id: ExcerptId,
26314        buffer: &Entity<Buffer>,
26315        buffer_position: text::Anchor,
26316        trigger: CompletionContext,
26317        window: &mut Window,
26318        cx: &mut Context<Editor>,
26319    ) -> Task<Result<Vec<CompletionResponse>>>;
26320
26321    fn resolve_completions(
26322        &self,
26323        _buffer: Entity<Buffer>,
26324        _completion_indices: Vec<usize>,
26325        _completions: Rc<RefCell<Box<[Completion]>>>,
26326        _cx: &mut Context<Editor>,
26327    ) -> Task<Result<bool>> {
26328        Task::ready(Ok(false))
26329    }
26330
26331    fn apply_additional_edits_for_completion(
26332        &self,
26333        _buffer: Entity<Buffer>,
26334        _completions: Rc<RefCell<Box<[Completion]>>>,
26335        _completion_index: usize,
26336        _push_to_history: bool,
26337        _cx: &mut Context<Editor>,
26338    ) -> Task<Result<Option<language::Transaction>>> {
26339        Task::ready(Ok(None))
26340    }
26341
26342    fn is_completion_trigger(
26343        &self,
26344        buffer: &Entity<Buffer>,
26345        position: language::Anchor,
26346        text: &str,
26347        trigger_in_words: bool,
26348        cx: &mut Context<Editor>,
26349    ) -> bool;
26350
26351    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26352
26353    fn sort_completions(&self) -> bool {
26354        true
26355    }
26356
26357    fn filter_completions(&self) -> bool {
26358        true
26359    }
26360
26361    fn show_snippets(&self) -> bool {
26362        false
26363    }
26364}
26365
26366pub trait CodeActionProvider {
26367    fn id(&self) -> Arc<str>;
26368
26369    fn code_actions(
26370        &self,
26371        buffer: &Entity<Buffer>,
26372        range: Range<text::Anchor>,
26373        window: &mut Window,
26374        cx: &mut App,
26375    ) -> Task<Result<Vec<CodeAction>>>;
26376
26377    fn apply_code_action(
26378        &self,
26379        buffer_handle: Entity<Buffer>,
26380        action: CodeAction,
26381        excerpt_id: ExcerptId,
26382        push_to_history: bool,
26383        window: &mut Window,
26384        cx: &mut App,
26385    ) -> Task<Result<ProjectTransaction>>;
26386}
26387
26388impl CodeActionProvider for Entity<Project> {
26389    fn id(&self) -> Arc<str> {
26390        "project".into()
26391    }
26392
26393    fn code_actions(
26394        &self,
26395        buffer: &Entity<Buffer>,
26396        range: Range<text::Anchor>,
26397        _window: &mut Window,
26398        cx: &mut App,
26399    ) -> Task<Result<Vec<CodeAction>>> {
26400        self.update(cx, |project, cx| {
26401            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26402            let code_actions = project.code_actions(buffer, range, None, cx);
26403            cx.background_spawn(async move {
26404                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26405                Ok(code_lens_actions
26406                    .context("code lens fetch")?
26407                    .into_iter()
26408                    .flatten()
26409                    .chain(
26410                        code_actions
26411                            .context("code action fetch")?
26412                            .into_iter()
26413                            .flatten(),
26414                    )
26415                    .collect())
26416            })
26417        })
26418    }
26419
26420    fn apply_code_action(
26421        &self,
26422        buffer_handle: Entity<Buffer>,
26423        action: CodeAction,
26424        _excerpt_id: ExcerptId,
26425        push_to_history: bool,
26426        _window: &mut Window,
26427        cx: &mut App,
26428    ) -> Task<Result<ProjectTransaction>> {
26429        self.update(cx, |project, cx| {
26430            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26431        })
26432    }
26433}
26434
26435fn snippet_completions(
26436    project: &Project,
26437    buffer: &Entity<Buffer>,
26438    buffer_anchor: text::Anchor,
26439    classifier: CharClassifier,
26440    cx: &mut App,
26441) -> Task<Result<CompletionResponse>> {
26442    let languages = buffer.read(cx).languages_at(buffer_anchor);
26443    let snippet_store = project.snippets().read(cx);
26444
26445    let scopes: Vec<_> = languages
26446        .iter()
26447        .filter_map(|language| {
26448            let language_name = language.lsp_id();
26449            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26450
26451            if snippets.is_empty() {
26452                None
26453            } else {
26454                Some((language.default_scope(), snippets))
26455            }
26456        })
26457        .collect();
26458
26459    if scopes.is_empty() {
26460        return Task::ready(Ok(CompletionResponse {
26461            completions: vec![],
26462            display_options: CompletionDisplayOptions::default(),
26463            is_incomplete: false,
26464        }));
26465    }
26466
26467    let snapshot = buffer.read(cx).text_snapshot();
26468    let executor = cx.background_executor().clone();
26469
26470    cx.background_spawn(async move {
26471        let is_word_char = |c| classifier.is_word(c);
26472
26473        let mut is_incomplete = false;
26474        let mut completions: Vec<Completion> = Vec::new();
26475
26476        const MAX_PREFIX_LEN: usize = 128;
26477        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
26478        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
26479        let window_start = snapshot.clip_offset(window_start, Bias::Left);
26480
26481        let max_buffer_window: String = snapshot
26482            .text_for_range(window_start..buffer_offset)
26483            .collect();
26484
26485        if max_buffer_window.is_empty() {
26486            return Ok(CompletionResponse {
26487                completions: vec![],
26488                display_options: CompletionDisplayOptions::default(),
26489                is_incomplete: true,
26490            });
26491        }
26492
26493        for (_scope, snippets) in scopes.into_iter() {
26494            // Sort snippets by word count to match longer snippet prefixes first.
26495            let mut sorted_snippet_candidates = snippets
26496                .iter()
26497                .enumerate()
26498                .flat_map(|(snippet_ix, snippet)| {
26499                    snippet
26500                        .prefix
26501                        .iter()
26502                        .enumerate()
26503                        .map(move |(prefix_ix, prefix)| {
26504                            let word_count =
26505                                snippet_candidate_suffixes(prefix, is_word_char).count();
26506                            ((snippet_ix, prefix_ix), prefix, word_count)
26507                        })
26508                })
26509                .collect_vec();
26510            sorted_snippet_candidates
26511                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
26512
26513            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
26514
26515            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, is_word_char)
26516                .take(
26517                    sorted_snippet_candidates
26518                        .first()
26519                        .map(|(_, _, word_count)| *word_count)
26520                        .unwrap_or_default(),
26521                )
26522                .collect_vec();
26523
26524            const MAX_RESULTS: usize = 100;
26525            // Each match also remembers how many characters from the buffer it consumed
26526            let mut matches: Vec<(StringMatch, usize)> = vec![];
26527
26528            let mut snippet_list_cutoff_index = 0;
26529            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
26530                let word_count = buffer_index + 1;
26531                // Increase `snippet_list_cutoff_index` until we have all of the
26532                // snippets with sufficiently many words.
26533                while sorted_snippet_candidates
26534                    .get(snippet_list_cutoff_index)
26535                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
26536                        *snippet_word_count >= word_count
26537                    })
26538                {
26539                    snippet_list_cutoff_index += 1;
26540                }
26541
26542                // Take only the candidates with at least `word_count` many words
26543                let snippet_candidates_at_word_len =
26544                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
26545
26546                let candidates = snippet_candidates_at_word_len
26547                    .iter()
26548                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
26549                    .enumerate() // index in `sorted_snippet_candidates`
26550                    // First char must match
26551                    .filter(|(_ix, prefix)| {
26552                        itertools::equal(
26553                            prefix
26554                                .chars()
26555                                .next()
26556                                .into_iter()
26557                                .flat_map(|c| c.to_lowercase()),
26558                            buffer_window
26559                                .chars()
26560                                .next()
26561                                .into_iter()
26562                                .flat_map(|c| c.to_lowercase()),
26563                        )
26564                    })
26565                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
26566                    .collect::<Vec<StringMatchCandidate>>();
26567
26568                matches.extend(
26569                    fuzzy::match_strings(
26570                        &candidates,
26571                        &buffer_window,
26572                        buffer_window.chars().any(|c| c.is_uppercase()),
26573                        true,
26574                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
26575                        &Default::default(),
26576                        executor.clone(),
26577                    )
26578                    .await
26579                    .into_iter()
26580                    .map(|string_match| (string_match, buffer_window.len())),
26581                );
26582
26583                if matches.len() >= MAX_RESULTS {
26584                    break;
26585                }
26586            }
26587
26588            let to_lsp = |point: &text::Anchor| {
26589                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
26590                point_to_lsp(end)
26591            };
26592            let lsp_end = to_lsp(&buffer_anchor);
26593
26594            if matches.len() >= MAX_RESULTS {
26595                is_incomplete = true;
26596            }
26597
26598            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
26599                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
26600                    sorted_snippet_candidates[string_match.candidate_id];
26601                let snippet = &snippets[snippet_index];
26602                let start = buffer_offset - buffer_window_len;
26603                let start = snapshot.anchor_before(start);
26604                let range = start..buffer_anchor;
26605                let lsp_start = to_lsp(&start);
26606                let lsp_range = lsp::Range {
26607                    start: lsp_start,
26608                    end: lsp_end,
26609                };
26610                Completion {
26611                    replace_range: range,
26612                    new_text: snippet.body.clone(),
26613                    source: CompletionSource::Lsp {
26614                        insert_range: None,
26615                        server_id: LanguageServerId(usize::MAX),
26616                        resolved: true,
26617                        lsp_completion: Box::new(lsp::CompletionItem {
26618                            label: snippet.prefix.first().unwrap().clone(),
26619                            kind: Some(CompletionItemKind::SNIPPET),
26620                            label_details: snippet.description.as_ref().map(|description| {
26621                                lsp::CompletionItemLabelDetails {
26622                                    detail: Some(description.clone()),
26623                                    description: None,
26624                                }
26625                            }),
26626                            insert_text_format: Some(InsertTextFormat::SNIPPET),
26627                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
26628                                lsp::InsertReplaceEdit {
26629                                    new_text: snippet.body.clone(),
26630                                    insert: lsp_range,
26631                                    replace: lsp_range,
26632                                },
26633                            )),
26634                            filter_text: Some(snippet.body.clone()),
26635                            sort_text: Some(char::MAX.to_string()),
26636                            ..lsp::CompletionItem::default()
26637                        }),
26638                        lsp_defaults: None,
26639                    },
26640                    label: CodeLabel {
26641                        text: matching_prefix.clone(),
26642                        runs: Vec::new(),
26643                        filter_range: 0..matching_prefix.len(),
26644                    },
26645                    icon_path: None,
26646                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
26647                        single_line: snippet.name.clone().into(),
26648                        plain_text: snippet
26649                            .description
26650                            .clone()
26651                            .map(|description| description.into()),
26652                    }),
26653                    insert_text_mode: None,
26654                    confirm: None,
26655                    match_start: Some(start),
26656                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
26657                }
26658            }));
26659        }
26660
26661        Ok(CompletionResponse {
26662            completions,
26663            display_options: CompletionDisplayOptions::default(),
26664            is_incomplete,
26665        })
26666    })
26667}
26668
26669impl CompletionProvider for Entity<Project> {
26670    fn completions(
26671        &self,
26672        _excerpt_id: ExcerptId,
26673        buffer: &Entity<Buffer>,
26674        buffer_position: text::Anchor,
26675        options: CompletionContext,
26676        _window: &mut Window,
26677        cx: &mut Context<Editor>,
26678    ) -> Task<Result<Vec<CompletionResponse>>> {
26679        self.update(cx, |project, cx| {
26680            let task = project.completions(buffer, buffer_position, options, cx);
26681            cx.background_spawn(task)
26682        })
26683    }
26684
26685    fn resolve_completions(
26686        &self,
26687        buffer: Entity<Buffer>,
26688        completion_indices: Vec<usize>,
26689        completions: Rc<RefCell<Box<[Completion]>>>,
26690        cx: &mut Context<Editor>,
26691    ) -> Task<Result<bool>> {
26692        self.update(cx, |project, cx| {
26693            project.lsp_store().update(cx, |lsp_store, cx| {
26694                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
26695            })
26696        })
26697    }
26698
26699    fn apply_additional_edits_for_completion(
26700        &self,
26701        buffer: Entity<Buffer>,
26702        completions: Rc<RefCell<Box<[Completion]>>>,
26703        completion_index: usize,
26704        push_to_history: bool,
26705        cx: &mut Context<Editor>,
26706    ) -> Task<Result<Option<language::Transaction>>> {
26707        self.update(cx, |project, cx| {
26708            project.lsp_store().update(cx, |lsp_store, cx| {
26709                lsp_store.apply_additional_edits_for_completion(
26710                    buffer,
26711                    completions,
26712                    completion_index,
26713                    push_to_history,
26714                    cx,
26715                )
26716            })
26717        })
26718    }
26719
26720    fn is_completion_trigger(
26721        &self,
26722        buffer: &Entity<Buffer>,
26723        position: language::Anchor,
26724        text: &str,
26725        trigger_in_words: bool,
26726        cx: &mut Context<Editor>,
26727    ) -> bool {
26728        let mut chars = text.chars();
26729        let char = if let Some(char) = chars.next() {
26730            char
26731        } else {
26732            return false;
26733        };
26734        if chars.next().is_some() {
26735            return false;
26736        }
26737
26738        let buffer = buffer.read(cx);
26739        let snapshot = buffer.snapshot();
26740        let classifier = snapshot
26741            .char_classifier_at(position)
26742            .scope_context(Some(CharScopeContext::Completion));
26743        if trigger_in_words && classifier.is_word(char) {
26744            return true;
26745        }
26746
26747        buffer.completion_triggers().contains(text)
26748    }
26749
26750    fn show_snippets(&self) -> bool {
26751        true
26752    }
26753}
26754
26755impl SemanticsProvider for Entity<Project> {
26756    fn hover(
26757        &self,
26758        buffer: &Entity<Buffer>,
26759        position: text::Anchor,
26760        cx: &mut App,
26761    ) -> Option<Task<Option<Vec<project::Hover>>>> {
26762        Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
26763    }
26764
26765    fn document_highlights(
26766        &self,
26767        buffer: &Entity<Buffer>,
26768        position: text::Anchor,
26769        cx: &mut App,
26770    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
26771        Some(self.update(cx, |project, cx| {
26772            project.document_highlights(buffer, position, cx)
26773        }))
26774    }
26775
26776    fn definitions(
26777        &self,
26778        buffer: &Entity<Buffer>,
26779        position: text::Anchor,
26780        kind: GotoDefinitionKind,
26781        cx: &mut App,
26782    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
26783        Some(self.update(cx, |project, cx| match kind {
26784            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
26785            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
26786            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
26787            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
26788        }))
26789    }
26790
26791    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
26792        self.update(cx, |project, cx| {
26793            if project
26794                .active_debug_session(cx)
26795                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
26796            {
26797                return true;
26798            }
26799
26800            buffer.update(cx, |buffer, cx| {
26801                project.any_language_server_supports_inlay_hints(buffer, cx)
26802            })
26803        })
26804    }
26805
26806    fn inline_values(
26807        &self,
26808        buffer_handle: Entity<Buffer>,
26809        range: Range<text::Anchor>,
26810        cx: &mut App,
26811    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
26812        self.update(cx, |project, cx| {
26813            let (session, active_stack_frame) = project.active_debug_session(cx)?;
26814
26815            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
26816        })
26817    }
26818
26819    fn applicable_inlay_chunks(
26820        &self,
26821        buffer: &Entity<Buffer>,
26822        ranges: &[Range<text::Anchor>],
26823        cx: &mut App,
26824    ) -> Vec<Range<BufferRow>> {
26825        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
26826            lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
26827        })
26828    }
26829
26830    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
26831        self.read(cx).lsp_store().update(cx, |lsp_store, _| {
26832            lsp_store.invalidate_inlay_hints(for_buffers)
26833        });
26834    }
26835
26836    fn inlay_hints(
26837        &self,
26838        invalidate: InvalidationStrategy,
26839        buffer: Entity<Buffer>,
26840        ranges: Vec<Range<text::Anchor>>,
26841        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26842        cx: &mut App,
26843    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
26844        Some(self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
26845            lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
26846        }))
26847    }
26848
26849    fn range_for_rename(
26850        &self,
26851        buffer: &Entity<Buffer>,
26852        position: text::Anchor,
26853        cx: &mut App,
26854    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
26855        Some(self.update(cx, |project, cx| {
26856            let buffer = buffer.clone();
26857            let task = project.prepare_rename(buffer.clone(), position, cx);
26858            cx.spawn(async move |_, cx| {
26859                Ok(match task.await? {
26860                    PrepareRenameResponse::Success(range) => Some(range),
26861                    PrepareRenameResponse::InvalidPosition => None,
26862                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
26863                        // Fallback on using TreeSitter info to determine identifier range
26864                        buffer.read_with(cx, |buffer, _| {
26865                            let snapshot = buffer.snapshot();
26866                            let (range, kind) = snapshot.surrounding_word(position, None);
26867                            if kind != Some(CharKind::Word) {
26868                                return None;
26869                            }
26870                            Some(
26871                                snapshot.anchor_before(range.start)
26872                                    ..snapshot.anchor_after(range.end),
26873                            )
26874                        })
26875                    }
26876                })
26877            })
26878        }))
26879    }
26880
26881    fn perform_rename(
26882        &self,
26883        buffer: &Entity<Buffer>,
26884        position: text::Anchor,
26885        new_name: String,
26886        cx: &mut App,
26887    ) -> Option<Task<Result<ProjectTransaction>>> {
26888        Some(self.update(cx, |project, cx| {
26889            project.perform_rename(buffer.clone(), position, new_name, cx)
26890        }))
26891    }
26892}
26893
26894fn consume_contiguous_rows(
26895    contiguous_row_selections: &mut Vec<Selection<Point>>,
26896    selection: &Selection<Point>,
26897    display_map: &DisplaySnapshot,
26898    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
26899) -> (MultiBufferRow, MultiBufferRow) {
26900    contiguous_row_selections.push(selection.clone());
26901    let start_row = starting_row(selection, display_map);
26902    let mut end_row = ending_row(selection, display_map);
26903
26904    while let Some(next_selection) = selections.peek() {
26905        if next_selection.start.row <= end_row.0 {
26906            end_row = ending_row(next_selection, display_map);
26907            contiguous_row_selections.push(selections.next().unwrap().clone());
26908        } else {
26909            break;
26910        }
26911    }
26912    (start_row, end_row)
26913}
26914
26915fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
26916    if selection.start.column > 0 {
26917        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
26918    } else {
26919        MultiBufferRow(selection.start.row)
26920    }
26921}
26922
26923fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
26924    if next_selection.end.column > 0 || next_selection.is_empty() {
26925        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
26926    } else {
26927        MultiBufferRow(next_selection.end.row)
26928    }
26929}
26930
26931impl EditorSnapshot {
26932    pub fn remote_selections_in_range<'a>(
26933        &'a self,
26934        range: &'a Range<Anchor>,
26935        collaboration_hub: &dyn CollaborationHub,
26936        cx: &'a App,
26937    ) -> impl 'a + Iterator<Item = RemoteSelection> {
26938        let participant_names = collaboration_hub.user_names(cx);
26939        let participant_indices = collaboration_hub.user_participant_indices(cx);
26940        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
26941        let collaborators_by_replica_id = collaborators_by_peer_id
26942            .values()
26943            .map(|collaborator| (collaborator.replica_id, collaborator))
26944            .collect::<HashMap<_, _>>();
26945        self.buffer_snapshot()
26946            .selections_in_range(range, false)
26947            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
26948                if replica_id == ReplicaId::AGENT {
26949                    Some(RemoteSelection {
26950                        replica_id,
26951                        selection,
26952                        cursor_shape,
26953                        line_mode,
26954                        collaborator_id: CollaboratorId::Agent,
26955                        user_name: Some("Agent".into()),
26956                        color: cx.theme().players().agent(),
26957                    })
26958                } else {
26959                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
26960                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
26961                    let user_name = participant_names.get(&collaborator.user_id).cloned();
26962                    Some(RemoteSelection {
26963                        replica_id,
26964                        selection,
26965                        cursor_shape,
26966                        line_mode,
26967                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
26968                        user_name,
26969                        color: if let Some(index) = participant_index {
26970                            cx.theme().players().color_for_participant(index.0)
26971                        } else {
26972                            cx.theme().players().absent()
26973                        },
26974                    })
26975                }
26976            })
26977    }
26978
26979    pub fn hunks_for_ranges(
26980        &self,
26981        ranges: impl IntoIterator<Item = Range<Point>>,
26982    ) -> Vec<MultiBufferDiffHunk> {
26983        let mut hunks = Vec::new();
26984        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
26985            HashMap::default();
26986        for query_range in ranges {
26987            let query_rows =
26988                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
26989            for hunk in self.buffer_snapshot().diff_hunks_in_range(
26990                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
26991            ) {
26992                // Include deleted hunks that are adjacent to the query range, because
26993                // otherwise they would be missed.
26994                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
26995                if hunk.status().is_deleted() {
26996                    intersects_range |= hunk.row_range.start == query_rows.end;
26997                    intersects_range |= hunk.row_range.end == query_rows.start;
26998                }
26999                if intersects_range {
27000                    if !processed_buffer_rows
27001                        .entry(hunk.buffer_id)
27002                        .or_default()
27003                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27004                    {
27005                        continue;
27006                    }
27007                    hunks.push(hunk);
27008                }
27009            }
27010        }
27011
27012        hunks
27013    }
27014
27015    fn display_diff_hunks_for_rows<'a>(
27016        &'a self,
27017        display_rows: Range<DisplayRow>,
27018        folded_buffers: &'a HashSet<BufferId>,
27019    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27020        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27021        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27022
27023        self.buffer_snapshot()
27024            .diff_hunks_in_range(buffer_start..buffer_end)
27025            .filter_map(|hunk| {
27026                if folded_buffers.contains(&hunk.buffer_id) {
27027                    return None;
27028                }
27029
27030                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27031                let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
27032
27033                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27034                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27035
27036                let display_hunk = if hunk_display_start.column() != 0 {
27037                    DisplayDiffHunk::Folded {
27038                        display_row: hunk_display_start.row(),
27039                    }
27040                } else {
27041                    let mut end_row = hunk_display_end.row();
27042                    if hunk_display_end.column() > 0 {
27043                        end_row.0 += 1;
27044                    }
27045                    let is_created_file = hunk.is_created_file();
27046
27047                    DisplayDiffHunk::Unfolded {
27048                        status: hunk.status(),
27049                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27050                            ..hunk.diff_base_byte_range.end.0,
27051                        word_diffs: hunk.word_diffs,
27052                        display_row_range: hunk_display_start.row()..end_row,
27053                        multi_buffer_range: Anchor::range_in_buffer(
27054                            hunk.excerpt_id,
27055                            hunk.buffer_range,
27056                        ),
27057                        is_created_file,
27058                    }
27059                };
27060
27061                Some(display_hunk)
27062            })
27063    }
27064
27065    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27066        self.display_snapshot
27067            .buffer_snapshot()
27068            .language_at(position)
27069    }
27070
27071    pub fn is_focused(&self) -> bool {
27072        self.is_focused
27073    }
27074
27075    pub fn placeholder_text(&self) -> Option<String> {
27076        self.placeholder_display_snapshot
27077            .as_ref()
27078            .map(|display_map| display_map.text())
27079    }
27080
27081    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27082        self.scroll_anchor.scroll_position(&self.display_snapshot)
27083    }
27084
27085    pub fn gutter_dimensions(
27086        &self,
27087        font_id: FontId,
27088        font_size: Pixels,
27089        style: &EditorStyle,
27090        window: &mut Window,
27091        cx: &App,
27092    ) -> GutterDimensions {
27093        if self.show_gutter
27094            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27095            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27096        {
27097            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27098                matches!(
27099                    ProjectSettings::get_global(cx).git.git_gutter,
27100                    GitGutterSetting::TrackedFiles
27101                )
27102            });
27103            let gutter_settings = EditorSettings::get_global(cx).gutter;
27104            let show_line_numbers = self
27105                .show_line_numbers
27106                .unwrap_or(gutter_settings.line_numbers);
27107            let line_gutter_width = if show_line_numbers {
27108                // Avoid flicker-like gutter resizes when the line number gains another digit by
27109                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27110                let min_width_for_number_on_gutter =
27111                    ch_advance * gutter_settings.min_line_number_digits as f32;
27112                self.max_line_number_width(style, window)
27113                    .max(min_width_for_number_on_gutter)
27114            } else {
27115                0.0.into()
27116            };
27117
27118            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27119            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27120
27121            let git_blame_entries_width =
27122                self.git_blame_gutter_max_author_length
27123                    .map(|max_author_length| {
27124                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27125                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27126
27127                        /// The number of characters to dedicate to gaps and margins.
27128                        const SPACING_WIDTH: usize = 4;
27129
27130                        let max_char_count = max_author_length.min(renderer.max_author_length())
27131                            + ::git::SHORT_SHA_LENGTH
27132                            + MAX_RELATIVE_TIMESTAMP.len()
27133                            + SPACING_WIDTH;
27134
27135                        ch_advance * max_char_count
27136                    });
27137
27138            let is_singleton = self.buffer_snapshot().is_singleton();
27139
27140            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27141            left_padding += if !is_singleton {
27142                ch_width * 4.0
27143            } else if show_runnables || show_breakpoints {
27144                ch_width * 3.0
27145            } else if show_git_gutter && show_line_numbers {
27146                ch_width * 2.0
27147            } else if show_git_gutter || show_line_numbers {
27148                ch_width
27149            } else {
27150                px(0.)
27151            };
27152
27153            let shows_folds = is_singleton && gutter_settings.folds;
27154
27155            let right_padding = if shows_folds && show_line_numbers {
27156                ch_width * 4.0
27157            } else if shows_folds || (!is_singleton && show_line_numbers) {
27158                ch_width * 3.0
27159            } else if show_line_numbers {
27160                ch_width
27161            } else {
27162                px(0.)
27163            };
27164
27165            GutterDimensions {
27166                left_padding,
27167                right_padding,
27168                width: line_gutter_width + left_padding + right_padding,
27169                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27170                git_blame_entries_width,
27171            }
27172        } else if self.offset_content {
27173            GutterDimensions::default_with_margin(font_id, font_size, cx)
27174        } else {
27175            GutterDimensions::default()
27176        }
27177    }
27178
27179    pub fn render_crease_toggle(
27180        &self,
27181        buffer_row: MultiBufferRow,
27182        row_contains_cursor: bool,
27183        editor: Entity<Editor>,
27184        window: &mut Window,
27185        cx: &mut App,
27186    ) -> Option<AnyElement> {
27187        let folded = self.is_line_folded(buffer_row);
27188        let mut is_foldable = false;
27189
27190        if let Some(crease) = self
27191            .crease_snapshot
27192            .query_row(buffer_row, self.buffer_snapshot())
27193        {
27194            is_foldable = true;
27195            match crease {
27196                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27197                    if let Some(render_toggle) = render_toggle {
27198                        let toggle_callback =
27199                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27200                                if folded {
27201                                    editor.update(cx, |editor, cx| {
27202                                        editor.fold_at(buffer_row, window, cx)
27203                                    });
27204                                } else {
27205                                    editor.update(cx, |editor, cx| {
27206                                        editor.unfold_at(buffer_row, window, cx)
27207                                    });
27208                                }
27209                            });
27210                        return Some((render_toggle)(
27211                            buffer_row,
27212                            folded,
27213                            toggle_callback,
27214                            window,
27215                            cx,
27216                        ));
27217                    }
27218                }
27219            }
27220        }
27221
27222        is_foldable |= self.starts_indent(buffer_row);
27223
27224        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27225            Some(
27226                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27227                    .toggle_state(folded)
27228                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27229                        if folded {
27230                            this.unfold_at(buffer_row, window, cx);
27231                        } else {
27232                            this.fold_at(buffer_row, window, cx);
27233                        }
27234                    }))
27235                    .into_any_element(),
27236            )
27237        } else {
27238            None
27239        }
27240    }
27241
27242    pub fn render_crease_trailer(
27243        &self,
27244        buffer_row: MultiBufferRow,
27245        window: &mut Window,
27246        cx: &mut App,
27247    ) -> Option<AnyElement> {
27248        let folded = self.is_line_folded(buffer_row);
27249        if let Crease::Inline { render_trailer, .. } = self
27250            .crease_snapshot
27251            .query_row(buffer_row, self.buffer_snapshot())?
27252        {
27253            let render_trailer = render_trailer.as_ref()?;
27254            Some(render_trailer(buffer_row, folded, window, cx))
27255        } else {
27256            None
27257        }
27258    }
27259
27260    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27261        let digit_count = self.widest_line_number().ilog10() + 1;
27262        column_pixels(style, digit_count as usize, window)
27263    }
27264
27265    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27266    ///
27267    /// This is positive if `base` is before `line`.
27268    fn relative_line_delta(
27269        &self,
27270        current_selection_head: DisplayRow,
27271        first_visible_row: DisplayRow,
27272        consider_wrapped_lines: bool,
27273    ) -> i64 {
27274        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27275        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27276
27277        if consider_wrapped_lines {
27278            let wrap_snapshot = self.wrap_snapshot();
27279            let base_wrap_row = wrap_snapshot
27280                .make_wrap_point(current_selection_head, Bias::Left)
27281                .row();
27282            let wrap_row = wrap_snapshot
27283                .make_wrap_point(first_visible_row, Bias::Left)
27284                .row();
27285
27286            wrap_row.0 as i64 - base_wrap_row.0 as i64
27287        } else {
27288            let fold_snapshot = self.fold_snapshot();
27289            let base_fold_row = fold_snapshot
27290                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27291                .row();
27292            let fold_row = fold_snapshot
27293                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27294                .row();
27295
27296            fold_row as i64 - base_fold_row as i64
27297        }
27298    }
27299
27300    /// Returns the unsigned relative line number to display for each row in `rows`.
27301    ///
27302    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27303    pub fn calculate_relative_line_numbers(
27304        &self,
27305        rows: &Range<DisplayRow>,
27306        current_selection_head: DisplayRow,
27307        count_wrapped_lines: bool,
27308    ) -> HashMap<DisplayRow, u32> {
27309        let initial_offset =
27310            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27311        let current_selection_point = current_selection_head.as_display_point().to_point(self);
27312
27313        self.row_infos(rows.start)
27314            .take(rows.len())
27315            .enumerate()
27316            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27317            .filter(|(_row, row_info)| {
27318                row_info.buffer_row.is_some()
27319                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27320            })
27321            .enumerate()
27322            .filter(|(_, (row, row_info))| {
27323                // We want to check here that
27324                // - the row is not the current selection head to ensure the current
27325                // line has absolute numbering
27326                // - similarly, should the selection head live in a soft-wrapped line
27327                // and we are not counting those, that the parent line keeps its
27328                // absolute number
27329                // - lastly, if we are in a deleted line, it is fine to number this
27330                // relative with 0, as otherwise it would have no line number at all
27331                (*row != current_selection_head
27332                    && (count_wrapped_lines
27333                        || row_info.buffer_row != Some(current_selection_point.row)))
27334                    || row_info
27335                        .diff_status
27336                        .is_some_and(|status| status.is_deleted())
27337            })
27338            .map(|(i, (row, _))| (row, (initial_offset + i as i64).unsigned_abs() as u32))
27339            .collect()
27340    }
27341}
27342
27343pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27344    let font_size = style.text.font_size.to_pixels(window.rem_size());
27345    let layout = window.text_system().shape_line(
27346        SharedString::from(" ".repeat(column)),
27347        font_size,
27348        &[TextRun {
27349            len: column,
27350            font: style.text.font(),
27351            color: Hsla::default(),
27352            ..Default::default()
27353        }],
27354        None,
27355    );
27356
27357    layout.width
27358}
27359
27360impl Deref for EditorSnapshot {
27361    type Target = DisplaySnapshot;
27362
27363    fn deref(&self) -> &Self::Target {
27364        &self.display_snapshot
27365    }
27366}
27367
27368#[derive(Clone, Debug, PartialEq, Eq)]
27369pub enum EditorEvent {
27370    /// Emitted when the stored review comments change (added, removed, or updated).
27371    ReviewCommentsChanged {
27372        /// The new total count of review comments.
27373        total_count: usize,
27374    },
27375    InputIgnored {
27376        text: Arc<str>,
27377    },
27378    InputHandled {
27379        utf16_range_to_replace: Option<Range<isize>>,
27380        text: Arc<str>,
27381    },
27382    ExcerptsAdded {
27383        buffer: Entity<Buffer>,
27384        predecessor: ExcerptId,
27385        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27386    },
27387    ExcerptsRemoved {
27388        ids: Vec<ExcerptId>,
27389        removed_buffer_ids: Vec<BufferId>,
27390    },
27391    BufferFoldToggled {
27392        ids: Vec<ExcerptId>,
27393        folded: bool,
27394    },
27395    ExcerptsEdited {
27396        ids: Vec<ExcerptId>,
27397    },
27398    ExcerptsExpanded {
27399        ids: Vec<ExcerptId>,
27400    },
27401    ExpandExcerptsRequested {
27402        excerpt_ids: Vec<ExcerptId>,
27403        lines: u32,
27404        direction: ExpandExcerptDirection,
27405    },
27406    StageOrUnstageRequested {
27407        stage: bool,
27408        hunks: Vec<MultiBufferDiffHunk>,
27409    },
27410    RestoreRequested {
27411        hunks: Vec<MultiBufferDiffHunk>,
27412    },
27413    BufferEdited,
27414    Edited {
27415        transaction_id: clock::Lamport,
27416    },
27417    Reparsed(BufferId),
27418    Focused,
27419    FocusedIn,
27420    Blurred,
27421    DirtyChanged,
27422    Saved,
27423    TitleChanged,
27424    SelectionsChanged {
27425        local: bool,
27426    },
27427    ScrollPositionChanged {
27428        local: bool,
27429        autoscroll: bool,
27430    },
27431    TransactionUndone {
27432        transaction_id: clock::Lamport,
27433    },
27434    TransactionBegun {
27435        transaction_id: clock::Lamport,
27436    },
27437    CursorShapeChanged,
27438    BreadcrumbsChanged,
27439    PushedToNavHistory {
27440        anchor: Anchor,
27441        is_deactivate: bool,
27442    },
27443}
27444
27445impl EventEmitter<EditorEvent> for Editor {}
27446
27447impl Focusable for Editor {
27448    fn focus_handle(&self, _cx: &App) -> FocusHandle {
27449        self.focus_handle.clone()
27450    }
27451}
27452
27453impl Render for Editor {
27454    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27455        EditorElement::new(&cx.entity(), self.create_style(cx))
27456    }
27457}
27458
27459impl EntityInputHandler for Editor {
27460    fn text_for_range(
27461        &mut self,
27462        range_utf16: Range<usize>,
27463        adjusted_range: &mut Option<Range<usize>>,
27464        _: &mut Window,
27465        cx: &mut Context<Self>,
27466    ) -> Option<String> {
27467        let snapshot = self.buffer.read(cx).read(cx);
27468        let start = snapshot.clip_offset_utf16(
27469            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
27470            Bias::Left,
27471        );
27472        let end = snapshot.clip_offset_utf16(
27473            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
27474            Bias::Right,
27475        );
27476        if (start.0.0..end.0.0) != range_utf16 {
27477            adjusted_range.replace(start.0.0..end.0.0);
27478        }
27479        Some(snapshot.text_for_range(start..end).collect())
27480    }
27481
27482    fn selected_text_range(
27483        &mut self,
27484        ignore_disabled_input: bool,
27485        _: &mut Window,
27486        cx: &mut Context<Self>,
27487    ) -> Option<UTF16Selection> {
27488        // Prevent the IME menu from appearing when holding down an alphabetic key
27489        // while input is disabled.
27490        if !ignore_disabled_input && !self.input_enabled {
27491            return None;
27492        }
27493
27494        let selection = self
27495            .selections
27496            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
27497        let range = selection.range();
27498
27499        Some(UTF16Selection {
27500            range: range.start.0.0..range.end.0.0,
27501            reversed: selection.reversed,
27502        })
27503    }
27504
27505    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
27506        let snapshot = self.buffer.read(cx).read(cx);
27507        let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
27508        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
27509    }
27510
27511    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
27512        self.clear_highlights::<InputComposition>(cx);
27513        self.ime_transaction.take();
27514    }
27515
27516    fn replace_text_in_range(
27517        &mut self,
27518        range_utf16: Option<Range<usize>>,
27519        text: &str,
27520        window: &mut Window,
27521        cx: &mut Context<Self>,
27522    ) {
27523        if !self.input_enabled {
27524            cx.emit(EditorEvent::InputIgnored { text: text.into() });
27525            return;
27526        }
27527
27528        self.transact(window, cx, |this, window, cx| {
27529            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
27530                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27531                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27532                Some(this.selection_replacement_ranges(range_utf16, cx))
27533            } else {
27534                this.marked_text_ranges(cx)
27535            };
27536
27537            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
27538                let newest_selection_id = this.selections.newest_anchor().id;
27539                this.selections
27540                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27541                    .iter()
27542                    .zip(ranges_to_replace.iter())
27543                    .find_map(|(selection, range)| {
27544                        if selection.id == newest_selection_id {
27545                            Some(
27546                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27547                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27548                            )
27549                        } else {
27550                            None
27551                        }
27552                    })
27553            });
27554
27555            cx.emit(EditorEvent::InputHandled {
27556                utf16_range_to_replace: range_to_replace,
27557                text: text.into(),
27558            });
27559
27560            if let Some(new_selected_ranges) = new_selected_ranges {
27561                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27562                    selections.select_ranges(new_selected_ranges)
27563                });
27564                this.backspace(&Default::default(), window, cx);
27565            }
27566
27567            this.handle_input(text, window, cx);
27568        });
27569
27570        if let Some(transaction) = self.ime_transaction {
27571            self.buffer.update(cx, |buffer, cx| {
27572                buffer.group_until_transaction(transaction, cx);
27573            });
27574        }
27575
27576        self.unmark_text(window, cx);
27577    }
27578
27579    fn replace_and_mark_text_in_range(
27580        &mut self,
27581        range_utf16: Option<Range<usize>>,
27582        text: &str,
27583        new_selected_range_utf16: Option<Range<usize>>,
27584        window: &mut Window,
27585        cx: &mut Context<Self>,
27586    ) {
27587        if !self.input_enabled {
27588            return;
27589        }
27590
27591        let transaction = self.transact(window, cx, |this, window, cx| {
27592            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
27593                let snapshot = this.buffer.read(cx).read(cx);
27594                if let Some(relative_range_utf16) = range_utf16.as_ref() {
27595                    for marked_range in &mut marked_ranges {
27596                        marked_range.end = marked_range.start + relative_range_utf16.end;
27597                        marked_range.start += relative_range_utf16.start;
27598                        marked_range.start =
27599                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
27600                        marked_range.end =
27601                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
27602                    }
27603                }
27604                Some(marked_ranges)
27605            } else if let Some(range_utf16) = range_utf16 {
27606                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27607                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27608                Some(this.selection_replacement_ranges(range_utf16, cx))
27609            } else {
27610                None
27611            };
27612
27613            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
27614                let newest_selection_id = this.selections.newest_anchor().id;
27615                this.selections
27616                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27617                    .iter()
27618                    .zip(ranges_to_replace.iter())
27619                    .find_map(|(selection, range)| {
27620                        if selection.id == newest_selection_id {
27621                            Some(
27622                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27623                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27624                            )
27625                        } else {
27626                            None
27627                        }
27628                    })
27629            });
27630
27631            cx.emit(EditorEvent::InputHandled {
27632                utf16_range_to_replace: range_to_replace,
27633                text: text.into(),
27634            });
27635
27636            if let Some(ranges) = ranges_to_replace {
27637                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
27638                    s.select_ranges(ranges)
27639                });
27640            }
27641
27642            let marked_ranges = {
27643                let snapshot = this.buffer.read(cx).read(cx);
27644                this.selections
27645                    .disjoint_anchors_arc()
27646                    .iter()
27647                    .map(|selection| {
27648                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
27649                    })
27650                    .collect::<Vec<_>>()
27651            };
27652
27653            if text.is_empty() {
27654                this.unmark_text(window, cx);
27655            } else {
27656                this.highlight_text::<InputComposition>(
27657                    marked_ranges.clone(),
27658                    HighlightStyle {
27659                        underline: Some(UnderlineStyle {
27660                            thickness: px(1.),
27661                            color: None,
27662                            wavy: false,
27663                        }),
27664                        ..Default::default()
27665                    },
27666                    cx,
27667                );
27668            }
27669
27670            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
27671            let use_autoclose = this.use_autoclose;
27672            let use_auto_surround = this.use_auto_surround;
27673            this.set_use_autoclose(false);
27674            this.set_use_auto_surround(false);
27675            this.handle_input(text, window, cx);
27676            this.set_use_autoclose(use_autoclose);
27677            this.set_use_auto_surround(use_auto_surround);
27678
27679            if let Some(new_selected_range) = new_selected_range_utf16 {
27680                let snapshot = this.buffer.read(cx).read(cx);
27681                let new_selected_ranges = marked_ranges
27682                    .into_iter()
27683                    .map(|marked_range| {
27684                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
27685                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
27686                            insertion_start.0 + new_selected_range.start,
27687                        ));
27688                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
27689                            insertion_start.0 + new_selected_range.end,
27690                        ));
27691                        snapshot.clip_offset_utf16(new_start, Bias::Left)
27692                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
27693                    })
27694                    .collect::<Vec<_>>();
27695
27696                drop(snapshot);
27697                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27698                    selections.select_ranges(new_selected_ranges)
27699                });
27700            }
27701        });
27702
27703        self.ime_transaction = self.ime_transaction.or(transaction);
27704        if let Some(transaction) = self.ime_transaction {
27705            self.buffer.update(cx, |buffer, cx| {
27706                buffer.group_until_transaction(transaction, cx);
27707            });
27708        }
27709
27710        if self.text_highlights::<InputComposition>(cx).is_none() {
27711            self.ime_transaction.take();
27712        }
27713    }
27714
27715    fn bounds_for_range(
27716        &mut self,
27717        range_utf16: Range<usize>,
27718        element_bounds: gpui::Bounds<Pixels>,
27719        window: &mut Window,
27720        cx: &mut Context<Self>,
27721    ) -> Option<gpui::Bounds<Pixels>> {
27722        let text_layout_details = self.text_layout_details(window, cx);
27723        let CharacterDimensions {
27724            em_width,
27725            em_advance,
27726            line_height,
27727        } = self.character_dimensions(window, cx);
27728
27729        let snapshot = self.snapshot(window, cx);
27730        let scroll_position = snapshot.scroll_position();
27731        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
27732
27733        let start =
27734            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
27735        let x = Pixels::from(
27736            ScrollOffset::from(
27737                snapshot.x_for_display_point(start, &text_layout_details)
27738                    + self.gutter_dimensions.full_width(),
27739            ) - scroll_left,
27740        );
27741        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
27742
27743        Some(Bounds {
27744            origin: element_bounds.origin + point(x, y),
27745            size: size(em_width, line_height),
27746        })
27747    }
27748
27749    fn character_index_for_point(
27750        &mut self,
27751        point: gpui::Point<Pixels>,
27752        _window: &mut Window,
27753        _cx: &mut Context<Self>,
27754    ) -> Option<usize> {
27755        let position_map = self.last_position_map.as_ref()?;
27756        if !position_map.text_hitbox.contains(&point) {
27757            return None;
27758        }
27759        let display_point = position_map.point_for_position(point).previous_valid;
27760        let anchor = position_map
27761            .snapshot
27762            .display_point_to_anchor(display_point, Bias::Left);
27763        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
27764        Some(utf16_offset.0.0)
27765    }
27766
27767    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
27768        self.input_enabled
27769    }
27770}
27771
27772trait SelectionExt {
27773    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
27774    fn spanned_rows(
27775        &self,
27776        include_end_if_at_line_start: bool,
27777        map: &DisplaySnapshot,
27778    ) -> Range<MultiBufferRow>;
27779}
27780
27781impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
27782    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
27783        let start = self
27784            .start
27785            .to_point(map.buffer_snapshot())
27786            .to_display_point(map);
27787        let end = self
27788            .end
27789            .to_point(map.buffer_snapshot())
27790            .to_display_point(map);
27791        if self.reversed {
27792            end..start
27793        } else {
27794            start..end
27795        }
27796    }
27797
27798    fn spanned_rows(
27799        &self,
27800        include_end_if_at_line_start: bool,
27801        map: &DisplaySnapshot,
27802    ) -> Range<MultiBufferRow> {
27803        let start = self.start.to_point(map.buffer_snapshot());
27804        let mut end = self.end.to_point(map.buffer_snapshot());
27805        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
27806            end.row -= 1;
27807        }
27808
27809        let buffer_start = map.prev_line_boundary(start).0;
27810        let buffer_end = map.next_line_boundary(end).0;
27811        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
27812    }
27813}
27814
27815impl<T: InvalidationRegion> InvalidationStack<T> {
27816    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
27817    where
27818        S: Clone + ToOffset,
27819    {
27820        while let Some(region) = self.last() {
27821            let all_selections_inside_invalidation_ranges =
27822                if selections.len() == region.ranges().len() {
27823                    selections
27824                        .iter()
27825                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
27826                        .all(|(selection, invalidation_range)| {
27827                            let head = selection.head().to_offset(buffer);
27828                            invalidation_range.start <= head && invalidation_range.end >= head
27829                        })
27830                } else {
27831                    false
27832                };
27833
27834            if all_selections_inside_invalidation_ranges {
27835                break;
27836            } else {
27837                self.pop();
27838            }
27839        }
27840    }
27841}
27842
27843#[derive(Clone)]
27844struct ErasedEditorImpl(Entity<Editor>);
27845
27846impl ui_input::ErasedEditor for ErasedEditorImpl {
27847    fn text(&self, cx: &App) -> String {
27848        self.0.read(cx).text(cx)
27849    }
27850
27851    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
27852        self.0.update(cx, |this, cx| {
27853            this.set_text(text, window, cx);
27854        })
27855    }
27856
27857    fn clear(&self, window: &mut Window, cx: &mut App) {
27858        self.0.update(cx, |this, cx| this.clear(window, cx));
27859    }
27860
27861    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
27862        self.0.update(cx, |this, cx| {
27863            this.set_placeholder_text(text, window, cx);
27864        });
27865    }
27866
27867    fn focus_handle(&self, cx: &App) -> FocusHandle {
27868        self.0.read(cx).focus_handle(cx)
27869    }
27870
27871    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
27872        let settings = ThemeSettings::get_global(cx);
27873        let theme_color = cx.theme().colors();
27874
27875        let text_style = TextStyle {
27876            font_family: settings.ui_font.family.clone(),
27877            font_features: settings.ui_font.features.clone(),
27878            font_size: rems(0.875).into(),
27879            font_weight: settings.buffer_font.weight,
27880            font_style: FontStyle::Normal,
27881            line_height: relative(1.2),
27882            color: theme_color.text,
27883            ..Default::default()
27884        };
27885        let editor_style = EditorStyle {
27886            background: theme_color.ghost_element_background,
27887            local_player: cx.theme().players().local(),
27888            syntax: cx.theme().syntax().clone(),
27889            text: text_style,
27890            ..Default::default()
27891        };
27892        EditorElement::new(&self.0, editor_style).into_any()
27893    }
27894
27895    fn as_any(&self) -> &dyn Any {
27896        &self.0
27897    }
27898
27899    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
27900        self.0.update(cx, |editor, cx| {
27901            let editor_offset = editor.buffer().read(cx).len(cx);
27902            editor.change_selections(
27903                SelectionEffects::scroll(Autoscroll::Next),
27904                window,
27905                cx,
27906                |s| s.select_ranges(Some(editor_offset..editor_offset)),
27907            );
27908        });
27909    }
27910
27911    fn subscribe(
27912        &self,
27913        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
27914        window: &mut Window,
27915        cx: &mut App,
27916    ) -> Subscription {
27917        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
27918            let event = match event {
27919                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
27920                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
27921                _ => return,
27922            };
27923            (callback)(event, window, cx);
27924        })
27925    }
27926
27927    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
27928        self.0.update(cx, |editor, cx| {
27929            editor.set_masked(masked, cx);
27930        });
27931    }
27932}
27933impl<T> Default for InvalidationStack<T> {
27934    fn default() -> Self {
27935        Self(Default::default())
27936    }
27937}
27938
27939impl<T> Deref for InvalidationStack<T> {
27940    type Target = Vec<T>;
27941
27942    fn deref(&self) -> &Self::Target {
27943        &self.0
27944    }
27945}
27946
27947impl<T> DerefMut for InvalidationStack<T> {
27948    fn deref_mut(&mut self) -> &mut Self::Target {
27949        &mut self.0
27950    }
27951}
27952
27953impl InvalidationRegion for SnippetState {
27954    fn ranges(&self) -> &[Range<Anchor>] {
27955        &self.ranges[self.active_index]
27956    }
27957}
27958
27959fn edit_prediction_edit_text(
27960    current_snapshot: &BufferSnapshot,
27961    edits: &[(Range<Anchor>, impl AsRef<str>)],
27962    edit_preview: &EditPreview,
27963    include_deletions: bool,
27964    cx: &App,
27965) -> HighlightedText {
27966    let edits = edits
27967        .iter()
27968        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
27969        .collect::<Vec<_>>();
27970
27971    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
27972}
27973
27974fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
27975    // Fallback for providers that don't provide edit_preview (like Copilot/Supermaven)
27976    // Just show the raw edit text with basic styling
27977    let mut text = String::new();
27978    let mut highlights = Vec::new();
27979
27980    let insertion_highlight_style = HighlightStyle {
27981        color: Some(cx.theme().colors().text),
27982        ..Default::default()
27983    };
27984
27985    for (_, edit_text) in edits {
27986        let start_offset = text.len();
27987        text.push_str(edit_text);
27988        let end_offset = text.len();
27989
27990        if start_offset < end_offset {
27991            highlights.push((start_offset..end_offset, insertion_highlight_style));
27992        }
27993    }
27994
27995    HighlightedText {
27996        text: text.into(),
27997        highlights,
27998    }
27999}
28000
28001pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28002    match severity {
28003        lsp::DiagnosticSeverity::ERROR => colors.error,
28004        lsp::DiagnosticSeverity::WARNING => colors.warning,
28005        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28006        lsp::DiagnosticSeverity::HINT => colors.info,
28007        _ => colors.ignored,
28008    }
28009}
28010
28011pub fn styled_runs_for_code_label<'a>(
28012    label: &'a CodeLabel,
28013    syntax_theme: &'a theme::SyntaxTheme,
28014    local_player: &'a theme::PlayerColor,
28015) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28016    let fade_out = HighlightStyle {
28017        fade_out: Some(0.35),
28018        ..Default::default()
28019    };
28020
28021    let mut prev_end = label.filter_range.end;
28022    label
28023        .runs
28024        .iter()
28025        .enumerate()
28026        .flat_map(move |(ix, (range, highlight_id))| {
28027            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28028                HighlightStyle {
28029                    color: Some(local_player.cursor),
28030                    ..Default::default()
28031                }
28032            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28033                HighlightStyle {
28034                    background_color: Some(local_player.selection),
28035                    ..Default::default()
28036                }
28037            } else if let Some(style) = highlight_id.style(syntax_theme) {
28038                style
28039            } else {
28040                return Default::default();
28041            };
28042            let muted_style = style.highlight(fade_out);
28043
28044            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28045            if range.start >= label.filter_range.end {
28046                if range.start > prev_end {
28047                    runs.push((prev_end..range.start, fade_out));
28048                }
28049                runs.push((range.clone(), muted_style));
28050            } else if range.end <= label.filter_range.end {
28051                runs.push((range.clone(), style));
28052            } else {
28053                runs.push((range.start..label.filter_range.end, style));
28054                runs.push((label.filter_range.end..range.end, muted_style));
28055            }
28056            prev_end = cmp::max(prev_end, range.end);
28057
28058            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28059                runs.push((prev_end..label.text.len(), fade_out));
28060            }
28061
28062            runs
28063        })
28064}
28065
28066pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28067    let mut prev_index = 0;
28068    let mut prev_codepoint: Option<char> = None;
28069    text.char_indices()
28070        .chain([(text.len(), '\0')])
28071        .filter_map(move |(index, codepoint)| {
28072            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28073            let is_boundary = index == text.len()
28074                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28075                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28076            if is_boundary {
28077                let chunk = &text[prev_index..index];
28078                prev_index = index;
28079                Some(chunk)
28080            } else {
28081                None
28082            }
28083        })
28084}
28085
28086/// Given a string of text immediately before the cursor, iterates over possible
28087/// strings a snippet could match to. More precisely: returns an iterator over
28088/// suffixes of `text` created by splitting at word boundaries (before & after
28089/// every non-word character).
28090///
28091/// Shorter suffixes are returned first.
28092pub(crate) fn snippet_candidate_suffixes(
28093    text: &str,
28094    is_word_char: impl Fn(char) -> bool,
28095) -> impl std::iter::Iterator<Item = &str> {
28096    let mut prev_index = text.len();
28097    let mut prev_codepoint = None;
28098    text.char_indices()
28099        .rev()
28100        .chain([(0, '\0')])
28101        .filter_map(move |(index, codepoint)| {
28102            let prev_index = std::mem::replace(&mut prev_index, index);
28103            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28104            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28105                None
28106            } else {
28107                let chunk = &text[prev_index..]; // go to end of string
28108                Some(chunk)
28109            }
28110        })
28111}
28112
28113pub trait RangeToAnchorExt: Sized {
28114    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28115
28116    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28117        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28118        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28119    }
28120}
28121
28122impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28123    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28124        let start_offset = self.start.to_offset(snapshot);
28125        let end_offset = self.end.to_offset(snapshot);
28126        if start_offset == end_offset {
28127            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28128        } else {
28129            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28130        }
28131    }
28132}
28133
28134pub trait RowExt {
28135    fn as_f64(&self) -> f64;
28136
28137    fn next_row(&self) -> Self;
28138
28139    fn previous_row(&self) -> Self;
28140
28141    fn minus(&self, other: Self) -> u32;
28142}
28143
28144impl RowExt for DisplayRow {
28145    fn as_f64(&self) -> f64 {
28146        self.0 as _
28147    }
28148
28149    fn next_row(&self) -> Self {
28150        Self(self.0 + 1)
28151    }
28152
28153    fn previous_row(&self) -> Self {
28154        Self(self.0.saturating_sub(1))
28155    }
28156
28157    fn minus(&self, other: Self) -> u32 {
28158        self.0 - other.0
28159    }
28160}
28161
28162impl RowExt for MultiBufferRow {
28163    fn as_f64(&self) -> f64 {
28164        self.0 as _
28165    }
28166
28167    fn next_row(&self) -> Self {
28168        Self(self.0 + 1)
28169    }
28170
28171    fn previous_row(&self) -> Self {
28172        Self(self.0.saturating_sub(1))
28173    }
28174
28175    fn minus(&self, other: Self) -> u32 {
28176        self.0 - other.0
28177    }
28178}
28179
28180trait RowRangeExt {
28181    type Row;
28182
28183    fn len(&self) -> usize;
28184
28185    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28186}
28187
28188impl RowRangeExt for Range<MultiBufferRow> {
28189    type Row = MultiBufferRow;
28190
28191    fn len(&self) -> usize {
28192        (self.end.0 - self.start.0) as usize
28193    }
28194
28195    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28196        (self.start.0..self.end.0).map(MultiBufferRow)
28197    }
28198}
28199
28200impl RowRangeExt for Range<DisplayRow> {
28201    type Row = DisplayRow;
28202
28203    fn len(&self) -> usize {
28204        (self.end.0 - self.start.0) as usize
28205    }
28206
28207    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28208        (self.start.0..self.end.0).map(DisplayRow)
28209    }
28210}
28211
28212/// If select range has more than one line, we
28213/// just point the cursor to range.start.
28214fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28215    if range.start.row == range.end.row {
28216        range
28217    } else {
28218        range.start..range.start
28219    }
28220}
28221pub struct KillRing(ClipboardItem);
28222impl Global for KillRing {}
28223
28224const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28225
28226enum BreakpointPromptEditAction {
28227    Log,
28228    Condition,
28229    HitCondition,
28230}
28231
28232struct BreakpointPromptEditor {
28233    pub(crate) prompt: Entity<Editor>,
28234    editor: WeakEntity<Editor>,
28235    breakpoint_anchor: Anchor,
28236    breakpoint: Breakpoint,
28237    edit_action: BreakpointPromptEditAction,
28238    block_ids: HashSet<CustomBlockId>,
28239    editor_margins: Arc<Mutex<EditorMargins>>,
28240    _subscriptions: Vec<Subscription>,
28241}
28242
28243impl BreakpointPromptEditor {
28244    const MAX_LINES: u8 = 4;
28245
28246    fn new(
28247        editor: WeakEntity<Editor>,
28248        breakpoint_anchor: Anchor,
28249        breakpoint: Breakpoint,
28250        edit_action: BreakpointPromptEditAction,
28251        window: &mut Window,
28252        cx: &mut Context<Self>,
28253    ) -> Self {
28254        let base_text = match edit_action {
28255            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28256            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28257            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28258        }
28259        .map(|msg| msg.to_string())
28260        .unwrap_or_default();
28261
28262        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28263        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28264
28265        let prompt = cx.new(|cx| {
28266            let mut prompt = Editor::new(
28267                EditorMode::AutoHeight {
28268                    min_lines: 1,
28269                    max_lines: Some(Self::MAX_LINES as usize),
28270                },
28271                buffer,
28272                None,
28273                window,
28274                cx,
28275            );
28276            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28277            prompt.set_show_cursor_when_unfocused(false, cx);
28278            prompt.set_placeholder_text(
28279                match edit_action {
28280                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28281                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28282                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28283                },
28284                window,
28285                cx,
28286            );
28287
28288            prompt
28289        });
28290
28291        Self {
28292            prompt,
28293            editor,
28294            breakpoint_anchor,
28295            breakpoint,
28296            edit_action,
28297            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28298            block_ids: Default::default(),
28299            _subscriptions: vec![],
28300        }
28301    }
28302
28303    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28304        self.block_ids.extend(block_ids)
28305    }
28306
28307    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28308        if let Some(editor) = self.editor.upgrade() {
28309            let message = self
28310                .prompt
28311                .read(cx)
28312                .buffer
28313                .read(cx)
28314                .as_singleton()
28315                .expect("A multi buffer in breakpoint prompt isn't possible")
28316                .read(cx)
28317                .as_rope()
28318                .to_string();
28319
28320            editor.update(cx, |editor, cx| {
28321                editor.edit_breakpoint_at_anchor(
28322                    self.breakpoint_anchor,
28323                    self.breakpoint.clone(),
28324                    match self.edit_action {
28325                        BreakpointPromptEditAction::Log => {
28326                            BreakpointEditAction::EditLogMessage(message.into())
28327                        }
28328                        BreakpointPromptEditAction::Condition => {
28329                            BreakpointEditAction::EditCondition(message.into())
28330                        }
28331                        BreakpointPromptEditAction::HitCondition => {
28332                            BreakpointEditAction::EditHitCondition(message.into())
28333                        }
28334                    },
28335                    cx,
28336                );
28337
28338                editor.remove_blocks(self.block_ids.clone(), None, cx);
28339                cx.focus_self(window);
28340            });
28341        }
28342    }
28343
28344    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28345        self.editor
28346            .update(cx, |editor, cx| {
28347                editor.remove_blocks(self.block_ids.clone(), None, cx);
28348                window.focus(&editor.focus_handle, cx);
28349            })
28350            .log_err();
28351    }
28352
28353    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28354        let settings = ThemeSettings::get_global(cx);
28355        let text_style = TextStyle {
28356            color: if self.prompt.read(cx).read_only(cx) {
28357                cx.theme().colors().text_disabled
28358            } else {
28359                cx.theme().colors().text
28360            },
28361            font_family: settings.buffer_font.family.clone(),
28362            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28363            font_size: settings.buffer_font_size(cx).into(),
28364            font_weight: settings.buffer_font.weight,
28365            line_height: relative(settings.buffer_line_height.value()),
28366            ..Default::default()
28367        };
28368        EditorElement::new(
28369            &self.prompt,
28370            EditorStyle {
28371                background: cx.theme().colors().editor_background,
28372                local_player: cx.theme().players().local(),
28373                text: text_style,
28374                ..Default::default()
28375            },
28376        )
28377    }
28378}
28379
28380impl Render for BreakpointPromptEditor {
28381    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28382        let editor_margins = *self.editor_margins.lock();
28383        let gutter_dimensions = editor_margins.gutter;
28384        h_flex()
28385            .key_context("Editor")
28386            .bg(cx.theme().colors().editor_background)
28387            .border_y_1()
28388            .border_color(cx.theme().status().info_border)
28389            .size_full()
28390            .py(window.line_height() / 2.5)
28391            .on_action(cx.listener(Self::confirm))
28392            .on_action(cx.listener(Self::cancel))
28393            .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
28394            .child(div().flex_1().child(self.render_prompt_editor(cx)))
28395    }
28396}
28397
28398impl Focusable for BreakpointPromptEditor {
28399    fn focus_handle(&self, cx: &App) -> FocusHandle {
28400        self.prompt.focus_handle(cx)
28401    }
28402}
28403
28404fn all_edits_insertions_or_deletions(
28405    edits: &Vec<(Range<Anchor>, Arc<str>)>,
28406    snapshot: &MultiBufferSnapshot,
28407) -> bool {
28408    let mut all_insertions = true;
28409    let mut all_deletions = true;
28410
28411    for (range, new_text) in edits.iter() {
28412        let range_is_empty = range.to_offset(snapshot).is_empty();
28413        let text_is_empty = new_text.is_empty();
28414
28415        if range_is_empty != text_is_empty {
28416            if range_is_empty {
28417                all_deletions = false;
28418            } else {
28419                all_insertions = false;
28420            }
28421        } else {
28422            return false;
28423        }
28424
28425        if !all_insertions && !all_deletions {
28426            return false;
28427        }
28428    }
28429    all_insertions || all_deletions
28430}
28431
28432struct MissingEditPredictionKeybindingTooltip;
28433
28434impl Render for MissingEditPredictionKeybindingTooltip {
28435    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28436        ui::tooltip_container(cx, |container, cx| {
28437            container
28438                .flex_shrink_0()
28439                .max_w_80()
28440                .min_h(rems_from_px(124.))
28441                .justify_between()
28442                .child(
28443                    v_flex()
28444                        .flex_1()
28445                        .text_ui_sm(cx)
28446                        .child(Label::new("Conflict with Accept Keybinding"))
28447                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
28448                )
28449                .child(
28450                    h_flex()
28451                        .pb_1()
28452                        .gap_1()
28453                        .items_end()
28454                        .w_full()
28455                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
28456                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
28457                        }))
28458                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
28459                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
28460                        })),
28461                )
28462        })
28463    }
28464}
28465
28466#[derive(Debug, Clone, Copy, PartialEq)]
28467pub struct LineHighlight {
28468    pub background: Background,
28469    pub border: Option<gpui::Hsla>,
28470    pub include_gutter: bool,
28471    pub type_id: Option<TypeId>,
28472}
28473
28474struct LineManipulationResult {
28475    pub new_text: String,
28476    pub line_count_before: usize,
28477    pub line_count_after: usize,
28478}
28479
28480fn render_diff_hunk_controls(
28481    row: u32,
28482    status: &DiffHunkStatus,
28483    hunk_range: Range<Anchor>,
28484    is_created_file: bool,
28485    line_height: Pixels,
28486    editor: &Entity<Editor>,
28487    _window: &mut Window,
28488    cx: &mut App,
28489) -> AnyElement {
28490    h_flex()
28491        .h(line_height)
28492        .mr_1()
28493        .gap_1()
28494        .px_0p5()
28495        .pb_1()
28496        .border_x_1()
28497        .border_b_1()
28498        .border_color(cx.theme().colors().border_variant)
28499        .rounded_b_lg()
28500        .bg(cx.theme().colors().editor_background)
28501        .gap_1()
28502        .block_mouse_except_scroll()
28503        .shadow_md()
28504        .child(if status.has_secondary_hunk() {
28505            Button::new(("stage", row as u64), "Stage")
28506                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28507                .tooltip({
28508                    let focus_handle = editor.focus_handle(cx);
28509                    move |_window, cx| {
28510                        Tooltip::for_action_in(
28511                            "Stage Hunk",
28512                            &::git::ToggleStaged,
28513                            &focus_handle,
28514                            cx,
28515                        )
28516                    }
28517                })
28518                .on_click({
28519                    let editor = editor.clone();
28520                    move |_event, _window, cx| {
28521                        editor.update(cx, |editor, cx| {
28522                            editor.stage_or_unstage_diff_hunks(
28523                                true,
28524                                vec![hunk_range.start..hunk_range.start],
28525                                cx,
28526                            );
28527                        });
28528                    }
28529                })
28530        } else {
28531            Button::new(("unstage", row as u64), "Unstage")
28532                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28533                .tooltip({
28534                    let focus_handle = editor.focus_handle(cx);
28535                    move |_window, cx| {
28536                        Tooltip::for_action_in(
28537                            "Unstage Hunk",
28538                            &::git::ToggleStaged,
28539                            &focus_handle,
28540                            cx,
28541                        )
28542                    }
28543                })
28544                .on_click({
28545                    let editor = editor.clone();
28546                    move |_event, _window, cx| {
28547                        editor.update(cx, |editor, cx| {
28548                            editor.stage_or_unstage_diff_hunks(
28549                                false,
28550                                vec![hunk_range.start..hunk_range.start],
28551                                cx,
28552                            );
28553                        });
28554                    }
28555                })
28556        })
28557        .child(
28558            Button::new(("restore", row as u64), "Restore")
28559                .tooltip({
28560                    let focus_handle = editor.focus_handle(cx);
28561                    move |_window, cx| {
28562                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
28563                    }
28564                })
28565                .on_click({
28566                    let editor = editor.clone();
28567                    move |_event, window, cx| {
28568                        editor.update(cx, |editor, cx| {
28569                            let snapshot = editor.snapshot(window, cx);
28570                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
28571                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
28572                        });
28573                    }
28574                })
28575                .disabled(is_created_file),
28576        )
28577        .when(
28578            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
28579            |el| {
28580                el.child(
28581                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
28582                        .shape(IconButtonShape::Square)
28583                        .icon_size(IconSize::Small)
28584                        // .disabled(!has_multiple_hunks)
28585                        .tooltip({
28586                            let focus_handle = editor.focus_handle(cx);
28587                            move |_window, cx| {
28588                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
28589                            }
28590                        })
28591                        .on_click({
28592                            let editor = editor.clone();
28593                            move |_event, window, cx| {
28594                                editor.update(cx, |editor, cx| {
28595                                    let snapshot = editor.snapshot(window, cx);
28596                                    let position =
28597                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
28598                                    editor.go_to_hunk_before_or_after_position(
28599                                        &snapshot,
28600                                        position,
28601                                        Direction::Next,
28602                                        window,
28603                                        cx,
28604                                    );
28605                                    editor.expand_selected_diff_hunks(cx);
28606                                });
28607                            }
28608                        }),
28609                )
28610                .child(
28611                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
28612                        .shape(IconButtonShape::Square)
28613                        .icon_size(IconSize::Small)
28614                        // .disabled(!has_multiple_hunks)
28615                        .tooltip({
28616                            let focus_handle = editor.focus_handle(cx);
28617                            move |_window, cx| {
28618                                Tooltip::for_action_in(
28619                                    "Previous Hunk",
28620                                    &GoToPreviousHunk,
28621                                    &focus_handle,
28622                                    cx,
28623                                )
28624                            }
28625                        })
28626                        .on_click({
28627                            let editor = editor.clone();
28628                            move |_event, window, cx| {
28629                                editor.update(cx, |editor, cx| {
28630                                    let snapshot = editor.snapshot(window, cx);
28631                                    let point =
28632                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
28633                                    editor.go_to_hunk_before_or_after_position(
28634                                        &snapshot,
28635                                        point,
28636                                        Direction::Prev,
28637                                        window,
28638                                        cx,
28639                                    );
28640                                    editor.expand_selected_diff_hunks(cx);
28641                                });
28642                            }
28643                        }),
28644                )
28645            },
28646        )
28647        .into_any_element()
28648}
28649
28650pub fn multibuffer_context_lines(cx: &App) -> u32 {
28651    EditorSettings::try_get(cx)
28652        .map(|settings| settings.excerpt_context_lines)
28653        .unwrap_or(2)
28654        .min(32)
28655}