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 tasks;
   41
   42#[cfg(test)]
   43mod code_completion_tests;
   44#[cfg(test)]
   45mod edit_prediction_tests;
   46#[cfg(test)]
   47mod editor_tests;
   48mod signature_help;
   49#[cfg(any(test, feature = "test-support"))]
   50pub mod test;
   51
   52pub(crate) use actions::*;
   53pub use display_map::{ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder};
   54pub use edit_prediction_types::Direction;
   55pub use editor_settings::{
   56    CompletionDetailAlignment, CurrentLineHighlight, DocumentColorsRenderMode, EditorSettings,
   57    HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowMinimap,
   58};
   59pub use element::{
   60    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   61    render_breadcrumb_text,
   62};
   63pub use git::blame::BlameRenderer;
   64pub use hover_popover::hover_markdown_style;
   65pub use inlays::Inlay;
   66pub use items::MAX_TAB_TITLE_LEN;
   67pub use lsp::CompletionContext;
   68pub use lsp_ext::lsp_tasks;
   69pub use multi_buffer::{
   70    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   71    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   72    ToPoint,
   73};
   74pub use split::SplittableEditor;
   75pub use text::Bias;
   76
   77use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   78use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   79use anyhow::{Context as _, Result, anyhow, bail};
   80use blink_manager::BlinkManager;
   81use buffer_diff::DiffHunkStatus;
   82use client::{Collaborator, ParticipantIndex, parse_zed_link};
   83use clock::ReplicaId;
   84use code_context_menus::{
   85    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   86    CompletionsMenu, ContextMenuOrigin,
   87};
   88use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   89use convert_case::{Case, Casing};
   90use dap::TelemetrySpawnLocation;
   91use display_map::*;
   92use edit_prediction_types::{
   93    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionGranularity,
   94    SuggestionDisplayType,
   95};
   96use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
   97use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
   98use futures::{
   99    FutureExt, StreamExt as _,
  100    future::{self, Shared, join},
  101    stream::FuturesUnordered,
  102};
  103use fuzzy::{StringMatch, StringMatchCandidate};
  104use git::blame::{GitBlame, GlobalBlameRenderer};
  105use gpui::{
  106    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  107    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  108    DispatchPhase, Edges, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
  109    Focusable, FontId, FontWeight, Global, HighlightStyle, Hsla, KeyContext, Modifiers,
  110    MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement, Pixels, PressureStage,
  111    Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled, Subscription, Task,
  112    TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  113    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  114    pulsating_between, px, relative, size,
  115};
  116use hover_links::{HoverLink, HoveredLinkState, find_file};
  117use hover_popover::{HoverState, hide_hover};
  118use indent_guides::ActiveIndentGuidesState;
  119use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  120use itertools::{Either, Itertools};
  121use language::{
  122    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  123    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  124    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  125    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, OffsetRangeExt,
  126    OutlineItem, Point, Runnable, Selection, SelectionGoal, TextObject, TransactionId,
  127    TreeSitterOptions, WordsQuery,
  128    language_settings::{
  129        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  130        all_language_settings, language_settings,
  131    },
  132    point_from_lsp, point_to_lsp, text_diff_with_options,
  133};
  134use linked_editing_ranges::refresh_linked_ranges;
  135use lsp::{
  136    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  137    LanguageServerId,
  138};
  139use lsp_colors::LspColorData;
  140use markdown::Markdown;
  141use mouse_context_menu::MouseContextMenu;
  142use movement::TextLayoutDetails;
  143use multi_buffer::{
  144    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  145};
  146use parking_lot::Mutex;
  147use persistence::DB;
  148use project::{
  149    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  150    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  151    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  152    ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
  153    debugger::{
  154        breakpoint_store::{
  155            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  156            BreakpointStore, BreakpointStoreEvent,
  157        },
  158        session::{Session, SessionEvent},
  159    },
  160    git_store::GitStoreEvent,
  161    lsp_store::{
  162        CacheInlayHints, CompletionDocumentation, FormatTrigger, LspFormatTarget,
  163        OpenLspBufferHandle,
  164    },
  165    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  166};
  167use rand::seq::SliceRandom;
  168use regex::Regex;
  169use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  170use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager};
  171use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  172use serde::{Deserialize, Serialize};
  173use settings::{
  174    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  175    update_settings_file,
  176};
  177use smallvec::{SmallVec, smallvec};
  178use snippet::Snippet;
  179use std::{
  180    any::{Any, TypeId},
  181    borrow::Cow,
  182    cell::{OnceCell, RefCell},
  183    cmp::{self, Ordering, Reverse},
  184    collections::hash_map,
  185    iter::{self, Peekable},
  186    mem,
  187    num::NonZeroU32,
  188    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  189    path::{Path, PathBuf},
  190    rc::Rc,
  191    sync::Arc,
  192    time::{Duration, Instant},
  193};
  194use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
  195use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _};
  196use theme::{
  197    AccentColors, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, Theme, ThemeSettings,
  198    observe_buffer_font_size_adjustment,
  199};
  200use ui::{
  201    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  202    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  203};
  204use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  205use workspace::{
  206    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
  207    RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection, TabBarSettings, Toast,
  208    ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  209    item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  210    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  211    searchable::{CollapseDirection, SearchEvent},
  212};
  213
  214use crate::{
  215    code_context_menus::CompletionsMenuSource,
  216    editor_settings::MultiCursorModifier,
  217    hover_links::{find_url, find_url_from_range},
  218    inlays::{
  219        InlineValueCache,
  220        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  221    },
  222    scroll::{ScrollOffset, ScrollPixelOffset},
  223    selections_collection::resolve_selections_wrapping_blocks,
  224    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  225};
  226
  227pub const FILE_HEADER_HEIGHT: u32 = 2;
  228pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  229const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  230const MAX_LINE_LEN: usize = 1024;
  231const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  232const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  233pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  234#[doc(hidden)]
  235pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  236pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  237
  238pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  239pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  240pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  241pub const FETCH_COLORS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(150);
  242
  243pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  244pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  245pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  246
  247pub type RenderDiffHunkControlsFn = Arc<
  248    dyn Fn(
  249        u32,
  250        &DiffHunkStatus,
  251        Range<Anchor>,
  252        bool,
  253        Pixels,
  254        &Entity<Editor>,
  255        &mut Window,
  256        &mut App,
  257    ) -> AnyElement,
  258>;
  259
  260enum ReportEditorEvent {
  261    Saved { auto_saved: bool },
  262    EditorOpened,
  263    Closed,
  264}
  265
  266impl ReportEditorEvent {
  267    pub fn event_type(&self) -> &'static str {
  268        match self {
  269            Self::Saved { .. } => "Editor Saved",
  270            Self::EditorOpened => "Editor Opened",
  271            Self::Closed => "Editor Closed",
  272        }
  273    }
  274}
  275
  276pub enum ActiveDebugLine {}
  277pub enum DebugStackFrameLine {}
  278enum DocumentHighlightRead {}
  279enum DocumentHighlightWrite {}
  280enum InputComposition {}
  281pub enum PendingInput {}
  282enum SelectedTextHighlight {}
  283
  284pub enum ConflictsOuter {}
  285pub enum ConflictsOurs {}
  286pub enum ConflictsTheirs {}
  287pub enum ConflictsOursMarker {}
  288pub enum ConflictsTheirsMarker {}
  289
  290pub struct HunkAddedColor;
  291pub struct HunkRemovedColor;
  292
  293#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  294pub enum Navigated {
  295    Yes,
  296    No,
  297}
  298
  299impl Navigated {
  300    pub fn from_bool(yes: bool) -> Navigated {
  301        if yes { Navigated::Yes } else { Navigated::No }
  302    }
  303}
  304
  305#[derive(Debug, Clone, PartialEq, Eq)]
  306enum DisplayDiffHunk {
  307    Folded {
  308        display_row: DisplayRow,
  309    },
  310    Unfolded {
  311        is_created_file: bool,
  312        diff_base_byte_range: Range<usize>,
  313        display_row_range: Range<DisplayRow>,
  314        multi_buffer_range: Range<Anchor>,
  315        status: DiffHunkStatus,
  316        word_diffs: Vec<Range<MultiBufferOffset>>,
  317    },
  318}
  319
  320pub enum HideMouseCursorOrigin {
  321    TypingAction,
  322    MovementAction,
  323}
  324
  325pub fn init(cx: &mut App) {
  326    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  327
  328    workspace::register_project_item::<Editor>(cx);
  329    workspace::FollowableViewRegistry::register::<Editor>(cx);
  330    workspace::register_serializable_item::<Editor>(cx);
  331
  332    cx.observe_new(
  333        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  334            workspace.register_action(Editor::new_file);
  335            workspace.register_action(Editor::new_file_split);
  336            workspace.register_action(Editor::new_file_vertical);
  337            workspace.register_action(Editor::new_file_horizontal);
  338            workspace.register_action(Editor::cancel_language_server_work);
  339            workspace.register_action(Editor::toggle_focus);
  340        },
  341    )
  342    .detach();
  343
  344    cx.on_action(move |_: &workspace::NewFile, cx| {
  345        let app_state = workspace::AppState::global(cx);
  346        if let Some(app_state) = app_state.upgrade() {
  347            workspace::open_new(
  348                Default::default(),
  349                app_state,
  350                cx,
  351                |workspace, window, cx| {
  352                    Editor::new_file(workspace, &Default::default(), window, cx)
  353                },
  354            )
  355            .detach();
  356        }
  357    })
  358    .on_action(move |_: &workspace::NewWindow, cx| {
  359        let app_state = workspace::AppState::global(cx);
  360        if let Some(app_state) = app_state.upgrade() {
  361            workspace::open_new(
  362                Default::default(),
  363                app_state,
  364                cx,
  365                |workspace, window, cx| {
  366                    cx.activate(true);
  367                    Editor::new_file(workspace, &Default::default(), window, cx)
  368                },
  369            )
  370            .detach();
  371        }
  372    });
  373}
  374
  375pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  376    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  377}
  378
  379pub trait DiagnosticRenderer {
  380    fn render_group(
  381        &self,
  382        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  383        buffer_id: BufferId,
  384        snapshot: EditorSnapshot,
  385        editor: WeakEntity<Editor>,
  386        language_registry: Option<Arc<LanguageRegistry>>,
  387        cx: &mut App,
  388    ) -> Vec<BlockProperties<Anchor>>;
  389
  390    fn render_hover(
  391        &self,
  392        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  393        range: Range<Point>,
  394        buffer_id: BufferId,
  395        language_registry: Option<Arc<LanguageRegistry>>,
  396        cx: &mut App,
  397    ) -> Option<Entity<markdown::Markdown>>;
  398
  399    fn open_link(
  400        &self,
  401        editor: &mut Editor,
  402        link: SharedString,
  403        window: &mut Window,
  404        cx: &mut Context<Editor>,
  405    );
  406}
  407
  408pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  409
  410impl GlobalDiagnosticRenderer {
  411    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  412        cx.try_global::<Self>().map(|g| g.0.clone())
  413    }
  414}
  415
  416impl gpui::Global for GlobalDiagnosticRenderer {}
  417pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  418    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  419}
  420
  421pub struct SearchWithinRange;
  422
  423trait InvalidationRegion {
  424    fn ranges(&self) -> &[Range<Anchor>];
  425}
  426
  427#[derive(Clone, Debug, PartialEq)]
  428pub enum SelectPhase {
  429    Begin {
  430        position: DisplayPoint,
  431        add: bool,
  432        click_count: usize,
  433    },
  434    BeginColumnar {
  435        position: DisplayPoint,
  436        reset: bool,
  437        mode: ColumnarMode,
  438        goal_column: u32,
  439    },
  440    Extend {
  441        position: DisplayPoint,
  442        click_count: usize,
  443    },
  444    Update {
  445        position: DisplayPoint,
  446        goal_column: u32,
  447        scroll_delta: gpui::Point<f32>,
  448    },
  449    End,
  450}
  451
  452#[derive(Clone, Debug, PartialEq)]
  453pub enum ColumnarMode {
  454    FromMouse,
  455    FromSelection,
  456}
  457
  458#[derive(Clone, Debug)]
  459pub enum SelectMode {
  460    Character,
  461    Word(Range<Anchor>),
  462    Line(Range<Anchor>),
  463    All,
  464}
  465
  466#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  467pub enum SizingBehavior {
  468    /// The editor will layout itself using `size_full` and will include the vertical
  469    /// scroll margin as requested by user settings.
  470    #[default]
  471    Default,
  472    /// The editor will layout itself using `size_full`, but will not have any
  473    /// vertical overscroll.
  474    ExcludeOverscrollMargin,
  475    /// The editor will request a vertical size according to its content and will be
  476    /// layouted without a vertical scroll margin.
  477    SizeByContent,
  478}
  479
  480#[derive(Clone, PartialEq, Eq, Debug)]
  481pub enum EditorMode {
  482    SingleLine,
  483    AutoHeight {
  484        min_lines: usize,
  485        max_lines: Option<usize>,
  486    },
  487    Full {
  488        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  489        scale_ui_elements_with_buffer_font_size: bool,
  490        /// When set to `true`, the editor will render a background for the active line.
  491        show_active_line_background: bool,
  492        /// Determines the sizing behavior for this editor
  493        sizing_behavior: SizingBehavior,
  494    },
  495    Minimap {
  496        parent: WeakEntity<Editor>,
  497    },
  498}
  499
  500impl EditorMode {
  501    pub fn full() -> Self {
  502        Self::Full {
  503            scale_ui_elements_with_buffer_font_size: true,
  504            show_active_line_background: true,
  505            sizing_behavior: SizingBehavior::Default,
  506        }
  507    }
  508
  509    #[inline]
  510    pub fn is_full(&self) -> bool {
  511        matches!(self, Self::Full { .. })
  512    }
  513
  514    #[inline]
  515    pub fn is_single_line(&self) -> bool {
  516        matches!(self, Self::SingleLine { .. })
  517    }
  518
  519    #[inline]
  520    fn is_minimap(&self) -> bool {
  521        matches!(self, Self::Minimap { .. })
  522    }
  523}
  524
  525#[derive(Copy, Clone, Debug)]
  526pub enum SoftWrap {
  527    /// Prefer not to wrap at all.
  528    ///
  529    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  530    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  531    GitDiff,
  532    /// Prefer a single line generally, unless an overly long line is encountered.
  533    None,
  534    /// Soft wrap lines that exceed the editor width.
  535    EditorWidth,
  536    /// Soft wrap lines at the preferred line length.
  537    Column(u32),
  538    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  539    Bounded(u32),
  540}
  541
  542#[derive(Clone)]
  543pub struct EditorStyle {
  544    pub background: Hsla,
  545    pub border: Hsla,
  546    pub local_player: PlayerColor,
  547    pub text: TextStyle,
  548    pub scrollbar_width: Pixels,
  549    pub syntax: Arc<SyntaxTheme>,
  550    pub status: StatusColors,
  551    pub inlay_hints_style: HighlightStyle,
  552    pub edit_prediction_styles: EditPredictionStyles,
  553    pub unnecessary_code_fade: f32,
  554    pub show_underlines: bool,
  555}
  556
  557impl Default for EditorStyle {
  558    fn default() -> Self {
  559        Self {
  560            background: Hsla::default(),
  561            border: Hsla::default(),
  562            local_player: PlayerColor::default(),
  563            text: TextStyle::default(),
  564            scrollbar_width: Pixels::default(),
  565            syntax: Default::default(),
  566            // HACK: Status colors don't have a real default.
  567            // We should look into removing the status colors from the editor
  568            // style and retrieve them directly from the theme.
  569            status: StatusColors::dark(),
  570            inlay_hints_style: HighlightStyle::default(),
  571            edit_prediction_styles: EditPredictionStyles {
  572                insertion: HighlightStyle::default(),
  573                whitespace: HighlightStyle::default(),
  574            },
  575            unnecessary_code_fade: Default::default(),
  576            show_underlines: true,
  577        }
  578    }
  579}
  580
  581pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  582    let show_background = language_settings::language_settings(None, None, cx)
  583        .inlay_hints
  584        .show_background;
  585
  586    let mut style = cx.theme().syntax().get("hint");
  587
  588    if style.color.is_none() {
  589        style.color = Some(cx.theme().status().hint);
  590    }
  591
  592    if !show_background {
  593        style.background_color = None;
  594        return style;
  595    }
  596
  597    if style.background_color.is_none() {
  598        style.background_color = Some(cx.theme().status().hint_background);
  599    }
  600
  601    style
  602}
  603
  604pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  605    EditPredictionStyles {
  606        insertion: HighlightStyle {
  607            color: Some(cx.theme().status().predictive),
  608            ..HighlightStyle::default()
  609        },
  610        whitespace: HighlightStyle {
  611            background_color: Some(cx.theme().status().created_background),
  612            ..HighlightStyle::default()
  613        },
  614    }
  615}
  616
  617type CompletionId = usize;
  618
  619pub(crate) enum EditDisplayMode {
  620    TabAccept,
  621    DiffPopover,
  622    Inline,
  623}
  624
  625enum EditPrediction {
  626    Edit {
  627        edits: Vec<(Range<Anchor>, Arc<str>)>,
  628        edit_preview: Option<EditPreview>,
  629        display_mode: EditDisplayMode,
  630        snapshot: BufferSnapshot,
  631    },
  632    /// Move to a specific location in the active editor
  633    MoveWithin {
  634        target: Anchor,
  635        snapshot: BufferSnapshot,
  636    },
  637    /// Move to a specific location in a different editor (not the active one)
  638    MoveOutside {
  639        target: language::Anchor,
  640        snapshot: BufferSnapshot,
  641    },
  642}
  643
  644struct EditPredictionState {
  645    inlay_ids: Vec<InlayId>,
  646    completion: EditPrediction,
  647    completion_id: Option<SharedString>,
  648    invalidation_range: Option<Range<Anchor>>,
  649}
  650
  651enum EditPredictionSettings {
  652    Disabled,
  653    Enabled {
  654        show_in_menu: bool,
  655        preview_requires_modifier: bool,
  656    },
  657}
  658
  659enum EditPredictionHighlight {}
  660
  661#[derive(Debug, Clone)]
  662struct InlineDiagnostic {
  663    message: SharedString,
  664    group_id: usize,
  665    is_primary: bool,
  666    start: Point,
  667    severity: lsp::DiagnosticSeverity,
  668}
  669
  670pub enum MenuEditPredictionsPolicy {
  671    Never,
  672    ByProvider,
  673}
  674
  675pub enum EditPredictionPreview {
  676    /// Modifier is not pressed
  677    Inactive { released_too_fast: bool },
  678    /// Modifier pressed
  679    Active {
  680        since: Instant,
  681        previous_scroll_position: Option<ScrollAnchor>,
  682    },
  683}
  684
  685impl EditPredictionPreview {
  686    pub fn released_too_fast(&self) -> bool {
  687        match self {
  688            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  689            EditPredictionPreview::Active { .. } => false,
  690        }
  691    }
  692
  693    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
  694        if let EditPredictionPreview::Active {
  695            previous_scroll_position,
  696            ..
  697        } = self
  698        {
  699            *previous_scroll_position = scroll_position;
  700        }
  701    }
  702}
  703
  704pub struct ContextMenuOptions {
  705    pub min_entries_visible: usize,
  706    pub max_entries_visible: usize,
  707    pub placement: Option<ContextMenuPlacement>,
  708}
  709
  710#[derive(Debug, Clone, PartialEq, Eq)]
  711pub enum ContextMenuPlacement {
  712    Above,
  713    Below,
  714}
  715
  716#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  717struct EditorActionId(usize);
  718
  719impl EditorActionId {
  720    pub fn post_inc(&mut self) -> Self {
  721        let answer = self.0;
  722
  723        *self = Self(answer + 1);
  724
  725        Self(answer)
  726    }
  727}
  728
  729// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  730// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  731
  732type BackgroundHighlight = (
  733    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  734    Arc<[Range<Anchor>]>,
  735);
  736type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  737
  738#[derive(Default)]
  739struct ScrollbarMarkerState {
  740    scrollbar_size: Size<Pixels>,
  741    dirty: bool,
  742    markers: Arc<[PaintQuad]>,
  743    pending_refresh: Option<Task<Result<()>>>,
  744}
  745
  746impl ScrollbarMarkerState {
  747    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  748        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  749    }
  750}
  751
  752#[derive(Clone, Copy, PartialEq, Eq)]
  753pub enum MinimapVisibility {
  754    Disabled,
  755    Enabled {
  756        /// The configuration currently present in the users settings.
  757        setting_configuration: bool,
  758        /// Whether to override the currently set visibility from the users setting.
  759        toggle_override: bool,
  760    },
  761}
  762
  763impl MinimapVisibility {
  764    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  765        if mode.is_full() {
  766            Self::Enabled {
  767                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  768                toggle_override: false,
  769            }
  770        } else {
  771            Self::Disabled
  772        }
  773    }
  774
  775    fn hidden(&self) -> Self {
  776        match *self {
  777            Self::Enabled {
  778                setting_configuration,
  779                ..
  780            } => Self::Enabled {
  781                setting_configuration,
  782                toggle_override: setting_configuration,
  783            },
  784            Self::Disabled => Self::Disabled,
  785        }
  786    }
  787
  788    fn disabled(&self) -> bool {
  789        matches!(*self, Self::Disabled)
  790    }
  791
  792    fn settings_visibility(&self) -> bool {
  793        match *self {
  794            Self::Enabled {
  795                setting_configuration,
  796                ..
  797            } => setting_configuration,
  798            _ => false,
  799        }
  800    }
  801
  802    fn visible(&self) -> bool {
  803        match *self {
  804            Self::Enabled {
  805                setting_configuration,
  806                toggle_override,
  807            } => setting_configuration ^ toggle_override,
  808            _ => false,
  809        }
  810    }
  811
  812    fn toggle_visibility(&self) -> Self {
  813        match *self {
  814            Self::Enabled {
  815                toggle_override,
  816                setting_configuration,
  817            } => Self::Enabled {
  818                setting_configuration,
  819                toggle_override: !toggle_override,
  820            },
  821            Self::Disabled => Self::Disabled,
  822        }
  823    }
  824}
  825
  826#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  827pub enum BufferSerialization {
  828    All,
  829    NonDirtyBuffers,
  830}
  831
  832impl BufferSerialization {
  833    fn new(restore_unsaved_buffers: bool) -> Self {
  834        if restore_unsaved_buffers {
  835            Self::All
  836        } else {
  837            Self::NonDirtyBuffers
  838        }
  839    }
  840}
  841
  842#[derive(Clone, Debug)]
  843struct RunnableTasks {
  844    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  845    offset: multi_buffer::Anchor,
  846    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  847    column: u32,
  848    // Values of all named captures, including those starting with '_'
  849    extra_variables: HashMap<String, String>,
  850    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  851    context_range: Range<BufferOffset>,
  852}
  853
  854impl RunnableTasks {
  855    fn resolve<'a>(
  856        &'a self,
  857        cx: &'a task::TaskContext,
  858    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  859        self.templates.iter().filter_map(|(kind, template)| {
  860            template
  861                .resolve_task(&kind.to_id_base(), cx)
  862                .map(|task| (kind.clone(), task))
  863        })
  864    }
  865}
  866
  867#[derive(Clone)]
  868pub struct ResolvedTasks {
  869    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  870    position: Anchor,
  871}
  872
  873/// Addons allow storing per-editor state in other crates (e.g. Vim)
  874pub trait Addon: 'static {
  875    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  876
  877    fn render_buffer_header_controls(
  878        &self,
  879        _: &ExcerptInfo,
  880        _: &Window,
  881        _: &App,
  882    ) -> Option<AnyElement> {
  883        None
  884    }
  885
  886    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  887        None
  888    }
  889
  890    fn to_any(&self) -> &dyn std::any::Any;
  891
  892    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  893        None
  894    }
  895}
  896
  897struct ChangeLocation {
  898    current: Option<Vec<Anchor>>,
  899    original: Vec<Anchor>,
  900}
  901impl ChangeLocation {
  902    fn locations(&self) -> &[Anchor] {
  903        self.current.as_ref().unwrap_or(&self.original)
  904    }
  905}
  906
  907/// A set of caret positions, registered when the editor was edited.
  908pub struct ChangeList {
  909    changes: Vec<ChangeLocation>,
  910    /// Currently "selected" change.
  911    position: Option<usize>,
  912}
  913
  914impl ChangeList {
  915    pub fn new() -> Self {
  916        Self {
  917            changes: Vec::new(),
  918            position: None,
  919        }
  920    }
  921
  922    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  923    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  924    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  925        if self.changes.is_empty() {
  926            return None;
  927        }
  928
  929        let prev = self.position.unwrap_or(self.changes.len());
  930        let next = if direction == Direction::Prev {
  931            prev.saturating_sub(count)
  932        } else {
  933            (prev + count).min(self.changes.len() - 1)
  934        };
  935        self.position = Some(next);
  936        self.changes.get(next).map(|change| change.locations())
  937    }
  938
  939    /// Adds a new change to the list, resetting the change list position.
  940    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  941        self.position.take();
  942        if let Some(last) = self.changes.last_mut()
  943            && group
  944        {
  945            last.current = Some(new_positions)
  946        } else {
  947            self.changes.push(ChangeLocation {
  948                original: new_positions,
  949                current: None,
  950            });
  951        }
  952    }
  953
  954    pub fn last(&self) -> Option<&[Anchor]> {
  955        self.changes.last().map(|change| change.locations())
  956    }
  957
  958    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  959        self.changes.last().map(|change| change.original.as_slice())
  960    }
  961
  962    pub fn invert_last_group(&mut self) {
  963        if let Some(last) = self.changes.last_mut()
  964            && let Some(current) = last.current.as_mut()
  965        {
  966            mem::swap(&mut last.original, current);
  967        }
  968    }
  969}
  970
  971#[derive(Clone)]
  972struct InlineBlamePopoverState {
  973    scroll_handle: ScrollHandle,
  974    commit_message: Option<ParsedCommitMessage>,
  975    markdown: Entity<Markdown>,
  976}
  977
  978struct InlineBlamePopover {
  979    position: gpui::Point<Pixels>,
  980    hide_task: Option<Task<()>>,
  981    popover_bounds: Option<Bounds<Pixels>>,
  982    popover_state: InlineBlamePopoverState,
  983    keyboard_grace: bool,
  984}
  985
  986enum SelectionDragState {
  987    /// State when no drag related activity is detected.
  988    None,
  989    /// State when the mouse is down on a selection that is about to be dragged.
  990    ReadyToDrag {
  991        selection: Selection<Anchor>,
  992        click_position: gpui::Point<Pixels>,
  993        mouse_down_time: Instant,
  994    },
  995    /// State when the mouse is dragging the selection in the editor.
  996    Dragging {
  997        selection: Selection<Anchor>,
  998        drop_cursor: Selection<Anchor>,
  999        hide_drop_cursor: bool,
 1000    },
 1001}
 1002
 1003enum ColumnarSelectionState {
 1004    FromMouse {
 1005        selection_tail: Anchor,
 1006        display_point: Option<DisplayPoint>,
 1007    },
 1008    FromSelection {
 1009        selection_tail: Anchor,
 1010    },
 1011}
 1012
 1013/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1014/// a breakpoint on them.
 1015#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1016struct PhantomBreakpointIndicator {
 1017    display_row: DisplayRow,
 1018    /// There's a small debounce between hovering over the line and showing the indicator.
 1019    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1020    is_active: bool,
 1021    collides_with_existing_breakpoint: bool,
 1022}
 1023
 1024/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1025/// in diff view mode.
 1026#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1027pub(crate) struct PhantomDiffReviewIndicator {
 1028    pub display_row: DisplayRow,
 1029    /// There's a small debounce between hovering over the line and showing the indicator.
 1030    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1031    pub is_active: bool,
 1032}
 1033
 1034/// Identifies a specific hunk in the diff buffer.
 1035/// Used as a key to group comments by their location.
 1036#[derive(Clone, Debug)]
 1037pub struct DiffHunkKey {
 1038    /// The file path (relative to worktree) this hunk belongs to.
 1039    pub file_path: Arc<util::rel_path::RelPath>,
 1040    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1041    pub hunk_start_anchor: Anchor,
 1042}
 1043
 1044/// A review comment stored locally before being sent to the Agent panel.
 1045#[derive(Clone)]
 1046pub struct StoredReviewComment {
 1047    /// Unique identifier for this comment (for edit/delete operations).
 1048    pub id: usize,
 1049    /// The comment text entered by the user.
 1050    pub comment: String,
 1051    /// The display row where this comment was added (within the hunk).
 1052    pub display_row: DisplayRow,
 1053    /// Anchors for the code range being reviewed.
 1054    pub anchor_range: Range<Anchor>,
 1055    /// Timestamp when the comment was created (for chronological ordering).
 1056    pub created_at: Instant,
 1057    /// Whether this comment is currently being edited inline.
 1058    pub is_editing: bool,
 1059}
 1060
 1061impl StoredReviewComment {
 1062    pub fn new(
 1063        id: usize,
 1064        comment: String,
 1065        display_row: DisplayRow,
 1066        anchor_range: Range<Anchor>,
 1067    ) -> Self {
 1068        Self {
 1069            id,
 1070            comment,
 1071            display_row,
 1072            anchor_range,
 1073            created_at: Instant::now(),
 1074            is_editing: false,
 1075        }
 1076    }
 1077}
 1078
 1079/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1080pub(crate) struct DiffReviewOverlay {
 1081    /// The display row where the overlay is anchored.
 1082    pub display_row: DisplayRow,
 1083    /// The block ID for the overlay.
 1084    pub block_id: CustomBlockId,
 1085    /// The editor entity for the review input.
 1086    pub prompt_editor: Entity<Editor>,
 1087    /// The hunk key this overlay belongs to.
 1088    pub hunk_key: DiffHunkKey,
 1089    /// Whether the comments section is expanded.
 1090    pub comments_expanded: bool,
 1091    /// Editors for comments currently being edited inline.
 1092    /// Key: comment ID, Value: Editor entity for inline editing.
 1093    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1094    /// Subscriptions for inline edit editors' action handlers.
 1095    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1096    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1097    /// The current user's avatar URI for display in comment rows.
 1098    pub user_avatar_uri: Option<SharedUri>,
 1099    /// Subscription to keep the action handler alive.
 1100    _subscription: Subscription,
 1101}
 1102
 1103/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1104///
 1105/// See the [module level documentation](self) for more information.
 1106pub struct Editor {
 1107    focus_handle: FocusHandle,
 1108    last_focused_descendant: Option<WeakFocusHandle>,
 1109    /// The text buffer being edited
 1110    buffer: Entity<MultiBuffer>,
 1111    /// Map of how text in the buffer should be displayed.
 1112    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1113    pub display_map: Entity<DisplayMap>,
 1114    placeholder_display_map: Option<Entity<DisplayMap>>,
 1115    pub selections: SelectionsCollection,
 1116    pub scroll_manager: ScrollManager,
 1117    /// When inline assist editors are linked, they all render cursors because
 1118    /// typing enters text into each of them, even the ones that aren't focused.
 1119    pub(crate) show_cursor_when_unfocused: bool,
 1120    columnar_selection_state: Option<ColumnarSelectionState>,
 1121    add_selections_state: Option<AddSelectionsState>,
 1122    select_next_state: Option<SelectNextState>,
 1123    select_prev_state: Option<SelectNextState>,
 1124    selection_history: SelectionHistory,
 1125    defer_selection_effects: bool,
 1126    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1127    autoclose_regions: Vec<AutocloseRegion>,
 1128    snippet_stack: InvalidationStack<SnippetState>,
 1129    select_syntax_node_history: SelectSyntaxNodeHistory,
 1130    ime_transaction: Option<TransactionId>,
 1131    pub diagnostics_max_severity: DiagnosticSeverity,
 1132    active_diagnostics: ActiveDiagnostic,
 1133    show_inline_diagnostics: bool,
 1134    inline_diagnostics_update: Task<()>,
 1135    inline_diagnostics_enabled: bool,
 1136    diagnostics_enabled: bool,
 1137    word_completions_enabled: bool,
 1138    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1139    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1140    hard_wrap: Option<usize>,
 1141    project: Option<Entity<Project>>,
 1142    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1143    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1144    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1145    blink_manager: Entity<BlinkManager>,
 1146    show_cursor_names: bool,
 1147    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1148    pub show_local_selections: bool,
 1149    mode: EditorMode,
 1150    show_breadcrumbs: bool,
 1151    show_gutter: bool,
 1152    show_scrollbars: ScrollbarAxes,
 1153    minimap_visibility: MinimapVisibility,
 1154    offset_content: bool,
 1155    disable_expand_excerpt_buttons: bool,
 1156    delegate_expand_excerpts: bool,
 1157    show_line_numbers: Option<bool>,
 1158    use_relative_line_numbers: Option<bool>,
 1159    show_git_diff_gutter: Option<bool>,
 1160    show_code_actions: Option<bool>,
 1161    show_runnables: Option<bool>,
 1162    show_breakpoints: Option<bool>,
 1163    show_diff_review_button: bool,
 1164    show_wrap_guides: Option<bool>,
 1165    show_indent_guides: Option<bool>,
 1166    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1167    highlight_order: usize,
 1168    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1169    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1170    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1171    scrollbar_marker_state: ScrollbarMarkerState,
 1172    active_indent_guides_state: ActiveIndentGuidesState,
 1173    nav_history: Option<ItemNavHistory>,
 1174    context_menu: RefCell<Option<CodeContextMenu>>,
 1175    context_menu_options: Option<ContextMenuOptions>,
 1176    mouse_context_menu: Option<MouseContextMenu>,
 1177    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1178    inline_blame_popover: Option<InlineBlamePopover>,
 1179    inline_blame_popover_show_task: Option<Task<()>>,
 1180    signature_help_state: SignatureHelpState,
 1181    auto_signature_help: Option<bool>,
 1182    find_all_references_task_sources: Vec<Anchor>,
 1183    next_completion_id: CompletionId,
 1184    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1185    code_actions_task: Option<Task<Result<()>>>,
 1186    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1187    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1188    debounced_selection_highlight_complete: bool,
 1189    document_highlights_task: Option<Task<()>>,
 1190    linked_editing_range_task: Option<Task<Option<()>>>,
 1191    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1192    pending_rename: Option<RenameState>,
 1193    searchable: bool,
 1194    cursor_shape: CursorShape,
 1195    /// Whether the cursor is offset one character to the left when something is
 1196    /// selected (needed for vim visual mode)
 1197    cursor_offset_on_selection: bool,
 1198    current_line_highlight: Option<CurrentLineHighlight>,
 1199    pub collapse_matches: bool,
 1200    autoindent_mode: Option<AutoindentMode>,
 1201    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1202    input_enabled: bool,
 1203    use_modal_editing: bool,
 1204    read_only: bool,
 1205    leader_id: Option<CollaboratorId>,
 1206    remote_id: Option<ViewId>,
 1207    pub hover_state: HoverState,
 1208    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1209    prev_pressure_stage: Option<PressureStage>,
 1210    gutter_hovered: bool,
 1211    hovered_link_state: Option<HoveredLinkState>,
 1212    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1213    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1214    active_edit_prediction: Option<EditPredictionState>,
 1215    /// Used to prevent flickering as the user types while the menu is open
 1216    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1217    edit_prediction_settings: EditPredictionSettings,
 1218    edit_predictions_hidden_for_vim_mode: bool,
 1219    show_edit_predictions_override: Option<bool>,
 1220    show_completions_on_input_override: Option<bool>,
 1221    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1222    edit_prediction_preview: EditPredictionPreview,
 1223    edit_prediction_indent_conflict: bool,
 1224    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1225    next_inlay_id: usize,
 1226    next_color_inlay_id: usize,
 1227    _subscriptions: Vec<Subscription>,
 1228    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1229    gutter_dimensions: GutterDimensions,
 1230    style: Option<EditorStyle>,
 1231    text_style_refinement: Option<TextStyleRefinement>,
 1232    next_editor_action_id: EditorActionId,
 1233    editor_actions: Rc<
 1234        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1235    >,
 1236    use_autoclose: bool,
 1237    use_auto_surround: bool,
 1238    auto_replace_emoji_shortcode: bool,
 1239    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1240    show_git_blame_gutter: bool,
 1241    show_git_blame_inline: bool,
 1242    show_git_blame_inline_delay_task: Option<Task<()>>,
 1243    git_blame_inline_enabled: bool,
 1244    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1245    buffer_serialization: Option<BufferSerialization>,
 1246    show_selection_menu: Option<bool>,
 1247    blame: Option<Entity<GitBlame>>,
 1248    blame_subscription: Option<Subscription>,
 1249    custom_context_menu: Option<
 1250        Box<
 1251            dyn 'static
 1252                + Fn(
 1253                    &mut Self,
 1254                    DisplayPoint,
 1255                    &mut Window,
 1256                    &mut Context<Self>,
 1257                ) -> Option<Entity<ui::ContextMenu>>,
 1258        >,
 1259    >,
 1260    last_bounds: Option<Bounds<Pixels>>,
 1261    last_position_map: Option<Rc<PositionMap>>,
 1262    expect_bounds_change: Option<Bounds<Pixels>>,
 1263    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1264    tasks_update_task: Option<Task<()>>,
 1265    breakpoint_store: Option<Entity<BreakpointStore>>,
 1266    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1267    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1268    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1269    /// when hunks have comments stored.
 1270    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1271    /// Stored review comments grouped by hunk.
 1272    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1273    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1274    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1275    /// Counter for generating unique comment IDs.
 1276    next_review_comment_id: usize,
 1277    hovered_diff_hunk_row: Option<DisplayRow>,
 1278    pull_diagnostics_task: Task<()>,
 1279    pull_diagnostics_background_task: Task<()>,
 1280    in_project_search: bool,
 1281    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1282    breadcrumb_header: Option<String>,
 1283    focused_block: Option<FocusedBlock>,
 1284    next_scroll_position: NextScrollCursorCenterTopBottom,
 1285    addons: HashMap<TypeId, Box<dyn Addon>>,
 1286    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1287    load_diff_task: Option<Shared<Task<()>>>,
 1288    /// Whether we are temporarily displaying a diff other than git's
 1289    temporary_diff_override: bool,
 1290    selection_mark_mode: bool,
 1291    toggle_fold_multiple_buffers: Task<()>,
 1292    _scroll_cursor_center_top_bottom_task: Task<()>,
 1293    serialize_selections: Task<()>,
 1294    serialize_folds: Task<()>,
 1295    mouse_cursor_hidden: bool,
 1296    minimap: Option<Entity<Self>>,
 1297    hide_mouse_mode: HideMouseMode,
 1298    pub change_list: ChangeList,
 1299    inline_value_cache: InlineValueCache,
 1300    number_deleted_lines: bool,
 1301
 1302    selection_drag_state: SelectionDragState,
 1303    colors: Option<LspColorData>,
 1304    post_scroll_update: Task<()>,
 1305    refresh_colors_task: Task<()>,
 1306    inlay_hints: Option<LspInlayHintData>,
 1307    folding_newlines: Task<()>,
 1308    select_next_is_case_sensitive: Option<bool>,
 1309    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1310    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1311    accent_data: Option<AccentData>,
 1312    fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1313}
 1314
 1315#[derive(Debug, PartialEq)]
 1316struct AccentData {
 1317    colors: AccentColors,
 1318    overrides: Vec<SharedString>,
 1319}
 1320
 1321fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1322    if debounce_ms > 0 {
 1323        Some(Duration::from_millis(debounce_ms))
 1324    } else {
 1325        None
 1326    }
 1327}
 1328
 1329#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1330enum NextScrollCursorCenterTopBottom {
 1331    #[default]
 1332    Center,
 1333    Top,
 1334    Bottom,
 1335}
 1336
 1337impl NextScrollCursorCenterTopBottom {
 1338    fn next(&self) -> Self {
 1339        match self {
 1340            Self::Center => Self::Top,
 1341            Self::Top => Self::Bottom,
 1342            Self::Bottom => Self::Center,
 1343        }
 1344    }
 1345}
 1346
 1347#[derive(Clone)]
 1348pub struct EditorSnapshot {
 1349    pub mode: EditorMode,
 1350    show_gutter: bool,
 1351    offset_content: bool,
 1352    show_line_numbers: Option<bool>,
 1353    number_deleted_lines: bool,
 1354    show_git_diff_gutter: Option<bool>,
 1355    show_code_actions: Option<bool>,
 1356    show_runnables: Option<bool>,
 1357    show_breakpoints: Option<bool>,
 1358    git_blame_gutter_max_author_length: Option<usize>,
 1359    pub display_snapshot: DisplaySnapshot,
 1360    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1361    is_focused: bool,
 1362    scroll_anchor: ScrollAnchor,
 1363    ongoing_scroll: OngoingScroll,
 1364    current_line_highlight: CurrentLineHighlight,
 1365    gutter_hovered: bool,
 1366}
 1367
 1368#[derive(Default, Debug, Clone, Copy)]
 1369pub struct GutterDimensions {
 1370    pub left_padding: Pixels,
 1371    pub right_padding: Pixels,
 1372    pub width: Pixels,
 1373    pub margin: Pixels,
 1374    pub git_blame_entries_width: Option<Pixels>,
 1375}
 1376
 1377impl GutterDimensions {
 1378    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1379        Self {
 1380            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1381            ..Default::default()
 1382        }
 1383    }
 1384
 1385    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1386        -cx.text_system().descent(font_id, font_size)
 1387    }
 1388    /// The full width of the space taken up by the gutter.
 1389    pub fn full_width(&self) -> Pixels {
 1390        self.margin + self.width
 1391    }
 1392
 1393    /// The width of the space reserved for the fold indicators,
 1394    /// use alongside 'justify_end' and `gutter_width` to
 1395    /// right align content with the line numbers
 1396    pub fn fold_area_width(&self) -> Pixels {
 1397        self.margin + self.right_padding
 1398    }
 1399}
 1400
 1401struct CharacterDimensions {
 1402    em_width: Pixels,
 1403    em_advance: Pixels,
 1404    line_height: Pixels,
 1405}
 1406
 1407#[derive(Debug)]
 1408pub struct RemoteSelection {
 1409    pub replica_id: ReplicaId,
 1410    pub selection: Selection<Anchor>,
 1411    pub cursor_shape: CursorShape,
 1412    pub collaborator_id: CollaboratorId,
 1413    pub line_mode: bool,
 1414    pub user_name: Option<SharedString>,
 1415    pub color: PlayerColor,
 1416}
 1417
 1418#[derive(Clone, Debug)]
 1419struct SelectionHistoryEntry {
 1420    selections: Arc<[Selection<Anchor>]>,
 1421    select_next_state: Option<SelectNextState>,
 1422    select_prev_state: Option<SelectNextState>,
 1423    add_selections_state: Option<AddSelectionsState>,
 1424}
 1425
 1426#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1427enum SelectionHistoryMode {
 1428    #[default]
 1429    Normal,
 1430    Undoing,
 1431    Redoing,
 1432    Skipping,
 1433}
 1434
 1435#[derive(Clone, PartialEq, Eq, Hash)]
 1436struct HoveredCursor {
 1437    replica_id: ReplicaId,
 1438    selection_id: usize,
 1439}
 1440
 1441#[derive(Debug)]
 1442/// SelectionEffects controls the side-effects of updating the selection.
 1443///
 1444/// The default behaviour does "what you mostly want":
 1445/// - it pushes to the nav history if the cursor moved by >10 lines
 1446/// - it re-triggers completion requests
 1447/// - it scrolls to fit
 1448///
 1449/// You might want to modify these behaviours. For example when doing a "jump"
 1450/// like go to definition, we always want to add to nav history; but when scrolling
 1451/// in vim mode we never do.
 1452///
 1453/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1454/// move.
 1455#[derive(Clone)]
 1456pub struct SelectionEffects {
 1457    nav_history: Option<bool>,
 1458    completions: bool,
 1459    scroll: Option<Autoscroll>,
 1460}
 1461
 1462impl Default for SelectionEffects {
 1463    fn default() -> Self {
 1464        Self {
 1465            nav_history: None,
 1466            completions: true,
 1467            scroll: Some(Autoscroll::fit()),
 1468        }
 1469    }
 1470}
 1471impl SelectionEffects {
 1472    pub fn scroll(scroll: Autoscroll) -> Self {
 1473        Self {
 1474            scroll: Some(scroll),
 1475            ..Default::default()
 1476        }
 1477    }
 1478
 1479    pub fn no_scroll() -> Self {
 1480        Self {
 1481            scroll: None,
 1482            ..Default::default()
 1483        }
 1484    }
 1485
 1486    pub fn completions(self, completions: bool) -> Self {
 1487        Self {
 1488            completions,
 1489            ..self
 1490        }
 1491    }
 1492
 1493    pub fn nav_history(self, nav_history: bool) -> Self {
 1494        Self {
 1495            nav_history: Some(nav_history),
 1496            ..self
 1497        }
 1498    }
 1499}
 1500
 1501struct DeferredSelectionEffectsState {
 1502    changed: bool,
 1503    effects: SelectionEffects,
 1504    old_cursor_position: Anchor,
 1505    history_entry: SelectionHistoryEntry,
 1506}
 1507
 1508#[derive(Default)]
 1509struct SelectionHistory {
 1510    #[allow(clippy::type_complexity)]
 1511    selections_by_transaction:
 1512        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1513    mode: SelectionHistoryMode,
 1514    undo_stack: VecDeque<SelectionHistoryEntry>,
 1515    redo_stack: VecDeque<SelectionHistoryEntry>,
 1516}
 1517
 1518impl SelectionHistory {
 1519    #[track_caller]
 1520    fn insert_transaction(
 1521        &mut self,
 1522        transaction_id: TransactionId,
 1523        selections: Arc<[Selection<Anchor>]>,
 1524    ) {
 1525        if selections.is_empty() {
 1526            log::error!(
 1527                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1528                std::panic::Location::caller()
 1529            );
 1530            return;
 1531        }
 1532        self.selections_by_transaction
 1533            .insert(transaction_id, (selections, None));
 1534    }
 1535
 1536    #[allow(clippy::type_complexity)]
 1537    fn transaction(
 1538        &self,
 1539        transaction_id: TransactionId,
 1540    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1541        self.selections_by_transaction.get(&transaction_id)
 1542    }
 1543
 1544    #[allow(clippy::type_complexity)]
 1545    fn transaction_mut(
 1546        &mut self,
 1547        transaction_id: TransactionId,
 1548    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1549        self.selections_by_transaction.get_mut(&transaction_id)
 1550    }
 1551
 1552    fn push(&mut self, entry: SelectionHistoryEntry) {
 1553        if !entry.selections.is_empty() {
 1554            match self.mode {
 1555                SelectionHistoryMode::Normal => {
 1556                    self.push_undo(entry);
 1557                    self.redo_stack.clear();
 1558                }
 1559                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1560                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1561                SelectionHistoryMode::Skipping => {}
 1562            }
 1563        }
 1564    }
 1565
 1566    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1567        if self
 1568            .undo_stack
 1569            .back()
 1570            .is_none_or(|e| e.selections != entry.selections)
 1571        {
 1572            self.undo_stack.push_back(entry);
 1573            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1574                self.undo_stack.pop_front();
 1575            }
 1576        }
 1577    }
 1578
 1579    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1580        if self
 1581            .redo_stack
 1582            .back()
 1583            .is_none_or(|e| e.selections != entry.selections)
 1584        {
 1585            self.redo_stack.push_back(entry);
 1586            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1587                self.redo_stack.pop_front();
 1588            }
 1589        }
 1590    }
 1591}
 1592
 1593#[derive(Clone, Copy)]
 1594pub struct RowHighlightOptions {
 1595    pub autoscroll: bool,
 1596    pub include_gutter: bool,
 1597}
 1598
 1599impl Default for RowHighlightOptions {
 1600    fn default() -> Self {
 1601        Self {
 1602            autoscroll: Default::default(),
 1603            include_gutter: true,
 1604        }
 1605    }
 1606}
 1607
 1608struct RowHighlight {
 1609    index: usize,
 1610    range: Range<Anchor>,
 1611    color: Hsla,
 1612    options: RowHighlightOptions,
 1613    type_id: TypeId,
 1614}
 1615
 1616#[derive(Clone, Debug)]
 1617struct AddSelectionsState {
 1618    groups: Vec<AddSelectionsGroup>,
 1619}
 1620
 1621#[derive(Clone, Debug)]
 1622struct AddSelectionsGroup {
 1623    above: bool,
 1624    stack: Vec<usize>,
 1625}
 1626
 1627#[derive(Clone)]
 1628struct SelectNextState {
 1629    query: AhoCorasick,
 1630    wordwise: bool,
 1631    done: bool,
 1632}
 1633
 1634impl std::fmt::Debug for SelectNextState {
 1635    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1636        f.debug_struct(std::any::type_name::<Self>())
 1637            .field("wordwise", &self.wordwise)
 1638            .field("done", &self.done)
 1639            .finish()
 1640    }
 1641}
 1642
 1643#[derive(Debug)]
 1644struct AutocloseRegion {
 1645    selection_id: usize,
 1646    range: Range<Anchor>,
 1647    pair: BracketPair,
 1648}
 1649
 1650#[derive(Debug)]
 1651struct SnippetState {
 1652    ranges: Vec<Vec<Range<Anchor>>>,
 1653    active_index: usize,
 1654    choices: Vec<Option<Vec<String>>>,
 1655}
 1656
 1657#[doc(hidden)]
 1658pub struct RenameState {
 1659    pub range: Range<Anchor>,
 1660    pub old_name: Arc<str>,
 1661    pub editor: Entity<Editor>,
 1662    block_id: CustomBlockId,
 1663}
 1664
 1665struct InvalidationStack<T>(Vec<T>);
 1666
 1667struct RegisteredEditPredictionDelegate {
 1668    provider: Arc<dyn EditPredictionDelegateHandle>,
 1669    _subscription: Subscription,
 1670}
 1671
 1672#[derive(Debug, PartialEq, Eq)]
 1673pub struct ActiveDiagnosticGroup {
 1674    pub active_range: Range<Anchor>,
 1675    pub active_message: String,
 1676    pub group_id: usize,
 1677    pub blocks: HashSet<CustomBlockId>,
 1678}
 1679
 1680#[derive(Debug, PartialEq, Eq)]
 1681
 1682pub(crate) enum ActiveDiagnostic {
 1683    None,
 1684    All,
 1685    Group(ActiveDiagnosticGroup),
 1686}
 1687
 1688#[derive(Serialize, Deserialize, Clone, Debug)]
 1689pub struct ClipboardSelection {
 1690    /// The number of bytes in this selection.
 1691    pub len: usize,
 1692    /// Whether this was a full-line selection.
 1693    pub is_entire_line: bool,
 1694    /// The indentation of the first line when this content was originally copied.
 1695    pub first_line_indent: u32,
 1696    #[serde(default)]
 1697    pub file_path: Option<PathBuf>,
 1698    #[serde(default)]
 1699    pub line_range: Option<RangeInclusive<u32>>,
 1700}
 1701
 1702impl ClipboardSelection {
 1703    pub fn for_buffer(
 1704        len: usize,
 1705        is_entire_line: bool,
 1706        range: Range<Point>,
 1707        buffer: &MultiBufferSnapshot,
 1708        project: Option<&Entity<Project>>,
 1709        cx: &App,
 1710    ) -> Self {
 1711        let first_line_indent = buffer
 1712            .indent_size_for_line(MultiBufferRow(range.start.row))
 1713            .len;
 1714
 1715        let file_path = util::maybe!({
 1716            let project = project?.read(cx);
 1717            let file = buffer.file_at(range.start)?;
 1718            let project_path = ProjectPath {
 1719                worktree_id: file.worktree_id(cx),
 1720                path: file.path().clone(),
 1721            };
 1722            project.absolute_path(&project_path, cx)
 1723        });
 1724
 1725        let line_range = file_path.as_ref().and_then(|_| {
 1726            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1727            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1728            if start_excerpt_id == end_excerpt_id {
 1729                Some(start_point.row..=end_point.row)
 1730            } else {
 1731                None
 1732            }
 1733        });
 1734
 1735        Self {
 1736            len,
 1737            is_entire_line,
 1738            first_line_indent,
 1739            file_path,
 1740            line_range,
 1741        }
 1742    }
 1743}
 1744
 1745// selections, scroll behavior, was newest selection reversed
 1746type SelectSyntaxNodeHistoryState = (
 1747    Box<[Selection<MultiBufferOffset>]>,
 1748    SelectSyntaxNodeScrollBehavior,
 1749    bool,
 1750);
 1751
 1752#[derive(Default)]
 1753struct SelectSyntaxNodeHistory {
 1754    stack: Vec<SelectSyntaxNodeHistoryState>,
 1755    // disable temporarily to allow changing selections without losing the stack
 1756    pub disable_clearing: bool,
 1757}
 1758
 1759impl SelectSyntaxNodeHistory {
 1760    pub fn try_clear(&mut self) {
 1761        if !self.disable_clearing {
 1762            self.stack.clear();
 1763        }
 1764    }
 1765
 1766    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1767        self.stack.push(selection);
 1768    }
 1769
 1770    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1771        self.stack.pop()
 1772    }
 1773}
 1774
 1775enum SelectSyntaxNodeScrollBehavior {
 1776    CursorTop,
 1777    FitSelection,
 1778    CursorBottom,
 1779}
 1780
 1781#[derive(Debug)]
 1782pub(crate) struct NavigationData {
 1783    cursor_anchor: Anchor,
 1784    cursor_position: Point,
 1785    scroll_anchor: ScrollAnchor,
 1786    scroll_top_row: u32,
 1787}
 1788
 1789#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1790pub enum GotoDefinitionKind {
 1791    Symbol,
 1792    Declaration,
 1793    Type,
 1794    Implementation,
 1795}
 1796
 1797pub enum FormatTarget {
 1798    Buffers(HashSet<Entity<Buffer>>),
 1799    Ranges(Vec<Range<MultiBufferPoint>>),
 1800}
 1801
 1802pub(crate) struct FocusedBlock {
 1803    id: BlockId,
 1804    focus_handle: WeakFocusHandle,
 1805}
 1806
 1807#[derive(Clone, Debug)]
 1808enum JumpData {
 1809    MultiBufferRow {
 1810        row: MultiBufferRow,
 1811        line_offset_from_top: u32,
 1812    },
 1813    MultiBufferPoint {
 1814        excerpt_id: ExcerptId,
 1815        position: Point,
 1816        anchor: text::Anchor,
 1817        line_offset_from_top: u32,
 1818    },
 1819}
 1820
 1821pub enum MultibufferSelectionMode {
 1822    First,
 1823    All,
 1824}
 1825
 1826#[derive(Clone, Copy, Debug, Default)]
 1827pub struct RewrapOptions {
 1828    pub override_language_settings: bool,
 1829    pub preserve_existing_whitespace: bool,
 1830}
 1831
 1832impl Editor {
 1833    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1834        let buffer = cx.new(|cx| Buffer::local("", cx));
 1835        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1836        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1837    }
 1838
 1839    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1840        let buffer = cx.new(|cx| Buffer::local("", cx));
 1841        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1842        Self::new(EditorMode::full(), buffer, None, window, cx)
 1843    }
 1844
 1845    pub fn auto_height(
 1846        min_lines: usize,
 1847        max_lines: usize,
 1848        window: &mut Window,
 1849        cx: &mut Context<Self>,
 1850    ) -> Self {
 1851        let buffer = cx.new(|cx| Buffer::local("", cx));
 1852        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1853        Self::new(
 1854            EditorMode::AutoHeight {
 1855                min_lines,
 1856                max_lines: Some(max_lines),
 1857            },
 1858            buffer,
 1859            None,
 1860            window,
 1861            cx,
 1862        )
 1863    }
 1864
 1865    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1866    /// The editor grows as tall as needed to fit its content.
 1867    pub fn auto_height_unbounded(
 1868        min_lines: usize,
 1869        window: &mut Window,
 1870        cx: &mut Context<Self>,
 1871    ) -> Self {
 1872        let buffer = cx.new(|cx| Buffer::local("", cx));
 1873        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1874        Self::new(
 1875            EditorMode::AutoHeight {
 1876                min_lines,
 1877                max_lines: None,
 1878            },
 1879            buffer,
 1880            None,
 1881            window,
 1882            cx,
 1883        )
 1884    }
 1885
 1886    pub fn for_buffer(
 1887        buffer: Entity<Buffer>,
 1888        project: Option<Entity<Project>>,
 1889        window: &mut Window,
 1890        cx: &mut Context<Self>,
 1891    ) -> Self {
 1892        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1893        Self::new(EditorMode::full(), buffer, project, window, cx)
 1894    }
 1895
 1896    pub fn for_multibuffer(
 1897        buffer: Entity<MultiBuffer>,
 1898        project: Option<Entity<Project>>,
 1899        window: &mut Window,
 1900        cx: &mut Context<Self>,
 1901    ) -> Self {
 1902        Self::new(EditorMode::full(), buffer, project, window, cx)
 1903    }
 1904
 1905    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1906        let mut clone = Self::new(
 1907            self.mode.clone(),
 1908            self.buffer.clone(),
 1909            self.project.clone(),
 1910            window,
 1911            cx,
 1912        );
 1913        self.display_map.update(cx, |display_map, cx| {
 1914            let snapshot = display_map.snapshot(cx);
 1915            clone.display_map.update(cx, |display_map, cx| {
 1916                display_map.set_state(&snapshot, cx);
 1917            });
 1918        });
 1919        clone.folds_did_change(cx);
 1920        clone.selections.clone_state(&self.selections);
 1921        clone.scroll_manager.clone_state(&self.scroll_manager);
 1922        clone.searchable = self.searchable;
 1923        clone.read_only = self.read_only;
 1924        clone
 1925    }
 1926
 1927    pub fn new(
 1928        mode: EditorMode,
 1929        buffer: Entity<MultiBuffer>,
 1930        project: Option<Entity<Project>>,
 1931        window: &mut Window,
 1932        cx: &mut Context<Self>,
 1933    ) -> Self {
 1934        Editor::new_internal(mode, buffer, project, None, window, cx)
 1935    }
 1936
 1937    pub fn sticky_headers(
 1938        &self,
 1939        style: &EditorStyle,
 1940        cx: &App,
 1941    ) -> Option<Vec<OutlineItem<Anchor>>> {
 1942        let multi_buffer = self.buffer().read(cx);
 1943        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 1944        let multi_buffer_visible_start = self
 1945            .scroll_manager
 1946            .anchor()
 1947            .anchor
 1948            .to_point(&multi_buffer_snapshot);
 1949        let max_row = multi_buffer_snapshot.max_point().row;
 1950
 1951        let start_row = (multi_buffer_visible_start.row).min(max_row);
 1952        let end_row = (multi_buffer_visible_start.row + 10).min(max_row);
 1953
 1954        if let Some((excerpt_id, _, buffer)) = multi_buffer.read(cx).as_singleton() {
 1955            let outline_items = buffer
 1956                .outline_items_containing(
 1957                    Point::new(start_row, 0)..Point::new(end_row, 0),
 1958                    true,
 1959                    Some(style.syntax.as_ref()),
 1960                )
 1961                .into_iter()
 1962                .map(|outline_item| OutlineItem {
 1963                    depth: outline_item.depth,
 1964                    range: Anchor::range_in_buffer(*excerpt_id, outline_item.range),
 1965                    source_range_for_text: Anchor::range_in_buffer(
 1966                        *excerpt_id,
 1967                        outline_item.source_range_for_text,
 1968                    ),
 1969                    text: outline_item.text,
 1970                    highlight_ranges: outline_item.highlight_ranges,
 1971                    name_ranges: outline_item.name_ranges,
 1972                    body_range: outline_item
 1973                        .body_range
 1974                        .map(|range| Anchor::range_in_buffer(*excerpt_id, range)),
 1975                    annotation_range: outline_item
 1976                        .annotation_range
 1977                        .map(|range| Anchor::range_in_buffer(*excerpt_id, range)),
 1978                });
 1979            return Some(outline_items.collect());
 1980        }
 1981
 1982        None
 1983    }
 1984
 1985    fn new_internal(
 1986        mode: EditorMode,
 1987        multi_buffer: Entity<MultiBuffer>,
 1988        project: Option<Entity<Project>>,
 1989        display_map: Option<Entity<DisplayMap>>,
 1990        window: &mut Window,
 1991        cx: &mut Context<Self>,
 1992    ) -> Self {
 1993        debug_assert!(
 1994            display_map.is_none() || mode.is_minimap(),
 1995            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 1996        );
 1997
 1998        let full_mode = mode.is_full();
 1999        let is_minimap = mode.is_minimap();
 2000        let diagnostics_max_severity = if full_mode {
 2001            EditorSettings::get_global(cx)
 2002                .diagnostics_max_severity
 2003                .unwrap_or(DiagnosticSeverity::Hint)
 2004        } else {
 2005            DiagnosticSeverity::Off
 2006        };
 2007        let style = window.text_style();
 2008        let font_size = style.font_size.to_pixels(window.rem_size());
 2009        let editor = cx.entity().downgrade();
 2010        let fold_placeholder = FoldPlaceholder {
 2011            constrain_width: false,
 2012            render: Arc::new(move |fold_id, fold_range, cx| {
 2013                let editor = editor.clone();
 2014                div()
 2015                    .id(fold_id)
 2016                    .bg(cx.theme().colors().ghost_element_background)
 2017                    .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
 2018                    .active(|style| style.bg(cx.theme().colors().ghost_element_active))
 2019                    .rounded_xs()
 2020                    .size_full()
 2021                    .cursor_pointer()
 2022                    .child("")
 2023                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2024                    .on_click(move |_, _window, cx| {
 2025                        editor
 2026                            .update(cx, |editor, cx| {
 2027                                editor.unfold_ranges(
 2028                                    &[fold_range.start..fold_range.end],
 2029                                    true,
 2030                                    false,
 2031                                    cx,
 2032                                );
 2033                                cx.stop_propagation();
 2034                            })
 2035                            .ok();
 2036                    })
 2037                    .into_any()
 2038            }),
 2039            merge_adjacent: true,
 2040            ..FoldPlaceholder::default()
 2041        };
 2042        let display_map = display_map.unwrap_or_else(|| {
 2043            cx.new(|cx| {
 2044                DisplayMap::new(
 2045                    multi_buffer.clone(),
 2046                    style.font(),
 2047                    font_size,
 2048                    None,
 2049                    FILE_HEADER_HEIGHT,
 2050                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2051                    fold_placeholder,
 2052                    diagnostics_max_severity,
 2053                    cx,
 2054                )
 2055            })
 2056        });
 2057
 2058        let selections = SelectionsCollection::new();
 2059
 2060        let blink_manager = cx.new(|cx| {
 2061            let mut blink_manager = BlinkManager::new(
 2062                CURSOR_BLINK_INTERVAL,
 2063                |cx| EditorSettings::get_global(cx).cursor_blink,
 2064                cx,
 2065            );
 2066            if is_minimap {
 2067                blink_manager.disable(cx);
 2068            }
 2069            blink_manager
 2070        });
 2071
 2072        let soft_wrap_mode_override =
 2073            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2074
 2075        let mut project_subscriptions = Vec::new();
 2076        if full_mode && let Some(project) = project.as_ref() {
 2077            project_subscriptions.push(cx.subscribe_in(
 2078                project,
 2079                window,
 2080                |editor, _, event, window, cx| match event {
 2081                    project::Event::RefreshCodeLens => {
 2082                        // we always query lens with actions, without storing them, always refreshing them
 2083                    }
 2084                    project::Event::RefreshInlayHints {
 2085                        server_id,
 2086                        request_id,
 2087                    } => {
 2088                        editor.refresh_inlay_hints(
 2089                            InlayHintRefreshReason::RefreshRequested {
 2090                                server_id: *server_id,
 2091                                request_id: *request_id,
 2092                            },
 2093                            cx,
 2094                        );
 2095                    }
 2096                    project::Event::LanguageServerRemoved(..) => {
 2097                        if editor.tasks_update_task.is_none() {
 2098                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2099                        }
 2100                        editor.registered_buffers.clear();
 2101                        editor.register_visible_buffers(cx);
 2102                    }
 2103                    project::Event::LanguageServerAdded(..) => {
 2104                        if editor.tasks_update_task.is_none() {
 2105                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2106                        }
 2107                    }
 2108                    project::Event::SnippetEdit(id, snippet_edits) => {
 2109                        // todo(lw): Non singletons
 2110                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2111                            let snapshot = buffer.read(cx).snapshot();
 2112                            let focus_handle = editor.focus_handle(cx);
 2113                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2114                                for (range, snippet) in snippet_edits {
 2115                                    let buffer_range =
 2116                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2117                                    editor
 2118                                        .insert_snippet(
 2119                                            &[MultiBufferOffset(buffer_range.start)
 2120                                                ..MultiBufferOffset(buffer_range.end)],
 2121                                            snippet.clone(),
 2122                                            window,
 2123                                            cx,
 2124                                        )
 2125                                        .ok();
 2126                                }
 2127                            }
 2128                        }
 2129                    }
 2130                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2131                        let buffer_id = *buffer_id;
 2132                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2133                            editor.register_buffer(buffer_id, cx);
 2134                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2135                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2136                            refresh_linked_ranges(editor, window, cx);
 2137                            editor.refresh_code_actions(window, cx);
 2138                            editor.refresh_document_highlights(cx);
 2139                        }
 2140                    }
 2141
 2142                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2143                        let Some(workspace) = editor.workspace() else {
 2144                            return;
 2145                        };
 2146                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2147                        else {
 2148                            return;
 2149                        };
 2150
 2151                        if active_editor.entity_id() == cx.entity_id() {
 2152                            let entity_id = cx.entity_id();
 2153                            workspace.update(cx, |this, cx| {
 2154                                this.panes_mut()
 2155                                    .iter_mut()
 2156                                    .filter(|pane| pane.entity_id() != entity_id)
 2157                                    .for_each(|p| {
 2158                                        p.update(cx, |pane, _| {
 2159                                            pane.nav_history_mut().rename_item(
 2160                                                entity_id,
 2161                                                project_path.clone(),
 2162                                                abs_path.clone().into(),
 2163                                            );
 2164                                        })
 2165                                    });
 2166                            });
 2167
 2168                            Self::open_transaction_for_hidden_buffers(
 2169                                workspace,
 2170                                transaction.clone(),
 2171                                "Rename".to_string(),
 2172                                window,
 2173                                cx,
 2174                            );
 2175                        }
 2176                    }
 2177
 2178                    project::Event::WorkspaceEditApplied(transaction) => {
 2179                        let Some(workspace) = editor.workspace() else {
 2180                            return;
 2181                        };
 2182                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2183                        else {
 2184                            return;
 2185                        };
 2186
 2187                        if active_editor.entity_id() == cx.entity_id() {
 2188                            Self::open_transaction_for_hidden_buffers(
 2189                                workspace,
 2190                                transaction.clone(),
 2191                                "LSP Edit".to_string(),
 2192                                window,
 2193                                cx,
 2194                            );
 2195                        }
 2196                    }
 2197
 2198                    _ => {}
 2199                },
 2200            ));
 2201            if let Some(task_inventory) = project
 2202                .read(cx)
 2203                .task_store()
 2204                .read(cx)
 2205                .task_inventory()
 2206                .cloned()
 2207            {
 2208                project_subscriptions.push(cx.observe_in(
 2209                    &task_inventory,
 2210                    window,
 2211                    |editor, _, window, cx| {
 2212                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2213                    },
 2214                ));
 2215            };
 2216
 2217            project_subscriptions.push(cx.subscribe_in(
 2218                &project.read(cx).breakpoint_store(),
 2219                window,
 2220                |editor, _, event, window, cx| match event {
 2221                    BreakpointStoreEvent::ClearDebugLines => {
 2222                        editor.clear_row_highlights::<ActiveDebugLine>();
 2223                        editor.refresh_inline_values(cx);
 2224                    }
 2225                    BreakpointStoreEvent::SetDebugLine => {
 2226                        if editor.go_to_active_debug_line(window, cx) {
 2227                            cx.stop_propagation();
 2228                        }
 2229
 2230                        editor.refresh_inline_values(cx);
 2231                    }
 2232                    _ => {}
 2233                },
 2234            ));
 2235            let git_store = project.read(cx).git_store().clone();
 2236            let project = project.clone();
 2237            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2238                if let GitStoreEvent::RepositoryAdded = event {
 2239                    this.load_diff_task = Some(
 2240                        update_uncommitted_diff_for_buffer(
 2241                            cx.entity(),
 2242                            &project,
 2243                            this.buffer.read(cx).all_buffers(),
 2244                            this.buffer.clone(),
 2245                            cx,
 2246                        )
 2247                        .shared(),
 2248                    );
 2249                }
 2250            }));
 2251        }
 2252
 2253        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2254
 2255        let inlay_hint_settings =
 2256            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2257        let focus_handle = cx.focus_handle();
 2258        if !is_minimap {
 2259            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2260                .detach();
 2261            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2262                .detach();
 2263            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2264                .detach();
 2265            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2266                .detach();
 2267            cx.observe_pending_input(window, Self::observe_pending_input)
 2268                .detach();
 2269        }
 2270
 2271        let show_indent_guides =
 2272            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2273                Some(false)
 2274            } else {
 2275                None
 2276            };
 2277
 2278        let breakpoint_store = match (&mode, project.as_ref()) {
 2279            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2280            _ => None,
 2281        };
 2282
 2283        let mut code_action_providers = Vec::new();
 2284        let mut load_uncommitted_diff = None;
 2285        if let Some(project) = project.clone() {
 2286            load_uncommitted_diff = Some(
 2287                update_uncommitted_diff_for_buffer(
 2288                    cx.entity(),
 2289                    &project,
 2290                    multi_buffer.read(cx).all_buffers(),
 2291                    multi_buffer.clone(),
 2292                    cx,
 2293                )
 2294                .shared(),
 2295            );
 2296            code_action_providers.push(Rc::new(project) as Rc<_>);
 2297        }
 2298
 2299        let mut editor = Self {
 2300            focus_handle,
 2301            show_cursor_when_unfocused: false,
 2302            last_focused_descendant: None,
 2303            buffer: multi_buffer.clone(),
 2304            display_map: display_map.clone(),
 2305            placeholder_display_map: None,
 2306            selections,
 2307            scroll_manager: ScrollManager::new(cx),
 2308            columnar_selection_state: None,
 2309            add_selections_state: None,
 2310            select_next_state: None,
 2311            select_prev_state: None,
 2312            selection_history: SelectionHistory::default(),
 2313            defer_selection_effects: false,
 2314            deferred_selection_effects_state: None,
 2315            autoclose_regions: Vec::new(),
 2316            snippet_stack: InvalidationStack::default(),
 2317            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2318            ime_transaction: None,
 2319            active_diagnostics: ActiveDiagnostic::None,
 2320            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2321            inline_diagnostics_update: Task::ready(()),
 2322            inline_diagnostics: Vec::new(),
 2323            soft_wrap_mode_override,
 2324            diagnostics_max_severity,
 2325            hard_wrap: None,
 2326            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2327            semantics_provider: project.clone().map(|project| Rc::new(project) as _),
 2328            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2329            project,
 2330            blink_manager: blink_manager.clone(),
 2331            show_local_selections: true,
 2332            show_scrollbars: ScrollbarAxes {
 2333                horizontal: full_mode,
 2334                vertical: full_mode,
 2335            },
 2336            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2337            offset_content: !matches!(mode, EditorMode::SingleLine),
 2338            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2339            show_gutter: full_mode,
 2340            show_line_numbers: (!full_mode).then_some(false),
 2341            use_relative_line_numbers: None,
 2342            disable_expand_excerpt_buttons: !full_mode,
 2343            delegate_expand_excerpts: false,
 2344            show_git_diff_gutter: None,
 2345            show_code_actions: None,
 2346            show_runnables: None,
 2347            show_breakpoints: None,
 2348            show_diff_review_button: false,
 2349            show_wrap_guides: None,
 2350            show_indent_guides,
 2351            buffers_with_disabled_indent_guides: HashSet::default(),
 2352            highlight_order: 0,
 2353            highlighted_rows: HashMap::default(),
 2354            background_highlights: HashMap::default(),
 2355            gutter_highlights: HashMap::default(),
 2356            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2357            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2358            nav_history: None,
 2359            context_menu: RefCell::new(None),
 2360            context_menu_options: None,
 2361            mouse_context_menu: None,
 2362            completion_tasks: Vec::new(),
 2363            inline_blame_popover: None,
 2364            inline_blame_popover_show_task: None,
 2365            signature_help_state: SignatureHelpState::default(),
 2366            auto_signature_help: None,
 2367            find_all_references_task_sources: Vec::new(),
 2368            next_completion_id: 0,
 2369            next_inlay_id: 0,
 2370            code_action_providers,
 2371            available_code_actions: None,
 2372            code_actions_task: None,
 2373            quick_selection_highlight_task: None,
 2374            debounced_selection_highlight_task: None,
 2375            debounced_selection_highlight_complete: false,
 2376            document_highlights_task: None,
 2377            linked_editing_range_task: None,
 2378            pending_rename: None,
 2379            searchable: !is_minimap,
 2380            cursor_shape: EditorSettings::get_global(cx)
 2381                .cursor_shape
 2382                .unwrap_or_default(),
 2383            cursor_offset_on_selection: false,
 2384            current_line_highlight: None,
 2385            autoindent_mode: Some(AutoindentMode::EachLine),
 2386            collapse_matches: false,
 2387            workspace: None,
 2388            input_enabled: !is_minimap,
 2389            use_modal_editing: full_mode,
 2390            read_only: is_minimap,
 2391            use_autoclose: true,
 2392            use_auto_surround: true,
 2393            auto_replace_emoji_shortcode: false,
 2394            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2395            leader_id: None,
 2396            remote_id: None,
 2397            hover_state: HoverState::default(),
 2398            pending_mouse_down: None,
 2399            prev_pressure_stage: None,
 2400            hovered_link_state: None,
 2401            edit_prediction_provider: None,
 2402            active_edit_prediction: None,
 2403            stale_edit_prediction_in_menu: None,
 2404            edit_prediction_preview: EditPredictionPreview::Inactive {
 2405                released_too_fast: false,
 2406            },
 2407            inline_diagnostics_enabled: full_mode,
 2408            diagnostics_enabled: full_mode,
 2409            word_completions_enabled: full_mode,
 2410            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2411            gutter_hovered: false,
 2412            pixel_position_of_newest_cursor: None,
 2413            last_bounds: None,
 2414            last_position_map: None,
 2415            expect_bounds_change: None,
 2416            gutter_dimensions: GutterDimensions::default(),
 2417            style: None,
 2418            show_cursor_names: false,
 2419            hovered_cursors: HashMap::default(),
 2420            next_editor_action_id: EditorActionId::default(),
 2421            editor_actions: Rc::default(),
 2422            edit_predictions_hidden_for_vim_mode: false,
 2423            show_edit_predictions_override: None,
 2424            show_completions_on_input_override: None,
 2425            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2426            edit_prediction_settings: EditPredictionSettings::Disabled,
 2427            edit_prediction_indent_conflict: false,
 2428            edit_prediction_requires_modifier_in_indent_conflict: true,
 2429            custom_context_menu: None,
 2430            show_git_blame_gutter: false,
 2431            show_git_blame_inline: false,
 2432            show_selection_menu: None,
 2433            show_git_blame_inline_delay_task: None,
 2434            git_blame_inline_enabled: full_mode
 2435                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2436            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2437            buffer_serialization: is_minimap.not().then(|| {
 2438                BufferSerialization::new(
 2439                    ProjectSettings::get_global(cx)
 2440                        .session
 2441                        .restore_unsaved_buffers,
 2442                )
 2443            }),
 2444            blame: None,
 2445            blame_subscription: None,
 2446            tasks: BTreeMap::default(),
 2447
 2448            breakpoint_store,
 2449            gutter_breakpoint_indicator: (None, None),
 2450            gutter_diff_review_indicator: (None, None),
 2451            diff_review_overlays: Vec::new(),
 2452            stored_review_comments: Vec::new(),
 2453            next_review_comment_id: 0,
 2454            hovered_diff_hunk_row: None,
 2455            _subscriptions: (!is_minimap)
 2456                .then(|| {
 2457                    vec![
 2458                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2459                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2460                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2461                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2462                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2463                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2464                        cx.observe_window_activation(window, |editor, window, cx| {
 2465                            let active = window.is_window_active();
 2466                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2467                                if active {
 2468                                    blink_manager.enable(cx);
 2469                                } else {
 2470                                    blink_manager.disable(cx);
 2471                                }
 2472                            });
 2473                            if active {
 2474                                editor.show_mouse_cursor(cx);
 2475                            }
 2476                        }),
 2477                    ]
 2478                })
 2479                .unwrap_or_default(),
 2480            tasks_update_task: None,
 2481            pull_diagnostics_task: Task::ready(()),
 2482            pull_diagnostics_background_task: Task::ready(()),
 2483            colors: None,
 2484            refresh_colors_task: Task::ready(()),
 2485            inlay_hints: None,
 2486            next_color_inlay_id: 0,
 2487            post_scroll_update: Task::ready(()),
 2488            linked_edit_ranges: Default::default(),
 2489            in_project_search: false,
 2490            previous_search_ranges: None,
 2491            breadcrumb_header: None,
 2492            focused_block: None,
 2493            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2494            addons: HashMap::default(),
 2495            registered_buffers: HashMap::default(),
 2496            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2497            selection_mark_mode: false,
 2498            toggle_fold_multiple_buffers: Task::ready(()),
 2499            serialize_selections: Task::ready(()),
 2500            serialize_folds: Task::ready(()),
 2501            text_style_refinement: None,
 2502            load_diff_task: load_uncommitted_diff,
 2503            temporary_diff_override: false,
 2504            mouse_cursor_hidden: false,
 2505            minimap: None,
 2506            hide_mouse_mode: EditorSettings::get_global(cx)
 2507                .hide_mouse
 2508                .unwrap_or_default(),
 2509            change_list: ChangeList::new(),
 2510            mode,
 2511            selection_drag_state: SelectionDragState::None,
 2512            folding_newlines: Task::ready(()),
 2513            lookup_key: None,
 2514            select_next_is_case_sensitive: None,
 2515            applicable_language_settings: HashMap::default(),
 2516            accent_data: None,
 2517            fetched_tree_sitter_chunks: HashMap::default(),
 2518            number_deleted_lines: false,
 2519        };
 2520
 2521        if is_minimap {
 2522            return editor;
 2523        }
 2524
 2525        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2526        editor.accent_data = editor.fetch_accent_data(cx);
 2527
 2528        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2529            editor
 2530                ._subscriptions
 2531                .push(cx.observe(breakpoints, |_, _, cx| {
 2532                    cx.notify();
 2533                }));
 2534        }
 2535        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2536        editor._subscriptions.extend(project_subscriptions);
 2537
 2538        editor._subscriptions.push(cx.subscribe_in(
 2539            &cx.entity(),
 2540            window,
 2541            |editor, _, e: &EditorEvent, window, cx| match e {
 2542                EditorEvent::ScrollPositionChanged { local, .. } => {
 2543                    if *local {
 2544                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2545                        editor.inline_blame_popover.take();
 2546                        let new_anchor = editor.scroll_manager.anchor();
 2547                        let snapshot = editor.snapshot(window, cx);
 2548                        editor.update_restoration_data(cx, move |data| {
 2549                            data.scroll_position = (
 2550                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2551                                new_anchor.offset,
 2552                            );
 2553                        });
 2554
 2555                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2556                            cx.background_executor()
 2557                                .timer(Duration::from_millis(50))
 2558                                .await;
 2559                            editor
 2560                                .update_in(cx, |editor, window, cx| {
 2561                                    editor.register_visible_buffers(cx);
 2562                                    editor.refresh_colors_for_visible_range(None, window, cx);
 2563                                    editor.refresh_inlay_hints(
 2564                                        InlayHintRefreshReason::NewLinesShown,
 2565                                        cx,
 2566                                    );
 2567                                    editor.colorize_brackets(false, cx);
 2568                                })
 2569                                .ok();
 2570                        });
 2571                    }
 2572                }
 2573                EditorEvent::Edited { .. } => {
 2574                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2575                        .map(|vim_mode| vim_mode.0)
 2576                        .unwrap_or(false);
 2577                    if !vim_mode {
 2578                        let display_map = editor.display_snapshot(cx);
 2579                        let selections = editor.selections.all_adjusted_display(&display_map);
 2580                        let pop_state = editor
 2581                            .change_list
 2582                            .last()
 2583                            .map(|previous| {
 2584                                previous.len() == selections.len()
 2585                                    && previous.iter().enumerate().all(|(ix, p)| {
 2586                                        p.to_display_point(&display_map).row()
 2587                                            == selections[ix].head().row()
 2588                                    })
 2589                            })
 2590                            .unwrap_or(false);
 2591                        let new_positions = selections
 2592                            .into_iter()
 2593                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2594                            .collect();
 2595                        editor
 2596                            .change_list
 2597                            .push_to_change_list(pop_state, new_positions);
 2598                    }
 2599                }
 2600                _ => (),
 2601            },
 2602        ));
 2603
 2604        if let Some(dap_store) = editor
 2605            .project
 2606            .as_ref()
 2607            .map(|project| project.read(cx).dap_store())
 2608        {
 2609            let weak_editor = cx.weak_entity();
 2610
 2611            editor
 2612                ._subscriptions
 2613                .push(
 2614                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2615                        let session_entity = cx.entity();
 2616                        weak_editor
 2617                            .update(cx, |editor, cx| {
 2618                                editor._subscriptions.push(
 2619                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2620                                );
 2621                            })
 2622                            .ok();
 2623                    }),
 2624                );
 2625
 2626            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2627                editor
 2628                    ._subscriptions
 2629                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2630            }
 2631        }
 2632
 2633        // skip adding the initial selection to selection history
 2634        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2635        editor.end_selection(window, cx);
 2636        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2637
 2638        editor.scroll_manager.show_scrollbars(window, cx);
 2639        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2640
 2641        if full_mode {
 2642            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2643            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2644
 2645            if editor.git_blame_inline_enabled {
 2646                editor.start_git_blame_inline(false, window, cx);
 2647            }
 2648
 2649            editor.go_to_active_debug_line(window, cx);
 2650
 2651            editor.minimap =
 2652                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2653            editor.colors = Some(LspColorData::new(cx));
 2654            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2655
 2656            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2657                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2658            }
 2659            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2660        }
 2661
 2662        editor
 2663    }
 2664
 2665    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2666        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2667    }
 2668
 2669    pub fn deploy_mouse_context_menu(
 2670        &mut self,
 2671        position: gpui::Point<Pixels>,
 2672        context_menu: Entity<ContextMenu>,
 2673        window: &mut Window,
 2674        cx: &mut Context<Self>,
 2675    ) {
 2676        self.mouse_context_menu = Some(MouseContextMenu::new(
 2677            self,
 2678            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2679            context_menu,
 2680            window,
 2681            cx,
 2682        ));
 2683    }
 2684
 2685    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2686        self.mouse_context_menu
 2687            .as_ref()
 2688            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2689    }
 2690
 2691    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2692        if self
 2693            .selections
 2694            .pending_anchor()
 2695            .is_some_and(|pending_selection| {
 2696                let snapshot = self.buffer().read(cx).snapshot(cx);
 2697                pending_selection.range().includes(range, &snapshot)
 2698            })
 2699        {
 2700            return true;
 2701        }
 2702
 2703        self.selections
 2704            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2705            .into_iter()
 2706            .any(|selection| {
 2707                // This is needed to cover a corner case, if we just check for an existing
 2708                // selection in the fold range, having a cursor at the start of the fold
 2709                // marks it as selected. Non-empty selections don't cause this.
 2710                let length = selection.end - selection.start;
 2711                length > 0
 2712            })
 2713    }
 2714
 2715    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2716        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2717    }
 2718
 2719    fn key_context_internal(
 2720        &self,
 2721        has_active_edit_prediction: bool,
 2722        window: &mut Window,
 2723        cx: &mut App,
 2724    ) -> KeyContext {
 2725        let mut key_context = KeyContext::new_with_defaults();
 2726        key_context.add("Editor");
 2727        let mode = match self.mode {
 2728            EditorMode::SingleLine => "single_line",
 2729            EditorMode::AutoHeight { .. } => "auto_height",
 2730            EditorMode::Minimap { .. } => "minimap",
 2731            EditorMode::Full { .. } => "full",
 2732        };
 2733
 2734        if EditorSettings::jupyter_enabled(cx) {
 2735            key_context.add("jupyter");
 2736        }
 2737
 2738        key_context.set("mode", mode);
 2739        if self.pending_rename.is_some() {
 2740            key_context.add("renaming");
 2741        }
 2742
 2743        if let Some(snippet_stack) = self.snippet_stack.last() {
 2744            key_context.add("in_snippet");
 2745
 2746            if snippet_stack.active_index > 0 {
 2747                key_context.add("has_previous_tabstop");
 2748            }
 2749
 2750            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2751                key_context.add("has_next_tabstop");
 2752            }
 2753        }
 2754
 2755        match self.context_menu.borrow().as_ref() {
 2756            Some(CodeContextMenu::Completions(menu)) => {
 2757                if menu.visible() {
 2758                    key_context.add("menu");
 2759                    key_context.add("showing_completions");
 2760                }
 2761            }
 2762            Some(CodeContextMenu::CodeActions(menu)) => {
 2763                if menu.visible() {
 2764                    key_context.add("menu");
 2765                    key_context.add("showing_code_actions")
 2766                }
 2767            }
 2768            None => {}
 2769        }
 2770
 2771        if self.signature_help_state.has_multiple_signatures() {
 2772            key_context.add("showing_signature_help");
 2773        }
 2774
 2775        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2776        if !self.focus_handle(cx).contains_focused(window, cx)
 2777            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2778        {
 2779            for addon in self.addons.values() {
 2780                addon.extend_key_context(&mut key_context, cx)
 2781            }
 2782        }
 2783
 2784        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2785            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2786                Some(
 2787                    file.full_path(cx)
 2788                        .extension()?
 2789                        .to_string_lossy()
 2790                        .to_lowercase(),
 2791                )
 2792            }) {
 2793                key_context.set("extension", extension);
 2794            }
 2795        } else {
 2796            key_context.add("multibuffer");
 2797        }
 2798
 2799        if has_active_edit_prediction {
 2800            if self.edit_prediction_in_conflict() {
 2801                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2802            } else {
 2803                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2804                key_context.add("copilot_suggestion");
 2805            }
 2806        }
 2807
 2808        if self.selection_mark_mode {
 2809            key_context.add("selection_mode");
 2810        }
 2811
 2812        let disjoint = self.selections.disjoint_anchors();
 2813        let snapshot = self.snapshot(window, cx);
 2814        let snapshot = snapshot.buffer_snapshot();
 2815        if self.mode == EditorMode::SingleLine
 2816            && let [selection] = disjoint
 2817            && selection.start == selection.end
 2818            && selection.end.to_offset(snapshot) == snapshot.len()
 2819        {
 2820            key_context.add("end_of_input");
 2821        }
 2822
 2823        if self.has_any_expanded_diff_hunks(cx) {
 2824            key_context.add("diffs_expanded");
 2825        }
 2826
 2827        key_context
 2828    }
 2829
 2830    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2831        self.last_bounds.as_ref()
 2832    }
 2833
 2834    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2835        if self.mouse_cursor_hidden {
 2836            self.mouse_cursor_hidden = false;
 2837            cx.notify();
 2838        }
 2839    }
 2840
 2841    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2842        let hide_mouse_cursor = match origin {
 2843            HideMouseCursorOrigin::TypingAction => {
 2844                matches!(
 2845                    self.hide_mouse_mode,
 2846                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2847                )
 2848            }
 2849            HideMouseCursorOrigin::MovementAction => {
 2850                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2851            }
 2852        };
 2853        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2854            self.mouse_cursor_hidden = hide_mouse_cursor;
 2855            cx.notify();
 2856        }
 2857    }
 2858
 2859    pub fn edit_prediction_in_conflict(&self) -> bool {
 2860        if !self.show_edit_predictions_in_menu() {
 2861            return false;
 2862        }
 2863
 2864        let showing_completions = self
 2865            .context_menu
 2866            .borrow()
 2867            .as_ref()
 2868            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2869
 2870        showing_completions
 2871            || self.edit_prediction_requires_modifier()
 2872            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2873            // bindings to insert tab characters.
 2874            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2875    }
 2876
 2877    pub fn accept_edit_prediction_keybind(
 2878        &self,
 2879        granularity: EditPredictionGranularity,
 2880        window: &mut Window,
 2881        cx: &mut App,
 2882    ) -> AcceptEditPredictionBinding {
 2883        let key_context = self.key_context_internal(true, window, cx);
 2884        let in_conflict = self.edit_prediction_in_conflict();
 2885
 2886        let bindings =
 2887            match granularity {
 2888                EditPredictionGranularity::Word => window
 2889                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2890                EditPredictionGranularity::Line => window
 2891                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2892                EditPredictionGranularity::Full => {
 2893                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2894                }
 2895            };
 2896
 2897        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2898            !in_conflict
 2899                || binding
 2900                    .keystrokes()
 2901                    .first()
 2902                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 2903        }))
 2904    }
 2905
 2906    pub fn new_file(
 2907        workspace: &mut Workspace,
 2908        _: &workspace::NewFile,
 2909        window: &mut Window,
 2910        cx: &mut Context<Workspace>,
 2911    ) {
 2912        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 2913            "Failed to create buffer",
 2914            window,
 2915            cx,
 2916            |e, _, _| match e.error_code() {
 2917                ErrorCode::RemoteUpgradeRequired => Some(format!(
 2918                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 2919                e.error_tag("required").unwrap_or("the latest version")
 2920            )),
 2921                _ => None,
 2922            },
 2923        );
 2924    }
 2925
 2926    pub fn new_in_workspace(
 2927        workspace: &mut Workspace,
 2928        window: &mut Window,
 2929        cx: &mut Context<Workspace>,
 2930    ) -> Task<Result<Entity<Editor>>> {
 2931        let project = workspace.project().clone();
 2932        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 2933
 2934        cx.spawn_in(window, async move |workspace, cx| {
 2935            let buffer = create.await?;
 2936            workspace.update_in(cx, |workspace, window, cx| {
 2937                let editor =
 2938                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 2939                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 2940                editor
 2941            })
 2942        })
 2943    }
 2944
 2945    fn new_file_vertical(
 2946        workspace: &mut Workspace,
 2947        _: &workspace::NewFileSplitVertical,
 2948        window: &mut Window,
 2949        cx: &mut Context<Workspace>,
 2950    ) {
 2951        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 2952    }
 2953
 2954    fn new_file_horizontal(
 2955        workspace: &mut Workspace,
 2956        _: &workspace::NewFileSplitHorizontal,
 2957        window: &mut Window,
 2958        cx: &mut Context<Workspace>,
 2959    ) {
 2960        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 2961    }
 2962
 2963    fn new_file_split(
 2964        workspace: &mut Workspace,
 2965        action: &workspace::NewFileSplit,
 2966        window: &mut Window,
 2967        cx: &mut Context<Workspace>,
 2968    ) {
 2969        Self::new_file_in_direction(workspace, action.0, window, cx)
 2970    }
 2971
 2972    fn new_file_in_direction(
 2973        workspace: &mut Workspace,
 2974        direction: SplitDirection,
 2975        window: &mut Window,
 2976        cx: &mut Context<Workspace>,
 2977    ) {
 2978        let project = workspace.project().clone();
 2979        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 2980
 2981        cx.spawn_in(window, async move |workspace, cx| {
 2982            let buffer = create.await?;
 2983            workspace.update_in(cx, move |workspace, window, cx| {
 2984                workspace.split_item(
 2985                    direction,
 2986                    Box::new(
 2987                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 2988                    ),
 2989                    window,
 2990                    cx,
 2991                )
 2992            })?;
 2993            anyhow::Ok(())
 2994        })
 2995        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 2996            match e.error_code() {
 2997                ErrorCode::RemoteUpgradeRequired => Some(format!(
 2998                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 2999                e.error_tag("required").unwrap_or("the latest version")
 3000            )),
 3001                _ => None,
 3002            }
 3003        });
 3004    }
 3005
 3006    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3007        self.leader_id
 3008    }
 3009
 3010    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3011        &self.buffer
 3012    }
 3013
 3014    pub fn project(&self) -> Option<&Entity<Project>> {
 3015        self.project.as_ref()
 3016    }
 3017
 3018    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3019        self.workspace.as_ref()?.0.upgrade()
 3020    }
 3021
 3022    /// Returns the workspace serialization ID if this editor should be serialized.
 3023    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3024        self.workspace
 3025            .as_ref()
 3026            .filter(|_| self.should_serialize_buffer())
 3027            .and_then(|workspace| workspace.1)
 3028    }
 3029
 3030    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3031        self.buffer().read(cx).title(cx)
 3032    }
 3033
 3034    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3035        let git_blame_gutter_max_author_length = self
 3036            .render_git_blame_gutter(cx)
 3037            .then(|| {
 3038                if let Some(blame) = self.blame.as_ref() {
 3039                    let max_author_length =
 3040                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3041                    Some(max_author_length)
 3042                } else {
 3043                    None
 3044                }
 3045            })
 3046            .flatten();
 3047
 3048        EditorSnapshot {
 3049            mode: self.mode.clone(),
 3050            show_gutter: self.show_gutter,
 3051            offset_content: self.offset_content,
 3052            show_line_numbers: self.show_line_numbers,
 3053            number_deleted_lines: self.number_deleted_lines,
 3054            show_git_diff_gutter: self.show_git_diff_gutter,
 3055            show_code_actions: self.show_code_actions,
 3056            show_runnables: self.show_runnables,
 3057            show_breakpoints: self.show_breakpoints,
 3058            git_blame_gutter_max_author_length,
 3059            display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
 3060            placeholder_display_snapshot: self
 3061                .placeholder_display_map
 3062                .as_ref()
 3063                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3064            scroll_anchor: self.scroll_manager.anchor(),
 3065            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3066            is_focused: self.focus_handle.is_focused(window),
 3067            current_line_highlight: self
 3068                .current_line_highlight
 3069                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3070            gutter_hovered: self.gutter_hovered,
 3071        }
 3072    }
 3073
 3074    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3075        self.buffer.read(cx).language_at(point, cx)
 3076    }
 3077
 3078    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3079        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3080    }
 3081
 3082    pub fn active_excerpt(
 3083        &self,
 3084        cx: &App,
 3085    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3086        self.buffer
 3087            .read(cx)
 3088            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3089    }
 3090
 3091    pub fn mode(&self) -> &EditorMode {
 3092        &self.mode
 3093    }
 3094
 3095    pub fn set_mode(&mut self, mode: EditorMode) {
 3096        self.mode = mode;
 3097    }
 3098
 3099    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3100        self.collaboration_hub.as_deref()
 3101    }
 3102
 3103    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3104        self.collaboration_hub = Some(hub);
 3105    }
 3106
 3107    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3108        self.in_project_search = in_project_search;
 3109    }
 3110
 3111    pub fn set_custom_context_menu(
 3112        &mut self,
 3113        f: impl 'static
 3114        + Fn(
 3115            &mut Self,
 3116            DisplayPoint,
 3117            &mut Window,
 3118            &mut Context<Self>,
 3119        ) -> Option<Entity<ui::ContextMenu>>,
 3120    ) {
 3121        self.custom_context_menu = Some(Box::new(f))
 3122    }
 3123
 3124    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3125        self.completion_provider = provider;
 3126    }
 3127
 3128    #[cfg(any(test, feature = "test-support"))]
 3129    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3130        self.completion_provider.clone()
 3131    }
 3132
 3133    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3134        self.semantics_provider.clone()
 3135    }
 3136
 3137    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3138        self.semantics_provider = provider;
 3139    }
 3140
 3141    pub fn set_edit_prediction_provider<T>(
 3142        &mut self,
 3143        provider: Option<Entity<T>>,
 3144        window: &mut Window,
 3145        cx: &mut Context<Self>,
 3146    ) where
 3147        T: EditPredictionDelegate,
 3148    {
 3149        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3150            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3151                if this.focus_handle.is_focused(window) {
 3152                    this.update_visible_edit_prediction(window, cx);
 3153                }
 3154            }),
 3155            provider: Arc::new(provider),
 3156        });
 3157        self.update_edit_prediction_settings(cx);
 3158        self.refresh_edit_prediction(false, false, window, cx);
 3159    }
 3160
 3161    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3162        self.placeholder_display_map
 3163            .as_ref()
 3164            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3165    }
 3166
 3167    pub fn set_placeholder_text(
 3168        &mut self,
 3169        placeholder_text: &str,
 3170        window: &mut Window,
 3171        cx: &mut Context<Self>,
 3172    ) {
 3173        let multibuffer = cx
 3174            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3175
 3176        let style = window.text_style();
 3177
 3178        self.placeholder_display_map = Some(cx.new(|cx| {
 3179            DisplayMap::new(
 3180                multibuffer,
 3181                style.font(),
 3182                style.font_size.to_pixels(window.rem_size()),
 3183                None,
 3184                FILE_HEADER_HEIGHT,
 3185                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3186                Default::default(),
 3187                DiagnosticSeverity::Off,
 3188                cx,
 3189            )
 3190        }));
 3191        cx.notify();
 3192    }
 3193
 3194    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3195        self.cursor_shape = cursor_shape;
 3196
 3197        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3198        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3199
 3200        cx.notify();
 3201    }
 3202
 3203    pub fn cursor_shape(&self) -> CursorShape {
 3204        self.cursor_shape
 3205    }
 3206
 3207    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3208        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3209    }
 3210
 3211    pub fn set_current_line_highlight(
 3212        &mut self,
 3213        current_line_highlight: Option<CurrentLineHighlight>,
 3214    ) {
 3215        self.current_line_highlight = current_line_highlight;
 3216    }
 3217
 3218    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3219        self.collapse_matches = collapse_matches;
 3220    }
 3221
 3222    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3223        if self.collapse_matches {
 3224            return range.start..range.start;
 3225        }
 3226        range.clone()
 3227    }
 3228
 3229    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3230        self.display_map.read(cx).clip_at_line_ends
 3231    }
 3232
 3233    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3234        if self.display_map.read(cx).clip_at_line_ends != clip {
 3235            self.display_map
 3236                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3237        }
 3238    }
 3239
 3240    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3241        self.input_enabled = input_enabled;
 3242    }
 3243
 3244    pub fn set_edit_predictions_hidden_for_vim_mode(
 3245        &mut self,
 3246        hidden: bool,
 3247        window: &mut Window,
 3248        cx: &mut Context<Self>,
 3249    ) {
 3250        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3251            self.edit_predictions_hidden_for_vim_mode = hidden;
 3252            if hidden {
 3253                self.update_visible_edit_prediction(window, cx);
 3254            } else {
 3255                self.refresh_edit_prediction(true, false, window, cx);
 3256            }
 3257        }
 3258    }
 3259
 3260    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3261        self.menu_edit_predictions_policy = value;
 3262    }
 3263
 3264    pub fn set_autoindent(&mut self, autoindent: bool) {
 3265        if autoindent {
 3266            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3267        } else {
 3268            self.autoindent_mode = None;
 3269        }
 3270    }
 3271
 3272    pub fn capability(&self, cx: &App) -> Capability {
 3273        if self.read_only {
 3274            Capability::ReadOnly
 3275        } else {
 3276            self.buffer.read(cx).capability()
 3277        }
 3278    }
 3279
 3280    pub fn read_only(&self, cx: &App) -> bool {
 3281        self.read_only || self.buffer.read(cx).read_only()
 3282    }
 3283
 3284    pub fn set_read_only(&mut self, read_only: bool) {
 3285        self.read_only = read_only;
 3286    }
 3287
 3288    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3289        self.use_autoclose = autoclose;
 3290    }
 3291
 3292    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3293        self.use_auto_surround = auto_surround;
 3294    }
 3295
 3296    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3297        self.auto_replace_emoji_shortcode = auto_replace;
 3298    }
 3299
 3300    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3301        self.buffer_serialization = should_serialize.then(|| {
 3302            BufferSerialization::new(
 3303                ProjectSettings::get_global(cx)
 3304                    .session
 3305                    .restore_unsaved_buffers,
 3306            )
 3307        })
 3308    }
 3309
 3310    fn should_serialize_buffer(&self) -> bool {
 3311        self.buffer_serialization.is_some()
 3312    }
 3313
 3314    pub fn toggle_edit_predictions(
 3315        &mut self,
 3316        _: &ToggleEditPrediction,
 3317        window: &mut Window,
 3318        cx: &mut Context<Self>,
 3319    ) {
 3320        if self.show_edit_predictions_override.is_some() {
 3321            self.set_show_edit_predictions(None, window, cx);
 3322        } else {
 3323            let show_edit_predictions = !self.edit_predictions_enabled();
 3324            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3325        }
 3326    }
 3327
 3328    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3329        self.show_completions_on_input_override = show_completions_on_input;
 3330    }
 3331
 3332    pub fn set_show_edit_predictions(
 3333        &mut self,
 3334        show_edit_predictions: Option<bool>,
 3335        window: &mut Window,
 3336        cx: &mut Context<Self>,
 3337    ) {
 3338        self.show_edit_predictions_override = show_edit_predictions;
 3339        self.update_edit_prediction_settings(cx);
 3340
 3341        if let Some(false) = show_edit_predictions {
 3342            self.discard_edit_prediction(false, cx);
 3343        } else {
 3344            self.refresh_edit_prediction(false, true, window, cx);
 3345        }
 3346    }
 3347
 3348    fn edit_predictions_disabled_in_scope(
 3349        &self,
 3350        buffer: &Entity<Buffer>,
 3351        buffer_position: language::Anchor,
 3352        cx: &App,
 3353    ) -> bool {
 3354        let snapshot = buffer.read(cx).snapshot();
 3355        let settings = snapshot.settings_at(buffer_position, cx);
 3356
 3357        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3358            return false;
 3359        };
 3360
 3361        scope.override_name().is_some_and(|scope_name| {
 3362            settings
 3363                .edit_predictions_disabled_in
 3364                .iter()
 3365                .any(|s| s == scope_name)
 3366        })
 3367    }
 3368
 3369    pub fn set_use_modal_editing(&mut self, to: bool) {
 3370        self.use_modal_editing = to;
 3371    }
 3372
 3373    pub fn use_modal_editing(&self) -> bool {
 3374        self.use_modal_editing
 3375    }
 3376
 3377    fn selections_did_change(
 3378        &mut self,
 3379        local: bool,
 3380        old_cursor_position: &Anchor,
 3381        effects: SelectionEffects,
 3382        window: &mut Window,
 3383        cx: &mut Context<Self>,
 3384    ) {
 3385        window.invalidate_character_coordinates();
 3386
 3387        // Copy selections to primary selection buffer
 3388        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3389        if local {
 3390            let selections = self
 3391                .selections
 3392                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3393            let buffer_handle = self.buffer.read(cx).read(cx);
 3394
 3395            let mut text = String::new();
 3396            for (index, selection) in selections.iter().enumerate() {
 3397                let text_for_selection = buffer_handle
 3398                    .text_for_range(selection.start..selection.end)
 3399                    .collect::<String>();
 3400
 3401                text.push_str(&text_for_selection);
 3402                if index != selections.len() - 1 {
 3403                    text.push('\n');
 3404                }
 3405            }
 3406
 3407            if !text.is_empty() {
 3408                cx.write_to_primary(ClipboardItem::new_string(text));
 3409            }
 3410        }
 3411
 3412        let selection_anchors = self.selections.disjoint_anchors_arc();
 3413
 3414        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3415            self.buffer.update(cx, |buffer, cx| {
 3416                buffer.set_active_selections(
 3417                    &selection_anchors,
 3418                    self.selections.line_mode(),
 3419                    self.cursor_shape,
 3420                    cx,
 3421                )
 3422            });
 3423        }
 3424        let display_map = self
 3425            .display_map
 3426            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3427        let buffer = display_map.buffer_snapshot();
 3428        if self.selections.count() == 1 {
 3429            self.add_selections_state = None;
 3430        }
 3431        self.select_next_state = None;
 3432        self.select_prev_state = None;
 3433        self.select_syntax_node_history.try_clear();
 3434        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3435        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3436        self.take_rename(false, window, cx);
 3437
 3438        let newest_selection = self.selections.newest_anchor();
 3439        let new_cursor_position = newest_selection.head();
 3440        let selection_start = newest_selection.start;
 3441
 3442        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3443            self.push_to_nav_history(
 3444                *old_cursor_position,
 3445                Some(new_cursor_position.to_point(buffer)),
 3446                false,
 3447                effects.nav_history == Some(true),
 3448                cx,
 3449            );
 3450        }
 3451
 3452        if local {
 3453            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3454                self.register_buffer(buffer_id, cx);
 3455            }
 3456
 3457            let mut context_menu = self.context_menu.borrow_mut();
 3458            let completion_menu = match context_menu.as_ref() {
 3459                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3460                Some(CodeContextMenu::CodeActions(_)) => {
 3461                    *context_menu = None;
 3462                    None
 3463                }
 3464                None => None,
 3465            };
 3466            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3467            drop(context_menu);
 3468
 3469            if effects.completions
 3470                && let Some(completion_position) = completion_position
 3471            {
 3472                let start_offset = selection_start.to_offset(buffer);
 3473                let position_matches = start_offset == completion_position.to_offset(buffer);
 3474                let continue_showing = if let Some((snap, ..)) =
 3475                    buffer.point_to_buffer_offset(completion_position)
 3476                    && !snap.capability.editable()
 3477                {
 3478                    false
 3479                } else if position_matches {
 3480                    if self.snippet_stack.is_empty() {
 3481                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3482                            == Some(CharKind::Word)
 3483                    } else {
 3484                        // Snippet choices can be shown even when the cursor is in whitespace.
 3485                        // Dismissing the menu with actions like backspace is handled by
 3486                        // invalidation regions.
 3487                        true
 3488                    }
 3489                } else {
 3490                    false
 3491                };
 3492
 3493                if continue_showing {
 3494                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3495                } else {
 3496                    self.hide_context_menu(window, cx);
 3497                }
 3498            }
 3499
 3500            hide_hover(self, cx);
 3501
 3502            if old_cursor_position.to_display_point(&display_map).row()
 3503                != new_cursor_position.to_display_point(&display_map).row()
 3504            {
 3505                self.available_code_actions.take();
 3506            }
 3507            self.refresh_code_actions(window, cx);
 3508            self.refresh_document_highlights(cx);
 3509            refresh_linked_ranges(self, window, cx);
 3510
 3511            self.refresh_selected_text_highlights(false, window, cx);
 3512            self.refresh_matching_bracket_highlights(window, cx);
 3513            self.update_visible_edit_prediction(window, cx);
 3514            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3515            self.inline_blame_popover.take();
 3516            if self.git_blame_inline_enabled {
 3517                self.start_inline_blame_timer(window, cx);
 3518            }
 3519        }
 3520
 3521        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3522        cx.emit(EditorEvent::SelectionsChanged { local });
 3523
 3524        let selections = &self.selections.disjoint_anchors_arc();
 3525        if selections.len() == 1 {
 3526            cx.emit(SearchEvent::ActiveMatchChanged)
 3527        }
 3528        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3529            let inmemory_selections = selections
 3530                .iter()
 3531                .map(|s| {
 3532                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3533                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3534                })
 3535                .collect();
 3536            self.update_restoration_data(cx, |data| {
 3537                data.selections = inmemory_selections;
 3538            });
 3539
 3540            if WorkspaceSettings::get(None, cx).restore_on_startup
 3541                != RestoreOnStartupBehavior::EmptyTab
 3542                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3543            {
 3544                let snapshot = self.buffer().read(cx).snapshot(cx);
 3545                let selections = selections.clone();
 3546                let background_executor = cx.background_executor().clone();
 3547                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3548                self.serialize_selections = cx.background_spawn(async move {
 3549                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3550                    let db_selections = selections
 3551                        .iter()
 3552                        .map(|selection| {
 3553                            (
 3554                                selection.start.to_offset(&snapshot).0,
 3555                                selection.end.to_offset(&snapshot).0,
 3556                            )
 3557                        })
 3558                        .collect();
 3559
 3560                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3561                        .await
 3562                        .with_context(|| {
 3563                            format!(
 3564                                "persisting editor selections for editor {editor_id}, \
 3565                                workspace {workspace_id:?}"
 3566                            )
 3567                        })
 3568                        .log_err();
 3569                });
 3570            }
 3571        }
 3572
 3573        cx.notify();
 3574    }
 3575
 3576    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3577        use text::ToOffset as _;
 3578        use text::ToPoint as _;
 3579
 3580        if self.mode.is_minimap()
 3581            || WorkspaceSettings::get(None, cx).restore_on_startup
 3582                == RestoreOnStartupBehavior::EmptyTab
 3583        {
 3584            return;
 3585        }
 3586
 3587        if !self.buffer().read(cx).is_singleton() {
 3588            return;
 3589        }
 3590
 3591        let display_snapshot = self
 3592            .display_map
 3593            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3594        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3595            return;
 3596        };
 3597        let inmemory_folds = display_snapshot
 3598            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3599            .map(|fold| {
 3600                fold.range.start.text_anchor.to_point(&snapshot)
 3601                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3602            })
 3603            .collect();
 3604        self.update_restoration_data(cx, |data| {
 3605            data.folds = inmemory_folds;
 3606        });
 3607
 3608        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3609            return;
 3610        };
 3611        let background_executor = cx.background_executor().clone();
 3612        let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3613        const FINGERPRINT_LEN: usize = 32;
 3614        let db_folds = display_snapshot
 3615            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3616            .map(|fold| {
 3617                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3618                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3619
 3620                // Extract fingerprints - content at fold boundaries for validation on restore
 3621                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3622                // content that might change independently.
 3623                // start_fp: first min(32, fold_len) bytes of fold content
 3624                // end_fp: last min(32, fold_len) bytes of fold content
 3625                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3626                let fold_len = end - start;
 3627                let start_fp_end = snapshot
 3628                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3629                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3630                let end_fp_start = snapshot
 3631                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3632                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3633
 3634                (start, end, start_fp, end_fp)
 3635            })
 3636            .collect::<Vec<_>>();
 3637        self.serialize_folds = cx.background_spawn(async move {
 3638            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3639            DB.save_editor_folds(editor_id, workspace_id, db_folds)
 3640                .await
 3641                .with_context(|| {
 3642                    format!(
 3643                        "persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"
 3644                    )
 3645                })
 3646                .log_err();
 3647        });
 3648    }
 3649
 3650    pub fn sync_selections(
 3651        &mut self,
 3652        other: Entity<Editor>,
 3653        cx: &mut Context<Self>,
 3654    ) -> gpui::Subscription {
 3655        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3656        if !other_selections.is_empty() {
 3657            self.selections
 3658                .change_with(&self.display_snapshot(cx), |selections| {
 3659                    selections.select_anchors(other_selections);
 3660                });
 3661        }
 3662
 3663        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3664            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3665                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3666                if other_selections.is_empty() {
 3667                    return;
 3668                }
 3669                let snapshot = this.display_snapshot(cx);
 3670                this.selections.change_with(&snapshot, |selections| {
 3671                    selections.select_anchors(other_selections);
 3672                });
 3673            }
 3674        });
 3675
 3676        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3677            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3678                let these_selections = this.selections.disjoint_anchors().to_vec();
 3679                if these_selections.is_empty() {
 3680                    return;
 3681                }
 3682                other.update(cx, |other_editor, cx| {
 3683                    let snapshot = other_editor.display_snapshot(cx);
 3684                    other_editor
 3685                        .selections
 3686                        .change_with(&snapshot, |selections| {
 3687                            selections.select_anchors(these_selections);
 3688                        })
 3689                });
 3690            }
 3691        });
 3692
 3693        Subscription::join(other_subscription, this_subscription)
 3694    }
 3695
 3696    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3697        if self.buffer().read(cx).is_singleton() {
 3698            return;
 3699        }
 3700        let snapshot = self.buffer.read(cx).snapshot(cx);
 3701        let buffer_ids: HashSet<BufferId> = self
 3702            .selections
 3703            .disjoint_anchor_ranges()
 3704            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3705            .collect();
 3706        for buffer_id in buffer_ids {
 3707            self.unfold_buffer(buffer_id, cx);
 3708        }
 3709    }
 3710
 3711    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3712    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3713    /// effects of selection change occur at the end of the transaction.
 3714    pub fn change_selections<R>(
 3715        &mut self,
 3716        effects: SelectionEffects,
 3717        window: &mut Window,
 3718        cx: &mut Context<Self>,
 3719        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3720    ) -> R {
 3721        let snapshot = self.display_snapshot(cx);
 3722        if let Some(state) = &mut self.deferred_selection_effects_state {
 3723            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3724            state.effects.completions = effects.completions;
 3725            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3726            let (changed, result) = self.selections.change_with(&snapshot, change);
 3727            state.changed |= changed;
 3728            return result;
 3729        }
 3730        let mut state = DeferredSelectionEffectsState {
 3731            changed: false,
 3732            effects,
 3733            old_cursor_position: self.selections.newest_anchor().head(),
 3734            history_entry: SelectionHistoryEntry {
 3735                selections: self.selections.disjoint_anchors_arc(),
 3736                select_next_state: self.select_next_state.clone(),
 3737                select_prev_state: self.select_prev_state.clone(),
 3738                add_selections_state: self.add_selections_state.clone(),
 3739            },
 3740        };
 3741        let (changed, result) = self.selections.change_with(&snapshot, change);
 3742        state.changed = state.changed || changed;
 3743        if self.defer_selection_effects {
 3744            self.deferred_selection_effects_state = Some(state);
 3745        } else {
 3746            self.apply_selection_effects(state, window, cx);
 3747        }
 3748        result
 3749    }
 3750
 3751    /// Defers the effects of selection change, so that the effects of multiple calls to
 3752    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3753    /// to selection history and the state of popovers based on selection position aren't
 3754    /// erroneously updated.
 3755    pub fn with_selection_effects_deferred<R>(
 3756        &mut self,
 3757        window: &mut Window,
 3758        cx: &mut Context<Self>,
 3759        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3760    ) -> R {
 3761        let already_deferred = self.defer_selection_effects;
 3762        self.defer_selection_effects = true;
 3763        let result = update(self, window, cx);
 3764        if !already_deferred {
 3765            self.defer_selection_effects = false;
 3766            if let Some(state) = self.deferred_selection_effects_state.take() {
 3767                self.apply_selection_effects(state, window, cx);
 3768            }
 3769        }
 3770        result
 3771    }
 3772
 3773    fn apply_selection_effects(
 3774        &mut self,
 3775        state: DeferredSelectionEffectsState,
 3776        window: &mut Window,
 3777        cx: &mut Context<Self>,
 3778    ) {
 3779        if state.changed {
 3780            self.selection_history.push(state.history_entry);
 3781
 3782            if let Some(autoscroll) = state.effects.scroll {
 3783                self.request_autoscroll(autoscroll, cx);
 3784            }
 3785
 3786            let old_cursor_position = &state.old_cursor_position;
 3787
 3788            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3789
 3790            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3791                self.show_signature_help(&ShowSignatureHelp, window, cx);
 3792            }
 3793        }
 3794    }
 3795
 3796    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3797    where
 3798        I: IntoIterator<Item = (Range<S>, T)>,
 3799        S: ToOffset,
 3800        T: Into<Arc<str>>,
 3801    {
 3802        if self.read_only(cx) {
 3803            return;
 3804        }
 3805
 3806        self.buffer
 3807            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3808    }
 3809
 3810    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3811    where
 3812        I: IntoIterator<Item = (Range<S>, T)>,
 3813        S: ToOffset,
 3814        T: Into<Arc<str>>,
 3815    {
 3816        if self.read_only(cx) {
 3817            return;
 3818        }
 3819
 3820        self.buffer.update(cx, |buffer, cx| {
 3821            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3822        });
 3823    }
 3824
 3825    pub fn edit_with_block_indent<I, S, T>(
 3826        &mut self,
 3827        edits: I,
 3828        original_indent_columns: Vec<Option<u32>>,
 3829        cx: &mut Context<Self>,
 3830    ) where
 3831        I: IntoIterator<Item = (Range<S>, T)>,
 3832        S: ToOffset,
 3833        T: Into<Arc<str>>,
 3834    {
 3835        if self.read_only(cx) {
 3836            return;
 3837        }
 3838
 3839        self.buffer.update(cx, |buffer, cx| {
 3840            buffer.edit(
 3841                edits,
 3842                Some(AutoindentMode::Block {
 3843                    original_indent_columns,
 3844                }),
 3845                cx,
 3846            )
 3847        });
 3848    }
 3849
 3850    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3851        self.hide_context_menu(window, cx);
 3852
 3853        match phase {
 3854            SelectPhase::Begin {
 3855                position,
 3856                add,
 3857                click_count,
 3858            } => self.begin_selection(position, add, click_count, window, cx),
 3859            SelectPhase::BeginColumnar {
 3860                position,
 3861                goal_column,
 3862                reset,
 3863                mode,
 3864            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 3865            SelectPhase::Extend {
 3866                position,
 3867                click_count,
 3868            } => self.extend_selection(position, click_count, window, cx),
 3869            SelectPhase::Update {
 3870                position,
 3871                goal_column,
 3872                scroll_delta,
 3873            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 3874            SelectPhase::End => self.end_selection(window, cx),
 3875        }
 3876    }
 3877
 3878    fn extend_selection(
 3879        &mut self,
 3880        position: DisplayPoint,
 3881        click_count: usize,
 3882        window: &mut Window,
 3883        cx: &mut Context<Self>,
 3884    ) {
 3885        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3886        let tail = self
 3887            .selections
 3888            .newest::<MultiBufferOffset>(&display_map)
 3889            .tail();
 3890        let click_count = click_count.max(match self.selections.select_mode() {
 3891            SelectMode::Character => 1,
 3892            SelectMode::Word(_) => 2,
 3893            SelectMode::Line(_) => 3,
 3894            SelectMode::All => 4,
 3895        });
 3896        self.begin_selection(position, false, click_count, window, cx);
 3897
 3898        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 3899
 3900        let current_selection = match self.selections.select_mode() {
 3901            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 3902            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 3903        };
 3904
 3905        let mut pending_selection = self
 3906            .selections
 3907            .pending_anchor()
 3908            .cloned()
 3909            .expect("extend_selection not called with pending selection");
 3910
 3911        if pending_selection
 3912            .start
 3913            .cmp(&current_selection.start, display_map.buffer_snapshot())
 3914            == Ordering::Greater
 3915        {
 3916            pending_selection.start = current_selection.start;
 3917        }
 3918        if pending_selection
 3919            .end
 3920            .cmp(&current_selection.end, display_map.buffer_snapshot())
 3921            == Ordering::Less
 3922        {
 3923            pending_selection.end = current_selection.end;
 3924            pending_selection.reversed = true;
 3925        }
 3926
 3927        let mut pending_mode = self.selections.pending_mode().unwrap();
 3928        match &mut pending_mode {
 3929            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 3930            _ => {}
 3931        }
 3932
 3933        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 3934            SelectionEffects::scroll(Autoscroll::fit())
 3935        } else {
 3936            SelectionEffects::no_scroll()
 3937        };
 3938
 3939        self.change_selections(effects, window, cx, |s| {
 3940            s.set_pending(pending_selection.clone(), pending_mode);
 3941            s.set_is_extending(true);
 3942        });
 3943    }
 3944
 3945    fn begin_selection(
 3946        &mut self,
 3947        position: DisplayPoint,
 3948        add: bool,
 3949        click_count: usize,
 3950        window: &mut Window,
 3951        cx: &mut Context<Self>,
 3952    ) {
 3953        if !self.focus_handle.is_focused(window) {
 3954            self.last_focused_descendant = None;
 3955            window.focus(&self.focus_handle, cx);
 3956        }
 3957
 3958        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3959        let buffer = display_map.buffer_snapshot();
 3960        let position = display_map.clip_point(position, Bias::Left);
 3961
 3962        let start;
 3963        let end;
 3964        let mode;
 3965        let mut auto_scroll;
 3966        match click_count {
 3967            1 => {
 3968                start = buffer.anchor_before(position.to_point(&display_map));
 3969                end = start;
 3970                mode = SelectMode::Character;
 3971                auto_scroll = true;
 3972            }
 3973            2 => {
 3974                let position = display_map
 3975                    .clip_point(position, Bias::Left)
 3976                    .to_offset(&display_map, Bias::Left);
 3977                let (range, _) = buffer.surrounding_word(position, None);
 3978                start = buffer.anchor_before(range.start);
 3979                end = buffer.anchor_before(range.end);
 3980                mode = SelectMode::Word(start..end);
 3981                auto_scroll = true;
 3982            }
 3983            3 => {
 3984                let position = display_map
 3985                    .clip_point(position, Bias::Left)
 3986                    .to_point(&display_map);
 3987                let line_start = display_map.prev_line_boundary(position).0;
 3988                let next_line_start = buffer.clip_point(
 3989                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 3990                    Bias::Left,
 3991                );
 3992                start = buffer.anchor_before(line_start);
 3993                end = buffer.anchor_before(next_line_start);
 3994                mode = SelectMode::Line(start..end);
 3995                auto_scroll = true;
 3996            }
 3997            _ => {
 3998                start = buffer.anchor_before(MultiBufferOffset(0));
 3999                end = buffer.anchor_before(buffer.len());
 4000                mode = SelectMode::All;
 4001                auto_scroll = false;
 4002            }
 4003        }
 4004        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4005
 4006        let point_to_delete: Option<usize> = {
 4007            let selected_points: Vec<Selection<Point>> =
 4008                self.selections.disjoint_in_range(start..end, &display_map);
 4009
 4010            if !add || click_count > 1 {
 4011                None
 4012            } else if !selected_points.is_empty() {
 4013                Some(selected_points[0].id)
 4014            } else {
 4015                let clicked_point_already_selected =
 4016                    self.selections.disjoint_anchors().iter().find(|selection| {
 4017                        selection.start.to_point(buffer) == start.to_point(buffer)
 4018                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4019                    });
 4020
 4021                clicked_point_already_selected.map(|selection| selection.id)
 4022            }
 4023        };
 4024
 4025        let selections_count = self.selections.count();
 4026        let effects = if auto_scroll {
 4027            SelectionEffects::default()
 4028        } else {
 4029            SelectionEffects::no_scroll()
 4030        };
 4031
 4032        self.change_selections(effects, window, cx, |s| {
 4033            if let Some(point_to_delete) = point_to_delete {
 4034                s.delete(point_to_delete);
 4035
 4036                if selections_count == 1 {
 4037                    s.set_pending_anchor_range(start..end, mode);
 4038                }
 4039            } else {
 4040                if !add {
 4041                    s.clear_disjoint();
 4042                }
 4043
 4044                s.set_pending_anchor_range(start..end, mode);
 4045            }
 4046        });
 4047    }
 4048
 4049    fn begin_columnar_selection(
 4050        &mut self,
 4051        position: DisplayPoint,
 4052        goal_column: u32,
 4053        reset: bool,
 4054        mode: ColumnarMode,
 4055        window: &mut Window,
 4056        cx: &mut Context<Self>,
 4057    ) {
 4058        if !self.focus_handle.is_focused(window) {
 4059            self.last_focused_descendant = None;
 4060            window.focus(&self.focus_handle, cx);
 4061        }
 4062
 4063        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4064
 4065        if reset {
 4066            let pointer_position = display_map
 4067                .buffer_snapshot()
 4068                .anchor_before(position.to_point(&display_map));
 4069
 4070            self.change_selections(
 4071                SelectionEffects::scroll(Autoscroll::newest()),
 4072                window,
 4073                cx,
 4074                |s| {
 4075                    s.clear_disjoint();
 4076                    s.set_pending_anchor_range(
 4077                        pointer_position..pointer_position,
 4078                        SelectMode::Character,
 4079                    );
 4080                },
 4081            );
 4082        };
 4083
 4084        let tail = self.selections.newest::<Point>(&display_map).tail();
 4085        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4086        self.columnar_selection_state = match mode {
 4087            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4088                selection_tail: selection_anchor,
 4089                display_point: if reset {
 4090                    if position.column() != goal_column {
 4091                        Some(DisplayPoint::new(position.row(), goal_column))
 4092                    } else {
 4093                        None
 4094                    }
 4095                } else {
 4096                    None
 4097                },
 4098            }),
 4099            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4100                selection_tail: selection_anchor,
 4101            }),
 4102        };
 4103
 4104        if !reset {
 4105            self.select_columns(position, goal_column, &display_map, window, cx);
 4106        }
 4107    }
 4108
 4109    fn update_selection(
 4110        &mut self,
 4111        position: DisplayPoint,
 4112        goal_column: u32,
 4113        scroll_delta: gpui::Point<f32>,
 4114        window: &mut Window,
 4115        cx: &mut Context<Self>,
 4116    ) {
 4117        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4118
 4119        if self.columnar_selection_state.is_some() {
 4120            self.select_columns(position, goal_column, &display_map, window, cx);
 4121        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4122            let buffer = display_map.buffer_snapshot();
 4123            let head;
 4124            let tail;
 4125            let mode = self.selections.pending_mode().unwrap();
 4126            match &mode {
 4127                SelectMode::Character => {
 4128                    head = position.to_point(&display_map);
 4129                    tail = pending.tail().to_point(buffer);
 4130                }
 4131                SelectMode::Word(original_range) => {
 4132                    let offset = display_map
 4133                        .clip_point(position, Bias::Left)
 4134                        .to_offset(&display_map, Bias::Left);
 4135                    let original_range = original_range.to_offset(buffer);
 4136
 4137                    let head_offset = if buffer.is_inside_word(offset, None)
 4138                        || original_range.contains(&offset)
 4139                    {
 4140                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4141                        if word_range.start < original_range.start {
 4142                            word_range.start
 4143                        } else {
 4144                            word_range.end
 4145                        }
 4146                    } else {
 4147                        offset
 4148                    };
 4149
 4150                    head = head_offset.to_point(buffer);
 4151                    if head_offset <= original_range.start {
 4152                        tail = original_range.end.to_point(buffer);
 4153                    } else {
 4154                        tail = original_range.start.to_point(buffer);
 4155                    }
 4156                }
 4157                SelectMode::Line(original_range) => {
 4158                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4159
 4160                    let position = display_map
 4161                        .clip_point(position, Bias::Left)
 4162                        .to_point(&display_map);
 4163                    let line_start = display_map.prev_line_boundary(position).0;
 4164                    let next_line_start = buffer.clip_point(
 4165                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4166                        Bias::Left,
 4167                    );
 4168
 4169                    if line_start < original_range.start {
 4170                        head = line_start
 4171                    } else {
 4172                        head = next_line_start
 4173                    }
 4174
 4175                    if head <= original_range.start {
 4176                        tail = original_range.end;
 4177                    } else {
 4178                        tail = original_range.start;
 4179                    }
 4180                }
 4181                SelectMode::All => {
 4182                    return;
 4183                }
 4184            };
 4185
 4186            if head < tail {
 4187                pending.start = buffer.anchor_before(head);
 4188                pending.end = buffer.anchor_before(tail);
 4189                pending.reversed = true;
 4190            } else {
 4191                pending.start = buffer.anchor_before(tail);
 4192                pending.end = buffer.anchor_before(head);
 4193                pending.reversed = false;
 4194            }
 4195
 4196            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4197                s.set_pending(pending.clone(), mode);
 4198            });
 4199        } else {
 4200            log::error!("update_selection dispatched with no pending selection");
 4201            return;
 4202        }
 4203
 4204        self.apply_scroll_delta(scroll_delta, window, cx);
 4205        cx.notify();
 4206    }
 4207
 4208    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4209        self.columnar_selection_state.take();
 4210        if let Some(pending_mode) = self.selections.pending_mode() {
 4211            let selections = self
 4212                .selections
 4213                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4214            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4215                s.select(selections);
 4216                s.clear_pending();
 4217                if s.is_extending() {
 4218                    s.set_is_extending(false);
 4219                } else {
 4220                    s.set_select_mode(pending_mode);
 4221                }
 4222            });
 4223        }
 4224    }
 4225
 4226    fn select_columns(
 4227        &mut self,
 4228        head: DisplayPoint,
 4229        goal_column: u32,
 4230        display_map: &DisplaySnapshot,
 4231        window: &mut Window,
 4232        cx: &mut Context<Self>,
 4233    ) {
 4234        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4235            return;
 4236        };
 4237
 4238        let tail = match columnar_state {
 4239            ColumnarSelectionState::FromMouse {
 4240                selection_tail,
 4241                display_point,
 4242            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4243            ColumnarSelectionState::FromSelection { selection_tail } => {
 4244                selection_tail.to_display_point(display_map)
 4245            }
 4246        };
 4247
 4248        let start_row = cmp::min(tail.row(), head.row());
 4249        let end_row = cmp::max(tail.row(), head.row());
 4250        let start_column = cmp::min(tail.column(), goal_column);
 4251        let end_column = cmp::max(tail.column(), goal_column);
 4252        let reversed = start_column < tail.column();
 4253
 4254        let selection_ranges = (start_row.0..=end_row.0)
 4255            .map(DisplayRow)
 4256            .filter_map(|row| {
 4257                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4258                    || start_column <= display_map.line_len(row))
 4259                    && !display_map.is_block_line(row)
 4260                {
 4261                    let start = display_map
 4262                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4263                        .to_point(display_map);
 4264                    let end = display_map
 4265                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4266                        .to_point(display_map);
 4267                    if reversed {
 4268                        Some(end..start)
 4269                    } else {
 4270                        Some(start..end)
 4271                    }
 4272                } else {
 4273                    None
 4274                }
 4275            })
 4276            .collect::<Vec<_>>();
 4277        if selection_ranges.is_empty() {
 4278            return;
 4279        }
 4280
 4281        let ranges = match columnar_state {
 4282            ColumnarSelectionState::FromMouse { .. } => {
 4283                let mut non_empty_ranges = selection_ranges
 4284                    .iter()
 4285                    .filter(|selection_range| selection_range.start != selection_range.end)
 4286                    .peekable();
 4287                if non_empty_ranges.peek().is_some() {
 4288                    non_empty_ranges.cloned().collect()
 4289                } else {
 4290                    selection_ranges
 4291                }
 4292            }
 4293            _ => selection_ranges,
 4294        };
 4295
 4296        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4297            s.select_ranges(ranges);
 4298        });
 4299        cx.notify();
 4300    }
 4301
 4302    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4303        self.selections
 4304            .all_adjusted(snapshot)
 4305            .iter()
 4306            .any(|selection| !selection.is_empty())
 4307    }
 4308
 4309    pub fn has_pending_nonempty_selection(&self) -> bool {
 4310        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4311            Some(Selection { start, end, .. }) => start != end,
 4312            None => false,
 4313        };
 4314
 4315        pending_nonempty_selection
 4316            || (self.columnar_selection_state.is_some()
 4317                && self.selections.disjoint_anchors().len() > 1)
 4318    }
 4319
 4320    pub fn has_pending_selection(&self) -> bool {
 4321        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4322    }
 4323
 4324    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4325        self.selection_mark_mode = false;
 4326        self.selection_drag_state = SelectionDragState::None;
 4327
 4328        if self.dismiss_menus_and_popups(true, window, cx) {
 4329            cx.notify();
 4330            return;
 4331        }
 4332        if self.clear_expanded_diff_hunks(cx) {
 4333            cx.notify();
 4334            return;
 4335        }
 4336        if self.show_git_blame_gutter {
 4337            self.show_git_blame_gutter = false;
 4338            cx.notify();
 4339            return;
 4340        }
 4341
 4342        if self.mode.is_full()
 4343            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4344        {
 4345            cx.notify();
 4346            return;
 4347        }
 4348
 4349        cx.propagate();
 4350    }
 4351
 4352    pub fn dismiss_menus_and_popups(
 4353        &mut self,
 4354        is_user_requested: bool,
 4355        window: &mut Window,
 4356        cx: &mut Context<Self>,
 4357    ) -> bool {
 4358        let mut dismissed = false;
 4359
 4360        dismissed |= self.take_rename(false, window, cx).is_some();
 4361        dismissed |= self.hide_blame_popover(true, cx);
 4362        dismissed |= hide_hover(self, cx);
 4363        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4364        dismissed |= self.hide_context_menu(window, cx).is_some();
 4365        dismissed |= self.mouse_context_menu.take().is_some();
 4366        dismissed |= is_user_requested && self.discard_edit_prediction(true, cx);
 4367        dismissed |= self.snippet_stack.pop().is_some();
 4368        if !self.diff_review_overlays.is_empty() {
 4369            self.dismiss_all_diff_review_overlays(cx);
 4370            dismissed = true;
 4371        }
 4372
 4373        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4374            self.dismiss_diagnostics(cx);
 4375            dismissed = true;
 4376        }
 4377
 4378        dismissed
 4379    }
 4380
 4381    fn linked_editing_ranges_for(
 4382        &self,
 4383        selection: Range<text::Anchor>,
 4384        cx: &App,
 4385    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4386        if self.linked_edit_ranges.is_empty() {
 4387            return None;
 4388        }
 4389        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4390            selection.end.buffer_id.and_then(|end_buffer_id| {
 4391                if selection.start.buffer_id != Some(end_buffer_id) {
 4392                    return None;
 4393                }
 4394                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4395                let snapshot = buffer.read(cx).snapshot();
 4396                self.linked_edit_ranges
 4397                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4398                    .map(|ranges| (ranges, snapshot, buffer))
 4399            })?;
 4400        use text::ToOffset as TO;
 4401        // find offset from the start of current range to current cursor position
 4402        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4403
 4404        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4405        let start_difference = start_offset - start_byte_offset;
 4406        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4407        let end_difference = end_offset - start_byte_offset;
 4408        // Current range has associated linked ranges.
 4409        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4410        for range in linked_ranges.iter() {
 4411            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4412            let end_offset = start_offset + end_difference;
 4413            let start_offset = start_offset + start_difference;
 4414            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4415                continue;
 4416            }
 4417            if self.selections.disjoint_anchor_ranges().any(|s| {
 4418                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4419                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4420                {
 4421                    return false;
 4422                }
 4423                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4424                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4425            }) {
 4426                continue;
 4427            }
 4428            let start = buffer_snapshot.anchor_after(start_offset);
 4429            let end = buffer_snapshot.anchor_after(end_offset);
 4430            linked_edits
 4431                .entry(buffer.clone())
 4432                .or_default()
 4433                .push(start..end);
 4434        }
 4435        Some(linked_edits)
 4436    }
 4437
 4438    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4439        let text: Arc<str> = text.into();
 4440
 4441        if self.read_only(cx) {
 4442            return;
 4443        }
 4444
 4445        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4446
 4447        self.unfold_buffers_with_selections(cx);
 4448
 4449        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4450        let mut bracket_inserted = false;
 4451        let mut edits = Vec::new();
 4452        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4453        let mut new_selections = Vec::with_capacity(selections.len());
 4454        let mut new_autoclose_regions = Vec::new();
 4455        let snapshot = self.buffer.read(cx).read(cx);
 4456        let mut clear_linked_edit_ranges = false;
 4457        let mut all_selections_read_only = true;
 4458        let mut has_adjacent_edits = false;
 4459        let mut in_adjacent_group = false;
 4460
 4461        let mut regions = self
 4462            .selections_with_autoclose_regions(selections, &snapshot)
 4463            .peekable();
 4464
 4465        while let Some((selection, autoclose_region)) = regions.next() {
 4466            if snapshot
 4467                .point_to_buffer_point(selection.head())
 4468                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4469            {
 4470                continue;
 4471            }
 4472            if snapshot
 4473                .point_to_buffer_point(selection.tail())
 4474                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4475            {
 4476                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4477                continue;
 4478            }
 4479            all_selections_read_only = false;
 4480
 4481            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4482                // Determine if the inserted text matches the opening or closing
 4483                // bracket of any of this language's bracket pairs.
 4484                let mut bracket_pair = None;
 4485                let mut is_bracket_pair_start = false;
 4486                let mut is_bracket_pair_end = false;
 4487                if !text.is_empty() {
 4488                    let mut bracket_pair_matching_end = None;
 4489                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4490                    //  and they are removing the character that triggered IME popup.
 4491                    for (pair, enabled) in scope.brackets() {
 4492                        if !pair.close && !pair.surround {
 4493                            continue;
 4494                        }
 4495
 4496                        if enabled && pair.start.ends_with(text.as_ref()) {
 4497                            let prefix_len = pair.start.len() - text.len();
 4498                            let preceding_text_matches_prefix = prefix_len == 0
 4499                                || (selection.start.column >= (prefix_len as u32)
 4500                                    && snapshot.contains_str_at(
 4501                                        Point::new(
 4502                                            selection.start.row,
 4503                                            selection.start.column - (prefix_len as u32),
 4504                                        ),
 4505                                        &pair.start[..prefix_len],
 4506                                    ));
 4507                            if preceding_text_matches_prefix {
 4508                                bracket_pair = Some(pair.clone());
 4509                                is_bracket_pair_start = true;
 4510                                break;
 4511                            }
 4512                        }
 4513                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4514                        {
 4515                            // take first bracket pair matching end, but don't break in case a later bracket
 4516                            // pair matches start
 4517                            bracket_pair_matching_end = Some(pair.clone());
 4518                        }
 4519                    }
 4520                    if let Some(end) = bracket_pair_matching_end
 4521                        && bracket_pair.is_none()
 4522                    {
 4523                        bracket_pair = Some(end);
 4524                        is_bracket_pair_end = true;
 4525                    }
 4526                }
 4527
 4528                if let Some(bracket_pair) = bracket_pair {
 4529                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4530                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4531                    let auto_surround =
 4532                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4533                    if selection.is_empty() {
 4534                        if is_bracket_pair_start {
 4535                            // If the inserted text is a suffix of an opening bracket and the
 4536                            // selection is preceded by the rest of the opening bracket, then
 4537                            // insert the closing bracket.
 4538                            let following_text_allows_autoclose = snapshot
 4539                                .chars_at(selection.start)
 4540                                .next()
 4541                                .is_none_or(|c| scope.should_autoclose_before(c));
 4542
 4543                            let preceding_text_allows_autoclose = selection.start.column == 0
 4544                                || snapshot
 4545                                    .reversed_chars_at(selection.start)
 4546                                    .next()
 4547                                    .is_none_or(|c| {
 4548                                        bracket_pair.start != bracket_pair.end
 4549                                            || !snapshot
 4550                                                .char_classifier_at(selection.start)
 4551                                                .is_word(c)
 4552                                    });
 4553
 4554                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4555                                && bracket_pair.start.len() == 1
 4556                            {
 4557                                let target = bracket_pair.start.chars().next().unwrap();
 4558                                let mut byte_offset = 0u32;
 4559                                let current_line_count = snapshot
 4560                                    .reversed_chars_at(selection.start)
 4561                                    .take_while(|&c| c != '\n')
 4562                                    .filter(|c| {
 4563                                        byte_offset += c.len_utf8() as u32;
 4564                                        if *c != target {
 4565                                            return false;
 4566                                        }
 4567
 4568                                        let point = Point::new(
 4569                                            selection.start.row,
 4570                                            selection.start.column.saturating_sub(byte_offset),
 4571                                        );
 4572
 4573                                        let is_enabled = snapshot
 4574                                            .language_scope_at(point)
 4575                                            .and_then(|scope| {
 4576                                                scope
 4577                                                    .brackets()
 4578                                                    .find(|(pair, _)| {
 4579                                                        pair.start == bracket_pair.start
 4580                                                    })
 4581                                                    .map(|(_, enabled)| enabled)
 4582                                            })
 4583                                            .unwrap_or(true);
 4584
 4585                                        let is_delimiter = snapshot
 4586                                            .language_scope_at(Point::new(
 4587                                                point.row,
 4588                                                point.column + 1,
 4589                                            ))
 4590                                            .and_then(|scope| {
 4591                                                scope
 4592                                                    .brackets()
 4593                                                    .find(|(pair, _)| {
 4594                                                        pair.start == bracket_pair.start
 4595                                                    })
 4596                                                    .map(|(_, enabled)| !enabled)
 4597                                            })
 4598                                            .unwrap_or(false);
 4599
 4600                                        is_enabled && !is_delimiter
 4601                                    })
 4602                                    .count();
 4603                                current_line_count % 2 == 1
 4604                            } else {
 4605                                false
 4606                            };
 4607
 4608                            if autoclose
 4609                                && bracket_pair.close
 4610                                && following_text_allows_autoclose
 4611                                && preceding_text_allows_autoclose
 4612                                && !is_closing_quote
 4613                            {
 4614                                let anchor = snapshot.anchor_before(selection.end);
 4615                                new_selections.push((selection.map(|_| anchor), text.len()));
 4616                                new_autoclose_regions.push((
 4617                                    anchor,
 4618                                    text.len(),
 4619                                    selection.id,
 4620                                    bracket_pair.clone(),
 4621                                ));
 4622                                edits.push((
 4623                                    selection.range(),
 4624                                    format!("{}{}", text, bracket_pair.end).into(),
 4625                                ));
 4626                                bracket_inserted = true;
 4627                                continue;
 4628                            }
 4629                        }
 4630
 4631                        if let Some(region) = autoclose_region {
 4632                            // If the selection is followed by an auto-inserted closing bracket,
 4633                            // then don't insert that closing bracket again; just move the selection
 4634                            // past the closing bracket.
 4635                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4636                                && text.as_ref() == region.pair.end.as_str()
 4637                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4638                            if should_skip {
 4639                                let anchor = snapshot.anchor_after(selection.end);
 4640                                new_selections
 4641                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4642                                continue;
 4643                            }
 4644                        }
 4645
 4646                        let always_treat_brackets_as_autoclosed = snapshot
 4647                            .language_settings_at(selection.start, cx)
 4648                            .always_treat_brackets_as_autoclosed;
 4649                        if always_treat_brackets_as_autoclosed
 4650                            && is_bracket_pair_end
 4651                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4652                        {
 4653                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4654                            // and the inserted text is a closing bracket and the selection is followed
 4655                            // by the closing bracket then move the selection past the closing bracket.
 4656                            let anchor = snapshot.anchor_after(selection.end);
 4657                            new_selections.push((selection.map(|_| anchor), text.len()));
 4658                            continue;
 4659                        }
 4660                    }
 4661                    // If an opening bracket is 1 character long and is typed while
 4662                    // text is selected, then surround that text with the bracket pair.
 4663                    else if auto_surround
 4664                        && bracket_pair.surround
 4665                        && is_bracket_pair_start
 4666                        && bracket_pair.start.chars().count() == 1
 4667                    {
 4668                        edits.push((selection.start..selection.start, text.clone()));
 4669                        edits.push((
 4670                            selection.end..selection.end,
 4671                            bracket_pair.end.as_str().into(),
 4672                        ));
 4673                        bracket_inserted = true;
 4674                        new_selections.push((
 4675                            Selection {
 4676                                id: selection.id,
 4677                                start: snapshot.anchor_after(selection.start),
 4678                                end: snapshot.anchor_before(selection.end),
 4679                                reversed: selection.reversed,
 4680                                goal: selection.goal,
 4681                            },
 4682                            0,
 4683                        ));
 4684                        continue;
 4685                    }
 4686                }
 4687            }
 4688
 4689            if self.auto_replace_emoji_shortcode
 4690                && selection.is_empty()
 4691                && text.as_ref().ends_with(':')
 4692                && let Some(possible_emoji_short_code) =
 4693                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4694                && !possible_emoji_short_code.is_empty()
 4695                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4696            {
 4697                let emoji_shortcode_start = Point::new(
 4698                    selection.start.row,
 4699                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4700                );
 4701
 4702                // Remove shortcode from buffer
 4703                edits.push((
 4704                    emoji_shortcode_start..selection.start,
 4705                    "".to_string().into(),
 4706                ));
 4707                new_selections.push((
 4708                    Selection {
 4709                        id: selection.id,
 4710                        start: snapshot.anchor_after(emoji_shortcode_start),
 4711                        end: snapshot.anchor_before(selection.start),
 4712                        reversed: selection.reversed,
 4713                        goal: selection.goal,
 4714                    },
 4715                    0,
 4716                ));
 4717
 4718                // Insert emoji
 4719                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4720                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4721                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4722
 4723                continue;
 4724            }
 4725
 4726            let next_is_adjacent = regions
 4727                .peek()
 4728                .is_some_and(|(next, _)| selection.end == next.start);
 4729
 4730            // If not handling any auto-close operation, then just replace the selected
 4731            // text with the given input and move the selection to the end of the
 4732            // newly inserted text.
 4733            let anchor = if in_adjacent_group || next_is_adjacent {
 4734                // After edits the right bias would shift those anchor to the next visible fragment
 4735                // but we want to resolve to the previous one
 4736                snapshot.anchor_before(selection.end)
 4737            } else {
 4738                snapshot.anchor_after(selection.end)
 4739            };
 4740
 4741            if !self.linked_edit_ranges.is_empty() {
 4742                let start_anchor = snapshot.anchor_before(selection.start);
 4743
 4744                let is_word_char = text.chars().next().is_none_or(|char| {
 4745                    let classifier = snapshot
 4746                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4747                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4748                    classifier.is_word(char)
 4749                });
 4750
 4751                if is_word_char {
 4752                    if let Some(ranges) = self
 4753                        .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
 4754                    {
 4755                        for (buffer, edits) in ranges {
 4756                            linked_edits
 4757                                .entry(buffer.clone())
 4758                                .or_default()
 4759                                .extend(edits.into_iter().map(|range| (range, text.clone())));
 4760                        }
 4761                    }
 4762                } else {
 4763                    clear_linked_edit_ranges = true;
 4764                }
 4765            }
 4766
 4767            new_selections.push((selection.map(|_| anchor), 0));
 4768            edits.push((selection.start..selection.end, text.clone()));
 4769
 4770            has_adjacent_edits |= next_is_adjacent;
 4771            in_adjacent_group = next_is_adjacent;
 4772        }
 4773
 4774        if all_selections_read_only {
 4775            return;
 4776        }
 4777
 4778        drop(regions);
 4779        drop(snapshot);
 4780
 4781        self.transact(window, cx, |this, window, cx| {
 4782            if clear_linked_edit_ranges {
 4783                this.linked_edit_ranges.clear();
 4784            }
 4785            let initial_buffer_versions =
 4786                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4787
 4788            this.buffer.update(cx, |buffer, cx| {
 4789                if has_adjacent_edits {
 4790                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4791                } else {
 4792                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4793                }
 4794            });
 4795            for (buffer, edits) in linked_edits {
 4796                buffer.update(cx, |buffer, cx| {
 4797                    let snapshot = buffer.snapshot();
 4798                    let edits = edits
 4799                        .into_iter()
 4800                        .map(|(range, text)| {
 4801                            use text::ToPoint as TP;
 4802                            let end_point = TP::to_point(&range.end, &snapshot);
 4803                            let start_point = TP::to_point(&range.start, &snapshot);
 4804                            (start_point..end_point, text)
 4805                        })
 4806                        .sorted_by_key(|(range, _)| range.start);
 4807                    buffer.edit(edits, None, cx);
 4808                })
 4809            }
 4810            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4811            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4812            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4813            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4814                new_anchor_selections,
 4815                &map,
 4816            )
 4817            .zip(new_selection_deltas)
 4818            .map(|(selection, delta)| Selection {
 4819                id: selection.id,
 4820                start: selection.start + delta,
 4821                end: selection.end + delta,
 4822                reversed: selection.reversed,
 4823                goal: SelectionGoal::None,
 4824            })
 4825            .collect::<Vec<_>>();
 4826
 4827            let mut i = 0;
 4828            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4829                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4830                let start = map.buffer_snapshot().anchor_before(position);
 4831                let end = map.buffer_snapshot().anchor_after(position);
 4832                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4833                    match existing_state
 4834                        .range
 4835                        .start
 4836                        .cmp(&start, map.buffer_snapshot())
 4837                    {
 4838                        Ordering::Less => i += 1,
 4839                        Ordering::Greater => break,
 4840                        Ordering::Equal => {
 4841                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4842                                Ordering::Less => i += 1,
 4843                                Ordering::Equal => break,
 4844                                Ordering::Greater => break,
 4845                            }
 4846                        }
 4847                    }
 4848                }
 4849                this.autoclose_regions.insert(
 4850                    i,
 4851                    AutocloseRegion {
 4852                        selection_id,
 4853                        range: start..end,
 4854                        pair,
 4855                    },
 4856                );
 4857            }
 4858
 4859            let had_active_edit_prediction = this.has_active_edit_prediction();
 4860            this.change_selections(
 4861                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4862                window,
 4863                cx,
 4864                |s| s.select(new_selections),
 4865            );
 4866
 4867            if !bracket_inserted
 4868                && let Some(on_type_format_task) =
 4869                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 4870            {
 4871                on_type_format_task.detach_and_log_err(cx);
 4872            }
 4873
 4874            let editor_settings = EditorSettings::get_global(cx);
 4875            if bracket_inserted
 4876                && (editor_settings.auto_signature_help
 4877                    || editor_settings.show_signature_help_after_edits)
 4878            {
 4879                this.show_signature_help(&ShowSignatureHelp, window, cx);
 4880            }
 4881
 4882            let trigger_in_words =
 4883                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 4884            if this.hard_wrap.is_some() {
 4885                let latest: Range<Point> = this.selections.newest(&map).range();
 4886                if latest.is_empty()
 4887                    && this
 4888                        .buffer()
 4889                        .read(cx)
 4890                        .snapshot(cx)
 4891                        .line_len(MultiBufferRow(latest.start.row))
 4892                        == latest.start.column
 4893                {
 4894                    this.rewrap_impl(
 4895                        RewrapOptions {
 4896                            override_language_settings: true,
 4897                            preserve_existing_whitespace: true,
 4898                        },
 4899                        cx,
 4900                    )
 4901                }
 4902            }
 4903            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 4904            refresh_linked_ranges(this, window, cx);
 4905            this.refresh_edit_prediction(true, false, window, cx);
 4906            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 4907        });
 4908    }
 4909
 4910    fn find_possible_emoji_shortcode_at_position(
 4911        snapshot: &MultiBufferSnapshot,
 4912        position: Point,
 4913    ) -> Option<String> {
 4914        let mut chars = Vec::new();
 4915        let mut found_colon = false;
 4916        for char in snapshot.reversed_chars_at(position).take(100) {
 4917            // Found a possible emoji shortcode in the middle of the buffer
 4918            if found_colon {
 4919                if char.is_whitespace() {
 4920                    chars.reverse();
 4921                    return Some(chars.iter().collect());
 4922                }
 4923                // If the previous character is not a whitespace, we are in the middle of a word
 4924                // and we only want to complete the shortcode if the word is made up of other emojis
 4925                let mut containing_word = String::new();
 4926                for ch in snapshot
 4927                    .reversed_chars_at(position)
 4928                    .skip(chars.len() + 1)
 4929                    .take(100)
 4930                {
 4931                    if ch.is_whitespace() {
 4932                        break;
 4933                    }
 4934                    containing_word.push(ch);
 4935                }
 4936                let containing_word = containing_word.chars().rev().collect::<String>();
 4937                if util::word_consists_of_emojis(containing_word.as_str()) {
 4938                    chars.reverse();
 4939                    return Some(chars.iter().collect());
 4940                }
 4941            }
 4942
 4943            if char.is_whitespace() || !char.is_ascii() {
 4944                return None;
 4945            }
 4946            if char == ':' {
 4947                found_colon = true;
 4948            } else {
 4949                chars.push(char);
 4950            }
 4951        }
 4952        // Found a possible emoji shortcode at the beginning of the buffer
 4953        chars.reverse();
 4954        Some(chars.iter().collect())
 4955    }
 4956
 4957    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 4958        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4959        self.transact(window, cx, |this, window, cx| {
 4960            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 4961                let selections = this
 4962                    .selections
 4963                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 4964                let multi_buffer = this.buffer.read(cx);
 4965                let buffer = multi_buffer.snapshot(cx);
 4966                selections
 4967                    .iter()
 4968                    .map(|selection| {
 4969                        let start_point = selection.start.to_point(&buffer);
 4970                        let mut existing_indent =
 4971                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 4972                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 4973                        let start = selection.start;
 4974                        let end = selection.end;
 4975                        let selection_is_empty = start == end;
 4976                        let language_scope = buffer.language_scope_at(start);
 4977                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 4978                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 4979                                &buffer,
 4980                                start..end,
 4981                                language,
 4982                            )
 4983                                || NewlineConfig::insert_extra_newline_tree_sitter(
 4984                                    &buffer,
 4985                                    start..end,
 4986                                );
 4987
 4988                            let mut newline_config = NewlineConfig::Newline {
 4989                                additional_indent: IndentSize::spaces(0),
 4990                                extra_line_additional_indent: if needs_extra_newline {
 4991                                    Some(IndentSize::spaces(0))
 4992                                } else {
 4993                                    None
 4994                                },
 4995                                prevent_auto_indent: false,
 4996                            };
 4997
 4998                            let comment_delimiter = maybe!({
 4999                                if !selection_is_empty {
 5000                                    return None;
 5001                                }
 5002
 5003                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5004                                    return None;
 5005                                }
 5006
 5007                                return comment_delimiter_for_newline(
 5008                                    &start_point,
 5009                                    &buffer,
 5010                                    language,
 5011                                );
 5012                            });
 5013
 5014                            let doc_delimiter = maybe!({
 5015                                if !selection_is_empty {
 5016                                    return None;
 5017                                }
 5018
 5019                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5020                                    return None;
 5021                                }
 5022
 5023                                return documentation_delimiter_for_newline(
 5024                                    &start_point,
 5025                                    &buffer,
 5026                                    language,
 5027                                    &mut newline_config,
 5028                                );
 5029                            });
 5030
 5031                            let list_delimiter = maybe!({
 5032                                if !selection_is_empty {
 5033                                    return None;
 5034                                }
 5035
 5036                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5037                                    return None;
 5038                                }
 5039
 5040                                return list_delimiter_for_newline(
 5041                                    &start_point,
 5042                                    &buffer,
 5043                                    language,
 5044                                    &mut newline_config,
 5045                                );
 5046                            });
 5047
 5048                            (
 5049                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5050                                newline_config,
 5051                            )
 5052                        } else {
 5053                            (
 5054                                None,
 5055                                NewlineConfig::Newline {
 5056                                    additional_indent: IndentSize::spaces(0),
 5057                                    extra_line_additional_indent: None,
 5058                                    prevent_auto_indent: false,
 5059                                },
 5060                            )
 5061                        };
 5062
 5063                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5064                            NewlineConfig::ClearCurrentLine => {
 5065                                let row_start =
 5066                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5067                                (row_start, String::new(), false)
 5068                            }
 5069                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5070                                let row_start =
 5071                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5072                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5073                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5074                                let reduced_indent =
 5075                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5076                                let mut new_text = String::new();
 5077                                new_text.extend(reduced_indent.chars());
 5078                                new_text.push_str(continuation);
 5079                                (row_start, new_text, true)
 5080                            }
 5081                            NewlineConfig::Newline {
 5082                                additional_indent,
 5083                                extra_line_additional_indent,
 5084                                prevent_auto_indent,
 5085                            } => {
 5086                                let capacity_for_delimiter =
 5087                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5088                                let extra_line_len = extra_line_additional_indent
 5089                                    .map(|i| 1 + existing_indent.len as usize + i.len as usize)
 5090                                    .unwrap_or(0);
 5091                                let mut new_text = String::with_capacity(
 5092                                    1 + capacity_for_delimiter
 5093                                        + existing_indent.len as usize
 5094                                        + additional_indent.len as usize
 5095                                        + extra_line_len,
 5096                                );
 5097                                new_text.push('\n');
 5098                                new_text.extend(existing_indent.chars());
 5099                                new_text.extend(additional_indent.chars());
 5100                                if let Some(delimiter) = &delimiter {
 5101                                    new_text.push_str(delimiter);
 5102                                }
 5103                                if let Some(extra_indent) = extra_line_additional_indent {
 5104                                    new_text.push('\n');
 5105                                    new_text.extend(existing_indent.chars());
 5106                                    new_text.extend(extra_indent.chars());
 5107                                }
 5108                                (start, new_text, *prevent_auto_indent)
 5109                            }
 5110                        };
 5111
 5112                        let anchor = buffer.anchor_after(end);
 5113                        let new_selection = selection.map(|_| anchor);
 5114                        (
 5115                            ((edit_start..end, new_text), prevent_auto_indent),
 5116                            (newline_config.has_extra_line(), new_selection),
 5117                        )
 5118                    })
 5119                    .unzip()
 5120            };
 5121
 5122            let mut auto_indent_edits = Vec::new();
 5123            let mut edits = Vec::new();
 5124            for (edit, prevent_auto_indent) in edits_with_flags {
 5125                if prevent_auto_indent {
 5126                    edits.push(edit);
 5127                } else {
 5128                    auto_indent_edits.push(edit);
 5129                }
 5130            }
 5131            if !edits.is_empty() {
 5132                this.edit(edits, cx);
 5133            }
 5134            if !auto_indent_edits.is_empty() {
 5135                this.edit_with_autoindent(auto_indent_edits, cx);
 5136            }
 5137
 5138            let buffer = this.buffer.read(cx).snapshot(cx);
 5139            let new_selections = selection_info
 5140                .into_iter()
 5141                .map(|(extra_newline_inserted, new_selection)| {
 5142                    let mut cursor = new_selection.end.to_point(&buffer);
 5143                    if extra_newline_inserted {
 5144                        cursor.row -= 1;
 5145                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5146                    }
 5147                    new_selection.map(|_| cursor)
 5148                })
 5149                .collect();
 5150
 5151            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5152            this.refresh_edit_prediction(true, false, window, cx);
 5153            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5154                task.detach_and_log_err(cx);
 5155            }
 5156        });
 5157    }
 5158
 5159    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5160        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5161
 5162        let buffer = self.buffer.read(cx);
 5163        let snapshot = buffer.snapshot(cx);
 5164
 5165        let mut edits = Vec::new();
 5166        let mut rows = Vec::new();
 5167
 5168        for (rows_inserted, selection) in self
 5169            .selections
 5170            .all_adjusted(&self.display_snapshot(cx))
 5171            .into_iter()
 5172            .enumerate()
 5173        {
 5174            let cursor = selection.head();
 5175            let row = cursor.row;
 5176
 5177            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5178
 5179            let newline = "\n".to_string();
 5180            edits.push((start_of_line..start_of_line, newline));
 5181
 5182            rows.push(row + rows_inserted as u32);
 5183        }
 5184
 5185        self.transact(window, cx, |editor, window, cx| {
 5186            editor.edit(edits, cx);
 5187
 5188            editor.change_selections(Default::default(), window, cx, |s| {
 5189                let mut index = 0;
 5190                s.move_cursors_with(|map, _, _| {
 5191                    let row = rows[index];
 5192                    index += 1;
 5193
 5194                    let point = Point::new(row, 0);
 5195                    let boundary = map.next_line_boundary(point).1;
 5196                    let clipped = map.clip_point(boundary, Bias::Left);
 5197
 5198                    (clipped, SelectionGoal::None)
 5199                });
 5200            });
 5201
 5202            let mut indent_edits = Vec::new();
 5203            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5204            for row in rows {
 5205                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5206                for (row, indent) in indents {
 5207                    if indent.len == 0 {
 5208                        continue;
 5209                    }
 5210
 5211                    let text = match indent.kind {
 5212                        IndentKind::Space => " ".repeat(indent.len as usize),
 5213                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5214                    };
 5215                    let point = Point::new(row.0, 0);
 5216                    indent_edits.push((point..point, text));
 5217                }
 5218            }
 5219            editor.edit(indent_edits, cx);
 5220            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5221                format.detach_and_log_err(cx);
 5222            }
 5223        });
 5224    }
 5225
 5226    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5227        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5228
 5229        let buffer = self.buffer.read(cx);
 5230        let snapshot = buffer.snapshot(cx);
 5231
 5232        let mut edits = Vec::new();
 5233        let mut rows = Vec::new();
 5234        let mut rows_inserted = 0;
 5235
 5236        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5237            let cursor = selection.head();
 5238            let row = cursor.row;
 5239
 5240            let point = Point::new(row + 1, 0);
 5241            let start_of_line = snapshot.clip_point(point, Bias::Left);
 5242
 5243            let newline = "\n".to_string();
 5244            edits.push((start_of_line..start_of_line, newline));
 5245
 5246            rows_inserted += 1;
 5247            rows.push(row + rows_inserted);
 5248        }
 5249
 5250        self.transact(window, cx, |editor, window, cx| {
 5251            editor.edit(edits, cx);
 5252
 5253            editor.change_selections(Default::default(), window, cx, |s| {
 5254                let mut index = 0;
 5255                s.move_cursors_with(|map, _, _| {
 5256                    let row = rows[index];
 5257                    index += 1;
 5258
 5259                    let point = Point::new(row, 0);
 5260                    let boundary = map.next_line_boundary(point).1;
 5261                    let clipped = map.clip_point(boundary, Bias::Left);
 5262
 5263                    (clipped, SelectionGoal::None)
 5264                });
 5265            });
 5266
 5267            let mut indent_edits = Vec::new();
 5268            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5269            for row in rows {
 5270                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5271                for (row, indent) in indents {
 5272                    if indent.len == 0 {
 5273                        continue;
 5274                    }
 5275
 5276                    let text = match indent.kind {
 5277                        IndentKind::Space => " ".repeat(indent.len as usize),
 5278                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5279                    };
 5280                    let point = Point::new(row.0, 0);
 5281                    indent_edits.push((point..point, text));
 5282                }
 5283            }
 5284            editor.edit(indent_edits, cx);
 5285            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5286                format.detach_and_log_err(cx);
 5287            }
 5288        });
 5289    }
 5290
 5291    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5292        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5293            original_indent_columns: Vec::new(),
 5294        });
 5295        self.insert_with_autoindent_mode(text, autoindent, window, cx);
 5296    }
 5297
 5298    fn insert_with_autoindent_mode(
 5299        &mut self,
 5300        text: &str,
 5301        autoindent_mode: Option<AutoindentMode>,
 5302        window: &mut Window,
 5303        cx: &mut Context<Self>,
 5304    ) {
 5305        if self.read_only(cx) {
 5306            return;
 5307        }
 5308
 5309        let text: Arc<str> = text.into();
 5310        self.transact(window, cx, |this, window, cx| {
 5311            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5312            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5313                let anchors = {
 5314                    let snapshot = buffer.read(cx);
 5315                    old_selections
 5316                        .iter()
 5317                        .map(|s| {
 5318                            let anchor = snapshot.anchor_after(s.head());
 5319                            s.map(|_| anchor)
 5320                        })
 5321                        .collect::<Vec<_>>()
 5322                };
 5323                buffer.edit(
 5324                    old_selections
 5325                        .iter()
 5326                        .map(|s| (s.start..s.end, text.clone())),
 5327                    autoindent_mode,
 5328                    cx,
 5329                );
 5330                anchors
 5331            });
 5332
 5333            this.change_selections(Default::default(), window, cx, |s| {
 5334                s.select_anchors(selection_anchors);
 5335            });
 5336
 5337            cx.notify();
 5338        });
 5339    }
 5340
 5341    fn trigger_completion_on_input(
 5342        &mut self,
 5343        text: &str,
 5344        trigger_in_words: bool,
 5345        window: &mut Window,
 5346        cx: &mut Context<Self>,
 5347    ) {
 5348        let completions_source = self
 5349            .context_menu
 5350            .borrow()
 5351            .as_ref()
 5352            .and_then(|menu| match menu {
 5353                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5354                CodeContextMenu::CodeActions(_) => None,
 5355            });
 5356
 5357        match completions_source {
 5358            Some(CompletionsMenuSource::Words { .. }) => {
 5359                self.open_or_update_completions_menu(
 5360                    Some(CompletionsMenuSource::Words {
 5361                        ignore_threshold: false,
 5362                    }),
 5363                    None,
 5364                    trigger_in_words,
 5365                    window,
 5366                    cx,
 5367                );
 5368            }
 5369            _ => self.open_or_update_completions_menu(
 5370                None,
 5371                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5372                true,
 5373                window,
 5374                cx,
 5375            ),
 5376        }
 5377    }
 5378
 5379    /// If any empty selections is touching the start of its innermost containing autoclose
 5380    /// region, expand it to select the brackets.
 5381    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5382        let selections = self
 5383            .selections
 5384            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5385        let buffer = self.buffer.read(cx).read(cx);
 5386        let new_selections = self
 5387            .selections_with_autoclose_regions(selections, &buffer)
 5388            .map(|(mut selection, region)| {
 5389                if !selection.is_empty() {
 5390                    return selection;
 5391                }
 5392
 5393                if let Some(region) = region {
 5394                    let mut range = region.range.to_offset(&buffer);
 5395                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5396                        range.start -= region.pair.start.len();
 5397                        if buffer.contains_str_at(range.start, &region.pair.start)
 5398                            && buffer.contains_str_at(range.end, &region.pair.end)
 5399                        {
 5400                            range.end += region.pair.end.len();
 5401                            selection.start = range.start;
 5402                            selection.end = range.end;
 5403
 5404                            return selection;
 5405                        }
 5406                    }
 5407                }
 5408
 5409                let always_treat_brackets_as_autoclosed = buffer
 5410                    .language_settings_at(selection.start, cx)
 5411                    .always_treat_brackets_as_autoclosed;
 5412
 5413                if !always_treat_brackets_as_autoclosed {
 5414                    return selection;
 5415                }
 5416
 5417                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5418                    for (pair, enabled) in scope.brackets() {
 5419                        if !enabled || !pair.close {
 5420                            continue;
 5421                        }
 5422
 5423                        if buffer.contains_str_at(selection.start, &pair.end) {
 5424                            let pair_start_len = pair.start.len();
 5425                            if buffer.contains_str_at(
 5426                                selection.start.saturating_sub_usize(pair_start_len),
 5427                                &pair.start,
 5428                            ) {
 5429                                selection.start -= pair_start_len;
 5430                                selection.end += pair.end.len();
 5431
 5432                                return selection;
 5433                            }
 5434                        }
 5435                    }
 5436                }
 5437
 5438                selection
 5439            })
 5440            .collect();
 5441
 5442        drop(buffer);
 5443        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5444            selections.select(new_selections)
 5445        });
 5446    }
 5447
 5448    /// Iterate the given selections, and for each one, find the smallest surrounding
 5449    /// autoclose region. This uses the ordering of the selections and the autoclose
 5450    /// regions to avoid repeated comparisons.
 5451    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5452        &'a self,
 5453        selections: impl IntoIterator<Item = Selection<D>>,
 5454        buffer: &'a MultiBufferSnapshot,
 5455    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5456        let mut i = 0;
 5457        let mut regions = self.autoclose_regions.as_slice();
 5458        selections.into_iter().map(move |selection| {
 5459            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5460
 5461            let mut enclosing = None;
 5462            while let Some(pair_state) = regions.get(i) {
 5463                if pair_state.range.end.to_offset(buffer) < range.start {
 5464                    regions = &regions[i + 1..];
 5465                    i = 0;
 5466                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5467                    break;
 5468                } else {
 5469                    if pair_state.selection_id == selection.id {
 5470                        enclosing = Some(pair_state);
 5471                    }
 5472                    i += 1;
 5473                }
 5474            }
 5475
 5476            (selection, enclosing)
 5477        })
 5478    }
 5479
 5480    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5481    fn invalidate_autoclose_regions(
 5482        &mut self,
 5483        mut selections: &[Selection<Anchor>],
 5484        buffer: &MultiBufferSnapshot,
 5485    ) {
 5486        self.autoclose_regions.retain(|state| {
 5487            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5488                return false;
 5489            }
 5490
 5491            let mut i = 0;
 5492            while let Some(selection) = selections.get(i) {
 5493                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5494                    selections = &selections[1..];
 5495                    continue;
 5496                }
 5497                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5498                    break;
 5499                }
 5500                if selection.id == state.selection_id {
 5501                    return true;
 5502                } else {
 5503                    i += 1;
 5504                }
 5505            }
 5506            false
 5507        });
 5508    }
 5509
 5510    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5511        let offset = position.to_offset(buffer);
 5512        let (word_range, kind) =
 5513            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5514        if offset > word_range.start && kind == Some(CharKind::Word) {
 5515            Some(
 5516                buffer
 5517                    .text_for_range(word_range.start..offset)
 5518                    .collect::<String>(),
 5519            )
 5520        } else {
 5521            None
 5522        }
 5523    }
 5524
 5525    pub fn visible_excerpts(
 5526        &self,
 5527        lsp_related_only: bool,
 5528        cx: &mut Context<Editor>,
 5529    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5530        let project = self.project().cloned();
 5531        let multi_buffer = self.buffer().read(cx);
 5532        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5533        let multi_buffer_visible_start = self
 5534            .scroll_manager
 5535            .anchor()
 5536            .anchor
 5537            .to_point(&multi_buffer_snapshot);
 5538        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5539            multi_buffer_visible_start
 5540                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5541            Bias::Left,
 5542        );
 5543        multi_buffer_snapshot
 5544            .range_to_buffer_ranges(multi_buffer_visible_start..multi_buffer_visible_end)
 5545            .into_iter()
 5546            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5547            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5548                if !lsp_related_only {
 5549                    return Some((
 5550                        excerpt_id,
 5551                        (
 5552                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5553                            buffer.version().clone(),
 5554                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5555                        ),
 5556                    ));
 5557                }
 5558
 5559                let project = project.as_ref()?.read(cx);
 5560                let buffer_file = project::File::from_dyn(buffer.file())?;
 5561                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5562                let worktree_entry = buffer_worktree
 5563                    .read(cx)
 5564                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5565                if worktree_entry.is_ignored {
 5566                    None
 5567                } else {
 5568                    Some((
 5569                        excerpt_id,
 5570                        (
 5571                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5572                            buffer.version().clone(),
 5573                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5574                        ),
 5575                    ))
 5576                }
 5577            })
 5578            .collect()
 5579    }
 5580
 5581    pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
 5582        TextLayoutDetails {
 5583            text_system: window.text_system().clone(),
 5584            editor_style: self.style.clone().unwrap(),
 5585            rem_size: window.rem_size(),
 5586            scroll_anchor: self.scroll_manager.anchor(),
 5587            visible_rows: self.visible_line_count(),
 5588            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5589        }
 5590    }
 5591
 5592    fn trigger_on_type_formatting(
 5593        &self,
 5594        input: String,
 5595        window: &mut Window,
 5596        cx: &mut Context<Self>,
 5597    ) -> Option<Task<Result<()>>> {
 5598        if input.chars().count() != 1 {
 5599            return None;
 5600        }
 5601
 5602        let project = self.project()?;
 5603        let position = self.selections.newest_anchor().head();
 5604        let (buffer, buffer_position) = self
 5605            .buffer
 5606            .read(cx)
 5607            .text_anchor_for_position(position, cx)?;
 5608
 5609        let settings = language_settings::language_settings(
 5610            buffer
 5611                .read(cx)
 5612                .language_at(buffer_position)
 5613                .map(|l| l.name()),
 5614            buffer.read(cx).file(),
 5615            cx,
 5616        );
 5617        if !settings.use_on_type_format {
 5618            return None;
 5619        }
 5620
 5621        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5622        // hence we do LSP request & edit on host side only — add formats to host's history.
 5623        let push_to_lsp_host_history = true;
 5624        // If this is not the host, append its history with new edits.
 5625        let push_to_client_history = project.read(cx).is_via_collab();
 5626
 5627        let on_type_formatting = project.update(cx, |project, cx| {
 5628            project.on_type_format(
 5629                buffer.clone(),
 5630                buffer_position,
 5631                input,
 5632                push_to_lsp_host_history,
 5633                cx,
 5634            )
 5635        });
 5636        Some(cx.spawn_in(window, async move |editor, cx| {
 5637            if let Some(transaction) = on_type_formatting.await? {
 5638                if push_to_client_history {
 5639                    buffer.update(cx, |buffer, _| {
 5640                        buffer.push_transaction(transaction, Instant::now());
 5641                        buffer.finalize_last_transaction();
 5642                    });
 5643                }
 5644                editor.update(cx, |editor, cx| {
 5645                    editor.refresh_document_highlights(cx);
 5646                })?;
 5647            }
 5648            Ok(())
 5649        }))
 5650    }
 5651
 5652    pub fn show_word_completions(
 5653        &mut self,
 5654        _: &ShowWordCompletions,
 5655        window: &mut Window,
 5656        cx: &mut Context<Self>,
 5657    ) {
 5658        self.open_or_update_completions_menu(
 5659            Some(CompletionsMenuSource::Words {
 5660                ignore_threshold: true,
 5661            }),
 5662            None,
 5663            false,
 5664            window,
 5665            cx,
 5666        );
 5667    }
 5668
 5669    pub fn show_completions(
 5670        &mut self,
 5671        _: &ShowCompletions,
 5672        window: &mut Window,
 5673        cx: &mut Context<Self>,
 5674    ) {
 5675        self.open_or_update_completions_menu(None, None, false, window, cx);
 5676    }
 5677
 5678    fn open_or_update_completions_menu(
 5679        &mut self,
 5680        requested_source: Option<CompletionsMenuSource>,
 5681        trigger: Option<String>,
 5682        trigger_in_words: bool,
 5683        window: &mut Window,
 5684        cx: &mut Context<Self>,
 5685    ) {
 5686        if self.pending_rename.is_some() {
 5687            return;
 5688        }
 5689
 5690        let completions_source = self
 5691            .context_menu
 5692            .borrow()
 5693            .as_ref()
 5694            .and_then(|menu| match menu {
 5695                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5696                CodeContextMenu::CodeActions(_) => None,
 5697            });
 5698
 5699        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5700
 5701        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5702        // inserted and selected. To handle that case, the start of the selection is used so that
 5703        // the menu starts with all choices.
 5704        let position = self
 5705            .selections
 5706            .newest_anchor()
 5707            .start
 5708            .bias_right(&multibuffer_snapshot);
 5709        if position.diff_base_anchor.is_some() {
 5710            return;
 5711        }
 5712        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5713        let Some(buffer) = buffer_position
 5714            .text_anchor
 5715            .buffer_id
 5716            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5717        else {
 5718            return;
 5719        };
 5720        let buffer_snapshot = buffer.read(cx).snapshot();
 5721
 5722        let menu_is_open = matches!(
 5723            self.context_menu.borrow().as_ref(),
 5724            Some(CodeContextMenu::Completions(_))
 5725        );
 5726
 5727        let language = buffer_snapshot
 5728            .language_at(buffer_position.text_anchor)
 5729            .map(|language| language.name());
 5730
 5731        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5732        let completion_settings = language_settings.completions.clone();
 5733
 5734        let show_completions_on_input = self
 5735            .show_completions_on_input_override
 5736            .unwrap_or(language_settings.show_completions_on_input);
 5737        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5738            return;
 5739        }
 5740
 5741        let query: Option<Arc<String>> =
 5742            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5743                .map(|query| query.into());
 5744
 5745        drop(multibuffer_snapshot);
 5746
 5747        // Hide the current completions menu when query is empty. Without this, cached
 5748        // completions from before the trigger char may be reused (#32774).
 5749        if query.is_none() && menu_is_open {
 5750            self.hide_context_menu(window, cx);
 5751        }
 5752
 5753        let mut ignore_word_threshold = false;
 5754        let provider = match requested_source {
 5755            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5756            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5757                ignore_word_threshold = ignore_threshold;
 5758                None
 5759            }
 5760            Some(CompletionsMenuSource::SnippetChoices)
 5761            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5762                log::error!("bug: SnippetChoices requested_source is not handled");
 5763                None
 5764            }
 5765        };
 5766
 5767        let sort_completions = provider
 5768            .as_ref()
 5769            .is_some_and(|provider| provider.sort_completions());
 5770
 5771        let filter_completions = provider
 5772            .as_ref()
 5773            .is_none_or(|provider| provider.filter_completions());
 5774
 5775        let was_snippets_only = matches!(
 5776            completions_source,
 5777            Some(CompletionsMenuSource::SnippetsOnly)
 5778        );
 5779
 5780        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5781            if filter_completions {
 5782                menu.filter(
 5783                    query.clone().unwrap_or_default(),
 5784                    buffer_position.text_anchor,
 5785                    &buffer,
 5786                    provider.clone(),
 5787                    window,
 5788                    cx,
 5789                );
 5790            }
 5791            // When `is_incomplete` is false, no need to re-query completions when the current query
 5792            // is a suffix of the initial query.
 5793            let was_complete = !menu.is_incomplete;
 5794            if was_complete && !was_snippets_only {
 5795                // If the new query is a suffix of the old query (typing more characters) and
 5796                // the previous result was complete, the existing completions can be filtered.
 5797                //
 5798                // Note that snippet completions are always complete.
 5799                let query_matches = match (&menu.initial_query, &query) {
 5800                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 5801                    (None, _) => true,
 5802                    _ => false,
 5803                };
 5804                if query_matches {
 5805                    let position_matches = if menu.initial_position == position {
 5806                        true
 5807                    } else {
 5808                        let snapshot = self.buffer.read(cx).read(cx);
 5809                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 5810                    };
 5811                    if position_matches {
 5812                        return;
 5813                    }
 5814                }
 5815            }
 5816        };
 5817
 5818        let Anchor {
 5819            excerpt_id: buffer_excerpt_id,
 5820            text_anchor: buffer_position,
 5821            ..
 5822        } = buffer_position;
 5823
 5824        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 5825            buffer_snapshot.surrounding_word(buffer_position, None)
 5826        {
 5827            let word_to_exclude = buffer_snapshot
 5828                .text_for_range(word_range.clone())
 5829                .collect::<String>();
 5830            (
 5831                buffer_snapshot.anchor_before(word_range.start)
 5832                    ..buffer_snapshot.anchor_after(buffer_position),
 5833                Some(word_to_exclude),
 5834            )
 5835        } else {
 5836            (buffer_position..buffer_position, None)
 5837        };
 5838
 5839        let show_completion_documentation = buffer_snapshot
 5840            .settings_at(buffer_position, cx)
 5841            .show_completion_documentation;
 5842
 5843        // The document can be large, so stay in reasonable bounds when searching for words,
 5844        // otherwise completion pop-up might be slow to appear.
 5845        const WORD_LOOKUP_ROWS: u32 = 5_000;
 5846        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 5847        let min_word_search = buffer_snapshot.clip_point(
 5848            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 5849            Bias::Left,
 5850        );
 5851        let max_word_search = buffer_snapshot.clip_point(
 5852            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 5853            Bias::Right,
 5854        );
 5855        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 5856            ..buffer_snapshot.point_to_offset(max_word_search);
 5857
 5858        let skip_digits = query
 5859            .as_ref()
 5860            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 5861
 5862        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 5863            trigger.as_ref().is_none_or(|trigger| {
 5864                provider.is_completion_trigger(
 5865                    &buffer,
 5866                    position.text_anchor,
 5867                    trigger,
 5868                    trigger_in_words,
 5869                    cx,
 5870                )
 5871            })
 5872        });
 5873
 5874        let provider_responses = if let Some(provider) = &provider
 5875            && load_provider_completions
 5876        {
 5877            let trigger_character =
 5878                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 5879            let completion_context = CompletionContext {
 5880                trigger_kind: match &trigger_character {
 5881                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 5882                    None => CompletionTriggerKind::INVOKED,
 5883                },
 5884                trigger_character,
 5885            };
 5886
 5887            provider.completions(
 5888                buffer_excerpt_id,
 5889                &buffer,
 5890                buffer_position,
 5891                completion_context,
 5892                window,
 5893                cx,
 5894            )
 5895        } else {
 5896            Task::ready(Ok(Vec::new()))
 5897        };
 5898
 5899        let load_word_completions = if !self.word_completions_enabled {
 5900            false
 5901        } else if requested_source
 5902            == Some(CompletionsMenuSource::Words {
 5903                ignore_threshold: true,
 5904            })
 5905        {
 5906            true
 5907        } else {
 5908            load_provider_completions
 5909                && completion_settings.words != WordsCompletionMode::Disabled
 5910                && (ignore_word_threshold || {
 5911                    let words_min_length = completion_settings.words_min_length;
 5912                    // check whether word has at least `words_min_length` characters
 5913                    let query_chars = query.iter().flat_map(|q| q.chars());
 5914                    query_chars.take(words_min_length).count() == words_min_length
 5915                })
 5916        };
 5917
 5918        let mut words = if load_word_completions {
 5919            cx.background_spawn({
 5920                let buffer_snapshot = buffer_snapshot.clone();
 5921                async move {
 5922                    buffer_snapshot.words_in_range(WordsQuery {
 5923                        fuzzy_contents: None,
 5924                        range: word_search_range,
 5925                        skip_digits,
 5926                    })
 5927                }
 5928            })
 5929        } else {
 5930            Task::ready(BTreeMap::default())
 5931        };
 5932
 5933        let snippets = if let Some(provider) = &provider
 5934            && provider.show_snippets()
 5935            && let Some(project) = self.project()
 5936        {
 5937            let char_classifier = buffer_snapshot
 5938                .char_classifier_at(buffer_position)
 5939                .scope_context(Some(CharScopeContext::Completion));
 5940            project.update(cx, |project, cx| {
 5941                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 5942            })
 5943        } else {
 5944            Task::ready(Ok(CompletionResponse {
 5945                completions: Vec::new(),
 5946                display_options: Default::default(),
 5947                is_incomplete: false,
 5948            }))
 5949        };
 5950
 5951        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 5952
 5953        let id = post_inc(&mut self.next_completion_id);
 5954        let task = cx.spawn_in(window, async move |editor, cx| {
 5955            let Ok(()) = editor.update(cx, |this, _| {
 5956                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 5957            }) else {
 5958                return;
 5959            };
 5960
 5961            // TODO: Ideally completions from different sources would be selectively re-queried, so
 5962            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 5963            let mut completions = Vec::new();
 5964            let mut is_incomplete = false;
 5965            let mut display_options: Option<CompletionDisplayOptions> = None;
 5966            if let Some(provider_responses) = provider_responses.await.log_err()
 5967                && !provider_responses.is_empty()
 5968            {
 5969                for response in provider_responses {
 5970                    completions.extend(response.completions);
 5971                    is_incomplete = is_incomplete || response.is_incomplete;
 5972                    match display_options.as_mut() {
 5973                        None => {
 5974                            display_options = Some(response.display_options);
 5975                        }
 5976                        Some(options) => options.merge(&response.display_options),
 5977                    }
 5978                }
 5979                if completion_settings.words == WordsCompletionMode::Fallback {
 5980                    words = Task::ready(BTreeMap::default());
 5981                }
 5982            }
 5983            let display_options = display_options.unwrap_or_default();
 5984
 5985            let mut words = words.await;
 5986            if let Some(word_to_exclude) = &word_to_exclude {
 5987                words.remove(word_to_exclude);
 5988            }
 5989            for lsp_completion in &completions {
 5990                words.remove(&lsp_completion.new_text);
 5991            }
 5992            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 5993                replace_range: word_replace_range.clone(),
 5994                new_text: word.clone(),
 5995                label: CodeLabel::plain(word, None),
 5996                match_start: None,
 5997                snippet_deduplication_key: None,
 5998                icon_path: None,
 5999                documentation: None,
 6000                source: CompletionSource::BufferWord {
 6001                    word_range,
 6002                    resolved: false,
 6003                },
 6004                insert_text_mode: Some(InsertTextMode::AS_IS),
 6005                confirm: None,
 6006            }));
 6007
 6008            completions.extend(
 6009                snippets
 6010                    .await
 6011                    .into_iter()
 6012                    .flat_map(|response| response.completions),
 6013            );
 6014
 6015            let menu = if completions.is_empty() {
 6016                None
 6017            } else {
 6018                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6019                    let languages = editor
 6020                        .workspace
 6021                        .as_ref()
 6022                        .and_then(|(workspace, _)| workspace.upgrade())
 6023                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6024                    let menu = CompletionsMenu::new(
 6025                        id,
 6026                        requested_source.unwrap_or(if load_provider_completions {
 6027                            CompletionsMenuSource::Normal
 6028                        } else {
 6029                            CompletionsMenuSource::SnippetsOnly
 6030                        }),
 6031                        sort_completions,
 6032                        show_completion_documentation,
 6033                        position,
 6034                        query.clone(),
 6035                        is_incomplete,
 6036                        buffer.clone(),
 6037                        completions.into(),
 6038                        editor
 6039                            .context_menu()
 6040                            .borrow_mut()
 6041                            .as_ref()
 6042                            .map(|menu| menu.primary_scroll_handle()),
 6043                        display_options,
 6044                        snippet_sort_order,
 6045                        languages,
 6046                        language,
 6047                        cx,
 6048                    );
 6049
 6050                    let query = if filter_completions { query } else { None };
 6051                    let matches_task = menu.do_async_filtering(
 6052                        query.unwrap_or_default(),
 6053                        buffer_position,
 6054                        &buffer,
 6055                        cx,
 6056                    );
 6057                    (menu, matches_task)
 6058                }) else {
 6059                    return;
 6060                };
 6061
 6062                let matches = matches_task.await;
 6063
 6064                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6065                    // Newer menu already set, so exit.
 6066                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6067                        editor.context_menu.borrow().as_ref()
 6068                        && prev_menu.id > id
 6069                    {
 6070                        return;
 6071                    };
 6072
 6073                    // Only valid to take prev_menu because either the new menu is immediately set
 6074                    // below, or the menu is hidden.
 6075                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6076                        editor.context_menu.borrow_mut().take()
 6077                    {
 6078                        let position_matches =
 6079                            if prev_menu.initial_position == menu.initial_position {
 6080                                true
 6081                            } else {
 6082                                let snapshot = editor.buffer.read(cx).read(cx);
 6083                                prev_menu.initial_position.to_offset(&snapshot)
 6084                                    == menu.initial_position.to_offset(&snapshot)
 6085                            };
 6086                        if position_matches {
 6087                            // Preserve markdown cache before `set_filter_results` because it will
 6088                            // try to populate the documentation cache.
 6089                            menu.preserve_markdown_cache(prev_menu);
 6090                        }
 6091                    };
 6092
 6093                    menu.set_filter_results(matches, provider, window, cx);
 6094                }) else {
 6095                    return;
 6096                };
 6097
 6098                menu.visible().then_some(menu)
 6099            };
 6100
 6101            editor
 6102                .update_in(cx, |editor, window, cx| {
 6103                    if editor.focus_handle.is_focused(window)
 6104                        && let Some(menu) = menu
 6105                    {
 6106                        *editor.context_menu.borrow_mut() =
 6107                            Some(CodeContextMenu::Completions(menu));
 6108
 6109                        crate::hover_popover::hide_hover(editor, cx);
 6110                        if editor.show_edit_predictions_in_menu() {
 6111                            editor.update_visible_edit_prediction(window, cx);
 6112                        } else {
 6113                            editor.discard_edit_prediction(false, cx);
 6114                        }
 6115
 6116                        cx.notify();
 6117                        return;
 6118                    }
 6119
 6120                    if editor.completion_tasks.len() <= 1 {
 6121                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6122                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6123                        // If it was already hidden and we don't show edit predictions in the menu,
 6124                        // we should also show the edit prediction when available.
 6125                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6126                            editor.update_visible_edit_prediction(window, cx);
 6127                        }
 6128                    }
 6129                })
 6130                .ok();
 6131        });
 6132
 6133        self.completion_tasks.push((id, task));
 6134    }
 6135
 6136    #[cfg(feature = "test-support")]
 6137    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6138        let menu = self.context_menu.borrow();
 6139        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6140            let completions = menu.completions.borrow();
 6141            Some(completions.to_vec())
 6142        } else {
 6143            None
 6144        }
 6145    }
 6146
 6147    pub fn with_completions_menu_matching_id<R>(
 6148        &self,
 6149        id: CompletionId,
 6150        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6151    ) -> R {
 6152        let mut context_menu = self.context_menu.borrow_mut();
 6153        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6154            return f(None);
 6155        };
 6156        if completions_menu.id != id {
 6157            return f(None);
 6158        }
 6159        f(Some(completions_menu))
 6160    }
 6161
 6162    pub fn confirm_completion(
 6163        &mut self,
 6164        action: &ConfirmCompletion,
 6165        window: &mut Window,
 6166        cx: &mut Context<Self>,
 6167    ) -> Option<Task<Result<()>>> {
 6168        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6169        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6170    }
 6171
 6172    pub fn confirm_completion_insert(
 6173        &mut self,
 6174        _: &ConfirmCompletionInsert,
 6175        window: &mut Window,
 6176        cx: &mut Context<Self>,
 6177    ) -> Option<Task<Result<()>>> {
 6178        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6179        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6180    }
 6181
 6182    pub fn confirm_completion_replace(
 6183        &mut self,
 6184        _: &ConfirmCompletionReplace,
 6185        window: &mut Window,
 6186        cx: &mut Context<Self>,
 6187    ) -> Option<Task<Result<()>>> {
 6188        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6189        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6190    }
 6191
 6192    pub fn compose_completion(
 6193        &mut self,
 6194        action: &ComposeCompletion,
 6195        window: &mut Window,
 6196        cx: &mut Context<Self>,
 6197    ) -> Option<Task<Result<()>>> {
 6198        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6199        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6200    }
 6201
 6202    fn do_completion(
 6203        &mut self,
 6204        item_ix: Option<usize>,
 6205        intent: CompletionIntent,
 6206        window: &mut Window,
 6207        cx: &mut Context<Editor>,
 6208    ) -> Option<Task<Result<()>>> {
 6209        use language::ToOffset as _;
 6210
 6211        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6212        else {
 6213            return None;
 6214        };
 6215
 6216        let candidate_id = {
 6217            let entries = completions_menu.entries.borrow();
 6218            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6219            if self.show_edit_predictions_in_menu() {
 6220                self.discard_edit_prediction(true, cx);
 6221            }
 6222            mat.candidate_id
 6223        };
 6224
 6225        let completion = completions_menu
 6226            .completions
 6227            .borrow()
 6228            .get(candidate_id)?
 6229            .clone();
 6230        cx.stop_propagation();
 6231
 6232        let buffer_handle = completions_menu.buffer.clone();
 6233
 6234        let CompletionEdit {
 6235            new_text,
 6236            snippet,
 6237            replace_range,
 6238        } = process_completion_for_edit(
 6239            &completion,
 6240            intent,
 6241            &buffer_handle,
 6242            &completions_menu.initial_position.text_anchor,
 6243            cx,
 6244        );
 6245
 6246        let buffer = buffer_handle.read(cx);
 6247        let snapshot = self.buffer.read(cx).snapshot(cx);
 6248        let newest_anchor = self.selections.newest_anchor();
 6249        let replace_range_multibuffer = {
 6250            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6251            excerpt.map_range_from_buffer(replace_range.clone())
 6252        };
 6253        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6254            return None;
 6255        }
 6256
 6257        let old_text = buffer
 6258            .text_for_range(replace_range.clone())
 6259            .collect::<String>();
 6260        let lookbehind = newest_anchor
 6261            .start
 6262            .text_anchor
 6263            .to_offset(buffer)
 6264            .saturating_sub(replace_range.start.0);
 6265        let lookahead = replace_range
 6266            .end
 6267            .0
 6268            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6269        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6270        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6271
 6272        let selections = self
 6273            .selections
 6274            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6275        let mut ranges = Vec::new();
 6276        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 6277
 6278        for selection in &selections {
 6279            let range = if selection.id == newest_anchor.id {
 6280                replace_range_multibuffer.clone()
 6281            } else {
 6282                let mut range = selection.range();
 6283
 6284                // if prefix is present, don't duplicate it
 6285                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6286                    range.start = range.start.saturating_sub_usize(lookbehind);
 6287
 6288                    // if suffix is also present, mimic the newest cursor and replace it
 6289                    if selection.id != newest_anchor.id
 6290                        && snapshot.contains_str_at(range.end, suffix)
 6291                    {
 6292                        range.end += lookahead;
 6293                    }
 6294                }
 6295                range
 6296            };
 6297
 6298            ranges.push(range.clone());
 6299
 6300            if !self.linked_edit_ranges.is_empty() {
 6301                let start_anchor = snapshot.anchor_before(range.start);
 6302                let end_anchor = snapshot.anchor_after(range.end);
 6303                if let Some(ranges) = self
 6304                    .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
 6305                {
 6306                    for (buffer, edits) in ranges {
 6307                        linked_edits
 6308                            .entry(buffer.clone())
 6309                            .or_default()
 6310                            .extend(edits.into_iter().map(|range| (range, new_text.to_owned())));
 6311                    }
 6312                }
 6313            }
 6314        }
 6315
 6316        let common_prefix_len = old_text
 6317            .chars()
 6318            .zip(new_text.chars())
 6319            .take_while(|(a, b)| a == b)
 6320            .map(|(a, _)| a.len_utf8())
 6321            .sum::<usize>();
 6322
 6323        cx.emit(EditorEvent::InputHandled {
 6324            utf16_range_to_replace: None,
 6325            text: new_text[common_prefix_len..].into(),
 6326        });
 6327
 6328        self.transact(window, cx, |editor, window, cx| {
 6329            if let Some(mut snippet) = snippet {
 6330                snippet.text = new_text.to_string();
 6331                editor
 6332                    .insert_snippet(&ranges, snippet, window, cx)
 6333                    .log_err();
 6334            } else {
 6335                editor.buffer.update(cx, |multi_buffer, cx| {
 6336                    let auto_indent = match completion.insert_text_mode {
 6337                        Some(InsertTextMode::AS_IS) => None,
 6338                        _ => editor.autoindent_mode.clone(),
 6339                    };
 6340                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6341                    multi_buffer.edit(edits, auto_indent, cx);
 6342                });
 6343            }
 6344            for (buffer, edits) in linked_edits {
 6345                buffer.update(cx, |buffer, cx| {
 6346                    let snapshot = buffer.snapshot();
 6347                    let edits = edits
 6348                        .into_iter()
 6349                        .map(|(range, text)| {
 6350                            use text::ToPoint as TP;
 6351                            let end_point = TP::to_point(&range.end, &snapshot);
 6352                            let start_point = TP::to_point(&range.start, &snapshot);
 6353                            (start_point..end_point, text)
 6354                        })
 6355                        .sorted_by_key(|(range, _)| range.start);
 6356                    buffer.edit(edits, None, cx);
 6357                })
 6358            }
 6359
 6360            editor.refresh_edit_prediction(true, false, window, cx);
 6361        });
 6362        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6363
 6364        let show_new_completions_on_confirm = completion
 6365            .confirm
 6366            .as_ref()
 6367            .is_some_and(|confirm| confirm(intent, window, cx));
 6368        if show_new_completions_on_confirm {
 6369            self.open_or_update_completions_menu(None, None, false, window, cx);
 6370        }
 6371
 6372        let provider = self.completion_provider.as_ref()?;
 6373
 6374        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6375        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6376            let CompletionSource::Lsp {
 6377                lsp_completion,
 6378                server_id,
 6379                ..
 6380            } = &completion.source
 6381            else {
 6382                return None;
 6383            };
 6384            let lsp_command = lsp_completion.command.as_ref()?;
 6385            let available_commands = lsp_store
 6386                .read(cx)
 6387                .lsp_server_capabilities
 6388                .get(server_id)
 6389                .and_then(|server_capabilities| {
 6390                    server_capabilities
 6391                        .execute_command_provider
 6392                        .as_ref()
 6393                        .map(|options| options.commands.as_slice())
 6394                })?;
 6395            if available_commands.contains(&lsp_command.command) {
 6396                Some(CodeAction {
 6397                    server_id: *server_id,
 6398                    range: language::Anchor::MIN..language::Anchor::MIN,
 6399                    lsp_action: LspAction::Command(lsp_command.clone()),
 6400                    resolved: false,
 6401                })
 6402            } else {
 6403                None
 6404            }
 6405        });
 6406
 6407        drop(completion);
 6408        let apply_edits = provider.apply_additional_edits_for_completion(
 6409            buffer_handle.clone(),
 6410            completions_menu.completions.clone(),
 6411            candidate_id,
 6412            true,
 6413            cx,
 6414        );
 6415
 6416        let editor_settings = EditorSettings::get_global(cx);
 6417        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6418            // After the code completion is finished, users often want to know what signatures are needed.
 6419            // so we should automatically call signature_help
 6420            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6421        }
 6422
 6423        Some(cx.spawn_in(window, async move |editor, cx| {
 6424            apply_edits.await?;
 6425
 6426            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6427                let title = command.lsp_action.title().to_owned();
 6428                let project_transaction = lsp_store
 6429                    .update(cx, |lsp_store, cx| {
 6430                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6431                    })
 6432                    .await
 6433                    .context("applying post-completion command")?;
 6434                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6435                    Self::open_project_transaction(
 6436                        &editor,
 6437                        workspace.downgrade(),
 6438                        project_transaction,
 6439                        title,
 6440                        cx,
 6441                    )
 6442                    .await?;
 6443                }
 6444            }
 6445
 6446            Ok(())
 6447        }))
 6448    }
 6449
 6450    pub fn toggle_code_actions(
 6451        &mut self,
 6452        action: &ToggleCodeActions,
 6453        window: &mut Window,
 6454        cx: &mut Context<Self>,
 6455    ) {
 6456        let quick_launch = action.quick_launch;
 6457        let mut context_menu = self.context_menu.borrow_mut();
 6458        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6459            if code_actions.deployed_from == action.deployed_from {
 6460                // Toggle if we're selecting the same one
 6461                *context_menu = None;
 6462                cx.notify();
 6463                return;
 6464            } else {
 6465                // Otherwise, clear it and start a new one
 6466                *context_menu = None;
 6467                cx.notify();
 6468            }
 6469        }
 6470        drop(context_menu);
 6471        let snapshot = self.snapshot(window, cx);
 6472        let deployed_from = action.deployed_from.clone();
 6473        let action = action.clone();
 6474        self.completion_tasks.clear();
 6475        self.discard_edit_prediction(false, cx);
 6476
 6477        let multibuffer_point = match &action.deployed_from {
 6478            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6479                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6480            }
 6481            _ => self
 6482                .selections
 6483                .newest::<Point>(&snapshot.display_snapshot)
 6484                .head(),
 6485        };
 6486        let Some((buffer, buffer_row)) = snapshot
 6487            .buffer_snapshot()
 6488            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6489            .and_then(|(buffer_snapshot, range)| {
 6490                self.buffer()
 6491                    .read(cx)
 6492                    .buffer(buffer_snapshot.remote_id())
 6493                    .map(|buffer| (buffer, range.start.row))
 6494            })
 6495        else {
 6496            return;
 6497        };
 6498        let buffer_id = buffer.read(cx).remote_id();
 6499        let tasks = self
 6500            .tasks
 6501            .get(&(buffer_id, buffer_row))
 6502            .map(|t| Arc::new(t.to_owned()));
 6503
 6504        if !self.focus_handle.is_focused(window) {
 6505            return;
 6506        }
 6507        let project = self.project.clone();
 6508
 6509        let code_actions_task = match deployed_from {
 6510            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6511            _ => self.code_actions(buffer_row, window, cx),
 6512        };
 6513
 6514        let runnable_task = match deployed_from {
 6515            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6516            _ => {
 6517                let mut task_context_task = Task::ready(None);
 6518                if let Some(tasks) = &tasks
 6519                    && let Some(project) = project
 6520                {
 6521                    task_context_task =
 6522                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6523                }
 6524
 6525                cx.spawn_in(window, {
 6526                    let buffer = buffer.clone();
 6527                    async move |editor, cx| {
 6528                        let task_context = task_context_task.await;
 6529
 6530                        let resolved_tasks =
 6531                            tasks
 6532                                .zip(task_context.clone())
 6533                                .map(|(tasks, task_context)| ResolvedTasks {
 6534                                    templates: tasks.resolve(&task_context).collect(),
 6535                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6536                                        multibuffer_point.row,
 6537                                        tasks.column,
 6538                                    )),
 6539                                });
 6540                        let debug_scenarios = editor
 6541                            .update(cx, |editor, cx| {
 6542                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6543                            })?
 6544                            .await;
 6545                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6546                    }
 6547                })
 6548            }
 6549        };
 6550
 6551        cx.spawn_in(window, async move |editor, cx| {
 6552            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6553            let code_actions = code_actions_task.await;
 6554            let spawn_straight_away = quick_launch
 6555                && resolved_tasks
 6556                    .as_ref()
 6557                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6558                && code_actions
 6559                    .as_ref()
 6560                    .is_none_or(|actions| actions.is_empty())
 6561                && debug_scenarios.is_empty();
 6562
 6563            editor.update_in(cx, |editor, window, cx| {
 6564                crate::hover_popover::hide_hover(editor, cx);
 6565                let actions = CodeActionContents::new(
 6566                    resolved_tasks,
 6567                    code_actions,
 6568                    debug_scenarios,
 6569                    task_context.unwrap_or_default(),
 6570                );
 6571
 6572                // Don't show the menu if there are no actions available
 6573                if actions.is_empty() {
 6574                    cx.notify();
 6575                    return Task::ready(Ok(()));
 6576                }
 6577
 6578                *editor.context_menu.borrow_mut() =
 6579                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6580                        buffer,
 6581                        actions,
 6582                        selected_item: Default::default(),
 6583                        scroll_handle: UniformListScrollHandle::default(),
 6584                        deployed_from,
 6585                    }));
 6586                cx.notify();
 6587                if spawn_straight_away
 6588                    && let Some(task) = editor.confirm_code_action(
 6589                        &ConfirmCodeAction { item_ix: Some(0) },
 6590                        window,
 6591                        cx,
 6592                    )
 6593                {
 6594                    return task;
 6595                }
 6596
 6597                Task::ready(Ok(()))
 6598            })
 6599        })
 6600        .detach_and_log_err(cx);
 6601    }
 6602
 6603    fn debug_scenarios(
 6604        &mut self,
 6605        resolved_tasks: &Option<ResolvedTasks>,
 6606        buffer: &Entity<Buffer>,
 6607        cx: &mut App,
 6608    ) -> Task<Vec<task::DebugScenario>> {
 6609        maybe!({
 6610            let project = self.project()?;
 6611            let dap_store = project.read(cx).dap_store();
 6612            let mut scenarios = vec![];
 6613            let resolved_tasks = resolved_tasks.as_ref()?;
 6614            let buffer = buffer.read(cx);
 6615            let language = buffer.language()?;
 6616            let file = buffer.file();
 6617            let debug_adapter = language_settings(language.name().into(), file, cx)
 6618                .debuggers
 6619                .first()
 6620                .map(SharedString::from)
 6621                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6622
 6623            dap_store.update(cx, |dap_store, cx| {
 6624                for (_, task) in &resolved_tasks.templates {
 6625                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6626                        task.original_task().clone(),
 6627                        debug_adapter.clone().into(),
 6628                        task.display_label().to_owned().into(),
 6629                        cx,
 6630                    );
 6631                    scenarios.push(maybe_scenario);
 6632                }
 6633            });
 6634            Some(cx.background_spawn(async move {
 6635                futures::future::join_all(scenarios)
 6636                    .await
 6637                    .into_iter()
 6638                    .flatten()
 6639                    .collect::<Vec<_>>()
 6640            }))
 6641        })
 6642        .unwrap_or_else(|| Task::ready(vec![]))
 6643    }
 6644
 6645    fn code_actions(
 6646        &mut self,
 6647        buffer_row: u32,
 6648        window: &mut Window,
 6649        cx: &mut Context<Self>,
 6650    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6651        let mut task = self.code_actions_task.take();
 6652        cx.spawn_in(window, async move |editor, cx| {
 6653            while let Some(prev_task) = task {
 6654                prev_task.await.log_err();
 6655                task = editor
 6656                    .update(cx, |this, _| this.code_actions_task.take())
 6657                    .ok()?;
 6658            }
 6659
 6660            editor
 6661                .update(cx, |editor, cx| {
 6662                    editor
 6663                        .available_code_actions
 6664                        .clone()
 6665                        .and_then(|(location, code_actions)| {
 6666                            let snapshot = location.buffer.read(cx).snapshot();
 6667                            let point_range = location.range.to_point(&snapshot);
 6668                            let point_range = point_range.start.row..=point_range.end.row;
 6669                            if point_range.contains(&buffer_row) {
 6670                                Some(code_actions)
 6671                            } else {
 6672                                None
 6673                            }
 6674                        })
 6675                })
 6676                .ok()
 6677                .flatten()
 6678        })
 6679    }
 6680
 6681    pub fn confirm_code_action(
 6682        &mut self,
 6683        action: &ConfirmCodeAction,
 6684        window: &mut Window,
 6685        cx: &mut Context<Self>,
 6686    ) -> Option<Task<Result<()>>> {
 6687        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6688
 6689        let actions_menu =
 6690            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6691                menu
 6692            } else {
 6693                return None;
 6694            };
 6695
 6696        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6697        let action = actions_menu.actions.get(action_ix)?;
 6698        let title = action.label();
 6699        let buffer = actions_menu.buffer;
 6700        let workspace = self.workspace()?;
 6701
 6702        match action {
 6703            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6704                workspace.update(cx, |workspace, cx| {
 6705                    workspace.schedule_resolved_task(
 6706                        task_source_kind,
 6707                        resolved_task,
 6708                        false,
 6709                        window,
 6710                        cx,
 6711                    );
 6712
 6713                    Some(Task::ready(Ok(())))
 6714                })
 6715            }
 6716            CodeActionsItem::CodeAction {
 6717                excerpt_id,
 6718                action,
 6719                provider,
 6720            } => {
 6721                let apply_code_action =
 6722                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6723                let workspace = workspace.downgrade();
 6724                Some(cx.spawn_in(window, async move |editor, cx| {
 6725                    let project_transaction = apply_code_action.await?;
 6726                    Self::open_project_transaction(
 6727                        &editor,
 6728                        workspace,
 6729                        project_transaction,
 6730                        title,
 6731                        cx,
 6732                    )
 6733                    .await
 6734                }))
 6735            }
 6736            CodeActionsItem::DebugScenario(scenario) => {
 6737                let context = actions_menu.actions.context;
 6738
 6739                workspace.update(cx, |workspace, cx| {
 6740                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6741                    workspace.start_debug_session(
 6742                        scenario,
 6743                        context,
 6744                        Some(buffer),
 6745                        None,
 6746                        window,
 6747                        cx,
 6748                    );
 6749                });
 6750                Some(Task::ready(Ok(())))
 6751            }
 6752        }
 6753    }
 6754
 6755    fn open_transaction_for_hidden_buffers(
 6756        workspace: Entity<Workspace>,
 6757        transaction: ProjectTransaction,
 6758        title: String,
 6759        window: &mut Window,
 6760        cx: &mut Context<Self>,
 6761    ) {
 6762        if transaction.0.is_empty() {
 6763            return;
 6764        }
 6765
 6766        let edited_buffers_already_open = {
 6767            let other_editors: Vec<Entity<Editor>> = workspace
 6768                .read(cx)
 6769                .panes()
 6770                .iter()
 6771                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6772                .filter(|editor| editor.entity_id() != cx.entity_id())
 6773                .collect();
 6774
 6775            transaction.0.keys().all(|buffer| {
 6776                other_editors.iter().any(|editor| {
 6777                    let multi_buffer = editor.read(cx).buffer();
 6778                    multi_buffer.read(cx).is_singleton()
 6779                        && multi_buffer
 6780                            .read(cx)
 6781                            .as_singleton()
 6782                            .map_or(false, |singleton| {
 6783                                singleton.entity_id() == buffer.entity_id()
 6784                            })
 6785                })
 6786            })
 6787        };
 6788        if !edited_buffers_already_open {
 6789            let workspace = workspace.downgrade();
 6790            cx.defer_in(window, move |_, window, cx| {
 6791                cx.spawn_in(window, async move |editor, cx| {
 6792                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6793                        .await
 6794                        .ok()
 6795                })
 6796                .detach();
 6797            });
 6798        }
 6799    }
 6800
 6801    pub async fn open_project_transaction(
 6802        editor: &WeakEntity<Editor>,
 6803        workspace: WeakEntity<Workspace>,
 6804        transaction: ProjectTransaction,
 6805        title: String,
 6806        cx: &mut AsyncWindowContext,
 6807    ) -> Result<()> {
 6808        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 6809        cx.update(|_, cx| {
 6810            entries.sort_unstable_by_key(|(buffer, _)| {
 6811                buffer.read(cx).file().map(|f| f.path().clone())
 6812            });
 6813        })?;
 6814        if entries.is_empty() {
 6815            return Ok(());
 6816        }
 6817
 6818        // If the project transaction's edits are all contained within this editor, then
 6819        // avoid opening a new editor to display them.
 6820
 6821        if let [(buffer, transaction)] = &*entries {
 6822            let excerpt = editor.update(cx, |editor, cx| {
 6823                editor
 6824                    .buffer()
 6825                    .read(cx)
 6826                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 6827            })?;
 6828            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 6829                && excerpted_buffer == *buffer
 6830            {
 6831                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 6832                    let excerpt_range = excerpt_range.to_offset(buffer);
 6833                    buffer
 6834                        .edited_ranges_for_transaction::<usize>(transaction)
 6835                        .all(|range| {
 6836                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 6837                        })
 6838                });
 6839
 6840                if all_edits_within_excerpt {
 6841                    return Ok(());
 6842                }
 6843            }
 6844        }
 6845
 6846        let mut ranges_to_highlight = Vec::new();
 6847        let excerpt_buffer = cx.new(|cx| {
 6848            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 6849            for (buffer_handle, transaction) in &entries {
 6850                let edited_ranges = buffer_handle
 6851                    .read(cx)
 6852                    .edited_ranges_for_transaction::<Point>(transaction)
 6853                    .collect::<Vec<_>>();
 6854                let (ranges, _) = multibuffer.set_excerpts_for_path(
 6855                    PathKey::for_buffer(buffer_handle, cx),
 6856                    buffer_handle.clone(),
 6857                    edited_ranges,
 6858                    multibuffer_context_lines(cx),
 6859                    cx,
 6860                );
 6861
 6862                ranges_to_highlight.extend(ranges);
 6863            }
 6864            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 6865            multibuffer
 6866        });
 6867
 6868        workspace.update_in(cx, |workspace, window, cx| {
 6869            let project = workspace.project().clone();
 6870            let editor =
 6871                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 6872            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 6873            editor.update(cx, |editor, cx| {
 6874                editor.highlight_background::<Self>(
 6875                    &ranges_to_highlight,
 6876                    |_, theme| theme.colors().editor_highlighted_line_background,
 6877                    cx,
 6878                );
 6879            });
 6880        })?;
 6881
 6882        Ok(())
 6883    }
 6884
 6885    pub fn clear_code_action_providers(&mut self) {
 6886        self.code_action_providers.clear();
 6887        self.available_code_actions.take();
 6888    }
 6889
 6890    pub fn add_code_action_provider(
 6891        &mut self,
 6892        provider: Rc<dyn CodeActionProvider>,
 6893        window: &mut Window,
 6894        cx: &mut Context<Self>,
 6895    ) {
 6896        if self
 6897            .code_action_providers
 6898            .iter()
 6899            .any(|existing_provider| existing_provider.id() == provider.id())
 6900        {
 6901            return;
 6902        }
 6903
 6904        self.code_action_providers.push(provider);
 6905        self.refresh_code_actions(window, cx);
 6906    }
 6907
 6908    pub fn remove_code_action_provider(
 6909        &mut self,
 6910        id: Arc<str>,
 6911        window: &mut Window,
 6912        cx: &mut Context<Self>,
 6913    ) {
 6914        self.code_action_providers
 6915            .retain(|provider| provider.id() != id);
 6916        self.refresh_code_actions(window, cx);
 6917    }
 6918
 6919    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 6920        !self.code_action_providers.is_empty()
 6921            && EditorSettings::get_global(cx).toolbar.code_actions
 6922    }
 6923
 6924    pub fn has_available_code_actions(&self) -> bool {
 6925        self.available_code_actions
 6926            .as_ref()
 6927            .is_some_and(|(_, actions)| !actions.is_empty())
 6928    }
 6929
 6930    fn render_inline_code_actions(
 6931        &self,
 6932        icon_size: ui::IconSize,
 6933        display_row: DisplayRow,
 6934        is_active: bool,
 6935        cx: &mut Context<Self>,
 6936    ) -> AnyElement {
 6937        let show_tooltip = !self.context_menu_visible();
 6938        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 6939            .icon_size(icon_size)
 6940            .shape(ui::IconButtonShape::Square)
 6941            .icon_color(ui::Color::Hidden)
 6942            .toggle_state(is_active)
 6943            .when(show_tooltip, |this| {
 6944                this.tooltip({
 6945                    let focus_handle = self.focus_handle.clone();
 6946                    move |_window, cx| {
 6947                        Tooltip::for_action_in(
 6948                            "Toggle Code Actions",
 6949                            &ToggleCodeActions {
 6950                                deployed_from: None,
 6951                                quick_launch: false,
 6952                            },
 6953                            &focus_handle,
 6954                            cx,
 6955                        )
 6956                    }
 6957                })
 6958            })
 6959            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 6960                window.focus(&editor.focus_handle(cx), cx);
 6961                editor.toggle_code_actions(
 6962                    &crate::actions::ToggleCodeActions {
 6963                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 6964                            display_row,
 6965                        )),
 6966                        quick_launch: false,
 6967                    },
 6968                    window,
 6969                    cx,
 6970                );
 6971            }))
 6972            .into_any_element()
 6973    }
 6974
 6975    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 6976        &self.context_menu
 6977    }
 6978
 6979    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 6980        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 6981            cx.background_executor()
 6982                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 6983                .await;
 6984
 6985            let (start_buffer, start, _, end, newest_selection) = this
 6986                .update(cx, |this, cx| {
 6987                    let newest_selection = this.selections.newest_anchor().clone();
 6988                    if newest_selection.head().diff_base_anchor.is_some() {
 6989                        return None;
 6990                    }
 6991                    let display_snapshot = this.display_snapshot(cx);
 6992                    let newest_selection_adjusted =
 6993                        this.selections.newest_adjusted(&display_snapshot);
 6994                    let buffer = this.buffer.read(cx);
 6995
 6996                    let (start_buffer, start) =
 6997                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 6998                    let (end_buffer, end) =
 6999                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7000
 7001                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7002                })?
 7003                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7004                .context(
 7005                    "Expected selection to lie in a single buffer when refreshing code actions",
 7006                )?;
 7007            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7008                let providers = this.code_action_providers.clone();
 7009                let tasks = this
 7010                    .code_action_providers
 7011                    .iter()
 7012                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7013                    .collect::<Vec<_>>();
 7014                (providers, tasks)
 7015            })?;
 7016
 7017            let mut actions = Vec::new();
 7018            for (provider, provider_actions) in
 7019                providers.into_iter().zip(future::join_all(tasks).await)
 7020            {
 7021                if let Some(provider_actions) = provider_actions.log_err() {
 7022                    actions.extend(provider_actions.into_iter().map(|action| {
 7023                        AvailableCodeAction {
 7024                            excerpt_id: newest_selection.start.excerpt_id,
 7025                            action,
 7026                            provider: provider.clone(),
 7027                        }
 7028                    }));
 7029                }
 7030            }
 7031
 7032            this.update(cx, |this, cx| {
 7033                this.available_code_actions = if actions.is_empty() {
 7034                    None
 7035                } else {
 7036                    Some((
 7037                        Location {
 7038                            buffer: start_buffer,
 7039                            range: start..end,
 7040                        },
 7041                        actions.into(),
 7042                    ))
 7043                };
 7044                cx.notify();
 7045            })
 7046        }));
 7047    }
 7048
 7049    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7050        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7051            self.show_git_blame_inline = false;
 7052
 7053            self.show_git_blame_inline_delay_task =
 7054                Some(cx.spawn_in(window, async move |this, cx| {
 7055                    cx.background_executor().timer(delay).await;
 7056
 7057                    this.update(cx, |this, cx| {
 7058                        this.show_git_blame_inline = true;
 7059                        cx.notify();
 7060                    })
 7061                    .log_err();
 7062                }));
 7063        }
 7064    }
 7065
 7066    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7067        let snapshot = self.snapshot(window, cx);
 7068        let cursor = self
 7069            .selections
 7070            .newest::<Point>(&snapshot.display_snapshot)
 7071            .head();
 7072        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7073        else {
 7074            return;
 7075        };
 7076
 7077        if self.blame.is_none() {
 7078            self.start_git_blame(true, window, cx);
 7079        }
 7080        let Some(blame) = self.blame.as_ref() else {
 7081            return;
 7082        };
 7083
 7084        let row_info = RowInfo {
 7085            buffer_id: Some(buffer.remote_id()),
 7086            buffer_row: Some(point.row),
 7087            ..Default::default()
 7088        };
 7089        let Some((buffer, blame_entry)) = blame
 7090            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7091            .flatten()
 7092        else {
 7093            return;
 7094        };
 7095
 7096        let anchor = self.selections.newest_anchor().head();
 7097        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7098        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7099            self.show_blame_popover(
 7100                buffer,
 7101                &blame_entry,
 7102                position + last_bounds.origin,
 7103                true,
 7104                cx,
 7105            );
 7106        };
 7107    }
 7108
 7109    fn show_blame_popover(
 7110        &mut self,
 7111        buffer: BufferId,
 7112        blame_entry: &BlameEntry,
 7113        position: gpui::Point<Pixels>,
 7114        ignore_timeout: bool,
 7115        cx: &mut Context<Self>,
 7116    ) {
 7117        if let Some(state) = &mut self.inline_blame_popover {
 7118            state.hide_task.take();
 7119        } else {
 7120            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7121            let blame_entry = blame_entry.clone();
 7122            let show_task = cx.spawn(async move |editor, cx| {
 7123                if !ignore_timeout {
 7124                    cx.background_executor()
 7125                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7126                        .await;
 7127                }
 7128                editor
 7129                    .update(cx, |editor, cx| {
 7130                        editor.inline_blame_popover_show_task.take();
 7131                        let Some(blame) = editor.blame.as_ref() else {
 7132                            return;
 7133                        };
 7134                        let blame = blame.read(cx);
 7135                        let details = blame.details_for_entry(buffer, &blame_entry);
 7136                        let markdown = cx.new(|cx| {
 7137                            Markdown::new(
 7138                                details
 7139                                    .as_ref()
 7140                                    .map(|message| message.message.clone())
 7141                                    .unwrap_or_default(),
 7142                                None,
 7143                                None,
 7144                                cx,
 7145                            )
 7146                        });
 7147                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7148                            position,
 7149                            hide_task: None,
 7150                            popover_bounds: None,
 7151                            popover_state: InlineBlamePopoverState {
 7152                                scroll_handle: ScrollHandle::new(),
 7153                                commit_message: details,
 7154                                markdown,
 7155                            },
 7156                            keyboard_grace: ignore_timeout,
 7157                        });
 7158                        cx.notify();
 7159                    })
 7160                    .ok();
 7161            });
 7162            self.inline_blame_popover_show_task = Some(show_task);
 7163        }
 7164    }
 7165
 7166    pub fn has_mouse_context_menu(&self) -> bool {
 7167        self.mouse_context_menu.is_some()
 7168    }
 7169
 7170    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7171        self.inline_blame_popover_show_task.take();
 7172        if let Some(state) = &mut self.inline_blame_popover {
 7173            let hide_task = cx.spawn(async move |editor, cx| {
 7174                if !ignore_timeout {
 7175                    cx.background_executor()
 7176                        .timer(std::time::Duration::from_millis(100))
 7177                        .await;
 7178                }
 7179                editor
 7180                    .update(cx, |editor, cx| {
 7181                        editor.inline_blame_popover.take();
 7182                        cx.notify();
 7183                    })
 7184                    .ok();
 7185            });
 7186            state.hide_task = Some(hide_task);
 7187            true
 7188        } else {
 7189            false
 7190        }
 7191    }
 7192
 7193    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7194        if self.pending_rename.is_some() {
 7195            return None;
 7196        }
 7197
 7198        let provider = self.semantics_provider.clone()?;
 7199        let buffer = self.buffer.read(cx);
 7200        let newest_selection = self.selections.newest_anchor().clone();
 7201        let cursor_position = newest_selection.head();
 7202        let (cursor_buffer, cursor_buffer_position) =
 7203            buffer.text_anchor_for_position(cursor_position, cx)?;
 7204        let (tail_buffer, tail_buffer_position) =
 7205            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7206        if cursor_buffer != tail_buffer {
 7207            return None;
 7208        }
 7209
 7210        let snapshot = cursor_buffer.read(cx).snapshot();
 7211        let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7212        let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7213        if start_word_range != end_word_range {
 7214            self.document_highlights_task.take();
 7215            self.clear_background_highlights::<DocumentHighlightRead>(cx);
 7216            self.clear_background_highlights::<DocumentHighlightWrite>(cx);
 7217            return None;
 7218        }
 7219
 7220        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7221        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7222            cx.background_executor()
 7223                .timer(Duration::from_millis(debounce))
 7224                .await;
 7225
 7226            let highlights = if let Some(highlights) = cx.update(|cx| {
 7227                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7228            }) {
 7229                highlights.await.log_err()
 7230            } else {
 7231                None
 7232            };
 7233
 7234            if let Some(highlights) = highlights {
 7235                this.update(cx, |this, cx| {
 7236                    if this.pending_rename.is_some() {
 7237                        return;
 7238                    }
 7239
 7240                    let buffer = this.buffer.read(cx);
 7241                    if buffer
 7242                        .text_anchor_for_position(cursor_position, cx)
 7243                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7244                    {
 7245                        return;
 7246                    }
 7247
 7248                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7249                    let mut write_ranges = Vec::new();
 7250                    let mut read_ranges = Vec::new();
 7251                    for highlight in highlights {
 7252                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7253                        for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
 7254                        {
 7255                            let start = highlight
 7256                                .range
 7257                                .start
 7258                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7259                            let end = highlight
 7260                                .range
 7261                                .end
 7262                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7263                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7264                                continue;
 7265                            }
 7266
 7267                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7268                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7269                                write_ranges.push(range);
 7270                            } else {
 7271                                read_ranges.push(range);
 7272                            }
 7273                        }
 7274                    }
 7275
 7276                    this.highlight_background::<DocumentHighlightRead>(
 7277                        &read_ranges,
 7278                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7279                        cx,
 7280                    );
 7281                    this.highlight_background::<DocumentHighlightWrite>(
 7282                        &write_ranges,
 7283                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7284                        cx,
 7285                    );
 7286                    cx.notify();
 7287                })
 7288                .log_err();
 7289            }
 7290        }));
 7291        None
 7292    }
 7293
 7294    fn prepare_highlight_query_from_selection(
 7295        &mut self,
 7296        window: &Window,
 7297        cx: &mut Context<Editor>,
 7298    ) -> Option<(String, Range<Anchor>)> {
 7299        if matches!(self.mode, EditorMode::SingleLine) {
 7300            return None;
 7301        }
 7302        if !EditorSettings::get_global(cx).selection_highlight {
 7303            return None;
 7304        }
 7305        if self.selections.count() != 1 || self.selections.line_mode() {
 7306            return None;
 7307        }
 7308        let snapshot = self.snapshot(window, cx);
 7309        let selection = self.selections.newest::<Point>(&snapshot);
 7310        // If the selection spans multiple rows OR it is empty
 7311        if selection.start.row != selection.end.row
 7312            || selection.start.column == selection.end.column
 7313        {
 7314            return None;
 7315        }
 7316        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7317        let query = snapshot
 7318            .buffer_snapshot()
 7319            .text_for_range(selection_anchor_range.clone())
 7320            .collect::<String>();
 7321        if query.trim().is_empty() {
 7322            return None;
 7323        }
 7324        Some((query, selection_anchor_range))
 7325    }
 7326
 7327    #[ztracing::instrument(skip_all)]
 7328    fn update_selection_occurrence_highlights(
 7329        &mut self,
 7330        query_text: String,
 7331        query_range: Range<Anchor>,
 7332        multi_buffer_range_to_query: Range<Point>,
 7333        use_debounce: bool,
 7334        window: &mut Window,
 7335        cx: &mut Context<Editor>,
 7336    ) -> Task<()> {
 7337        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7338        cx.spawn_in(window, async move |editor, cx| {
 7339            if use_debounce {
 7340                cx.background_executor()
 7341                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7342                    .await;
 7343            }
 7344            let match_task = cx.background_spawn(async move {
 7345                let buffer_ranges = multi_buffer_snapshot
 7346                    .range_to_buffer_ranges(multi_buffer_range_to_query)
 7347                    .into_iter()
 7348                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7349                let mut match_ranges = Vec::new();
 7350                let Ok(regex) = project::search::SearchQuery::text(
 7351                    query_text.clone(),
 7352                    false,
 7353                    false,
 7354                    false,
 7355                    Default::default(),
 7356                    Default::default(),
 7357                    false,
 7358                    None,
 7359                ) else {
 7360                    return Vec::default();
 7361                };
 7362                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7363                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7364                    match_ranges.extend(
 7365                        regex
 7366                            .search(
 7367                                buffer_snapshot,
 7368                                Some(search_range.start.0..search_range.end.0),
 7369                            )
 7370                            .await
 7371                            .into_iter()
 7372                            .filter_map(|match_range| {
 7373                                let match_start = buffer_snapshot
 7374                                    .anchor_after(search_range.start + match_range.start);
 7375                                let match_end = buffer_snapshot
 7376                                    .anchor_before(search_range.start + match_range.end);
 7377                                let match_anchor_range =
 7378                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7379                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7380                            }),
 7381                    );
 7382                }
 7383                match_ranges
 7384            });
 7385            let match_ranges = match_task.await;
 7386            editor
 7387                .update_in(cx, |editor, _, cx| {
 7388                    if use_debounce {
 7389                        editor.clear_background_highlights::<SelectedTextHighlight>(cx);
 7390                        editor.debounced_selection_highlight_complete = true;
 7391                    } else if editor.debounced_selection_highlight_complete {
 7392                        return;
 7393                    }
 7394                    if !match_ranges.is_empty() {
 7395                        editor.highlight_background::<SelectedTextHighlight>(
 7396                            &match_ranges,
 7397                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7398                            cx,
 7399                        )
 7400                    }
 7401                })
 7402                .log_err();
 7403        })
 7404    }
 7405
 7406    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7407        struct NewlineFold;
 7408        let type_id = std::any::TypeId::of::<NewlineFold>();
 7409        if !self.mode.is_single_line() {
 7410            return;
 7411        }
 7412        let snapshot = self.snapshot(window, cx);
 7413        if snapshot.buffer_snapshot().max_point().row == 0 {
 7414            return;
 7415        }
 7416        let task = cx.background_spawn(async move {
 7417            let new_newlines = snapshot
 7418                .buffer_chars_at(MultiBufferOffset(0))
 7419                .filter_map(|(c, i)| {
 7420                    if c == '\n' {
 7421                        Some(
 7422                            snapshot.buffer_snapshot().anchor_after(i)
 7423                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7424                        )
 7425                    } else {
 7426                        None
 7427                    }
 7428                })
 7429                .collect::<Vec<_>>();
 7430            let existing_newlines = snapshot
 7431                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7432                .filter_map(|fold| {
 7433                    if fold.placeholder.type_tag == Some(type_id) {
 7434                        Some(fold.range.start..fold.range.end)
 7435                    } else {
 7436                        None
 7437                    }
 7438                })
 7439                .collect::<Vec<_>>();
 7440
 7441            (new_newlines, existing_newlines)
 7442        });
 7443        self.folding_newlines = cx.spawn(async move |this, cx| {
 7444            let (new_newlines, existing_newlines) = task.await;
 7445            if new_newlines == existing_newlines {
 7446                return;
 7447            }
 7448            let placeholder = FoldPlaceholder {
 7449                render: Arc::new(move |_, _, cx| {
 7450                    div()
 7451                        .bg(cx.theme().status().hint_background)
 7452                        .border_b_1()
 7453                        .size_full()
 7454                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7455                        .border_color(cx.theme().status().hint)
 7456                        .child("\\n")
 7457                        .into_any()
 7458                }),
 7459                constrain_width: false,
 7460                merge_adjacent: false,
 7461                type_tag: Some(type_id),
 7462            };
 7463            let creases = new_newlines
 7464                .into_iter()
 7465                .map(|range| Crease::simple(range, placeholder.clone()))
 7466                .collect();
 7467            this.update(cx, |this, cx| {
 7468                this.display_map.update(cx, |display_map, cx| {
 7469                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7470                    display_map.fold(creases, cx);
 7471                });
 7472            })
 7473            .ok();
 7474        });
 7475    }
 7476
 7477    #[ztracing::instrument(skip_all)]
 7478    fn refresh_selected_text_highlights(
 7479        &mut self,
 7480        on_buffer_edit: bool,
 7481        window: &mut Window,
 7482        cx: &mut Context<Editor>,
 7483    ) {
 7484        let Some((query_text, query_range)) =
 7485            self.prepare_highlight_query_from_selection(window, cx)
 7486        else {
 7487            self.clear_background_highlights::<SelectedTextHighlight>(cx);
 7488            self.quick_selection_highlight_task.take();
 7489            self.debounced_selection_highlight_task.take();
 7490            self.debounced_selection_highlight_complete = false;
 7491            return;
 7492        };
 7493        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7494        let query_changed = self
 7495            .quick_selection_highlight_task
 7496            .as_ref()
 7497            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7498        if query_changed {
 7499            self.debounced_selection_highlight_complete = false;
 7500        }
 7501        if on_buffer_edit || query_changed {
 7502            let multi_buffer_visible_start = self
 7503                .scroll_manager
 7504                .anchor()
 7505                .anchor
 7506                .to_point(&multi_buffer_snapshot);
 7507            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7508                multi_buffer_visible_start
 7509                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7510                Bias::Left,
 7511            );
 7512            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7513            self.quick_selection_highlight_task = Some((
 7514                query_range.clone(),
 7515                self.update_selection_occurrence_highlights(
 7516                    query_text.clone(),
 7517                    query_range.clone(),
 7518                    multi_buffer_visible_range,
 7519                    false,
 7520                    window,
 7521                    cx,
 7522                ),
 7523            ));
 7524        }
 7525        if on_buffer_edit
 7526            || self
 7527                .debounced_selection_highlight_task
 7528                .as_ref()
 7529                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7530        {
 7531            let multi_buffer_start = multi_buffer_snapshot
 7532                .anchor_before(MultiBufferOffset(0))
 7533                .to_point(&multi_buffer_snapshot);
 7534            let multi_buffer_end = multi_buffer_snapshot
 7535                .anchor_after(multi_buffer_snapshot.len())
 7536                .to_point(&multi_buffer_snapshot);
 7537            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7538            self.debounced_selection_highlight_task = Some((
 7539                query_range.clone(),
 7540                self.update_selection_occurrence_highlights(
 7541                    query_text,
 7542                    query_range,
 7543                    multi_buffer_full_range,
 7544                    true,
 7545                    window,
 7546                    cx,
 7547                ),
 7548            ));
 7549        }
 7550    }
 7551
 7552    pub fn refresh_edit_prediction(
 7553        &mut self,
 7554        debounce: bool,
 7555        user_requested: bool,
 7556        window: &mut Window,
 7557        cx: &mut Context<Self>,
 7558    ) -> Option<()> {
 7559        if DisableAiSettings::get_global(cx).disable_ai {
 7560            return None;
 7561        }
 7562
 7563        let provider = self.edit_prediction_provider()?;
 7564        let cursor = self.selections.newest_anchor().head();
 7565        let (buffer, cursor_buffer_position) =
 7566            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7567
 7568        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7569            self.discard_edit_prediction(false, cx);
 7570            return None;
 7571        }
 7572
 7573        self.update_visible_edit_prediction(window, cx);
 7574
 7575        if !user_requested
 7576            && (!self.should_show_edit_predictions()
 7577                || !self.is_focused(window)
 7578                || buffer.read(cx).is_empty())
 7579        {
 7580            self.discard_edit_prediction(false, cx);
 7581            return None;
 7582        }
 7583
 7584        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7585        Some(())
 7586    }
 7587
 7588    fn show_edit_predictions_in_menu(&self) -> bool {
 7589        match self.edit_prediction_settings {
 7590            EditPredictionSettings::Disabled => false,
 7591            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7592        }
 7593    }
 7594
 7595    pub fn edit_predictions_enabled(&self) -> bool {
 7596        match self.edit_prediction_settings {
 7597            EditPredictionSettings::Disabled => false,
 7598            EditPredictionSettings::Enabled { .. } => true,
 7599        }
 7600    }
 7601
 7602    fn edit_prediction_requires_modifier(&self) -> bool {
 7603        match self.edit_prediction_settings {
 7604            EditPredictionSettings::Disabled => false,
 7605            EditPredictionSettings::Enabled {
 7606                preview_requires_modifier,
 7607                ..
 7608            } => preview_requires_modifier,
 7609        }
 7610    }
 7611
 7612    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7613        if self.edit_prediction_provider.is_none() || DisableAiSettings::get_global(cx).disable_ai {
 7614            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7615            self.discard_edit_prediction(false, cx);
 7616        } else {
 7617            let selection = self.selections.newest_anchor();
 7618            let cursor = selection.head();
 7619
 7620            if let Some((buffer, cursor_buffer_position)) =
 7621                self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7622            {
 7623                self.edit_prediction_settings =
 7624                    self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7625            }
 7626        }
 7627    }
 7628
 7629    fn edit_prediction_settings_at_position(
 7630        &self,
 7631        buffer: &Entity<Buffer>,
 7632        buffer_position: language::Anchor,
 7633        cx: &App,
 7634    ) -> EditPredictionSettings {
 7635        if !self.mode.is_full()
 7636            || !self.show_edit_predictions_override.unwrap_or(true)
 7637            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7638        {
 7639            return EditPredictionSettings::Disabled;
 7640        }
 7641
 7642        let buffer = buffer.read(cx);
 7643
 7644        let file = buffer.file();
 7645
 7646        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7647            return EditPredictionSettings::Disabled;
 7648        };
 7649
 7650        let by_provider = matches!(
 7651            self.menu_edit_predictions_policy,
 7652            MenuEditPredictionsPolicy::ByProvider
 7653        );
 7654
 7655        let show_in_menu = by_provider
 7656            && self
 7657                .edit_prediction_provider
 7658                .as_ref()
 7659                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7660
 7661        let preview_requires_modifier =
 7662            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7663
 7664        EditPredictionSettings::Enabled {
 7665            show_in_menu,
 7666            preview_requires_modifier,
 7667        }
 7668    }
 7669
 7670    fn should_show_edit_predictions(&self) -> bool {
 7671        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7672    }
 7673
 7674    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7675        matches!(
 7676            self.edit_prediction_preview,
 7677            EditPredictionPreview::Active { .. }
 7678        )
 7679    }
 7680
 7681    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7682        let cursor = self.selections.newest_anchor().head();
 7683        if let Some((buffer, cursor_position)) =
 7684            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7685        {
 7686            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7687        } else {
 7688            false
 7689        }
 7690    }
 7691
 7692    pub fn supports_minimap(&self, cx: &App) -> bool {
 7693        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7694    }
 7695
 7696    fn edit_predictions_enabled_in_buffer(
 7697        &self,
 7698        buffer: &Entity<Buffer>,
 7699        buffer_position: language::Anchor,
 7700        cx: &App,
 7701    ) -> bool {
 7702        maybe!({
 7703            if self.read_only(cx) {
 7704                return Some(false);
 7705            }
 7706            let provider = self.edit_prediction_provider()?;
 7707            if !provider.is_enabled(buffer, buffer_position, cx) {
 7708                return Some(false);
 7709            }
 7710            let buffer = buffer.read(cx);
 7711            let Some(file) = buffer.file() else {
 7712                return Some(true);
 7713            };
 7714            let settings = all_language_settings(Some(file), cx);
 7715            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7716        })
 7717        .unwrap_or(false)
 7718    }
 7719
 7720    pub fn show_edit_prediction(
 7721        &mut self,
 7722        _: &ShowEditPrediction,
 7723        window: &mut Window,
 7724        cx: &mut Context<Self>,
 7725    ) {
 7726        if !self.has_active_edit_prediction() {
 7727            self.refresh_edit_prediction(false, true, window, cx);
 7728            return;
 7729        }
 7730
 7731        self.update_visible_edit_prediction(window, cx);
 7732    }
 7733
 7734    pub fn display_cursor_names(
 7735        &mut self,
 7736        _: &DisplayCursorNames,
 7737        window: &mut Window,
 7738        cx: &mut Context<Self>,
 7739    ) {
 7740        self.show_cursor_names(window, cx);
 7741    }
 7742
 7743    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7744        self.show_cursor_names = true;
 7745        cx.notify();
 7746        cx.spawn_in(window, async move |this, cx| {
 7747            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 7748            this.update(cx, |this, cx| {
 7749                this.show_cursor_names = false;
 7750                cx.notify()
 7751            })
 7752            .ok()
 7753        })
 7754        .detach();
 7755    }
 7756
 7757    pub fn accept_partial_edit_prediction(
 7758        &mut self,
 7759        granularity: EditPredictionGranularity,
 7760        window: &mut Window,
 7761        cx: &mut Context<Self>,
 7762    ) {
 7763        if self.show_edit_predictions_in_menu() {
 7764            self.hide_context_menu(window, cx);
 7765        }
 7766
 7767        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 7768            return;
 7769        };
 7770
 7771        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 7772            return;
 7773        }
 7774
 7775        match &active_edit_prediction.completion {
 7776            EditPrediction::MoveWithin { target, .. } => {
 7777                let target = *target;
 7778
 7779                if matches!(granularity, EditPredictionGranularity::Full) {
 7780                    if let Some(position_map) = &self.last_position_map {
 7781                        let target_row = target.to_display_point(&position_map.snapshot).row();
 7782                        let is_visible = position_map.visible_row_range.contains(&target_row);
 7783
 7784                        if is_visible || !self.edit_prediction_requires_modifier() {
 7785                            self.unfold_ranges(&[target..target], true, false, cx);
 7786                            self.change_selections(
 7787                                SelectionEffects::scroll(Autoscroll::newest()),
 7788                                window,
 7789                                cx,
 7790                                |selections| {
 7791                                    selections.select_anchor_ranges([target..target]);
 7792                                },
 7793                            );
 7794                            self.clear_row_highlights::<EditPredictionPreview>();
 7795                            self.edit_prediction_preview
 7796                                .set_previous_scroll_position(None);
 7797                        } else {
 7798                            // Highlight and request scroll
 7799                            self.edit_prediction_preview
 7800                                .set_previous_scroll_position(Some(
 7801                                    position_map.snapshot.scroll_anchor,
 7802                                ));
 7803                            self.highlight_rows::<EditPredictionPreview>(
 7804                                target..target,
 7805                                cx.theme().colors().editor_highlighted_line_background,
 7806                                RowHighlightOptions {
 7807                                    autoscroll: true,
 7808                                    ..Default::default()
 7809                                },
 7810                                cx,
 7811                            );
 7812                            self.request_autoscroll(Autoscroll::fit(), cx);
 7813                        }
 7814                    }
 7815                } else {
 7816                    self.change_selections(
 7817                        SelectionEffects::scroll(Autoscroll::newest()),
 7818                        window,
 7819                        cx,
 7820                        |selections| {
 7821                            selections.select_anchor_ranges([target..target]);
 7822                        },
 7823                    );
 7824                }
 7825            }
 7826            EditPrediction::MoveOutside { snapshot, target } => {
 7827                if let Some(workspace) = self.workspace() {
 7828                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 7829                        .detach_and_log_err(cx);
 7830                }
 7831            }
 7832            EditPrediction::Edit { edits, .. } => {
 7833                self.report_edit_prediction_event(
 7834                    active_edit_prediction.completion_id.clone(),
 7835                    true,
 7836                    cx,
 7837                );
 7838
 7839                match granularity {
 7840                    EditPredictionGranularity::Full => {
 7841                        if let Some(provider) = self.edit_prediction_provider() {
 7842                            provider.accept(cx);
 7843                        }
 7844
 7845                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 7846                        let snapshot = self.buffer.read(cx).snapshot(cx);
 7847                        let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
 7848
 7849                        self.buffer.update(cx, |buffer, cx| {
 7850                            buffer.edit(edits.iter().cloned(), None, cx)
 7851                        });
 7852
 7853                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 7854                            s.select_anchor_ranges([last_edit_end..last_edit_end]);
 7855                        });
 7856
 7857                        let selections = self.selections.disjoint_anchors_arc();
 7858                        if let Some(transaction_id_now) =
 7859                            self.buffer.read(cx).last_transaction_id(cx)
 7860                        {
 7861                            if transaction_id_prev != Some(transaction_id_now) {
 7862                                self.selection_history
 7863                                    .insert_transaction(transaction_id_now, selections);
 7864                            }
 7865                        }
 7866
 7867                        self.update_visible_edit_prediction(window, cx);
 7868                        if self.active_edit_prediction.is_none() {
 7869                            self.refresh_edit_prediction(true, true, window, cx);
 7870                        }
 7871                        cx.notify();
 7872                    }
 7873                    _ => {
 7874                        let snapshot = self.buffer.read(cx).snapshot(cx);
 7875                        let cursor_offset = self
 7876                            .selections
 7877                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 7878                            .head();
 7879
 7880                        let insertion = edits.iter().find_map(|(range, text)| {
 7881                            let range = range.to_offset(&snapshot);
 7882                            if range.is_empty() && range.start == cursor_offset {
 7883                                Some(text)
 7884                            } else {
 7885                                None
 7886                            }
 7887                        });
 7888
 7889                        if let Some(text) = insertion {
 7890                            let text_to_insert = match granularity {
 7891                                EditPredictionGranularity::Word => {
 7892                                    let mut partial = text
 7893                                        .chars()
 7894                                        .by_ref()
 7895                                        .take_while(|c| c.is_alphabetic())
 7896                                        .collect::<String>();
 7897                                    if partial.is_empty() {
 7898                                        partial = text
 7899                                            .chars()
 7900                                            .by_ref()
 7901                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 7902                                            .collect::<String>();
 7903                                    }
 7904                                    partial
 7905                                }
 7906                                EditPredictionGranularity::Line => {
 7907                                    if let Some(line) = text.split_inclusive('\n').next() {
 7908                                        line.to_string()
 7909                                    } else {
 7910                                        text.to_string()
 7911                                    }
 7912                                }
 7913                                EditPredictionGranularity::Full => unreachable!(),
 7914                            };
 7915
 7916                            cx.emit(EditorEvent::InputHandled {
 7917                                utf16_range_to_replace: None,
 7918                                text: text_to_insert.clone().into(),
 7919                            });
 7920
 7921                            self.insert_with_autoindent_mode(&text_to_insert, None, window, cx);
 7922                            self.refresh_edit_prediction(true, true, window, cx);
 7923                            cx.notify();
 7924                        } else {
 7925                            self.accept_partial_edit_prediction(
 7926                                EditPredictionGranularity::Full,
 7927                                window,
 7928                                cx,
 7929                            );
 7930                        }
 7931                    }
 7932                }
 7933            }
 7934        }
 7935
 7936        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 7937    }
 7938
 7939    pub fn accept_next_word_edit_prediction(
 7940        &mut self,
 7941        _: &AcceptNextWordEditPrediction,
 7942        window: &mut Window,
 7943        cx: &mut Context<Self>,
 7944    ) {
 7945        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 7946    }
 7947
 7948    pub fn accept_next_line_edit_prediction(
 7949        &mut self,
 7950        _: &AcceptNextLineEditPrediction,
 7951        window: &mut Window,
 7952        cx: &mut Context<Self>,
 7953    ) {
 7954        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 7955    }
 7956
 7957    pub fn accept_edit_prediction(
 7958        &mut self,
 7959        _: &AcceptEditPrediction,
 7960        window: &mut Window,
 7961        cx: &mut Context<Self>,
 7962    ) {
 7963        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 7964    }
 7965
 7966    fn discard_edit_prediction(
 7967        &mut self,
 7968        should_report_edit_prediction_event: bool,
 7969        cx: &mut Context<Self>,
 7970    ) -> bool {
 7971        if should_report_edit_prediction_event {
 7972            let completion_id = self
 7973                .active_edit_prediction
 7974                .as_ref()
 7975                .and_then(|active_completion| active_completion.completion_id.clone());
 7976
 7977            self.report_edit_prediction_event(completion_id, false, cx);
 7978        }
 7979
 7980        if let Some(provider) = self.edit_prediction_provider() {
 7981            provider.discard(cx);
 7982        }
 7983
 7984        self.take_active_edit_prediction(cx)
 7985    }
 7986
 7987    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 7988        let Some(provider) = self.edit_prediction_provider() else {
 7989            return;
 7990        };
 7991
 7992        let Some((_, buffer, _)) = self
 7993            .buffer
 7994            .read(cx)
 7995            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 7996        else {
 7997            return;
 7998        };
 7999
 8000        let extension = buffer
 8001            .read(cx)
 8002            .file()
 8003            .and_then(|file| Some(file.path().extension()?.to_string()));
 8004
 8005        let event_type = match accepted {
 8006            true => "Edit Prediction Accepted",
 8007            false => "Edit Prediction Discarded",
 8008        };
 8009        telemetry::event!(
 8010            event_type,
 8011            provider = provider.name(),
 8012            prediction_id = id,
 8013            suggestion_accepted = accepted,
 8014            file_extension = extension,
 8015        );
 8016    }
 8017
 8018    fn open_editor_at_anchor(
 8019        snapshot: &language::BufferSnapshot,
 8020        target: language::Anchor,
 8021        workspace: &Entity<Workspace>,
 8022        window: &mut Window,
 8023        cx: &mut App,
 8024    ) -> Task<Result<()>> {
 8025        workspace.update(cx, |workspace, cx| {
 8026            let path = snapshot.file().map(|file| file.full_path(cx));
 8027            let Some(path) =
 8028                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8029            else {
 8030                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8031            };
 8032            let target = text::ToPoint::to_point(&target, snapshot);
 8033            let item = workspace.open_path(path, None, true, window, cx);
 8034            window.spawn(cx, async move |cx| {
 8035                let Some(editor) = item.await?.downcast::<Editor>() else {
 8036                    return Ok(());
 8037                };
 8038                editor
 8039                    .update_in(cx, |editor, window, cx| {
 8040                        editor.go_to_singleton_buffer_point(target, window, cx);
 8041                    })
 8042                    .ok();
 8043                anyhow::Ok(())
 8044            })
 8045        })
 8046    }
 8047
 8048    pub fn has_active_edit_prediction(&self) -> bool {
 8049        self.active_edit_prediction.is_some()
 8050    }
 8051
 8052    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8053        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8054            return false;
 8055        };
 8056
 8057        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8058        self.clear_highlights::<EditPredictionHighlight>(cx);
 8059        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8060        true
 8061    }
 8062
 8063    /// Returns true when we're displaying the edit prediction popover below the cursor
 8064    /// like we are not previewing and the LSP autocomplete menu is visible
 8065    /// or we are in `when_holding_modifier` mode.
 8066    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8067        if self.edit_prediction_preview_is_active()
 8068            || !self.show_edit_predictions_in_menu()
 8069            || !self.edit_predictions_enabled()
 8070        {
 8071            return false;
 8072        }
 8073
 8074        if self.has_visible_completions_menu() {
 8075            return true;
 8076        }
 8077
 8078        has_completion && self.edit_prediction_requires_modifier()
 8079    }
 8080
 8081    fn handle_modifiers_changed(
 8082        &mut self,
 8083        modifiers: Modifiers,
 8084        position_map: &PositionMap,
 8085        window: &mut Window,
 8086        cx: &mut Context<Self>,
 8087    ) {
 8088        // Ensure that the edit prediction preview is updated, even when not
 8089        // enabled, if there's an active edit prediction preview.
 8090        if self.show_edit_predictions_in_menu()
 8091            || matches!(
 8092                self.edit_prediction_preview,
 8093                EditPredictionPreview::Active { .. }
 8094            )
 8095        {
 8096            self.update_edit_prediction_preview(&modifiers, window, cx);
 8097        }
 8098
 8099        self.update_selection_mode(&modifiers, position_map, window, cx);
 8100
 8101        let mouse_position = window.mouse_position();
 8102        if !position_map.text_hitbox.is_hovered(window) {
 8103            return;
 8104        }
 8105
 8106        self.update_hovered_link(
 8107            position_map.point_for_position(mouse_position),
 8108            &position_map.snapshot,
 8109            modifiers,
 8110            window,
 8111            cx,
 8112        )
 8113    }
 8114
 8115    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8116        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8117            MultiCursorModifier::Alt => modifiers.secondary(),
 8118            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8119        }
 8120    }
 8121
 8122    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8123        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8124            MultiCursorModifier::Alt => modifiers.alt,
 8125            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8126        }
 8127    }
 8128
 8129    fn columnar_selection_mode(
 8130        modifiers: &Modifiers,
 8131        cx: &mut Context<Self>,
 8132    ) -> Option<ColumnarMode> {
 8133        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8134            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8135                Some(ColumnarMode::FromMouse)
 8136            } else if Self::is_alt_pressed(modifiers, cx) {
 8137                Some(ColumnarMode::FromSelection)
 8138            } else {
 8139                None
 8140            }
 8141        } else {
 8142            None
 8143        }
 8144    }
 8145
 8146    fn update_selection_mode(
 8147        &mut self,
 8148        modifiers: &Modifiers,
 8149        position_map: &PositionMap,
 8150        window: &mut Window,
 8151        cx: &mut Context<Self>,
 8152    ) {
 8153        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8154            return;
 8155        };
 8156        if self.selections.pending_anchor().is_none() {
 8157            return;
 8158        }
 8159
 8160        let mouse_position = window.mouse_position();
 8161        let point_for_position = position_map.point_for_position(mouse_position);
 8162        let position = point_for_position.previous_valid;
 8163
 8164        self.select(
 8165            SelectPhase::BeginColumnar {
 8166                position,
 8167                reset: false,
 8168                mode,
 8169                goal_column: point_for_position.exact_unclipped.column(),
 8170            },
 8171            window,
 8172            cx,
 8173        );
 8174    }
 8175
 8176    fn update_edit_prediction_preview(
 8177        &mut self,
 8178        modifiers: &Modifiers,
 8179        window: &mut Window,
 8180        cx: &mut Context<Self>,
 8181    ) {
 8182        let mut modifiers_held = false;
 8183
 8184        // Check bindings for all granularities.
 8185        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8186        let granularities = [
 8187            EditPredictionGranularity::Full,
 8188            EditPredictionGranularity::Line,
 8189            EditPredictionGranularity::Word,
 8190        ];
 8191
 8192        for granularity in granularities {
 8193            if let Some(keystroke) = self
 8194                .accept_edit_prediction_keybind(granularity, window, cx)
 8195                .keystroke()
 8196            {
 8197                modifiers_held = modifiers_held
 8198                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8199            }
 8200        }
 8201
 8202        if modifiers_held {
 8203            if matches!(
 8204                self.edit_prediction_preview,
 8205                EditPredictionPreview::Inactive { .. }
 8206            ) {
 8207                self.edit_prediction_preview = EditPredictionPreview::Active {
 8208                    previous_scroll_position: None,
 8209                    since: Instant::now(),
 8210                };
 8211
 8212                self.update_visible_edit_prediction(window, cx);
 8213                cx.notify();
 8214            }
 8215        } else if let EditPredictionPreview::Active {
 8216            previous_scroll_position,
 8217            since,
 8218        } = self.edit_prediction_preview
 8219        {
 8220            if let (Some(previous_scroll_position), Some(position_map)) =
 8221                (previous_scroll_position, self.last_position_map.as_ref())
 8222            {
 8223                self.set_scroll_position(
 8224                    previous_scroll_position
 8225                        .scroll_position(&position_map.snapshot.display_snapshot),
 8226                    window,
 8227                    cx,
 8228                );
 8229            }
 8230
 8231            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8232                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8233            };
 8234            self.clear_row_highlights::<EditPredictionPreview>();
 8235            self.update_visible_edit_prediction(window, cx);
 8236            cx.notify();
 8237        }
 8238    }
 8239
 8240    fn update_visible_edit_prediction(
 8241        &mut self,
 8242        _window: &mut Window,
 8243        cx: &mut Context<Self>,
 8244    ) -> Option<()> {
 8245        if DisableAiSettings::get_global(cx).disable_ai {
 8246            return None;
 8247        }
 8248
 8249        if self.ime_transaction.is_some() {
 8250            self.discard_edit_prediction(false, cx);
 8251            return None;
 8252        }
 8253
 8254        let selection = self.selections.newest_anchor();
 8255        let cursor = selection.head();
 8256        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8257        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8258        let excerpt_id = cursor.excerpt_id;
 8259
 8260        let show_in_menu = self.show_edit_predictions_in_menu();
 8261        let completions_menu_has_precedence = !show_in_menu
 8262            && (self.context_menu.borrow().is_some()
 8263                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8264
 8265        if completions_menu_has_precedence
 8266            || !offset_selection.is_empty()
 8267            || self
 8268                .active_edit_prediction
 8269                .as_ref()
 8270                .is_some_and(|completion| {
 8271                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8272                        return false;
 8273                    };
 8274                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8275                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8276                    !invalidation_range.contains(&offset_selection.head())
 8277                })
 8278        {
 8279            self.discard_edit_prediction(false, cx);
 8280            return None;
 8281        }
 8282
 8283        self.take_active_edit_prediction(cx);
 8284        let Some(provider) = self.edit_prediction_provider() else {
 8285            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8286            return None;
 8287        };
 8288
 8289        let (buffer, cursor_buffer_position) =
 8290            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8291
 8292        self.edit_prediction_settings =
 8293            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8294
 8295        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8296
 8297        if self.edit_prediction_indent_conflict {
 8298            let cursor_point = cursor.to_point(&multibuffer);
 8299            let mut suggested_indent = None;
 8300            multibuffer.suggested_indents_callback(
 8301                cursor_point.row..cursor_point.row + 1,
 8302                |_, indent| {
 8303                    suggested_indent = Some(indent);
 8304                    ControlFlow::Break(())
 8305                },
 8306                cx,
 8307            );
 8308
 8309            if let Some(indent) = suggested_indent
 8310                && indent.len == cursor_point.column
 8311            {
 8312                self.edit_prediction_indent_conflict = false;
 8313            }
 8314        }
 8315
 8316        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8317
 8318        let (completion_id, edits, edit_preview) = match edit_prediction {
 8319            edit_prediction_types::EditPrediction::Local {
 8320                id,
 8321                edits,
 8322                edit_preview,
 8323            } => (id, edits, edit_preview),
 8324            edit_prediction_types::EditPrediction::Jump {
 8325                id,
 8326                snapshot,
 8327                target,
 8328            } => {
 8329                if let Some(provider) = &self.edit_prediction_provider {
 8330                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8331                }
 8332                self.stale_edit_prediction_in_menu = None;
 8333                self.active_edit_prediction = Some(EditPredictionState {
 8334                    inlay_ids: vec![],
 8335                    completion: EditPrediction::MoveOutside { snapshot, target },
 8336                    completion_id: id,
 8337                    invalidation_range: None,
 8338                });
 8339                cx.notify();
 8340                return Some(());
 8341            }
 8342        };
 8343
 8344        let edits = edits
 8345            .into_iter()
 8346            .flat_map(|(range, new_text)| {
 8347                Some((
 8348                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8349                    new_text,
 8350                ))
 8351            })
 8352            .collect::<Vec<_>>();
 8353        if edits.is_empty() {
 8354            return None;
 8355        }
 8356
 8357        let first_edit_start = edits.first().unwrap().0.start;
 8358        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8359        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8360
 8361        let last_edit_end = edits.last().unwrap().0.end;
 8362        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8363        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8364
 8365        let cursor_row = cursor.to_point(&multibuffer).row;
 8366
 8367        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8368
 8369        let mut inlay_ids = Vec::new();
 8370        let invalidation_row_range;
 8371        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8372            Some(cursor_row..edit_end_row)
 8373        } else if cursor_row > edit_end_row {
 8374            Some(edit_start_row..cursor_row)
 8375        } else {
 8376            None
 8377        };
 8378        let supports_jump = self
 8379            .edit_prediction_provider
 8380            .as_ref()
 8381            .map(|provider| provider.provider.supports_jump_to_edit())
 8382            .unwrap_or(true);
 8383
 8384        let is_move = supports_jump
 8385            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8386        let completion = if is_move {
 8387            if let Some(provider) = &self.edit_prediction_provider {
 8388                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8389            }
 8390            invalidation_row_range =
 8391                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8392            let target = first_edit_start;
 8393            EditPrediction::MoveWithin { target, snapshot }
 8394        } else {
 8395            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8396                && !self.edit_predictions_hidden_for_vim_mode;
 8397
 8398            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8399                if provider.show_tab_accept_marker() {
 8400                    EditDisplayMode::TabAccept
 8401                } else {
 8402                    EditDisplayMode::Inline
 8403                }
 8404            } else {
 8405                EditDisplayMode::DiffPopover
 8406            };
 8407
 8408            if show_completions_in_buffer {
 8409                if let Some(provider) = &self.edit_prediction_provider {
 8410                    let suggestion_display_type = match display_mode {
 8411                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8412                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8413                            SuggestionDisplayType::GhostText
 8414                        }
 8415                    };
 8416                    provider.provider.did_show(suggestion_display_type, cx);
 8417                }
 8418                if edits
 8419                    .iter()
 8420                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8421                {
 8422                    let mut inlays = Vec::new();
 8423                    for (range, new_text) in &edits {
 8424                        let inlay = Inlay::edit_prediction(
 8425                            post_inc(&mut self.next_inlay_id),
 8426                            range.start,
 8427                            new_text.as_ref(),
 8428                        );
 8429                        inlay_ids.push(inlay.id);
 8430                        inlays.push(inlay);
 8431                    }
 8432
 8433                    self.splice_inlays(&[], inlays, cx);
 8434                } else {
 8435                    let background_color = cx.theme().status().deleted_background;
 8436                    self.highlight_text::<EditPredictionHighlight>(
 8437                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8438                        HighlightStyle {
 8439                            background_color: Some(background_color),
 8440                            ..Default::default()
 8441                        },
 8442                        cx,
 8443                    );
 8444                }
 8445            }
 8446
 8447            invalidation_row_range = edit_start_row..edit_end_row;
 8448
 8449            EditPrediction::Edit {
 8450                edits,
 8451                edit_preview,
 8452                display_mode,
 8453                snapshot,
 8454            }
 8455        };
 8456
 8457        let invalidation_range = multibuffer
 8458            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8459            ..multibuffer.anchor_after(Point::new(
 8460                invalidation_row_range.end,
 8461                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8462            ));
 8463
 8464        self.stale_edit_prediction_in_menu = None;
 8465        self.active_edit_prediction = Some(EditPredictionState {
 8466            inlay_ids,
 8467            completion,
 8468            completion_id,
 8469            invalidation_range: Some(invalidation_range),
 8470        });
 8471
 8472        cx.notify();
 8473
 8474        Some(())
 8475    }
 8476
 8477    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8478        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8479    }
 8480
 8481    fn clear_tasks(&mut self) {
 8482        self.tasks.clear()
 8483    }
 8484
 8485    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8486        if self.tasks.insert(key, value).is_some() {
 8487            // This case should hopefully be rare, but just in case...
 8488            log::error!(
 8489                "multiple different run targets found on a single line, only the last target will be rendered"
 8490            )
 8491        }
 8492    }
 8493
 8494    /// Get all display points of breakpoints that will be rendered within editor
 8495    ///
 8496    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8497    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8498    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8499    fn active_breakpoints(
 8500        &self,
 8501        range: Range<DisplayRow>,
 8502        window: &mut Window,
 8503        cx: &mut Context<Self>,
 8504    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8505        let mut breakpoint_display_points = HashMap::default();
 8506
 8507        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8508            return breakpoint_display_points;
 8509        };
 8510
 8511        let snapshot = self.snapshot(window, cx);
 8512
 8513        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8514        let Some(project) = self.project() else {
 8515            return breakpoint_display_points;
 8516        };
 8517
 8518        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8519            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8520
 8521        for (buffer_snapshot, range, excerpt_id) in
 8522            multi_buffer_snapshot.range_to_buffer_ranges(range)
 8523        {
 8524            let Some(buffer) = project
 8525                .read(cx)
 8526                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8527            else {
 8528                continue;
 8529            };
 8530            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8531                &buffer,
 8532                Some(
 8533                    buffer_snapshot.anchor_before(range.start)
 8534                        ..buffer_snapshot.anchor_after(range.end),
 8535                ),
 8536                buffer_snapshot,
 8537                cx,
 8538            );
 8539            for (breakpoint, state) in breakpoints {
 8540                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8541                let position = multi_buffer_anchor
 8542                    .to_point(&multi_buffer_snapshot)
 8543                    .to_display_point(&snapshot);
 8544
 8545                breakpoint_display_points.insert(
 8546                    position.row(),
 8547                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8548                );
 8549            }
 8550        }
 8551
 8552        breakpoint_display_points
 8553    }
 8554
 8555    fn breakpoint_context_menu(
 8556        &self,
 8557        anchor: Anchor,
 8558        window: &mut Window,
 8559        cx: &mut Context<Self>,
 8560    ) -> Entity<ui::ContextMenu> {
 8561        let weak_editor = cx.weak_entity();
 8562        let focus_handle = self.focus_handle(cx);
 8563
 8564        let row = self
 8565            .buffer
 8566            .read(cx)
 8567            .snapshot(cx)
 8568            .summary_for_anchor::<Point>(&anchor)
 8569            .row;
 8570
 8571        let breakpoint = self
 8572            .breakpoint_at_row(row, window, cx)
 8573            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8574
 8575        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8576            "Edit Log Breakpoint"
 8577        } else {
 8578            "Set Log Breakpoint"
 8579        };
 8580
 8581        let condition_breakpoint_msg = if breakpoint
 8582            .as_ref()
 8583            .is_some_and(|bp| bp.1.condition.is_some())
 8584        {
 8585            "Edit Condition Breakpoint"
 8586        } else {
 8587            "Set Condition Breakpoint"
 8588        };
 8589
 8590        let hit_condition_breakpoint_msg = if breakpoint
 8591            .as_ref()
 8592            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8593        {
 8594            "Edit Hit Condition Breakpoint"
 8595        } else {
 8596            "Set Hit Condition Breakpoint"
 8597        };
 8598
 8599        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8600            "Unset Breakpoint"
 8601        } else {
 8602            "Set Breakpoint"
 8603        };
 8604
 8605        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8606
 8607        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8608            BreakpointState::Enabled => Some("Disable"),
 8609            BreakpointState::Disabled => Some("Enable"),
 8610        });
 8611
 8612        let (anchor, breakpoint) =
 8613            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8614
 8615        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8616            menu.on_blur_subscription(Subscription::new(|| {}))
 8617                .context(focus_handle)
 8618                .when(run_to_cursor, |this| {
 8619                    let weak_editor = weak_editor.clone();
 8620                    this.entry("Run to cursor", None, move |window, cx| {
 8621                        weak_editor
 8622                            .update(cx, |editor, cx| {
 8623                                editor.change_selections(
 8624                                    SelectionEffects::no_scroll(),
 8625                                    window,
 8626                                    cx,
 8627                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8628                                );
 8629                            })
 8630                            .ok();
 8631
 8632                        window.dispatch_action(Box::new(RunToCursor), cx);
 8633                    })
 8634                    .separator()
 8635                })
 8636                .when_some(toggle_state_msg, |this, msg| {
 8637                    this.entry(msg, None, {
 8638                        let weak_editor = weak_editor.clone();
 8639                        let breakpoint = breakpoint.clone();
 8640                        move |_window, cx| {
 8641                            weak_editor
 8642                                .update(cx, |this, cx| {
 8643                                    this.edit_breakpoint_at_anchor(
 8644                                        anchor,
 8645                                        breakpoint.as_ref().clone(),
 8646                                        BreakpointEditAction::InvertState,
 8647                                        cx,
 8648                                    );
 8649                                })
 8650                                .log_err();
 8651                        }
 8652                    })
 8653                })
 8654                .entry(set_breakpoint_msg, None, {
 8655                    let weak_editor = weak_editor.clone();
 8656                    let breakpoint = breakpoint.clone();
 8657                    move |_window, cx| {
 8658                        weak_editor
 8659                            .update(cx, |this, cx| {
 8660                                this.edit_breakpoint_at_anchor(
 8661                                    anchor,
 8662                                    breakpoint.as_ref().clone(),
 8663                                    BreakpointEditAction::Toggle,
 8664                                    cx,
 8665                                );
 8666                            })
 8667                            .log_err();
 8668                    }
 8669                })
 8670                .entry(log_breakpoint_msg, None, {
 8671                    let breakpoint = breakpoint.clone();
 8672                    let weak_editor = weak_editor.clone();
 8673                    move |window, cx| {
 8674                        weak_editor
 8675                            .update(cx, |this, cx| {
 8676                                this.add_edit_breakpoint_block(
 8677                                    anchor,
 8678                                    breakpoint.as_ref(),
 8679                                    BreakpointPromptEditAction::Log,
 8680                                    window,
 8681                                    cx,
 8682                                );
 8683                            })
 8684                            .log_err();
 8685                    }
 8686                })
 8687                .entry(condition_breakpoint_msg, None, {
 8688                    let breakpoint = breakpoint.clone();
 8689                    let weak_editor = weak_editor.clone();
 8690                    move |window, cx| {
 8691                        weak_editor
 8692                            .update(cx, |this, cx| {
 8693                                this.add_edit_breakpoint_block(
 8694                                    anchor,
 8695                                    breakpoint.as_ref(),
 8696                                    BreakpointPromptEditAction::Condition,
 8697                                    window,
 8698                                    cx,
 8699                                );
 8700                            })
 8701                            .log_err();
 8702                    }
 8703                })
 8704                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8705                    weak_editor
 8706                        .update(cx, |this, cx| {
 8707                            this.add_edit_breakpoint_block(
 8708                                anchor,
 8709                                breakpoint.as_ref(),
 8710                                BreakpointPromptEditAction::HitCondition,
 8711                                window,
 8712                                cx,
 8713                            );
 8714                        })
 8715                        .log_err();
 8716                })
 8717        })
 8718    }
 8719
 8720    fn render_breakpoint(
 8721        &self,
 8722        position: Anchor,
 8723        row: DisplayRow,
 8724        breakpoint: &Breakpoint,
 8725        state: Option<BreakpointSessionState>,
 8726        cx: &mut Context<Self>,
 8727    ) -> IconButton {
 8728        let is_rejected = state.is_some_and(|s| !s.verified);
 8729        // Is it a breakpoint that shows up when hovering over gutter?
 8730        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 8731            (false, false),
 8732            |PhantomBreakpointIndicator {
 8733                 is_active,
 8734                 display_row,
 8735                 collides_with_existing_breakpoint,
 8736             }| {
 8737                (
 8738                    is_active && display_row == row,
 8739                    collides_with_existing_breakpoint,
 8740                )
 8741            },
 8742        );
 8743
 8744        let (color, icon) = {
 8745            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 8746                (false, false) => ui::IconName::DebugBreakpoint,
 8747                (true, false) => ui::IconName::DebugLogBreakpoint,
 8748                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 8749                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 8750            };
 8751
 8752            let theme_colors = cx.theme().colors();
 8753
 8754            let color = if is_phantom {
 8755                if collides_with_existing {
 8756                    Color::Custom(
 8757                        theme_colors
 8758                            .debugger_accent
 8759                            .blend(theme_colors.text.opacity(0.6)),
 8760                    )
 8761                } else {
 8762                    Color::Hint
 8763                }
 8764            } else if is_rejected {
 8765                Color::Disabled
 8766            } else {
 8767                Color::Debugger
 8768            };
 8769
 8770            (color, icon)
 8771        };
 8772
 8773        let breakpoint = Arc::from(breakpoint.clone());
 8774
 8775        let alt_as_text = gpui::Keystroke {
 8776            modifiers: Modifiers::secondary_key(),
 8777            ..Default::default()
 8778        };
 8779        let primary_action_text = if breakpoint.is_disabled() {
 8780            "Enable breakpoint"
 8781        } else if is_phantom && !collides_with_existing {
 8782            "Set breakpoint"
 8783        } else {
 8784            "Unset breakpoint"
 8785        };
 8786        let focus_handle = self.focus_handle.clone();
 8787
 8788        let meta = if is_rejected {
 8789            SharedString::from("No executable code is associated with this line.")
 8790        } else if collides_with_existing && !breakpoint.is_disabled() {
 8791            SharedString::from(format!(
 8792                "{alt_as_text}-click to disable,\nright-click for more options."
 8793            ))
 8794        } else {
 8795            SharedString::from("Right-click for more options.")
 8796        };
 8797        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 8798            .icon_size(IconSize::XSmall)
 8799            .size(ui::ButtonSize::None)
 8800            .when(is_rejected, |this| {
 8801                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 8802            })
 8803            .icon_color(color)
 8804            .style(ButtonStyle::Transparent)
 8805            .on_click(cx.listener({
 8806                move |editor, event: &ClickEvent, window, cx| {
 8807                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 8808                        BreakpointEditAction::InvertState
 8809                    } else {
 8810                        BreakpointEditAction::Toggle
 8811                    };
 8812
 8813                    window.focus(&editor.focus_handle(cx), cx);
 8814                    editor.edit_breakpoint_at_anchor(
 8815                        position,
 8816                        breakpoint.as_ref().clone(),
 8817                        edit_action,
 8818                        cx,
 8819                    );
 8820                }
 8821            }))
 8822            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 8823                editor.set_breakpoint_context_menu(
 8824                    row,
 8825                    Some(position),
 8826                    event.position(),
 8827                    window,
 8828                    cx,
 8829                );
 8830            }))
 8831            .tooltip(move |_window, cx| {
 8832                Tooltip::with_meta_in(
 8833                    primary_action_text,
 8834                    Some(&ToggleBreakpoint),
 8835                    meta.clone(),
 8836                    &focus_handle,
 8837                    cx,
 8838                )
 8839            })
 8840    }
 8841
 8842    fn build_tasks_context(
 8843        project: &Entity<Project>,
 8844        buffer: &Entity<Buffer>,
 8845        buffer_row: u32,
 8846        tasks: &Arc<RunnableTasks>,
 8847        cx: &mut Context<Self>,
 8848    ) -> Task<Option<task::TaskContext>> {
 8849        let position = Point::new(buffer_row, tasks.column);
 8850        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 8851        let location = Location {
 8852            buffer: buffer.clone(),
 8853            range: range_start..range_start,
 8854        };
 8855        // Fill in the environmental variables from the tree-sitter captures
 8856        let mut captured_task_variables = TaskVariables::default();
 8857        for (capture_name, value) in tasks.extra_variables.clone() {
 8858            captured_task_variables.insert(
 8859                task::VariableName::Custom(capture_name.into()),
 8860                value.clone(),
 8861            );
 8862        }
 8863        project.update(cx, |project, cx| {
 8864            project.task_store().update(cx, |task_store, cx| {
 8865                task_store.task_context_for_location(captured_task_variables, location, cx)
 8866            })
 8867        })
 8868    }
 8869
 8870    pub fn spawn_nearest_task(
 8871        &mut self,
 8872        action: &SpawnNearestTask,
 8873        window: &mut Window,
 8874        cx: &mut Context<Self>,
 8875    ) {
 8876        let Some((workspace, _)) = self.workspace.clone() else {
 8877            return;
 8878        };
 8879        let Some(project) = self.project.clone() else {
 8880            return;
 8881        };
 8882
 8883        // Try to find a closest, enclosing node using tree-sitter that has a task
 8884        let Some((buffer, buffer_row, tasks)) = self
 8885            .find_enclosing_node_task(cx)
 8886            // Or find the task that's closest in row-distance.
 8887            .or_else(|| self.find_closest_task(cx))
 8888        else {
 8889            return;
 8890        };
 8891
 8892        let reveal_strategy = action.reveal;
 8893        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 8894        cx.spawn_in(window, async move |_, cx| {
 8895            let context = task_context.await?;
 8896            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 8897
 8898            let resolved = &mut resolved_task.resolved;
 8899            resolved.reveal = reveal_strategy;
 8900
 8901            workspace
 8902                .update_in(cx, |workspace, window, cx| {
 8903                    workspace.schedule_resolved_task(
 8904                        task_source_kind,
 8905                        resolved_task,
 8906                        false,
 8907                        window,
 8908                        cx,
 8909                    );
 8910                })
 8911                .ok()
 8912        })
 8913        .detach();
 8914    }
 8915
 8916    fn find_closest_task(
 8917        &mut self,
 8918        cx: &mut Context<Self>,
 8919    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 8920        let cursor_row = self
 8921            .selections
 8922            .newest_adjusted(&self.display_snapshot(cx))
 8923            .head()
 8924            .row;
 8925
 8926        let ((buffer_id, row), tasks) = self
 8927            .tasks
 8928            .iter()
 8929            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 8930
 8931        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 8932        let tasks = Arc::new(tasks.to_owned());
 8933        Some((buffer, *row, tasks))
 8934    }
 8935
 8936    fn find_enclosing_node_task(
 8937        &mut self,
 8938        cx: &mut Context<Self>,
 8939    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 8940        let snapshot = self.buffer.read(cx).snapshot(cx);
 8941        let offset = self
 8942            .selections
 8943            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8944            .head();
 8945        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 8946        let offset = excerpt.map_offset_to_buffer(offset);
 8947        let buffer_id = excerpt.buffer().remote_id();
 8948
 8949        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 8950        let mut cursor = layer.node().walk();
 8951
 8952        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 8953            if cursor.node().end_byte() == offset.0 {
 8954                cursor.goto_next_sibling();
 8955            }
 8956        }
 8957
 8958        // Ascend to the smallest ancestor that contains the range and has a task.
 8959        loop {
 8960            let node = cursor.node();
 8961            let node_range = node.byte_range();
 8962            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 8963
 8964            // Check if this node contains our offset
 8965            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 8966                // If it contains offset, check for task
 8967                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 8968                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 8969                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 8970                }
 8971            }
 8972
 8973            if !cursor.goto_parent() {
 8974                break;
 8975            }
 8976        }
 8977        None
 8978    }
 8979
 8980    fn render_run_indicator(
 8981        &self,
 8982        _style: &EditorStyle,
 8983        is_active: bool,
 8984        row: DisplayRow,
 8985        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 8986        cx: &mut Context<Self>,
 8987    ) -> IconButton {
 8988        let color = Color::Muted;
 8989        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 8990
 8991        IconButton::new(
 8992            ("run_indicator", row.0 as usize),
 8993            ui::IconName::PlayOutlined,
 8994        )
 8995        .shape(ui::IconButtonShape::Square)
 8996        .icon_size(IconSize::XSmall)
 8997        .icon_color(color)
 8998        .toggle_state(is_active)
 8999        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9000            let quick_launch = match e {
 9001                ClickEvent::Keyboard(_) => true,
 9002                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9003            };
 9004
 9005            window.focus(&editor.focus_handle(cx), cx);
 9006            editor.toggle_code_actions(
 9007                &ToggleCodeActions {
 9008                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9009                    quick_launch,
 9010                },
 9011                window,
 9012                cx,
 9013            );
 9014        }))
 9015        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9016            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9017        }))
 9018    }
 9019
 9020    pub fn context_menu_visible(&self) -> bool {
 9021        !self.edit_prediction_preview_is_active()
 9022            && self
 9023                .context_menu
 9024                .borrow()
 9025                .as_ref()
 9026                .is_some_and(|menu| menu.visible())
 9027    }
 9028
 9029    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9030        self.context_menu
 9031            .borrow()
 9032            .as_ref()
 9033            .map(|menu| menu.origin())
 9034    }
 9035
 9036    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9037        self.context_menu_options = Some(options);
 9038    }
 9039
 9040    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9041    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9042
 9043    fn render_edit_prediction_popover(
 9044        &mut self,
 9045        text_bounds: &Bounds<Pixels>,
 9046        content_origin: gpui::Point<Pixels>,
 9047        right_margin: Pixels,
 9048        editor_snapshot: &EditorSnapshot,
 9049        visible_row_range: Range<DisplayRow>,
 9050        scroll_top: ScrollOffset,
 9051        scroll_bottom: ScrollOffset,
 9052        line_layouts: &[LineWithInvisibles],
 9053        line_height: Pixels,
 9054        scroll_position: gpui::Point<ScrollOffset>,
 9055        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9056        newest_selection_head: Option<DisplayPoint>,
 9057        editor_width: Pixels,
 9058        style: &EditorStyle,
 9059        window: &mut Window,
 9060        cx: &mut App,
 9061    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9062        if self.mode().is_minimap() {
 9063            return None;
 9064        }
 9065        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9066
 9067        if self.edit_prediction_visible_in_cursor_popover(true) {
 9068            return None;
 9069        }
 9070
 9071        match &active_edit_prediction.completion {
 9072            EditPrediction::MoveWithin { target, .. } => {
 9073                let target_display_point = target.to_display_point(editor_snapshot);
 9074
 9075                if self.edit_prediction_requires_modifier() {
 9076                    if !self.edit_prediction_preview_is_active() {
 9077                        return None;
 9078                    }
 9079
 9080                    self.render_edit_prediction_modifier_jump_popover(
 9081                        text_bounds,
 9082                        content_origin,
 9083                        visible_row_range,
 9084                        line_layouts,
 9085                        line_height,
 9086                        scroll_pixel_position,
 9087                        newest_selection_head,
 9088                        target_display_point,
 9089                        window,
 9090                        cx,
 9091                    )
 9092                } else {
 9093                    self.render_edit_prediction_eager_jump_popover(
 9094                        text_bounds,
 9095                        content_origin,
 9096                        editor_snapshot,
 9097                        visible_row_range,
 9098                        scroll_top,
 9099                        scroll_bottom,
 9100                        line_height,
 9101                        scroll_pixel_position,
 9102                        target_display_point,
 9103                        editor_width,
 9104                        window,
 9105                        cx,
 9106                    )
 9107                }
 9108            }
 9109            EditPrediction::Edit {
 9110                display_mode: EditDisplayMode::Inline,
 9111                ..
 9112            } => None,
 9113            EditPrediction::Edit {
 9114                display_mode: EditDisplayMode::TabAccept,
 9115                edits,
 9116                ..
 9117            } => {
 9118                let range = &edits.first()?.0;
 9119                let target_display_point = range.end.to_display_point(editor_snapshot);
 9120
 9121                self.render_edit_prediction_end_of_line_popover(
 9122                    "Accept",
 9123                    editor_snapshot,
 9124                    visible_row_range,
 9125                    target_display_point,
 9126                    line_height,
 9127                    scroll_pixel_position,
 9128                    content_origin,
 9129                    editor_width,
 9130                    window,
 9131                    cx,
 9132                )
 9133            }
 9134            EditPrediction::Edit {
 9135                edits,
 9136                edit_preview,
 9137                display_mode: EditDisplayMode::DiffPopover,
 9138                snapshot,
 9139            } => self.render_edit_prediction_diff_popover(
 9140                text_bounds,
 9141                content_origin,
 9142                right_margin,
 9143                editor_snapshot,
 9144                visible_row_range,
 9145                line_layouts,
 9146                line_height,
 9147                scroll_position,
 9148                scroll_pixel_position,
 9149                newest_selection_head,
 9150                editor_width,
 9151                style,
 9152                edits,
 9153                edit_preview,
 9154                snapshot,
 9155                window,
 9156                cx,
 9157            ),
 9158            EditPrediction::MoveOutside { snapshot, .. } => {
 9159                let mut element = self
 9160                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9161                    .into_any();
 9162
 9163                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9164                let origin_x = text_bounds.size.width - size.width - px(30.);
 9165                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9166                element.prepaint_at(origin, window, cx);
 9167
 9168                Some((element, origin))
 9169            }
 9170        }
 9171    }
 9172
 9173    fn render_edit_prediction_modifier_jump_popover(
 9174        &mut self,
 9175        text_bounds: &Bounds<Pixels>,
 9176        content_origin: gpui::Point<Pixels>,
 9177        visible_row_range: Range<DisplayRow>,
 9178        line_layouts: &[LineWithInvisibles],
 9179        line_height: Pixels,
 9180        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9181        newest_selection_head: Option<DisplayPoint>,
 9182        target_display_point: DisplayPoint,
 9183        window: &mut Window,
 9184        cx: &mut App,
 9185    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9186        let scrolled_content_origin =
 9187            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9188
 9189        const SCROLL_PADDING_Y: Pixels = px(12.);
 9190
 9191        if target_display_point.row() < visible_row_range.start {
 9192            return self.render_edit_prediction_scroll_popover(
 9193                |_| SCROLL_PADDING_Y,
 9194                IconName::ArrowUp,
 9195                visible_row_range,
 9196                line_layouts,
 9197                newest_selection_head,
 9198                scrolled_content_origin,
 9199                window,
 9200                cx,
 9201            );
 9202        } else if target_display_point.row() >= visible_row_range.end {
 9203            return self.render_edit_prediction_scroll_popover(
 9204                |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9205                IconName::ArrowDown,
 9206                visible_row_range,
 9207                line_layouts,
 9208                newest_selection_head,
 9209                scrolled_content_origin,
 9210                window,
 9211                cx,
 9212            );
 9213        }
 9214
 9215        const POLE_WIDTH: Pixels = px(2.);
 9216
 9217        let line_layout =
 9218            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9219        let target_column = target_display_point.column() as usize;
 9220
 9221        let target_x = line_layout.x_for_index(target_column);
 9222        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9223            - scroll_pixel_position.y;
 9224
 9225        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9226
 9227        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9228        border_color.l += 0.001;
 9229
 9230        let mut element = v_flex()
 9231            .items_end()
 9232            .when(flag_on_right, |el| el.items_start())
 9233            .child(if flag_on_right {
 9234                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9235                    .rounded_bl(px(0.))
 9236                    .rounded_tl(px(0.))
 9237                    .border_l_2()
 9238                    .border_color(border_color)
 9239            } else {
 9240                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9241                    .rounded_br(px(0.))
 9242                    .rounded_tr(px(0.))
 9243                    .border_r_2()
 9244                    .border_color(border_color)
 9245            })
 9246            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9247            .into_any();
 9248
 9249        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9250
 9251        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9252            - point(
 9253                if flag_on_right {
 9254                    POLE_WIDTH
 9255                } else {
 9256                    size.width - POLE_WIDTH
 9257                },
 9258                size.height - line_height,
 9259            );
 9260
 9261        origin.x = origin.x.max(content_origin.x);
 9262
 9263        element.prepaint_at(origin, window, cx);
 9264
 9265        Some((element, origin))
 9266    }
 9267
 9268    fn render_edit_prediction_scroll_popover(
 9269        &mut self,
 9270        to_y: impl Fn(Size<Pixels>) -> Pixels,
 9271        scroll_icon: IconName,
 9272        visible_row_range: Range<DisplayRow>,
 9273        line_layouts: &[LineWithInvisibles],
 9274        newest_selection_head: Option<DisplayPoint>,
 9275        scrolled_content_origin: gpui::Point<Pixels>,
 9276        window: &mut Window,
 9277        cx: &mut App,
 9278    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9279        let mut element = self
 9280            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9281            .into_any();
 9282
 9283        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9284
 9285        let cursor = newest_selection_head?;
 9286        let cursor_row_layout =
 9287            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9288        let cursor_column = cursor.column() as usize;
 9289
 9290        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9291
 9292        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9293
 9294        element.prepaint_at(origin, window, cx);
 9295        Some((element, origin))
 9296    }
 9297
 9298    fn render_edit_prediction_eager_jump_popover(
 9299        &mut self,
 9300        text_bounds: &Bounds<Pixels>,
 9301        content_origin: gpui::Point<Pixels>,
 9302        editor_snapshot: &EditorSnapshot,
 9303        visible_row_range: Range<DisplayRow>,
 9304        scroll_top: ScrollOffset,
 9305        scroll_bottom: ScrollOffset,
 9306        line_height: Pixels,
 9307        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9308        target_display_point: DisplayPoint,
 9309        editor_width: Pixels,
 9310        window: &mut Window,
 9311        cx: &mut App,
 9312    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9313        if target_display_point.row().as_f64() < scroll_top {
 9314            let mut element = self
 9315                .render_edit_prediction_line_popover(
 9316                    "Jump to Edit",
 9317                    Some(IconName::ArrowUp),
 9318                    window,
 9319                    cx,
 9320                )
 9321                .into_any();
 9322
 9323            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9324            let offset = point(
 9325                (text_bounds.size.width - size.width) / 2.,
 9326                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9327            );
 9328
 9329            let origin = text_bounds.origin + offset;
 9330            element.prepaint_at(origin, window, cx);
 9331            Some((element, origin))
 9332        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9333            let mut element = self
 9334                .render_edit_prediction_line_popover(
 9335                    "Jump to Edit",
 9336                    Some(IconName::ArrowDown),
 9337                    window,
 9338                    cx,
 9339                )
 9340                .into_any();
 9341
 9342            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9343            let offset = point(
 9344                (text_bounds.size.width - size.width) / 2.,
 9345                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9346            );
 9347
 9348            let origin = text_bounds.origin + offset;
 9349            element.prepaint_at(origin, window, cx);
 9350            Some((element, origin))
 9351        } else {
 9352            self.render_edit_prediction_end_of_line_popover(
 9353                "Jump to Edit",
 9354                editor_snapshot,
 9355                visible_row_range,
 9356                target_display_point,
 9357                line_height,
 9358                scroll_pixel_position,
 9359                content_origin,
 9360                editor_width,
 9361                window,
 9362                cx,
 9363            )
 9364        }
 9365    }
 9366
 9367    fn render_edit_prediction_end_of_line_popover(
 9368        self: &mut Editor,
 9369        label: &'static str,
 9370        editor_snapshot: &EditorSnapshot,
 9371        visible_row_range: Range<DisplayRow>,
 9372        target_display_point: DisplayPoint,
 9373        line_height: Pixels,
 9374        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9375        content_origin: gpui::Point<Pixels>,
 9376        editor_width: Pixels,
 9377        window: &mut Window,
 9378        cx: &mut App,
 9379    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9380        let target_line_end = DisplayPoint::new(
 9381            target_display_point.row(),
 9382            editor_snapshot.line_len(target_display_point.row()),
 9383        );
 9384
 9385        let mut element = self
 9386            .render_edit_prediction_line_popover(label, None, window, cx)
 9387            .into_any();
 9388
 9389        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9390
 9391        let line_origin =
 9392            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9393
 9394        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9395        let mut origin = start_point
 9396            + line_origin
 9397            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9398        origin.x = origin.x.max(content_origin.x);
 9399
 9400        let max_x = content_origin.x + editor_width - size.width;
 9401
 9402        if origin.x > max_x {
 9403            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9404
 9405            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9406                origin.y += offset;
 9407                IconName::ArrowUp
 9408            } else {
 9409                origin.y -= offset;
 9410                IconName::ArrowDown
 9411            };
 9412
 9413            element = self
 9414                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9415                .into_any();
 9416
 9417            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9418
 9419            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9420        }
 9421
 9422        element.prepaint_at(origin, window, cx);
 9423        Some((element, origin))
 9424    }
 9425
 9426    fn render_edit_prediction_diff_popover(
 9427        self: &Editor,
 9428        text_bounds: &Bounds<Pixels>,
 9429        content_origin: gpui::Point<Pixels>,
 9430        right_margin: Pixels,
 9431        editor_snapshot: &EditorSnapshot,
 9432        visible_row_range: Range<DisplayRow>,
 9433        line_layouts: &[LineWithInvisibles],
 9434        line_height: Pixels,
 9435        scroll_position: gpui::Point<ScrollOffset>,
 9436        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9437        newest_selection_head: Option<DisplayPoint>,
 9438        editor_width: Pixels,
 9439        style: &EditorStyle,
 9440        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9441        edit_preview: &Option<language::EditPreview>,
 9442        snapshot: &language::BufferSnapshot,
 9443        window: &mut Window,
 9444        cx: &mut App,
 9445    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9446        let edit_start = edits
 9447            .first()
 9448            .unwrap()
 9449            .0
 9450            .start
 9451            .to_display_point(editor_snapshot);
 9452        let edit_end = edits
 9453            .last()
 9454            .unwrap()
 9455            .0
 9456            .end
 9457            .to_display_point(editor_snapshot);
 9458
 9459        let is_visible = visible_row_range.contains(&edit_start.row())
 9460            || visible_row_range.contains(&edit_end.row());
 9461        if !is_visible {
 9462            return None;
 9463        }
 9464
 9465        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9466            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9467        } else {
 9468            // Fallback for providers without edit_preview
 9469            crate::edit_prediction_fallback_text(edits, cx)
 9470        };
 9471
 9472        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9473        let line_count = highlighted_edits.text.lines().count();
 9474
 9475        const BORDER_WIDTH: Pixels = px(1.);
 9476
 9477        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9478        let has_keybind = keybind.is_some();
 9479
 9480        let mut element = h_flex()
 9481            .items_start()
 9482            .child(
 9483                h_flex()
 9484                    .bg(cx.theme().colors().editor_background)
 9485                    .border(BORDER_WIDTH)
 9486                    .shadow_xs()
 9487                    .border_color(cx.theme().colors().border)
 9488                    .rounded_l_lg()
 9489                    .when(line_count > 1, |el| el.rounded_br_lg())
 9490                    .pr_1()
 9491                    .child(styled_text),
 9492            )
 9493            .child(
 9494                h_flex()
 9495                    .h(line_height + BORDER_WIDTH * 2.)
 9496                    .px_1p5()
 9497                    .gap_1()
 9498                    // Workaround: For some reason, there's a gap if we don't do this
 9499                    .ml(-BORDER_WIDTH)
 9500                    .shadow(vec![gpui::BoxShadow {
 9501                        color: gpui::black().opacity(0.05),
 9502                        offset: point(px(1.), px(1.)),
 9503                        blur_radius: px(2.),
 9504                        spread_radius: px(0.),
 9505                    }])
 9506                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9507                    .border(BORDER_WIDTH)
 9508                    .border_color(cx.theme().colors().border)
 9509                    .rounded_r_lg()
 9510                    .id("edit_prediction_diff_popover_keybind")
 9511                    .when(!has_keybind, |el| {
 9512                        let status_colors = cx.theme().status();
 9513
 9514                        el.bg(status_colors.error_background)
 9515                            .border_color(status_colors.error.opacity(0.6))
 9516                            .child(Icon::new(IconName::Info).color(Color::Error))
 9517                            .cursor_default()
 9518                            .hoverable_tooltip(move |_window, cx| {
 9519                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9520                            })
 9521                    })
 9522                    .children(keybind),
 9523            )
 9524            .into_any();
 9525
 9526        let longest_row =
 9527            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9528        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9529            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9530        } else {
 9531            layout_line(
 9532                longest_row,
 9533                editor_snapshot,
 9534                style,
 9535                editor_width,
 9536                |_| false,
 9537                window,
 9538                cx,
 9539            )
 9540            .width
 9541        };
 9542
 9543        let viewport_bounds =
 9544            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9545                right: -right_margin,
 9546                ..Default::default()
 9547            });
 9548
 9549        let x_after_longest = Pixels::from(
 9550            ScrollPixelOffset::from(
 9551                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9552            ) - scroll_pixel_position.x,
 9553        );
 9554
 9555        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9556
 9557        // Fully visible if it can be displayed within the window (allow overlapping other
 9558        // panes). However, this is only allowed if the popover starts within text_bounds.
 9559        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9560            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9561
 9562        let mut origin = if can_position_to_the_right {
 9563            point(
 9564                x_after_longest,
 9565                text_bounds.origin.y
 9566                    + Pixels::from(
 9567                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9568                            - scroll_pixel_position.y,
 9569                    ),
 9570            )
 9571        } else {
 9572            let cursor_row = newest_selection_head.map(|head| head.row());
 9573            let above_edit = edit_start
 9574                .row()
 9575                .0
 9576                .checked_sub(line_count as u32)
 9577                .map(DisplayRow);
 9578            let below_edit = Some(edit_end.row() + 1);
 9579            let above_cursor =
 9580                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9581            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9582
 9583            // Place the edit popover adjacent to the edit if there is a location
 9584            // available that is onscreen and does not obscure the cursor. Otherwise,
 9585            // place it adjacent to the cursor.
 9586            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9587                .into_iter()
 9588                .flatten()
 9589                .find(|&start_row| {
 9590                    let end_row = start_row + line_count as u32;
 9591                    visible_row_range.contains(&start_row)
 9592                        && visible_row_range.contains(&end_row)
 9593                        && cursor_row
 9594                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9595                })?;
 9596
 9597            content_origin
 9598                + point(
 9599                    Pixels::from(-scroll_pixel_position.x),
 9600                    Pixels::from(
 9601                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9602                    ),
 9603                )
 9604        };
 9605
 9606        origin.x -= BORDER_WIDTH;
 9607
 9608        window.defer_draw(element, origin, 1);
 9609
 9610        // Do not return an element, since it will already be drawn due to defer_draw.
 9611        None
 9612    }
 9613
 9614    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9615        px(30.)
 9616    }
 9617
 9618    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9619        if self.read_only(cx) {
 9620            cx.theme().players().read_only()
 9621        } else {
 9622            self.style.as_ref().unwrap().local_player
 9623        }
 9624    }
 9625
 9626    fn render_edit_prediction_accept_keybind(
 9627        &self,
 9628        window: &mut Window,
 9629        cx: &mut App,
 9630    ) -> Option<AnyElement> {
 9631        let accept_binding =
 9632            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9633        let accept_keystroke = accept_binding.keystroke()?;
 9634
 9635        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9636
 9637        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9638            Color::Accent
 9639        } else {
 9640            Color::Muted
 9641        };
 9642
 9643        h_flex()
 9644            .px_0p5()
 9645            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9646            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9647            .text_size(TextSize::XSmall.rems(cx))
 9648            .child(h_flex().children(ui::render_modifiers(
 9649                accept_keystroke.modifiers(),
 9650                PlatformStyle::platform(),
 9651                Some(modifiers_color),
 9652                Some(IconSize::XSmall.rems().into()),
 9653                true,
 9654            )))
 9655            .when(is_platform_style_mac, |parent| {
 9656                parent.child(accept_keystroke.key().to_string())
 9657            })
 9658            .when(!is_platform_style_mac, |parent| {
 9659                parent.child(
 9660                    Key::new(
 9661                        util::capitalize(accept_keystroke.key()),
 9662                        Some(Color::Default),
 9663                    )
 9664                    .size(Some(IconSize::XSmall.rems().into())),
 9665                )
 9666            })
 9667            .into_any()
 9668            .into()
 9669    }
 9670
 9671    fn render_edit_prediction_line_popover(
 9672        &self,
 9673        label: impl Into<SharedString>,
 9674        icon: Option<IconName>,
 9675        window: &mut Window,
 9676        cx: &mut App,
 9677    ) -> Stateful<Div> {
 9678        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9679
 9680        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9681        let has_keybind = keybind.is_some();
 9682
 9683        h_flex()
 9684            .id("ep-line-popover")
 9685            .py_0p5()
 9686            .pl_1()
 9687            .pr(padding_right)
 9688            .gap_1()
 9689            .rounded_md()
 9690            .border_1()
 9691            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9692            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9693            .shadow_xs()
 9694            .when(!has_keybind, |el| {
 9695                let status_colors = cx.theme().status();
 9696
 9697                el.bg(status_colors.error_background)
 9698                    .border_color(status_colors.error.opacity(0.6))
 9699                    .pl_2()
 9700                    .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
 9701                    .cursor_default()
 9702                    .hoverable_tooltip(move |_window, cx| {
 9703                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9704                    })
 9705            })
 9706            .children(keybind)
 9707            .child(
 9708                Label::new(label)
 9709                    .size(LabelSize::Small)
 9710                    .when(!has_keybind, |el| {
 9711                        el.color(cx.theme().status().error.into()).strikethrough()
 9712                    }),
 9713            )
 9714            .when(!has_keybind, |el| {
 9715                el.child(
 9716                    h_flex().ml_1().child(
 9717                        Icon::new(IconName::Info)
 9718                            .size(IconSize::Small)
 9719                            .color(cx.theme().status().error.into()),
 9720                    ),
 9721                )
 9722            })
 9723            .when_some(icon, |element, icon| {
 9724                element.child(
 9725                    div()
 9726                        .mt(px(1.5))
 9727                        .child(Icon::new(icon).size(IconSize::Small)),
 9728                )
 9729            })
 9730    }
 9731
 9732    fn render_edit_prediction_jump_outside_popover(
 9733        &self,
 9734        snapshot: &BufferSnapshot,
 9735        window: &mut Window,
 9736        cx: &mut App,
 9737    ) -> Stateful<Div> {
 9738        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9739        let has_keybind = keybind.is_some();
 9740
 9741        let file_name = snapshot
 9742            .file()
 9743            .map(|file| SharedString::new(file.file_name(cx)))
 9744            .unwrap_or(SharedString::new_static("untitled"));
 9745
 9746        h_flex()
 9747            .id("ep-jump-outside-popover")
 9748            .py_1()
 9749            .px_2()
 9750            .gap_1()
 9751            .rounded_md()
 9752            .border_1()
 9753            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9754            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9755            .shadow_xs()
 9756            .when(!has_keybind, |el| {
 9757                let status_colors = cx.theme().status();
 9758
 9759                el.bg(status_colors.error_background)
 9760                    .border_color(status_colors.error.opacity(0.6))
 9761                    .pl_2()
 9762                    .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
 9763                    .cursor_default()
 9764                    .hoverable_tooltip(move |_window, cx| {
 9765                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9766                    })
 9767            })
 9768            .children(keybind)
 9769            .child(
 9770                Label::new(file_name)
 9771                    .size(LabelSize::Small)
 9772                    .buffer_font(cx)
 9773                    .when(!has_keybind, |el| {
 9774                        el.color(cx.theme().status().error.into()).strikethrough()
 9775                    }),
 9776            )
 9777            .when(!has_keybind, |el| {
 9778                el.child(
 9779                    h_flex().ml_1().child(
 9780                        Icon::new(IconName::Info)
 9781                            .size(IconSize::Small)
 9782                            .color(cx.theme().status().error.into()),
 9783                    ),
 9784                )
 9785            })
 9786            .child(
 9787                div()
 9788                    .mt(px(1.5))
 9789                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
 9790            )
 9791    }
 9792
 9793    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
 9794        let accent_color = cx.theme().colors().text_accent;
 9795        let editor_bg_color = cx.theme().colors().editor_background;
 9796        editor_bg_color.blend(accent_color.opacity(0.1))
 9797    }
 9798
 9799    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
 9800        let accent_color = cx.theme().colors().text_accent;
 9801        let editor_bg_color = cx.theme().colors().editor_background;
 9802        editor_bg_color.blend(accent_color.opacity(0.6))
 9803    }
 9804    fn get_prediction_provider_icon_name(
 9805        provider: &Option<RegisteredEditPredictionDelegate>,
 9806    ) -> IconName {
 9807        match provider {
 9808            Some(provider) => match provider.provider.name() {
 9809                "copilot" => IconName::Copilot,
 9810                "supermaven" => IconName::Supermaven,
 9811                _ => IconName::ZedPredict,
 9812            },
 9813            None => IconName::ZedPredict,
 9814        }
 9815    }
 9816
 9817    fn render_edit_prediction_cursor_popover(
 9818        &self,
 9819        min_width: Pixels,
 9820        max_width: Pixels,
 9821        cursor_point: Point,
 9822        style: &EditorStyle,
 9823        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
 9824        _window: &Window,
 9825        cx: &mut Context<Editor>,
 9826    ) -> Option<AnyElement> {
 9827        let provider = self.edit_prediction_provider.as_ref()?;
 9828        let provider_icon = Self::get_prediction_provider_icon_name(&self.edit_prediction_provider);
 9829
 9830        let is_refreshing = provider.provider.is_refreshing(cx);
 9831
 9832        fn pending_completion_container(icon: IconName) -> Div {
 9833            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
 9834        }
 9835
 9836        let completion = match &self.active_edit_prediction {
 9837            Some(prediction) => {
 9838                if !self.has_visible_completions_menu() {
 9839                    const RADIUS: Pixels = px(6.);
 9840                    const BORDER_WIDTH: Pixels = px(1.);
 9841
 9842                    return Some(
 9843                        h_flex()
 9844                            .elevation_2(cx)
 9845                            .border(BORDER_WIDTH)
 9846                            .border_color(cx.theme().colors().border)
 9847                            .when(accept_keystroke.is_none(), |el| {
 9848                                el.border_color(cx.theme().status().error)
 9849                            })
 9850                            .rounded(RADIUS)
 9851                            .rounded_tl(px(0.))
 9852                            .overflow_hidden()
 9853                            .child(div().px_1p5().child(match &prediction.completion {
 9854                                EditPrediction::MoveWithin { target, snapshot } => {
 9855                                    use text::ToPoint as _;
 9856                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
 9857                                    {
 9858                                        Icon::new(IconName::ZedPredictDown)
 9859                                    } else {
 9860                                        Icon::new(IconName::ZedPredictUp)
 9861                                    }
 9862                                }
 9863                                EditPrediction::MoveOutside { .. } => {
 9864                                    // TODO [zeta2] custom icon for external jump?
 9865                                    Icon::new(provider_icon)
 9866                                }
 9867                                EditPrediction::Edit { .. } => Icon::new(provider_icon),
 9868                            }))
 9869                            .child(
 9870                                h_flex()
 9871                                    .gap_1()
 9872                                    .py_1()
 9873                                    .px_2()
 9874                                    .rounded_r(RADIUS - BORDER_WIDTH)
 9875                                    .border_l_1()
 9876                                    .border_color(cx.theme().colors().border)
 9877                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9878                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
 9879                                        el.child(
 9880                                            Label::new("Hold")
 9881                                                .size(LabelSize::Small)
 9882                                                .when(accept_keystroke.is_none(), |el| {
 9883                                                    el.strikethrough()
 9884                                                })
 9885                                                .line_height_style(LineHeightStyle::UiLabel),
 9886                                        )
 9887                                    })
 9888                                    .id("edit_prediction_cursor_popover_keybind")
 9889                                    .when(accept_keystroke.is_none(), |el| {
 9890                                        let status_colors = cx.theme().status();
 9891
 9892                                        el.bg(status_colors.error_background)
 9893                                            .border_color(status_colors.error.opacity(0.6))
 9894                                            .child(Icon::new(IconName::Info).color(Color::Error))
 9895                                            .cursor_default()
 9896                                            .hoverable_tooltip(move |_window, cx| {
 9897                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
 9898                                                    .into()
 9899                                            })
 9900                                    })
 9901                                    .when_some(
 9902                                        accept_keystroke.as_ref(),
 9903                                        |el, accept_keystroke| {
 9904                                            el.child(h_flex().children(ui::render_modifiers(
 9905                                                accept_keystroke.modifiers(),
 9906                                                PlatformStyle::platform(),
 9907                                                Some(Color::Default),
 9908                                                Some(IconSize::XSmall.rems().into()),
 9909                                                false,
 9910                                            )))
 9911                                        },
 9912                                    ),
 9913                            )
 9914                            .into_any(),
 9915                    );
 9916                }
 9917
 9918                self.render_edit_prediction_cursor_popover_preview(
 9919                    prediction,
 9920                    cursor_point,
 9921                    style,
 9922                    cx,
 9923                )?
 9924            }
 9925
 9926            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
 9927                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
 9928                    stale_completion,
 9929                    cursor_point,
 9930                    style,
 9931                    cx,
 9932                )?,
 9933
 9934                None => pending_completion_container(provider_icon)
 9935                    .child(Label::new("...").size(LabelSize::Small)),
 9936            },
 9937
 9938            None => pending_completion_container(provider_icon)
 9939                .child(Label::new("...").size(LabelSize::Small)),
 9940        };
 9941
 9942        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
 9943            completion
 9944                .with_animation(
 9945                    "loading-completion",
 9946                    Animation::new(Duration::from_secs(2))
 9947                        .repeat()
 9948                        .with_easing(pulsating_between(0.4, 0.8)),
 9949                    |label, delta| label.opacity(delta),
 9950                )
 9951                .into_any_element()
 9952        } else {
 9953            completion.into_any_element()
 9954        };
 9955
 9956        let has_completion = self.active_edit_prediction.is_some();
 9957
 9958        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9959        Some(
 9960            h_flex()
 9961                .min_w(min_width)
 9962                .max_w(max_width)
 9963                .flex_1()
 9964                .elevation_2(cx)
 9965                .border_color(cx.theme().colors().border)
 9966                .child(
 9967                    div()
 9968                        .flex_1()
 9969                        .py_1()
 9970                        .px_2()
 9971                        .overflow_hidden()
 9972                        .child(completion),
 9973                )
 9974                .when_some(accept_keystroke, |el, accept_keystroke| {
 9975                    if !accept_keystroke.modifiers().modified() {
 9976                        return el;
 9977                    }
 9978
 9979                    el.child(
 9980                        h_flex()
 9981                            .h_full()
 9982                            .border_l_1()
 9983                            .rounded_r_lg()
 9984                            .border_color(cx.theme().colors().border)
 9985                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9986                            .gap_1()
 9987                            .py_1()
 9988                            .px_2()
 9989                            .child(
 9990                                h_flex()
 9991                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9992                                    .when(is_platform_style_mac, |parent| parent.gap_1())
 9993                                    .child(h_flex().children(ui::render_modifiers(
 9994                                        accept_keystroke.modifiers(),
 9995                                        PlatformStyle::platform(),
 9996                                        Some(if !has_completion {
 9997                                            Color::Muted
 9998                                        } else {
 9999                                            Color::Default
10000                                        }),
10001                                        None,
10002                                        false,
10003                                    ))),
10004                            )
10005                            .child(Label::new("Preview").into_any_element())
10006                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10007                    )
10008                })
10009                .into_any(),
10010        )
10011    }
10012
10013    fn render_edit_prediction_cursor_popover_preview(
10014        &self,
10015        completion: &EditPredictionState,
10016        cursor_point: Point,
10017        style: &EditorStyle,
10018        cx: &mut Context<Editor>,
10019    ) -> Option<Div> {
10020        use text::ToPoint as _;
10021
10022        fn render_relative_row_jump(
10023            prefix: impl Into<String>,
10024            current_row: u32,
10025            target_row: u32,
10026        ) -> Div {
10027            let (row_diff, arrow) = if target_row < current_row {
10028                (current_row - target_row, IconName::ArrowUp)
10029            } else {
10030                (target_row - current_row, IconName::ArrowDown)
10031            };
10032
10033            h_flex()
10034                .child(
10035                    Label::new(format!("{}{}", prefix.into(), row_diff))
10036                        .color(Color::Muted)
10037                        .size(LabelSize::Small),
10038                )
10039                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10040        }
10041
10042        let supports_jump = self
10043            .edit_prediction_provider
10044            .as_ref()
10045            .map(|provider| provider.provider.supports_jump_to_edit())
10046            .unwrap_or(true);
10047
10048        match &completion.completion {
10049            EditPrediction::MoveWithin {
10050                target, snapshot, ..
10051            } => {
10052                if !supports_jump {
10053                    return None;
10054                }
10055
10056                Some(
10057                    h_flex()
10058                        .px_2()
10059                        .gap_2()
10060                        .flex_1()
10061                        .child(
10062                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10063                                Icon::new(IconName::ZedPredictDown)
10064                            } else {
10065                                Icon::new(IconName::ZedPredictUp)
10066                            },
10067                        )
10068                        .child(Label::new("Jump to Edit")),
10069                )
10070            }
10071            EditPrediction::MoveOutside { snapshot, .. } => {
10072                let file_name = snapshot
10073                    .file()
10074                    .map(|file| file.file_name(cx))
10075                    .unwrap_or("untitled");
10076                Some(
10077                    h_flex()
10078                        .px_2()
10079                        .gap_2()
10080                        .flex_1()
10081                        .child(Icon::new(IconName::ZedPredict))
10082                        .child(Label::new(format!("Jump to {file_name}"))),
10083                )
10084            }
10085            EditPrediction::Edit {
10086                edits,
10087                edit_preview,
10088                snapshot,
10089                display_mode: _,
10090            } => {
10091                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10092
10093                let (highlighted_edits, has_more_lines) =
10094                    if let Some(edit_preview) = edit_preview.as_ref() {
10095                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10096                            .first_line_preview()
10097                    } else {
10098                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10099                    };
10100
10101                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10102                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10103
10104                let preview = h_flex()
10105                    .gap_1()
10106                    .min_w_16()
10107                    .child(styled_text)
10108                    .when(has_more_lines, |parent| parent.child(""));
10109
10110                let left = if supports_jump && first_edit_row != cursor_point.row {
10111                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10112                        .into_any_element()
10113                } else {
10114                    let icon_name =
10115                        Editor::get_prediction_provider_icon_name(&self.edit_prediction_provider);
10116                    Icon::new(icon_name).into_any_element()
10117                };
10118
10119                Some(
10120                    h_flex()
10121                        .h_full()
10122                        .flex_1()
10123                        .gap_2()
10124                        .pr_1()
10125                        .overflow_x_hidden()
10126                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10127                        .child(left)
10128                        .child(preview),
10129                )
10130            }
10131        }
10132    }
10133
10134    pub fn render_context_menu(
10135        &mut self,
10136        max_height_in_lines: u32,
10137        window: &mut Window,
10138        cx: &mut Context<Editor>,
10139    ) -> Option<AnyElement> {
10140        let menu = self.context_menu.borrow();
10141        let menu = menu.as_ref()?;
10142        if !menu.visible() {
10143            return None;
10144        };
10145        self.style
10146            .as_ref()
10147            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10148    }
10149
10150    fn render_context_menu_aside(
10151        &mut self,
10152        max_size: Size<Pixels>,
10153        window: &mut Window,
10154        cx: &mut Context<Editor>,
10155    ) -> Option<AnyElement> {
10156        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10157            if menu.visible() {
10158                menu.render_aside(max_size, window, cx)
10159            } else {
10160                None
10161            }
10162        })
10163    }
10164
10165    fn hide_context_menu(
10166        &mut self,
10167        window: &mut Window,
10168        cx: &mut Context<Self>,
10169    ) -> Option<CodeContextMenu> {
10170        cx.notify();
10171        self.completion_tasks.clear();
10172        let context_menu = self.context_menu.borrow_mut().take();
10173        self.stale_edit_prediction_in_menu.take();
10174        self.update_visible_edit_prediction(window, cx);
10175        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10176            && let Some(completion_provider) = &self.completion_provider
10177        {
10178            completion_provider.selection_changed(None, window, cx);
10179        }
10180        context_menu
10181    }
10182
10183    fn show_snippet_choices(
10184        &mut self,
10185        choices: &Vec<String>,
10186        selection: Range<Anchor>,
10187        cx: &mut Context<Self>,
10188    ) {
10189        let Some((_, buffer, _)) = self
10190            .buffer()
10191            .read(cx)
10192            .excerpt_containing(selection.start, cx)
10193        else {
10194            return;
10195        };
10196        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10197        else {
10198            return;
10199        };
10200        if buffer != end_buffer {
10201            log::error!("expected anchor range to have matching buffer IDs");
10202            return;
10203        }
10204
10205        let id = post_inc(&mut self.next_completion_id);
10206        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10207        let mut context_menu = self.context_menu.borrow_mut();
10208        let old_menu = context_menu.take();
10209        *context_menu = Some(CodeContextMenu::Completions(
10210            CompletionsMenu::new_snippet_choices(
10211                id,
10212                true,
10213                choices,
10214                selection,
10215                buffer,
10216                old_menu.map(|menu| menu.primary_scroll_handle()),
10217                snippet_sort_order,
10218            ),
10219        ));
10220    }
10221
10222    pub fn insert_snippet(
10223        &mut self,
10224        insertion_ranges: &[Range<MultiBufferOffset>],
10225        snippet: Snippet,
10226        window: &mut Window,
10227        cx: &mut Context<Self>,
10228    ) -> Result<()> {
10229        struct Tabstop<T> {
10230            is_end_tabstop: bool,
10231            ranges: Vec<Range<T>>,
10232            choices: Option<Vec<String>>,
10233        }
10234
10235        let tabstops = self.buffer.update(cx, |buffer, cx| {
10236            let snippet_text: Arc<str> = snippet.text.clone().into();
10237            let edits = insertion_ranges
10238                .iter()
10239                .cloned()
10240                .map(|range| (range, snippet_text.clone()));
10241            let autoindent_mode = AutoindentMode::Block {
10242                original_indent_columns: Vec::new(),
10243            };
10244            buffer.edit(edits, Some(autoindent_mode), cx);
10245
10246            let snapshot = &*buffer.read(cx);
10247            let snippet = &snippet;
10248            snippet
10249                .tabstops
10250                .iter()
10251                .map(|tabstop| {
10252                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10253                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10254                    });
10255                    let mut tabstop_ranges = tabstop
10256                        .ranges
10257                        .iter()
10258                        .flat_map(|tabstop_range| {
10259                            let mut delta = 0_isize;
10260                            insertion_ranges.iter().map(move |insertion_range| {
10261                                let insertion_start = insertion_range.start + delta;
10262                                delta += snippet.text.len() as isize
10263                                    - (insertion_range.end - insertion_range.start) as isize;
10264
10265                                let start =
10266                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10267                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10268                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10269                            })
10270                        })
10271                        .collect::<Vec<_>>();
10272                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10273
10274                    Tabstop {
10275                        is_end_tabstop,
10276                        ranges: tabstop_ranges,
10277                        choices: tabstop.choices.clone(),
10278                    }
10279                })
10280                .collect::<Vec<_>>()
10281        });
10282        if let Some(tabstop) = tabstops.first() {
10283            self.change_selections(Default::default(), window, cx, |s| {
10284                // Reverse order so that the first range is the newest created selection.
10285                // Completions will use it and autoscroll will prioritize it.
10286                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10287            });
10288
10289            if let Some(choices) = &tabstop.choices
10290                && let Some(selection) = tabstop.ranges.first()
10291            {
10292                self.show_snippet_choices(choices, selection.clone(), cx)
10293            }
10294
10295            // If we're already at the last tabstop and it's at the end of the snippet,
10296            // we're done, we don't need to keep the state around.
10297            if !tabstop.is_end_tabstop {
10298                let choices = tabstops
10299                    .iter()
10300                    .map(|tabstop| tabstop.choices.clone())
10301                    .collect();
10302
10303                let ranges = tabstops
10304                    .into_iter()
10305                    .map(|tabstop| tabstop.ranges)
10306                    .collect::<Vec<_>>();
10307
10308                self.snippet_stack.push(SnippetState {
10309                    active_index: 0,
10310                    ranges,
10311                    choices,
10312                });
10313            }
10314
10315            // Check whether the just-entered snippet ends with an auto-closable bracket.
10316            if self.autoclose_regions.is_empty() {
10317                let snapshot = self.buffer.read(cx).snapshot(cx);
10318                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10319                    let selection_head = selection.head();
10320                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10321                        continue;
10322                    };
10323
10324                    let mut bracket_pair = None;
10325                    let max_lookup_length = scope
10326                        .brackets()
10327                        .map(|(pair, _)| {
10328                            pair.start
10329                                .as_str()
10330                                .chars()
10331                                .count()
10332                                .max(pair.end.as_str().chars().count())
10333                        })
10334                        .max();
10335                    if let Some(max_lookup_length) = max_lookup_length {
10336                        let next_text = snapshot
10337                            .chars_at(selection_head)
10338                            .take(max_lookup_length)
10339                            .collect::<String>();
10340                        let prev_text = snapshot
10341                            .reversed_chars_at(selection_head)
10342                            .take(max_lookup_length)
10343                            .collect::<String>();
10344
10345                        for (pair, enabled) in scope.brackets() {
10346                            if enabled
10347                                && pair.close
10348                                && prev_text.starts_with(pair.start.as_str())
10349                                && next_text.starts_with(pair.end.as_str())
10350                            {
10351                                bracket_pair = Some(pair.clone());
10352                                break;
10353                            }
10354                        }
10355                    }
10356
10357                    if let Some(pair) = bracket_pair {
10358                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10359                        let autoclose_enabled =
10360                            self.use_autoclose && snapshot_settings.use_autoclose;
10361                        if autoclose_enabled {
10362                            let start = snapshot.anchor_after(selection_head);
10363                            let end = snapshot.anchor_after(selection_head);
10364                            self.autoclose_regions.push(AutocloseRegion {
10365                                selection_id: selection.id,
10366                                range: start..end,
10367                                pair,
10368                            });
10369                        }
10370                    }
10371                }
10372            }
10373        }
10374        Ok(())
10375    }
10376
10377    pub fn move_to_next_snippet_tabstop(
10378        &mut self,
10379        window: &mut Window,
10380        cx: &mut Context<Self>,
10381    ) -> bool {
10382        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10383    }
10384
10385    pub fn move_to_prev_snippet_tabstop(
10386        &mut self,
10387        window: &mut Window,
10388        cx: &mut Context<Self>,
10389    ) -> bool {
10390        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10391    }
10392
10393    pub fn move_to_snippet_tabstop(
10394        &mut self,
10395        bias: Bias,
10396        window: &mut Window,
10397        cx: &mut Context<Self>,
10398    ) -> bool {
10399        if let Some(mut snippet) = self.snippet_stack.pop() {
10400            match bias {
10401                Bias::Left => {
10402                    if snippet.active_index > 0 {
10403                        snippet.active_index -= 1;
10404                    } else {
10405                        self.snippet_stack.push(snippet);
10406                        return false;
10407                    }
10408                }
10409                Bias::Right => {
10410                    if snippet.active_index + 1 < snippet.ranges.len() {
10411                        snippet.active_index += 1;
10412                    } else {
10413                        self.snippet_stack.push(snippet);
10414                        return false;
10415                    }
10416                }
10417            }
10418            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10419                self.change_selections(Default::default(), window, cx, |s| {
10420                    // Reverse order so that the first range is the newest created selection.
10421                    // Completions will use it and autoscroll will prioritize it.
10422                    s.select_ranges(current_ranges.iter().rev().cloned())
10423                });
10424
10425                if let Some(choices) = &snippet.choices[snippet.active_index]
10426                    && let Some(selection) = current_ranges.first()
10427                {
10428                    self.show_snippet_choices(choices, selection.clone(), cx);
10429                }
10430
10431                // If snippet state is not at the last tabstop, push it back on the stack
10432                if snippet.active_index + 1 < snippet.ranges.len() {
10433                    self.snippet_stack.push(snippet);
10434                }
10435                return true;
10436            }
10437        }
10438
10439        false
10440    }
10441
10442    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10443        self.transact(window, cx, |this, window, cx| {
10444            this.select_all(&SelectAll, window, cx);
10445            this.insert("", window, cx);
10446        });
10447    }
10448
10449    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10450        if self.read_only(cx) {
10451            return;
10452        }
10453        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10454        self.transact(window, cx, |this, window, cx| {
10455            this.select_autoclose_pair(window, cx);
10456
10457            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10458
10459            let mut linked_ranges = HashMap::<_, Vec<_>>::default();
10460            if !this.linked_edit_ranges.is_empty() {
10461                let selections = this.selections.all::<MultiBufferPoint>(&display_map);
10462                let snapshot = this.buffer.read(cx).snapshot(cx);
10463
10464                for selection in selections.iter() {
10465                    let selection_start = snapshot.anchor_before(selection.start).text_anchor;
10466                    let selection_end = snapshot.anchor_after(selection.end).text_anchor;
10467                    if selection_start.buffer_id != selection_end.buffer_id {
10468                        continue;
10469                    }
10470                    if let Some(ranges) =
10471                        this.linked_editing_ranges_for(selection_start..selection_end, cx)
10472                    {
10473                        for (buffer, entries) in ranges {
10474                            linked_ranges.entry(buffer).or_default().extend(entries);
10475                        }
10476                    }
10477                }
10478            }
10479
10480            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10481            for selection in &mut selections {
10482                if selection.is_empty() {
10483                    let old_head = selection.head();
10484                    let mut new_head =
10485                        movement::left(&display_map, old_head.to_display_point(&display_map))
10486                            .to_point(&display_map);
10487                    if let Some((buffer, line_buffer_range)) = display_map
10488                        .buffer_snapshot()
10489                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10490                    {
10491                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10492                        let indent_len = match indent_size.kind {
10493                            IndentKind::Space => {
10494                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10495                            }
10496                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10497                        };
10498                        if old_head.column <= indent_size.len && old_head.column > 0 {
10499                            let indent_len = indent_len.get();
10500                            new_head = cmp::min(
10501                                new_head,
10502                                MultiBufferPoint::new(
10503                                    old_head.row,
10504                                    ((old_head.column - 1) / indent_len) * indent_len,
10505                                ),
10506                            );
10507                        }
10508                    }
10509
10510                    selection.set_head(new_head, SelectionGoal::None);
10511                }
10512            }
10513
10514            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10515            this.insert("", window, cx);
10516            let empty_str: Arc<str> = Arc::from("");
10517            for (buffer, edits) in linked_ranges {
10518                let snapshot = buffer.read(cx).snapshot();
10519                use text::ToPoint as TP;
10520
10521                let edits = edits
10522                    .into_iter()
10523                    .map(|range| {
10524                        let end_point = TP::to_point(&range.end, &snapshot);
10525                        let mut start_point = TP::to_point(&range.start, &snapshot);
10526
10527                        if end_point == start_point {
10528                            let offset = text::ToOffset::to_offset(&range.start, &snapshot)
10529                                .saturating_sub(1);
10530                            start_point =
10531                                snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
10532                        };
10533
10534                        (start_point..end_point, empty_str.clone())
10535                    })
10536                    .sorted_by_key(|(range, _)| range.start)
10537                    .collect::<Vec<_>>();
10538                buffer.update(cx, |this, cx| {
10539                    this.edit(edits, None, cx);
10540                })
10541            }
10542            this.refresh_edit_prediction(true, false, window, cx);
10543            refresh_linked_ranges(this, window, cx);
10544        });
10545    }
10546
10547    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10548        if self.read_only(cx) {
10549            return;
10550        }
10551        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10552        self.transact(window, cx, |this, window, cx| {
10553            this.change_selections(Default::default(), window, cx, |s| {
10554                s.move_with(|map, selection| {
10555                    if selection.is_empty() {
10556                        let cursor = movement::right(map, selection.head());
10557                        selection.end = cursor;
10558                        selection.reversed = true;
10559                        selection.goal = SelectionGoal::None;
10560                    }
10561                })
10562            });
10563            this.insert("", window, cx);
10564            this.refresh_edit_prediction(true, false, window, cx);
10565        });
10566    }
10567
10568    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10569        if self.mode.is_single_line() {
10570            cx.propagate();
10571            return;
10572        }
10573
10574        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10575        if self.move_to_prev_snippet_tabstop(window, cx) {
10576            return;
10577        }
10578        self.outdent(&Outdent, window, cx);
10579    }
10580
10581    pub fn next_snippet_tabstop(
10582        &mut self,
10583        _: &NextSnippetTabstop,
10584        window: &mut Window,
10585        cx: &mut Context<Self>,
10586    ) {
10587        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10588            cx.propagate();
10589            return;
10590        }
10591
10592        if self.move_to_next_snippet_tabstop(window, cx) {
10593            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10594            return;
10595        }
10596        cx.propagate();
10597    }
10598
10599    pub fn previous_snippet_tabstop(
10600        &mut self,
10601        _: &PreviousSnippetTabstop,
10602        window: &mut Window,
10603        cx: &mut Context<Self>,
10604    ) {
10605        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10606            cx.propagate();
10607            return;
10608        }
10609
10610        if self.move_to_prev_snippet_tabstop(window, cx) {
10611            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10612            return;
10613        }
10614        cx.propagate();
10615    }
10616
10617    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10618        if self.mode.is_single_line() {
10619            cx.propagate();
10620            return;
10621        }
10622
10623        if self.move_to_next_snippet_tabstop(window, cx) {
10624            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10625            return;
10626        }
10627        if self.read_only(cx) {
10628            return;
10629        }
10630        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10631        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10632        let buffer = self.buffer.read(cx);
10633        let snapshot = buffer.snapshot(cx);
10634        let rows_iter = selections.iter().map(|s| s.head().row);
10635        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10636
10637        let has_some_cursor_in_whitespace = selections
10638            .iter()
10639            .filter(|selection| selection.is_empty())
10640            .any(|selection| {
10641                let cursor = selection.head();
10642                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10643                cursor.column < current_indent.len
10644            });
10645
10646        let mut edits = Vec::new();
10647        let mut prev_edited_row = 0;
10648        let mut row_delta = 0;
10649        for selection in &mut selections {
10650            if selection.start.row != prev_edited_row {
10651                row_delta = 0;
10652            }
10653            prev_edited_row = selection.end.row;
10654
10655            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10656            if selection.is_empty() {
10657                let cursor = selection.head();
10658                let settings = buffer.language_settings_at(cursor, cx);
10659                if settings.indent_list_on_tab {
10660                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10661                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10662                            row_delta = Self::indent_selection(
10663                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10664                            );
10665                            continue;
10666                        }
10667                    }
10668                }
10669            }
10670
10671            // If the selection is non-empty, then increase the indentation of the selected lines.
10672            if !selection.is_empty() {
10673                row_delta =
10674                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10675                continue;
10676            }
10677
10678            let cursor = selection.head();
10679            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10680            if let Some(suggested_indent) =
10681                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10682            {
10683                // Don't do anything if already at suggested indent
10684                // and there is any other cursor which is not
10685                if has_some_cursor_in_whitespace
10686                    && cursor.column == current_indent.len
10687                    && current_indent.len == suggested_indent.len
10688                {
10689                    continue;
10690                }
10691
10692                // Adjust line and move cursor to suggested indent
10693                // if cursor is not at suggested indent
10694                if cursor.column < suggested_indent.len
10695                    && cursor.column <= current_indent.len
10696                    && current_indent.len <= suggested_indent.len
10697                {
10698                    selection.start = Point::new(cursor.row, suggested_indent.len);
10699                    selection.end = selection.start;
10700                    if row_delta == 0 {
10701                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10702                            cursor.row,
10703                            current_indent,
10704                            suggested_indent,
10705                        ));
10706                        row_delta = suggested_indent.len - current_indent.len;
10707                    }
10708                    continue;
10709                }
10710
10711                // If current indent is more than suggested indent
10712                // only move cursor to current indent and skip indent
10713                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10714                    selection.start = Point::new(cursor.row, current_indent.len);
10715                    selection.end = selection.start;
10716                    continue;
10717                }
10718            }
10719
10720            // Otherwise, insert a hard or soft tab.
10721            let settings = buffer.language_settings_at(cursor, cx);
10722            let tab_size = if settings.hard_tabs {
10723                IndentSize::tab()
10724            } else {
10725                let tab_size = settings.tab_size.get();
10726                let indent_remainder = snapshot
10727                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10728                    .flat_map(str::chars)
10729                    .fold(row_delta % tab_size, |counter: u32, c| {
10730                        if c == '\t' {
10731                            0
10732                        } else {
10733                            (counter + 1) % tab_size
10734                        }
10735                    });
10736
10737                let chars_to_next_tab_stop = tab_size - indent_remainder;
10738                IndentSize::spaces(chars_to_next_tab_stop)
10739            };
10740            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10741            selection.end = selection.start;
10742            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10743            row_delta += tab_size.len;
10744        }
10745
10746        self.transact(window, cx, |this, window, cx| {
10747            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10748            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10749            this.refresh_edit_prediction(true, false, window, cx);
10750        });
10751    }
10752
10753    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10754        if self.read_only(cx) {
10755            return;
10756        }
10757        if self.mode.is_single_line() {
10758            cx.propagate();
10759            return;
10760        }
10761
10762        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10763        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
10764        let mut prev_edited_row = 0;
10765        let mut row_delta = 0;
10766        let mut edits = Vec::new();
10767        let buffer = self.buffer.read(cx);
10768        let snapshot = buffer.snapshot(cx);
10769        for selection in &mut selections {
10770            if selection.start.row != prev_edited_row {
10771                row_delta = 0;
10772            }
10773            prev_edited_row = selection.end.row;
10774
10775            row_delta =
10776                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10777        }
10778
10779        self.transact(window, cx, |this, window, cx| {
10780            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10781            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10782        });
10783    }
10784
10785    fn indent_selection(
10786        buffer: &MultiBuffer,
10787        snapshot: &MultiBufferSnapshot,
10788        selection: &mut Selection<Point>,
10789        edits: &mut Vec<(Range<Point>, String)>,
10790        delta_for_start_row: u32,
10791        cx: &App,
10792    ) -> u32 {
10793        let settings = buffer.language_settings_at(selection.start, cx);
10794        let tab_size = settings.tab_size.get();
10795        let indent_kind = if settings.hard_tabs {
10796            IndentKind::Tab
10797        } else {
10798            IndentKind::Space
10799        };
10800        let mut start_row = selection.start.row;
10801        let mut end_row = selection.end.row + 1;
10802
10803        // If a selection ends at the beginning of a line, don't indent
10804        // that last line.
10805        if selection.end.column == 0 && selection.end.row > selection.start.row {
10806            end_row -= 1;
10807        }
10808
10809        // Avoid re-indenting a row that has already been indented by a
10810        // previous selection, but still update this selection's column
10811        // to reflect that indentation.
10812        if delta_for_start_row > 0 {
10813            start_row += 1;
10814            selection.start.column += delta_for_start_row;
10815            if selection.end.row == selection.start.row {
10816                selection.end.column += delta_for_start_row;
10817            }
10818        }
10819
10820        let mut delta_for_end_row = 0;
10821        let has_multiple_rows = start_row + 1 != end_row;
10822        for row in start_row..end_row {
10823            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
10824            let indent_delta = match (current_indent.kind, indent_kind) {
10825                (IndentKind::Space, IndentKind::Space) => {
10826                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
10827                    IndentSize::spaces(columns_to_next_tab_stop)
10828                }
10829                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
10830                (_, IndentKind::Tab) => IndentSize::tab(),
10831            };
10832
10833            let start = if has_multiple_rows || current_indent.len < selection.start.column {
10834                0
10835            } else {
10836                selection.start.column
10837            };
10838            let row_start = Point::new(row, start);
10839            edits.push((
10840                row_start..row_start,
10841                indent_delta.chars().collect::<String>(),
10842            ));
10843
10844            // Update this selection's endpoints to reflect the indentation.
10845            if row == selection.start.row {
10846                selection.start.column += indent_delta.len;
10847            }
10848            if row == selection.end.row {
10849                selection.end.column += indent_delta.len;
10850                delta_for_end_row = indent_delta.len;
10851            }
10852        }
10853
10854        if selection.start.row == selection.end.row {
10855            delta_for_start_row + delta_for_end_row
10856        } else {
10857            delta_for_end_row
10858        }
10859    }
10860
10861    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
10862        if self.read_only(cx) {
10863            return;
10864        }
10865        if self.mode.is_single_line() {
10866            cx.propagate();
10867            return;
10868        }
10869
10870        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10871        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10872        let selections = self.selections.all::<Point>(&display_map);
10873        let mut deletion_ranges = Vec::new();
10874        let mut last_outdent = None;
10875        {
10876            let buffer = self.buffer.read(cx);
10877            let snapshot = buffer.snapshot(cx);
10878            for selection in &selections {
10879                let settings = buffer.language_settings_at(selection.start, cx);
10880                let tab_size = settings.tab_size.get();
10881                let mut rows = selection.spanned_rows(false, &display_map);
10882
10883                // Avoid re-outdenting a row that has already been outdented by a
10884                // previous selection.
10885                if let Some(last_row) = last_outdent
10886                    && last_row == rows.start
10887                {
10888                    rows.start = rows.start.next_row();
10889                }
10890                let has_multiple_rows = rows.len() > 1;
10891                for row in rows.iter_rows() {
10892                    let indent_size = snapshot.indent_size_for_line(row);
10893                    if indent_size.len > 0 {
10894                        let deletion_len = match indent_size.kind {
10895                            IndentKind::Space => {
10896                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
10897                                if columns_to_prev_tab_stop == 0 {
10898                                    tab_size
10899                                } else {
10900                                    columns_to_prev_tab_stop
10901                                }
10902                            }
10903                            IndentKind::Tab => 1,
10904                        };
10905                        let start = if has_multiple_rows
10906                            || deletion_len > selection.start.column
10907                            || indent_size.len < selection.start.column
10908                        {
10909                            0
10910                        } else {
10911                            selection.start.column - deletion_len
10912                        };
10913                        deletion_ranges.push(
10914                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
10915                        );
10916                        last_outdent = Some(row);
10917                    }
10918                }
10919            }
10920        }
10921
10922        self.transact(window, cx, |this, window, cx| {
10923            this.buffer.update(cx, |buffer, cx| {
10924                let empty_str: Arc<str> = Arc::default();
10925                buffer.edit(
10926                    deletion_ranges
10927                        .into_iter()
10928                        .map(|range| (range, empty_str.clone())),
10929                    None,
10930                    cx,
10931                );
10932            });
10933            let selections = this
10934                .selections
10935                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
10936            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10937        });
10938    }
10939
10940    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
10941        if self.read_only(cx) {
10942            return;
10943        }
10944        if self.mode.is_single_line() {
10945            cx.propagate();
10946            return;
10947        }
10948
10949        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10950        let selections = self
10951            .selections
10952            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
10953            .into_iter()
10954            .map(|s| s.range());
10955
10956        self.transact(window, cx, |this, window, cx| {
10957            this.buffer.update(cx, |buffer, cx| {
10958                buffer.autoindent_ranges(selections, cx);
10959            });
10960            let selections = this
10961                .selections
10962                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
10963            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10964        });
10965    }
10966
10967    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
10968        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10969        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10970        let selections = self.selections.all::<Point>(&display_map);
10971
10972        let mut new_cursors = Vec::new();
10973        let mut edit_ranges = Vec::new();
10974        let mut selections = selections.iter().peekable();
10975        while let Some(selection) = selections.next() {
10976            let mut rows = selection.spanned_rows(false, &display_map);
10977
10978            // Accumulate contiguous regions of rows that we want to delete.
10979            while let Some(next_selection) = selections.peek() {
10980                let next_rows = next_selection.spanned_rows(false, &display_map);
10981                if next_rows.start <= rows.end {
10982                    rows.end = next_rows.end;
10983                    selections.next().unwrap();
10984                } else {
10985                    break;
10986                }
10987            }
10988
10989            let buffer = display_map.buffer_snapshot();
10990            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
10991            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
10992                // If there's a line after the range, delete the \n from the end of the row range
10993                (
10994                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
10995                    rows.end,
10996                )
10997            } else {
10998                // If there isn't a line after the range, delete the \n from the line before the
10999                // start of the row range
11000                edit_start = edit_start.saturating_sub_usize(1);
11001                (buffer.len(), rows.start.previous_row())
11002            };
11003
11004            let text_layout_details = self.text_layout_details(window);
11005            let x = display_map.x_for_display_point(
11006                selection.head().to_display_point(&display_map),
11007                &text_layout_details,
11008            );
11009            let row = Point::new(target_row.0, 0)
11010                .to_display_point(&display_map)
11011                .row();
11012            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11013
11014            new_cursors.push((
11015                selection.id,
11016                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11017                SelectionGoal::None,
11018            ));
11019            edit_ranges.push(edit_start..edit_end);
11020        }
11021
11022        self.transact(window, cx, |this, window, cx| {
11023            let buffer = this.buffer.update(cx, |buffer, cx| {
11024                let empty_str: Arc<str> = Arc::default();
11025                buffer.edit(
11026                    edit_ranges
11027                        .into_iter()
11028                        .map(|range| (range, empty_str.clone())),
11029                    None,
11030                    cx,
11031                );
11032                buffer.snapshot(cx)
11033            });
11034            let new_selections = new_cursors
11035                .into_iter()
11036                .map(|(id, cursor, goal)| {
11037                    let cursor = cursor.to_point(&buffer);
11038                    Selection {
11039                        id,
11040                        start: cursor,
11041                        end: cursor,
11042                        reversed: false,
11043                        goal,
11044                    }
11045                })
11046                .collect();
11047
11048            this.change_selections(Default::default(), window, cx, |s| {
11049                s.select(new_selections);
11050            });
11051        });
11052    }
11053
11054    pub fn join_lines_impl(
11055        &mut self,
11056        insert_whitespace: bool,
11057        window: &mut Window,
11058        cx: &mut Context<Self>,
11059    ) {
11060        if self.read_only(cx) {
11061            return;
11062        }
11063        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11064        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11065            let start = MultiBufferRow(selection.start.row);
11066            // Treat single line selections as if they include the next line. Otherwise this action
11067            // would do nothing for single line selections individual cursors.
11068            let end = if selection.start.row == selection.end.row {
11069                MultiBufferRow(selection.start.row + 1)
11070            } else {
11071                MultiBufferRow(selection.end.row)
11072            };
11073
11074            if let Some(last_row_range) = row_ranges.last_mut()
11075                && start <= last_row_range.end
11076            {
11077                last_row_range.end = end;
11078                continue;
11079            }
11080            row_ranges.push(start..end);
11081        }
11082
11083        let snapshot = self.buffer.read(cx).snapshot(cx);
11084        let mut cursor_positions = Vec::new();
11085        for row_range in &row_ranges {
11086            let anchor = snapshot.anchor_before(Point::new(
11087                row_range.end.previous_row().0,
11088                snapshot.line_len(row_range.end.previous_row()),
11089            ));
11090            cursor_positions.push(anchor..anchor);
11091        }
11092
11093        self.transact(window, cx, |this, window, cx| {
11094            for row_range in row_ranges.into_iter().rev() {
11095                for row in row_range.iter_rows().rev() {
11096                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11097                    let next_line_row = row.next_row();
11098                    let indent = snapshot.indent_size_for_line(next_line_row);
11099                    let start_of_next_line = Point::new(next_line_row.0, indent.len);
11100
11101                    let replace =
11102                        if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
11103                            " "
11104                        } else {
11105                            ""
11106                        };
11107
11108                    this.buffer.update(cx, |buffer, cx| {
11109                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11110                    });
11111                }
11112            }
11113
11114            this.change_selections(Default::default(), window, cx, |s| {
11115                s.select_anchor_ranges(cursor_positions)
11116            });
11117        });
11118    }
11119
11120    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11121        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11122        self.join_lines_impl(true, window, cx);
11123    }
11124
11125    pub fn sort_lines_case_sensitive(
11126        &mut self,
11127        _: &SortLinesCaseSensitive,
11128        window: &mut Window,
11129        cx: &mut Context<Self>,
11130    ) {
11131        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11132    }
11133
11134    pub fn sort_lines_by_length(
11135        &mut self,
11136        _: &SortLinesByLength,
11137        window: &mut Window,
11138        cx: &mut Context<Self>,
11139    ) {
11140        self.manipulate_immutable_lines(window, cx, |lines| {
11141            lines.sort_by_key(|&line| line.chars().count())
11142        })
11143    }
11144
11145    pub fn sort_lines_case_insensitive(
11146        &mut self,
11147        _: &SortLinesCaseInsensitive,
11148        window: &mut Window,
11149        cx: &mut Context<Self>,
11150    ) {
11151        self.manipulate_immutable_lines(window, cx, |lines| {
11152            lines.sort_by_key(|line| line.to_lowercase())
11153        })
11154    }
11155
11156    pub fn unique_lines_case_insensitive(
11157        &mut self,
11158        _: &UniqueLinesCaseInsensitive,
11159        window: &mut Window,
11160        cx: &mut Context<Self>,
11161    ) {
11162        self.manipulate_immutable_lines(window, cx, |lines| {
11163            let mut seen = HashSet::default();
11164            lines.retain(|line| seen.insert(line.to_lowercase()));
11165        })
11166    }
11167
11168    pub fn unique_lines_case_sensitive(
11169        &mut self,
11170        _: &UniqueLinesCaseSensitive,
11171        window: &mut Window,
11172        cx: &mut Context<Self>,
11173    ) {
11174        self.manipulate_immutable_lines(window, cx, |lines| {
11175            let mut seen = HashSet::default();
11176            lines.retain(|line| seen.insert(*line));
11177        })
11178    }
11179
11180    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11181        let snapshot = self.buffer.read(cx).snapshot(cx);
11182        for selection in self.selections.disjoint_anchors_arc().iter() {
11183            if snapshot
11184                .language_at(selection.start)
11185                .and_then(|lang| lang.config().wrap_characters.as_ref())
11186                .is_some()
11187            {
11188                return true;
11189            }
11190        }
11191        false
11192    }
11193
11194    fn wrap_selections_in_tag(
11195        &mut self,
11196        _: &WrapSelectionsInTag,
11197        window: &mut Window,
11198        cx: &mut Context<Self>,
11199    ) {
11200        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11201
11202        let snapshot = self.buffer.read(cx).snapshot(cx);
11203
11204        let mut edits = Vec::new();
11205        let mut boundaries = Vec::new();
11206
11207        for selection in self
11208            .selections
11209            .all_adjusted(&self.display_snapshot(cx))
11210            .iter()
11211        {
11212            let Some(wrap_config) = snapshot
11213                .language_at(selection.start)
11214                .and_then(|lang| lang.config().wrap_characters.clone())
11215            else {
11216                continue;
11217            };
11218
11219            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11220            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11221
11222            let start_before = snapshot.anchor_before(selection.start);
11223            let end_after = snapshot.anchor_after(selection.end);
11224
11225            edits.push((start_before..start_before, open_tag));
11226            edits.push((end_after..end_after, close_tag));
11227
11228            boundaries.push((
11229                start_before,
11230                end_after,
11231                wrap_config.start_prefix.len(),
11232                wrap_config.end_suffix.len(),
11233            ));
11234        }
11235
11236        if edits.is_empty() {
11237            return;
11238        }
11239
11240        self.transact(window, cx, |this, window, cx| {
11241            let buffer = this.buffer.update(cx, |buffer, cx| {
11242                buffer.edit(edits, None, cx);
11243                buffer.snapshot(cx)
11244            });
11245
11246            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11247            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11248                boundaries.into_iter()
11249            {
11250                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11251                let close_offset = end_after
11252                    .to_offset(&buffer)
11253                    .saturating_sub_usize(end_suffix_len);
11254                new_selections.push(open_offset..open_offset);
11255                new_selections.push(close_offset..close_offset);
11256            }
11257
11258            this.change_selections(Default::default(), window, cx, |s| {
11259                s.select_ranges(new_selections);
11260            });
11261
11262            this.request_autoscroll(Autoscroll::fit(), cx);
11263        });
11264    }
11265
11266    pub fn toggle_read_only(
11267        &mut self,
11268        _: &workspace::ToggleReadOnlyFile,
11269        _: &mut Window,
11270        cx: &mut Context<Self>,
11271    ) {
11272        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11273            buffer.update(cx, |buffer, cx| {
11274                buffer.set_capability(
11275                    match buffer.capability() {
11276                        Capability::ReadWrite => Capability::Read,
11277                        Capability::Read => Capability::ReadWrite,
11278                        Capability::ReadOnly => Capability::ReadOnly,
11279                    },
11280                    cx,
11281                );
11282            })
11283        }
11284    }
11285
11286    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11287        let Some(project) = self.project.clone() else {
11288            return;
11289        };
11290        self.reload(project, window, cx)
11291            .detach_and_notify_err(window, cx);
11292    }
11293
11294    pub fn restore_file(
11295        &mut self,
11296        _: &::git::RestoreFile,
11297        window: &mut Window,
11298        cx: &mut Context<Self>,
11299    ) {
11300        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11301        let mut buffer_ids = HashSet::default();
11302        let snapshot = self.buffer().read(cx).snapshot(cx);
11303        for selection in self
11304            .selections
11305            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11306        {
11307            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11308        }
11309
11310        let buffer = self.buffer().read(cx);
11311        let ranges = buffer_ids
11312            .into_iter()
11313            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11314            .collect::<Vec<_>>();
11315
11316        self.restore_hunks_in_ranges(ranges, window, cx);
11317    }
11318
11319    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11320        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11321        let selections = self
11322            .selections
11323            .all(&self.display_snapshot(cx))
11324            .into_iter()
11325            .map(|s| s.range())
11326            .collect();
11327        self.restore_hunks_in_ranges(selections, window, cx);
11328    }
11329
11330    pub fn restore_hunks_in_ranges(
11331        &mut self,
11332        ranges: Vec<Range<Point>>,
11333        window: &mut Window,
11334        cx: &mut Context<Editor>,
11335    ) {
11336        let mut revert_changes = HashMap::default();
11337        let chunk_by = self
11338            .snapshot(window, cx)
11339            .hunks_for_ranges(ranges)
11340            .into_iter()
11341            .chunk_by(|hunk| hunk.buffer_id);
11342        for (buffer_id, hunks) in &chunk_by {
11343            let hunks = hunks.collect::<Vec<_>>();
11344            for hunk in &hunks {
11345                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11346            }
11347            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11348        }
11349        drop(chunk_by);
11350        if !revert_changes.is_empty() {
11351            self.transact(window, cx, |editor, window, cx| {
11352                editor.restore(revert_changes, window, cx);
11353            });
11354        }
11355    }
11356
11357    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11358        if let Some(status) = self
11359            .addons
11360            .iter()
11361            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11362        {
11363            return Some(status);
11364        }
11365        self.project
11366            .as_ref()?
11367            .read(cx)
11368            .status_for_buffer_id(buffer_id, cx)
11369    }
11370
11371    pub fn open_active_item_in_terminal(
11372        &mut self,
11373        _: &OpenInTerminal,
11374        window: &mut Window,
11375        cx: &mut Context<Self>,
11376    ) {
11377        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11378            let project_path = buffer.read(cx).project_path(cx)?;
11379            let project = self.project()?.read(cx);
11380            let entry = project.entry_for_path(&project_path, cx)?;
11381            let parent = match &entry.canonical_path {
11382                Some(canonical_path) => canonical_path.to_path_buf(),
11383                None => project.absolute_path(&project_path, cx)?,
11384            }
11385            .parent()?
11386            .to_path_buf();
11387            Some(parent)
11388        }) {
11389            window.dispatch_action(
11390                OpenTerminal {
11391                    working_directory,
11392                    local: false,
11393                }
11394                .boxed_clone(),
11395                cx,
11396            );
11397        }
11398    }
11399
11400    fn set_breakpoint_context_menu(
11401        &mut self,
11402        display_row: DisplayRow,
11403        position: Option<Anchor>,
11404        clicked_point: gpui::Point<Pixels>,
11405        window: &mut Window,
11406        cx: &mut Context<Self>,
11407    ) {
11408        let source = self
11409            .buffer
11410            .read(cx)
11411            .snapshot(cx)
11412            .anchor_before(Point::new(display_row.0, 0u32));
11413
11414        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11415
11416        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11417            self,
11418            source,
11419            clicked_point,
11420            context_menu,
11421            window,
11422            cx,
11423        );
11424    }
11425
11426    fn add_edit_breakpoint_block(
11427        &mut self,
11428        anchor: Anchor,
11429        breakpoint: &Breakpoint,
11430        edit_action: BreakpointPromptEditAction,
11431        window: &mut Window,
11432        cx: &mut Context<Self>,
11433    ) {
11434        let weak_editor = cx.weak_entity();
11435        let bp_prompt = cx.new(|cx| {
11436            BreakpointPromptEditor::new(
11437                weak_editor,
11438                anchor,
11439                breakpoint.clone(),
11440                edit_action,
11441                window,
11442                cx,
11443            )
11444        });
11445
11446        let height = bp_prompt.update(cx, |this, cx| {
11447            this.prompt
11448                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11449        });
11450        let cloned_prompt = bp_prompt.clone();
11451        let blocks = vec![BlockProperties {
11452            style: BlockStyle::Sticky,
11453            placement: BlockPlacement::Above(anchor),
11454            height: Some(height),
11455            render: Arc::new(move |cx| {
11456                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11457                cloned_prompt.clone().into_any_element()
11458            }),
11459            priority: 0,
11460        }];
11461
11462        let focus_handle = bp_prompt.focus_handle(cx);
11463        window.focus(&focus_handle, cx);
11464
11465        let block_ids = self.insert_blocks(blocks, None, cx);
11466        bp_prompt.update(cx, |prompt, _| {
11467            prompt.add_block_ids(block_ids);
11468        });
11469    }
11470
11471    pub(crate) fn breakpoint_at_row(
11472        &self,
11473        row: u32,
11474        window: &mut Window,
11475        cx: &mut Context<Self>,
11476    ) -> Option<(Anchor, Breakpoint)> {
11477        let snapshot = self.snapshot(window, cx);
11478        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11479
11480        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11481    }
11482
11483    pub(crate) fn breakpoint_at_anchor(
11484        &self,
11485        breakpoint_position: Anchor,
11486        snapshot: &EditorSnapshot,
11487        cx: &mut Context<Self>,
11488    ) -> Option<(Anchor, Breakpoint)> {
11489        let buffer = self
11490            .buffer
11491            .read(cx)
11492            .buffer_for_anchor(breakpoint_position, cx)?;
11493
11494        let enclosing_excerpt = breakpoint_position.excerpt_id;
11495        let buffer_snapshot = buffer.read(cx).snapshot();
11496
11497        let row = buffer_snapshot
11498            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11499            .row;
11500
11501        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11502        let anchor_end = snapshot
11503            .buffer_snapshot()
11504            .anchor_after(Point::new(row, line_len));
11505
11506        self.breakpoint_store
11507            .as_ref()?
11508            .read_with(cx, |breakpoint_store, cx| {
11509                breakpoint_store
11510                    .breakpoints(
11511                        &buffer,
11512                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11513                        &buffer_snapshot,
11514                        cx,
11515                    )
11516                    .next()
11517                    .and_then(|(bp, _)| {
11518                        let breakpoint_row = buffer_snapshot
11519                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11520                            .row;
11521
11522                        if breakpoint_row == row {
11523                            snapshot
11524                                .buffer_snapshot()
11525                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11526                                .map(|position| (position, bp.bp.clone()))
11527                        } else {
11528                            None
11529                        }
11530                    })
11531            })
11532    }
11533
11534    pub fn edit_log_breakpoint(
11535        &mut self,
11536        _: &EditLogBreakpoint,
11537        window: &mut Window,
11538        cx: &mut Context<Self>,
11539    ) {
11540        if self.breakpoint_store.is_none() {
11541            return;
11542        }
11543
11544        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11545            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11546                message: None,
11547                state: BreakpointState::Enabled,
11548                condition: None,
11549                hit_condition: None,
11550            });
11551
11552            self.add_edit_breakpoint_block(
11553                anchor,
11554                &breakpoint,
11555                BreakpointPromptEditAction::Log,
11556                window,
11557                cx,
11558            );
11559        }
11560    }
11561
11562    fn breakpoints_at_cursors(
11563        &self,
11564        window: &mut Window,
11565        cx: &mut Context<Self>,
11566    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11567        let snapshot = self.snapshot(window, cx);
11568        let cursors = self
11569            .selections
11570            .disjoint_anchors_arc()
11571            .iter()
11572            .map(|selection| {
11573                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11574
11575                let breakpoint_position = self
11576                    .breakpoint_at_row(cursor_position.row, window, cx)
11577                    .map(|bp| bp.0)
11578                    .unwrap_or_else(|| {
11579                        snapshot
11580                            .display_snapshot
11581                            .buffer_snapshot()
11582                            .anchor_after(Point::new(cursor_position.row, 0))
11583                    });
11584
11585                let breakpoint = self
11586                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11587                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11588
11589                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11590            })
11591            // 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.
11592            .collect::<HashMap<Anchor, _>>();
11593
11594        cursors.into_iter().collect()
11595    }
11596
11597    pub fn enable_breakpoint(
11598        &mut self,
11599        _: &crate::actions::EnableBreakpoint,
11600        window: &mut Window,
11601        cx: &mut Context<Self>,
11602    ) {
11603        if self.breakpoint_store.is_none() {
11604            return;
11605        }
11606
11607        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11608            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11609                continue;
11610            };
11611            self.edit_breakpoint_at_anchor(
11612                anchor,
11613                breakpoint,
11614                BreakpointEditAction::InvertState,
11615                cx,
11616            );
11617        }
11618    }
11619
11620    pub fn disable_breakpoint(
11621        &mut self,
11622        _: &crate::actions::DisableBreakpoint,
11623        window: &mut Window,
11624        cx: &mut Context<Self>,
11625    ) {
11626        if self.breakpoint_store.is_none() {
11627            return;
11628        }
11629
11630        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11631            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11632                continue;
11633            };
11634            self.edit_breakpoint_at_anchor(
11635                anchor,
11636                breakpoint,
11637                BreakpointEditAction::InvertState,
11638                cx,
11639            );
11640        }
11641    }
11642
11643    pub fn toggle_breakpoint(
11644        &mut self,
11645        _: &crate::actions::ToggleBreakpoint,
11646        window: &mut Window,
11647        cx: &mut Context<Self>,
11648    ) {
11649        if self.breakpoint_store.is_none() {
11650            return;
11651        }
11652
11653        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11654            if let Some(breakpoint) = breakpoint {
11655                self.edit_breakpoint_at_anchor(
11656                    anchor,
11657                    breakpoint,
11658                    BreakpointEditAction::Toggle,
11659                    cx,
11660                );
11661            } else {
11662                self.edit_breakpoint_at_anchor(
11663                    anchor,
11664                    Breakpoint::new_standard(),
11665                    BreakpointEditAction::Toggle,
11666                    cx,
11667                );
11668            }
11669        }
11670    }
11671
11672    pub fn edit_breakpoint_at_anchor(
11673        &mut self,
11674        breakpoint_position: Anchor,
11675        breakpoint: Breakpoint,
11676        edit_action: BreakpointEditAction,
11677        cx: &mut Context<Self>,
11678    ) {
11679        let Some(breakpoint_store) = &self.breakpoint_store else {
11680            return;
11681        };
11682
11683        let Some(buffer) = self
11684            .buffer
11685            .read(cx)
11686            .buffer_for_anchor(breakpoint_position, cx)
11687        else {
11688            return;
11689        };
11690
11691        breakpoint_store.update(cx, |breakpoint_store, cx| {
11692            breakpoint_store.toggle_breakpoint(
11693                buffer,
11694                BreakpointWithPosition {
11695                    position: breakpoint_position.text_anchor,
11696                    bp: breakpoint,
11697                },
11698                edit_action,
11699                cx,
11700            );
11701        });
11702
11703        cx.notify();
11704    }
11705
11706    #[cfg(any(test, feature = "test-support"))]
11707    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
11708        self.breakpoint_store.clone()
11709    }
11710
11711    pub fn prepare_restore_change(
11712        &self,
11713        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11714        hunk: &MultiBufferDiffHunk,
11715        cx: &mut App,
11716    ) -> Option<()> {
11717        if hunk.is_created_file() {
11718            return None;
11719        }
11720        let buffer = self.buffer.read(cx);
11721        let diff = buffer.diff_for(hunk.buffer_id)?;
11722        let buffer = buffer.buffer(hunk.buffer_id)?;
11723        let buffer = buffer.read(cx);
11724        let original_text = diff
11725            .read(cx)
11726            .base_text(cx)
11727            .as_rope()
11728            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
11729        let buffer_snapshot = buffer.snapshot();
11730        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
11731        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
11732            probe
11733                .0
11734                .start
11735                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
11736                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
11737        }) {
11738            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
11739            Some(())
11740        } else {
11741            None
11742        }
11743    }
11744
11745    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
11746        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
11747    }
11748
11749    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
11750        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
11751    }
11752
11753    pub fn rotate_selections_forward(
11754        &mut self,
11755        _: &RotateSelectionsForward,
11756        window: &mut Window,
11757        cx: &mut Context<Self>,
11758    ) {
11759        self.rotate_selections(window, cx, false)
11760    }
11761
11762    pub fn rotate_selections_backward(
11763        &mut self,
11764        _: &RotateSelectionsBackward,
11765        window: &mut Window,
11766        cx: &mut Context<Self>,
11767    ) {
11768        self.rotate_selections(window, cx, true)
11769    }
11770
11771    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
11772        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11773        let display_snapshot = self.display_snapshot(cx);
11774        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
11775
11776        if selections.len() < 2 {
11777            return;
11778        }
11779
11780        let (edits, new_selections) = {
11781            let buffer = self.buffer.read(cx).read(cx);
11782            let has_selections = selections.iter().any(|s| !s.is_empty());
11783            if has_selections {
11784                let mut selected_texts: Vec<String> = selections
11785                    .iter()
11786                    .map(|selection| {
11787                        buffer
11788                            .text_for_range(selection.start..selection.end)
11789                            .collect()
11790                    })
11791                    .collect();
11792
11793                if reverse {
11794                    selected_texts.rotate_left(1);
11795                } else {
11796                    selected_texts.rotate_right(1);
11797                }
11798
11799                let mut offset_delta: i64 = 0;
11800                let mut new_selections = Vec::new();
11801                let edits: Vec<_> = selections
11802                    .iter()
11803                    .zip(selected_texts.iter())
11804                    .map(|(selection, new_text)| {
11805                        let old_len = (selection.end.0 - selection.start.0) as i64;
11806                        let new_len = new_text.len() as i64;
11807                        let adjusted_start =
11808                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
11809                        let adjusted_end =
11810                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
11811
11812                        new_selections.push(Selection {
11813                            id: selection.id,
11814                            start: adjusted_start,
11815                            end: adjusted_end,
11816                            reversed: selection.reversed,
11817                            goal: selection.goal,
11818                        });
11819
11820                        offset_delta += new_len - old_len;
11821                        (selection.start..selection.end, new_text.clone())
11822                    })
11823                    .collect();
11824                (edits, new_selections)
11825            } else {
11826                let mut all_rows: Vec<u32> = selections
11827                    .iter()
11828                    .map(|selection| buffer.offset_to_point(selection.start).row)
11829                    .collect();
11830                all_rows.sort_unstable();
11831                all_rows.dedup();
11832
11833                if all_rows.len() < 2 {
11834                    return;
11835                }
11836
11837                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
11838                    .iter()
11839                    .map(|&row| {
11840                        let start = Point::new(row, 0);
11841                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11842                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
11843                    })
11844                    .collect();
11845
11846                let mut line_texts: Vec<String> = line_ranges
11847                    .iter()
11848                    .map(|range| buffer.text_for_range(range.clone()).collect())
11849                    .collect();
11850
11851                if reverse {
11852                    line_texts.rotate_left(1);
11853                } else {
11854                    line_texts.rotate_right(1);
11855                }
11856
11857                let edits = line_ranges
11858                    .iter()
11859                    .zip(line_texts.iter())
11860                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
11861                    .collect();
11862
11863                let num_rows = all_rows.len();
11864                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
11865                    .iter()
11866                    .enumerate()
11867                    .map(|(i, &row)| (row, i))
11868                    .collect();
11869
11870                // Compute new line start offsets after rotation (handles CRLF)
11871                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
11872                let first_line_start = line_ranges[0].start.0;
11873                let mut new_line_starts: Vec<usize> = vec![first_line_start];
11874                for text in line_texts.iter().take(num_rows - 1) {
11875                    let prev_start = *new_line_starts.last().unwrap();
11876                    new_line_starts.push(prev_start + text.len() + newline_len);
11877                }
11878
11879                let new_selections = selections
11880                    .iter()
11881                    .map(|selection| {
11882                        let point = buffer.offset_to_point(selection.start);
11883                        let old_index = row_to_index[&point.row];
11884                        let new_index = if reverse {
11885                            (old_index + num_rows - 1) % num_rows
11886                        } else {
11887                            (old_index + 1) % num_rows
11888                        };
11889                        let new_offset =
11890                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
11891                        Selection {
11892                            id: selection.id,
11893                            start: new_offset,
11894                            end: new_offset,
11895                            reversed: selection.reversed,
11896                            goal: selection.goal,
11897                        }
11898                    })
11899                    .collect();
11900
11901                (edits, new_selections)
11902            }
11903        };
11904
11905        self.transact(window, cx, |this, window, cx| {
11906            this.buffer.update(cx, |buffer, cx| {
11907                buffer.edit(edits, None, cx);
11908            });
11909            this.change_selections(Default::default(), window, cx, |s| {
11910                s.select(new_selections);
11911            });
11912        });
11913    }
11914
11915    fn manipulate_lines<M>(
11916        &mut self,
11917        window: &mut Window,
11918        cx: &mut Context<Self>,
11919        mut manipulate: M,
11920    ) where
11921        M: FnMut(&str) -> LineManipulationResult,
11922    {
11923        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11924
11925        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11926        let buffer = self.buffer.read(cx).snapshot(cx);
11927
11928        let mut edits = Vec::new();
11929
11930        let selections = self.selections.all::<Point>(&display_map);
11931        let mut selections = selections.iter().peekable();
11932        let mut contiguous_row_selections = Vec::new();
11933        let mut new_selections = Vec::new();
11934        let mut added_lines = 0;
11935        let mut removed_lines = 0;
11936
11937        while let Some(selection) = selections.next() {
11938            let (start_row, end_row) = consume_contiguous_rows(
11939                &mut contiguous_row_selections,
11940                selection,
11941                &display_map,
11942                &mut selections,
11943            );
11944
11945            let start_point = Point::new(start_row.0, 0);
11946            let end_point = Point::new(
11947                end_row.previous_row().0,
11948                buffer.line_len(end_row.previous_row()),
11949            );
11950            let text = buffer
11951                .text_for_range(start_point..end_point)
11952                .collect::<String>();
11953
11954            let LineManipulationResult {
11955                new_text,
11956                line_count_before,
11957                line_count_after,
11958            } = manipulate(&text);
11959
11960            edits.push((start_point..end_point, new_text));
11961
11962            // Selections must change based on added and removed line count
11963            let start_row =
11964                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
11965            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
11966            new_selections.push(Selection {
11967                id: selection.id,
11968                start: start_row,
11969                end: end_row,
11970                goal: SelectionGoal::None,
11971                reversed: selection.reversed,
11972            });
11973
11974            if line_count_after > line_count_before {
11975                added_lines += line_count_after - line_count_before;
11976            } else if line_count_before > line_count_after {
11977                removed_lines += line_count_before - line_count_after;
11978            }
11979        }
11980
11981        self.transact(window, cx, |this, window, cx| {
11982            let buffer = this.buffer.update(cx, |buffer, cx| {
11983                buffer.edit(edits, None, cx);
11984                buffer.snapshot(cx)
11985            });
11986
11987            // Recalculate offsets on newly edited buffer
11988            let new_selections = new_selections
11989                .iter()
11990                .map(|s| {
11991                    let start_point = Point::new(s.start.0, 0);
11992                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
11993                    Selection {
11994                        id: s.id,
11995                        start: buffer.point_to_offset(start_point),
11996                        end: buffer.point_to_offset(end_point),
11997                        goal: s.goal,
11998                        reversed: s.reversed,
11999                    }
12000                })
12001                .collect();
12002
12003            this.change_selections(Default::default(), window, cx, |s| {
12004                s.select(new_selections);
12005            });
12006
12007            this.request_autoscroll(Autoscroll::fit(), cx);
12008        });
12009    }
12010
12011    fn manipulate_immutable_lines<Fn>(
12012        &mut self,
12013        window: &mut Window,
12014        cx: &mut Context<Self>,
12015        mut callback: Fn,
12016    ) where
12017        Fn: FnMut(&mut Vec<&str>),
12018    {
12019        self.manipulate_lines(window, cx, |text| {
12020            let mut lines: Vec<&str> = text.split('\n').collect();
12021            let line_count_before = lines.len();
12022
12023            callback(&mut lines);
12024
12025            LineManipulationResult {
12026                new_text: lines.join("\n"),
12027                line_count_before,
12028                line_count_after: lines.len(),
12029            }
12030        });
12031    }
12032
12033    fn manipulate_mutable_lines<Fn>(
12034        &mut self,
12035        window: &mut Window,
12036        cx: &mut Context<Self>,
12037        mut callback: Fn,
12038    ) where
12039        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12040    {
12041        self.manipulate_lines(window, cx, |text| {
12042            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12043            let line_count_before = lines.len();
12044
12045            callback(&mut lines);
12046
12047            LineManipulationResult {
12048                new_text: lines.join("\n"),
12049                line_count_before,
12050                line_count_after: lines.len(),
12051            }
12052        });
12053    }
12054
12055    pub fn convert_indentation_to_spaces(
12056        &mut self,
12057        _: &ConvertIndentationToSpaces,
12058        window: &mut Window,
12059        cx: &mut Context<Self>,
12060    ) {
12061        let settings = self.buffer.read(cx).language_settings(cx);
12062        let tab_size = settings.tab_size.get() as usize;
12063
12064        self.manipulate_mutable_lines(window, cx, |lines| {
12065            // Allocates a reasonably sized scratch buffer once for the whole loop
12066            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12067            // Avoids recomputing spaces that could be inserted many times
12068            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12069                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12070                .collect();
12071
12072            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12073                let mut chars = line.as_ref().chars();
12074                let mut col = 0;
12075                let mut changed = false;
12076
12077                for ch in chars.by_ref() {
12078                    match ch {
12079                        ' ' => {
12080                            reindented_line.push(' ');
12081                            col += 1;
12082                        }
12083                        '\t' => {
12084                            // \t are converted to spaces depending on the current column
12085                            let spaces_len = tab_size - (col % tab_size);
12086                            reindented_line.extend(&space_cache[spaces_len - 1]);
12087                            col += spaces_len;
12088                            changed = true;
12089                        }
12090                        _ => {
12091                            // If we dont append before break, the character is consumed
12092                            reindented_line.push(ch);
12093                            break;
12094                        }
12095                    }
12096                }
12097
12098                if !changed {
12099                    reindented_line.clear();
12100                    continue;
12101                }
12102                // Append the rest of the line and replace old reference with new one
12103                reindented_line.extend(chars);
12104                *line = Cow::Owned(reindented_line.clone());
12105                reindented_line.clear();
12106            }
12107        });
12108    }
12109
12110    pub fn convert_indentation_to_tabs(
12111        &mut self,
12112        _: &ConvertIndentationToTabs,
12113        window: &mut Window,
12114        cx: &mut Context<Self>,
12115    ) {
12116        let settings = self.buffer.read(cx).language_settings(cx);
12117        let tab_size = settings.tab_size.get() as usize;
12118
12119        self.manipulate_mutable_lines(window, cx, |lines| {
12120            // Allocates a reasonably sized buffer once for the whole loop
12121            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12122            // Avoids recomputing spaces that could be inserted many times
12123            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12124                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12125                .collect();
12126
12127            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12128                let mut chars = line.chars();
12129                let mut spaces_count = 0;
12130                let mut first_non_indent_char = None;
12131                let mut changed = false;
12132
12133                for ch in chars.by_ref() {
12134                    match ch {
12135                        ' ' => {
12136                            // Keep track of spaces. Append \t when we reach tab_size
12137                            spaces_count += 1;
12138                            changed = true;
12139                            if spaces_count == tab_size {
12140                                reindented_line.push('\t');
12141                                spaces_count = 0;
12142                            }
12143                        }
12144                        '\t' => {
12145                            reindented_line.push('\t');
12146                            spaces_count = 0;
12147                        }
12148                        _ => {
12149                            // Dont append it yet, we might have remaining spaces
12150                            first_non_indent_char = Some(ch);
12151                            break;
12152                        }
12153                    }
12154                }
12155
12156                if !changed {
12157                    reindented_line.clear();
12158                    continue;
12159                }
12160                // Remaining spaces that didn't make a full tab stop
12161                if spaces_count > 0 {
12162                    reindented_line.extend(&space_cache[spaces_count - 1]);
12163                }
12164                // If we consume an extra character that was not indentation, add it back
12165                if let Some(extra_char) = first_non_indent_char {
12166                    reindented_line.push(extra_char);
12167                }
12168                // Append the rest of the line and replace old reference with new one
12169                reindented_line.extend(chars);
12170                *line = Cow::Owned(reindented_line.clone());
12171                reindented_line.clear();
12172            }
12173        });
12174    }
12175
12176    pub fn convert_to_upper_case(
12177        &mut self,
12178        _: &ConvertToUpperCase,
12179        window: &mut Window,
12180        cx: &mut Context<Self>,
12181    ) {
12182        self.manipulate_text(window, cx, |text| text.to_uppercase())
12183    }
12184
12185    pub fn convert_to_lower_case(
12186        &mut self,
12187        _: &ConvertToLowerCase,
12188        window: &mut Window,
12189        cx: &mut Context<Self>,
12190    ) {
12191        self.manipulate_text(window, cx, |text| text.to_lowercase())
12192    }
12193
12194    pub fn convert_to_title_case(
12195        &mut self,
12196        _: &ConvertToTitleCase,
12197        window: &mut Window,
12198        cx: &mut Context<Self>,
12199    ) {
12200        self.manipulate_text(window, cx, |text| {
12201            text.split('\n')
12202                .map(|line| line.to_case(Case::Title))
12203                .join("\n")
12204        })
12205    }
12206
12207    pub fn convert_to_snake_case(
12208        &mut self,
12209        _: &ConvertToSnakeCase,
12210        window: &mut Window,
12211        cx: &mut Context<Self>,
12212    ) {
12213        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12214    }
12215
12216    pub fn convert_to_kebab_case(
12217        &mut self,
12218        _: &ConvertToKebabCase,
12219        window: &mut Window,
12220        cx: &mut Context<Self>,
12221    ) {
12222        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12223    }
12224
12225    pub fn convert_to_upper_camel_case(
12226        &mut self,
12227        _: &ConvertToUpperCamelCase,
12228        window: &mut Window,
12229        cx: &mut Context<Self>,
12230    ) {
12231        self.manipulate_text(window, cx, |text| {
12232            text.split('\n')
12233                .map(|line| line.to_case(Case::UpperCamel))
12234                .join("\n")
12235        })
12236    }
12237
12238    pub fn convert_to_lower_camel_case(
12239        &mut self,
12240        _: &ConvertToLowerCamelCase,
12241        window: &mut Window,
12242        cx: &mut Context<Self>,
12243    ) {
12244        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12245    }
12246
12247    pub fn convert_to_opposite_case(
12248        &mut self,
12249        _: &ConvertToOppositeCase,
12250        window: &mut Window,
12251        cx: &mut Context<Self>,
12252    ) {
12253        self.manipulate_text(window, cx, |text| {
12254            text.chars()
12255                .fold(String::with_capacity(text.len()), |mut t, c| {
12256                    if c.is_uppercase() {
12257                        t.extend(c.to_lowercase());
12258                    } else {
12259                        t.extend(c.to_uppercase());
12260                    }
12261                    t
12262                })
12263        })
12264    }
12265
12266    pub fn convert_to_sentence_case(
12267        &mut self,
12268        _: &ConvertToSentenceCase,
12269        window: &mut Window,
12270        cx: &mut Context<Self>,
12271    ) {
12272        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12273    }
12274
12275    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12276        self.manipulate_text(window, cx, |text| {
12277            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12278            if has_upper_case_characters {
12279                text.to_lowercase()
12280            } else {
12281                text.to_uppercase()
12282            }
12283        })
12284    }
12285
12286    pub fn convert_to_rot13(
12287        &mut self,
12288        _: &ConvertToRot13,
12289        window: &mut Window,
12290        cx: &mut Context<Self>,
12291    ) {
12292        self.manipulate_text(window, cx, |text| {
12293            text.chars()
12294                .map(|c| match c {
12295                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12296                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12297                    _ => c,
12298                })
12299                .collect()
12300        })
12301    }
12302
12303    pub fn convert_to_rot47(
12304        &mut self,
12305        _: &ConvertToRot47,
12306        window: &mut Window,
12307        cx: &mut Context<Self>,
12308    ) {
12309        self.manipulate_text(window, cx, |text| {
12310            text.chars()
12311                .map(|c| {
12312                    let code_point = c as u32;
12313                    if code_point >= 33 && code_point <= 126 {
12314                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12315                    }
12316                    c
12317                })
12318                .collect()
12319        })
12320    }
12321
12322    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12323    where
12324        Fn: FnMut(&str) -> String,
12325    {
12326        let buffer = self.buffer.read(cx).snapshot(cx);
12327
12328        let mut new_selections = Vec::new();
12329        let mut edits = Vec::new();
12330        let mut selection_adjustment = 0isize;
12331
12332        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12333            let selection_is_empty = selection.is_empty();
12334
12335            let (start, end) = if selection_is_empty {
12336                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12337                (word_range.start, word_range.end)
12338            } else {
12339                (
12340                    buffer.point_to_offset(selection.start),
12341                    buffer.point_to_offset(selection.end),
12342                )
12343            };
12344
12345            let text = buffer.text_for_range(start..end).collect::<String>();
12346            let old_length = text.len() as isize;
12347            let text = callback(&text);
12348
12349            new_selections.push(Selection {
12350                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12351                end: MultiBufferOffset(
12352                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12353                ),
12354                goal: SelectionGoal::None,
12355                id: selection.id,
12356                reversed: selection.reversed,
12357            });
12358
12359            selection_adjustment += old_length - text.len() as isize;
12360
12361            edits.push((start..end, text));
12362        }
12363
12364        self.transact(window, cx, |this, window, cx| {
12365            this.buffer.update(cx, |buffer, cx| {
12366                buffer.edit(edits, None, cx);
12367            });
12368
12369            this.change_selections(Default::default(), window, cx, |s| {
12370                s.select(new_selections);
12371            });
12372
12373            this.request_autoscroll(Autoscroll::fit(), cx);
12374        });
12375    }
12376
12377    pub fn move_selection_on_drop(
12378        &mut self,
12379        selection: &Selection<Anchor>,
12380        target: DisplayPoint,
12381        is_cut: bool,
12382        window: &mut Window,
12383        cx: &mut Context<Self>,
12384    ) {
12385        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12386        let buffer = display_map.buffer_snapshot();
12387        let mut edits = Vec::new();
12388        let insert_point = display_map
12389            .clip_point(target, Bias::Left)
12390            .to_point(&display_map);
12391        let text = buffer
12392            .text_for_range(selection.start..selection.end)
12393            .collect::<String>();
12394        if is_cut {
12395            edits.push(((selection.start..selection.end), String::new()));
12396        }
12397        let insert_anchor = buffer.anchor_before(insert_point);
12398        edits.push(((insert_anchor..insert_anchor), text));
12399        let last_edit_start = insert_anchor.bias_left(buffer);
12400        let last_edit_end = insert_anchor.bias_right(buffer);
12401        self.transact(window, cx, |this, window, cx| {
12402            this.buffer.update(cx, |buffer, cx| {
12403                buffer.edit(edits, None, cx);
12404            });
12405            this.change_selections(Default::default(), window, cx, |s| {
12406                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12407            });
12408        });
12409    }
12410
12411    pub fn clear_selection_drag_state(&mut self) {
12412        self.selection_drag_state = SelectionDragState::None;
12413    }
12414
12415    pub fn duplicate(
12416        &mut self,
12417        upwards: bool,
12418        whole_lines: bool,
12419        window: &mut Window,
12420        cx: &mut Context<Self>,
12421    ) {
12422        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12423
12424        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12425        let buffer = display_map.buffer_snapshot();
12426        let selections = self.selections.all::<Point>(&display_map);
12427
12428        let mut edits = Vec::new();
12429        let mut selections_iter = selections.iter().peekable();
12430        while let Some(selection) = selections_iter.next() {
12431            let mut rows = selection.spanned_rows(false, &display_map);
12432            // duplicate line-wise
12433            if whole_lines || selection.start == selection.end {
12434                // Avoid duplicating the same lines twice.
12435                while let Some(next_selection) = selections_iter.peek() {
12436                    let next_rows = next_selection.spanned_rows(false, &display_map);
12437                    if next_rows.start < rows.end {
12438                        rows.end = next_rows.end;
12439                        selections_iter.next().unwrap();
12440                    } else {
12441                        break;
12442                    }
12443                }
12444
12445                // Copy the text from the selected row region and splice it either at the start
12446                // or end of the region.
12447                let start = Point::new(rows.start.0, 0);
12448                let end = Point::new(
12449                    rows.end.previous_row().0,
12450                    buffer.line_len(rows.end.previous_row()),
12451                );
12452
12453                let mut text = buffer.text_for_range(start..end).collect::<String>();
12454
12455                let insert_location = if upwards {
12456                    // When duplicating upward, we need to insert before the current line.
12457                    // If we're on the last line and it doesn't end with a newline,
12458                    // we need to add a newline before the duplicated content.
12459                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12460                        && buffer.max_point().column > 0
12461                        && !text.ends_with('\n');
12462
12463                    if needs_leading_newline {
12464                        text.insert(0, '\n');
12465                        end
12466                    } else {
12467                        text.push('\n');
12468                        Point::new(rows.start.0, 0)
12469                    }
12470                } else {
12471                    text.push('\n');
12472                    start
12473                };
12474                edits.push((insert_location..insert_location, text));
12475            } else {
12476                // duplicate character-wise
12477                let start = selection.start;
12478                let end = selection.end;
12479                let text = buffer.text_for_range(start..end).collect::<String>();
12480                edits.push((selection.end..selection.end, text));
12481            }
12482        }
12483
12484        self.transact(window, cx, |this, window, cx| {
12485            this.buffer.update(cx, |buffer, cx| {
12486                buffer.edit(edits, None, cx);
12487            });
12488
12489            // When duplicating upward with whole lines, move the cursor to the duplicated line
12490            if upwards && whole_lines {
12491                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12492
12493                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12494                    let mut new_ranges = Vec::new();
12495                    let selections = s.all::<Point>(&display_map);
12496                    let mut selections_iter = selections.iter().peekable();
12497
12498                    while let Some(first_selection) = selections_iter.next() {
12499                        // Group contiguous selections together to find the total row span
12500                        let mut group_selections = vec![first_selection];
12501                        let mut rows = first_selection.spanned_rows(false, &display_map);
12502
12503                        while let Some(next_selection) = selections_iter.peek() {
12504                            let next_rows = next_selection.spanned_rows(false, &display_map);
12505                            if next_rows.start < rows.end {
12506                                rows.end = next_rows.end;
12507                                group_selections.push(selections_iter.next().unwrap());
12508                            } else {
12509                                break;
12510                            }
12511                        }
12512
12513                        let row_count = rows.end.0 - rows.start.0;
12514
12515                        // Move all selections in this group up by the total number of duplicated rows
12516                        for selection in group_selections {
12517                            let new_start = Point::new(
12518                                selection.start.row.saturating_sub(row_count),
12519                                selection.start.column,
12520                            );
12521
12522                            let new_end = Point::new(
12523                                selection.end.row.saturating_sub(row_count),
12524                                selection.end.column,
12525                            );
12526
12527                            new_ranges.push(new_start..new_end);
12528                        }
12529                    }
12530
12531                    s.select_ranges(new_ranges);
12532                });
12533            }
12534
12535            this.request_autoscroll(Autoscroll::fit(), cx);
12536        });
12537    }
12538
12539    pub fn duplicate_line_up(
12540        &mut self,
12541        _: &DuplicateLineUp,
12542        window: &mut Window,
12543        cx: &mut Context<Self>,
12544    ) {
12545        self.duplicate(true, true, window, cx);
12546    }
12547
12548    pub fn duplicate_line_down(
12549        &mut self,
12550        _: &DuplicateLineDown,
12551        window: &mut Window,
12552        cx: &mut Context<Self>,
12553    ) {
12554        self.duplicate(false, true, window, cx);
12555    }
12556
12557    pub fn duplicate_selection(
12558        &mut self,
12559        _: &DuplicateSelection,
12560        window: &mut Window,
12561        cx: &mut Context<Self>,
12562    ) {
12563        self.duplicate(false, false, window, cx);
12564    }
12565
12566    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12567        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12568        if self.mode.is_single_line() {
12569            cx.propagate();
12570            return;
12571        }
12572
12573        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12574        let buffer = self.buffer.read(cx).snapshot(cx);
12575
12576        let mut edits = Vec::new();
12577        let mut unfold_ranges = Vec::new();
12578        let mut refold_creases = Vec::new();
12579
12580        let selections = self.selections.all::<Point>(&display_map);
12581        let mut selections = selections.iter().peekable();
12582        let mut contiguous_row_selections = Vec::new();
12583        let mut new_selections = Vec::new();
12584
12585        while let Some(selection) = selections.next() {
12586            // Find all the selections that span a contiguous row range
12587            let (start_row, end_row) = consume_contiguous_rows(
12588                &mut contiguous_row_selections,
12589                selection,
12590                &display_map,
12591                &mut selections,
12592            );
12593
12594            // Move the text spanned by the row range to be before the line preceding the row range
12595            if start_row.0 > 0 {
12596                let range_to_move = Point::new(
12597                    start_row.previous_row().0,
12598                    buffer.line_len(start_row.previous_row()),
12599                )
12600                    ..Point::new(
12601                        end_row.previous_row().0,
12602                        buffer.line_len(end_row.previous_row()),
12603                    );
12604                let insertion_point = display_map
12605                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12606                    .0;
12607
12608                // Don't move lines across excerpts
12609                if buffer
12610                    .excerpt_containing(insertion_point..range_to_move.end)
12611                    .is_some()
12612                {
12613                    let text = buffer
12614                        .text_for_range(range_to_move.clone())
12615                        .flat_map(|s| s.chars())
12616                        .skip(1)
12617                        .chain(['\n'])
12618                        .collect::<String>();
12619
12620                    edits.push((
12621                        buffer.anchor_after(range_to_move.start)
12622                            ..buffer.anchor_before(range_to_move.end),
12623                        String::new(),
12624                    ));
12625                    let insertion_anchor = buffer.anchor_after(insertion_point);
12626                    edits.push((insertion_anchor..insertion_anchor, text));
12627
12628                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12629
12630                    // Move selections up
12631                    new_selections.extend(contiguous_row_selections.drain(..).map(
12632                        |mut selection| {
12633                            selection.start.row -= row_delta;
12634                            selection.end.row -= row_delta;
12635                            selection
12636                        },
12637                    ));
12638
12639                    // Move folds up
12640                    unfold_ranges.push(range_to_move.clone());
12641                    for fold in display_map.folds_in_range(
12642                        buffer.anchor_before(range_to_move.start)
12643                            ..buffer.anchor_after(range_to_move.end),
12644                    ) {
12645                        let mut start = fold.range.start.to_point(&buffer);
12646                        let mut end = fold.range.end.to_point(&buffer);
12647                        start.row -= row_delta;
12648                        end.row -= row_delta;
12649                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12650                    }
12651                }
12652            }
12653
12654            // If we didn't move line(s), preserve the existing selections
12655            new_selections.append(&mut contiguous_row_selections);
12656        }
12657
12658        self.transact(window, cx, |this, window, cx| {
12659            this.unfold_ranges(&unfold_ranges, true, true, cx);
12660            this.buffer.update(cx, |buffer, cx| {
12661                for (range, text) in edits {
12662                    buffer.edit([(range, text)], None, cx);
12663                }
12664            });
12665            this.fold_creases(refold_creases, true, window, cx);
12666            this.change_selections(Default::default(), window, cx, |s| {
12667                s.select(new_selections);
12668            })
12669        });
12670    }
12671
12672    pub fn move_line_down(
12673        &mut self,
12674        _: &MoveLineDown,
12675        window: &mut Window,
12676        cx: &mut Context<Self>,
12677    ) {
12678        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12679        if self.mode.is_single_line() {
12680            cx.propagate();
12681            return;
12682        }
12683
12684        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12685        let buffer = self.buffer.read(cx).snapshot(cx);
12686
12687        let mut edits = Vec::new();
12688        let mut unfold_ranges = Vec::new();
12689        let mut refold_creases = Vec::new();
12690
12691        let selections = self.selections.all::<Point>(&display_map);
12692        let mut selections = selections.iter().peekable();
12693        let mut contiguous_row_selections = Vec::new();
12694        let mut new_selections = Vec::new();
12695
12696        while let Some(selection) = selections.next() {
12697            // Find all the selections that span a contiguous row range
12698            let (start_row, end_row) = consume_contiguous_rows(
12699                &mut contiguous_row_selections,
12700                selection,
12701                &display_map,
12702                &mut selections,
12703            );
12704
12705            // Move the text spanned by the row range to be after the last line of the row range
12706            if end_row.0 <= buffer.max_point().row {
12707                let range_to_move =
12708                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
12709                let insertion_point = display_map
12710                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
12711                    .0;
12712
12713                // Don't move lines across excerpt boundaries
12714                if buffer
12715                    .excerpt_containing(range_to_move.start..insertion_point)
12716                    .is_some()
12717                {
12718                    let mut text = String::from("\n");
12719                    text.extend(buffer.text_for_range(range_to_move.clone()));
12720                    text.pop(); // Drop trailing newline
12721                    edits.push((
12722                        buffer.anchor_after(range_to_move.start)
12723                            ..buffer.anchor_before(range_to_move.end),
12724                        String::new(),
12725                    ));
12726                    let insertion_anchor = buffer.anchor_after(insertion_point);
12727                    edits.push((insertion_anchor..insertion_anchor, text));
12728
12729                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
12730
12731                    // Move selections down
12732                    new_selections.extend(contiguous_row_selections.drain(..).map(
12733                        |mut selection| {
12734                            selection.start.row += row_delta;
12735                            selection.end.row += row_delta;
12736                            selection
12737                        },
12738                    ));
12739
12740                    // Move folds down
12741                    unfold_ranges.push(range_to_move.clone());
12742                    for fold in display_map.folds_in_range(
12743                        buffer.anchor_before(range_to_move.start)
12744                            ..buffer.anchor_after(range_to_move.end),
12745                    ) {
12746                        let mut start = fold.range.start.to_point(&buffer);
12747                        let mut end = fold.range.end.to_point(&buffer);
12748                        start.row += row_delta;
12749                        end.row += row_delta;
12750                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12751                    }
12752                }
12753            }
12754
12755            // If we didn't move line(s), preserve the existing selections
12756            new_selections.append(&mut contiguous_row_selections);
12757        }
12758
12759        self.transact(window, cx, |this, window, cx| {
12760            this.unfold_ranges(&unfold_ranges, true, true, cx);
12761            this.buffer.update(cx, |buffer, cx| {
12762                for (range, text) in edits {
12763                    buffer.edit([(range, text)], None, cx);
12764                }
12765            });
12766            this.fold_creases(refold_creases, true, window, cx);
12767            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
12768        });
12769    }
12770
12771    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
12772        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12773        let text_layout_details = &self.text_layout_details(window);
12774        self.transact(window, cx, |this, window, cx| {
12775            let edits = this.change_selections(Default::default(), window, cx, |s| {
12776                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
12777                s.move_with(|display_map, selection| {
12778                    if !selection.is_empty() {
12779                        return;
12780                    }
12781
12782                    let mut head = selection.head();
12783                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
12784                    if head.column() == display_map.line_len(head.row()) {
12785                        transpose_offset = display_map
12786                            .buffer_snapshot()
12787                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
12788                    }
12789
12790                    if transpose_offset == MultiBufferOffset(0) {
12791                        return;
12792                    }
12793
12794                    *head.column_mut() += 1;
12795                    head = display_map.clip_point(head, Bias::Right);
12796                    let goal = SelectionGoal::HorizontalPosition(
12797                        display_map
12798                            .x_for_display_point(head, text_layout_details)
12799                            .into(),
12800                    );
12801                    selection.collapse_to(head, goal);
12802
12803                    let transpose_start = display_map
12804                        .buffer_snapshot()
12805                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
12806                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
12807                        let transpose_end = display_map
12808                            .buffer_snapshot()
12809                            .clip_offset(transpose_offset + 1usize, Bias::Right);
12810                        if let Some(ch) = display_map
12811                            .buffer_snapshot()
12812                            .chars_at(transpose_start)
12813                            .next()
12814                        {
12815                            edits.push((transpose_start..transpose_offset, String::new()));
12816                            edits.push((transpose_end..transpose_end, ch.to_string()));
12817                        }
12818                    }
12819                });
12820                edits
12821            });
12822            this.buffer
12823                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
12824            let selections = this
12825                .selections
12826                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
12827            this.change_selections(Default::default(), window, cx, |s| {
12828                s.select(selections);
12829            });
12830        });
12831    }
12832
12833    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
12834        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12835        if self.mode.is_single_line() {
12836            cx.propagate();
12837            return;
12838        }
12839
12840        self.rewrap_impl(RewrapOptions::default(), cx)
12841    }
12842
12843    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
12844        let buffer = self.buffer.read(cx).snapshot(cx);
12845        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
12846
12847        #[derive(Clone, Debug, PartialEq)]
12848        enum CommentFormat {
12849            /// single line comment, with prefix for line
12850            Line(String),
12851            /// single line within a block comment, with prefix for line
12852            BlockLine(String),
12853            /// a single line of a block comment that includes the initial delimiter
12854            BlockCommentWithStart(BlockCommentConfig),
12855            /// a single line of a block comment that includes the ending delimiter
12856            BlockCommentWithEnd(BlockCommentConfig),
12857        }
12858
12859        // Split selections to respect paragraph, indent, and comment prefix boundaries.
12860        let wrap_ranges = selections.into_iter().flat_map(|selection| {
12861            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
12862                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
12863                .peekable();
12864
12865            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
12866                row
12867            } else {
12868                return Vec::new();
12869            };
12870
12871            let language_settings = buffer.language_settings_at(selection.head(), cx);
12872            let language_scope = buffer.language_scope_at(selection.head());
12873
12874            let indent_and_prefix_for_row =
12875                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
12876                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
12877                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
12878                        &language_scope
12879                    {
12880                        let indent_end = Point::new(row, indent.len);
12881                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12882                        let line_text_after_indent = buffer
12883                            .text_for_range(indent_end..line_end)
12884                            .collect::<String>();
12885
12886                        let is_within_comment_override = buffer
12887                            .language_scope_at(indent_end)
12888                            .is_some_and(|scope| scope.override_name() == Some("comment"));
12889                        let comment_delimiters = if is_within_comment_override {
12890                            // we are within a comment syntax node, but we don't
12891                            // yet know what kind of comment: block, doc or line
12892                            match (
12893                                language_scope.documentation_comment(),
12894                                language_scope.block_comment(),
12895                            ) {
12896                                (Some(config), _) | (_, Some(config))
12897                                    if buffer.contains_str_at(indent_end, &config.start) =>
12898                                {
12899                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
12900                                }
12901                                (Some(config), _) | (_, Some(config))
12902                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
12903                                {
12904                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
12905                                }
12906                                (Some(config), _) | (_, Some(config))
12907                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
12908                                {
12909                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
12910                                }
12911                                (_, _) => language_scope
12912                                    .line_comment_prefixes()
12913                                    .iter()
12914                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
12915                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
12916                            }
12917                        } else {
12918                            // we not in an overridden comment node, but we may
12919                            // be within a non-overridden line comment node
12920                            language_scope
12921                                .line_comment_prefixes()
12922                                .iter()
12923                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
12924                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
12925                        };
12926
12927                        let rewrap_prefix = language_scope
12928                            .rewrap_prefixes()
12929                            .iter()
12930                            .find_map(|prefix_regex| {
12931                                prefix_regex.find(&line_text_after_indent).map(|mat| {
12932                                    if mat.start() == 0 {
12933                                        Some(mat.as_str().to_string())
12934                                    } else {
12935                                        None
12936                                    }
12937                                })
12938                            })
12939                            .flatten();
12940                        (comment_delimiters, rewrap_prefix)
12941                    } else {
12942                        (None, None)
12943                    };
12944                    (indent, comment_prefix, rewrap_prefix)
12945                };
12946
12947            let mut ranges = Vec::new();
12948            let from_empty_selection = selection.is_empty();
12949
12950            let mut current_range_start = first_row;
12951            let mut prev_row = first_row;
12952            let (
12953                mut current_range_indent,
12954                mut current_range_comment_delimiters,
12955                mut current_range_rewrap_prefix,
12956            ) = indent_and_prefix_for_row(first_row);
12957
12958            for row in non_blank_rows_iter.skip(1) {
12959                let has_paragraph_break = row > prev_row + 1;
12960
12961                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
12962                    indent_and_prefix_for_row(row);
12963
12964                let has_indent_change = row_indent != current_range_indent;
12965                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
12966
12967                let has_boundary_change = has_comment_change
12968                    || row_rewrap_prefix.is_some()
12969                    || (has_indent_change && current_range_comment_delimiters.is_some());
12970
12971                if has_paragraph_break || has_boundary_change {
12972                    ranges.push((
12973                        language_settings.clone(),
12974                        Point::new(current_range_start, 0)
12975                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
12976                        current_range_indent,
12977                        current_range_comment_delimiters.clone(),
12978                        current_range_rewrap_prefix.clone(),
12979                        from_empty_selection,
12980                    ));
12981                    current_range_start = row;
12982                    current_range_indent = row_indent;
12983                    current_range_comment_delimiters = row_comment_delimiters;
12984                    current_range_rewrap_prefix = row_rewrap_prefix;
12985                }
12986                prev_row = row;
12987            }
12988
12989            ranges.push((
12990                language_settings.clone(),
12991                Point::new(current_range_start, 0)
12992                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
12993                current_range_indent,
12994                current_range_comment_delimiters,
12995                current_range_rewrap_prefix,
12996                from_empty_selection,
12997            ));
12998
12999            ranges
13000        });
13001
13002        let mut edits = Vec::new();
13003        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13004
13005        for (
13006            language_settings,
13007            wrap_range,
13008            mut indent_size,
13009            comment_prefix,
13010            rewrap_prefix,
13011            from_empty_selection,
13012        ) in wrap_ranges
13013        {
13014            let mut start_row = wrap_range.start.row;
13015            let mut end_row = wrap_range.end.row;
13016
13017            // Skip selections that overlap with a range that has already been rewrapped.
13018            let selection_range = start_row..end_row;
13019            if rewrapped_row_ranges
13020                .iter()
13021                .any(|range| range.overlaps(&selection_range))
13022            {
13023                continue;
13024            }
13025
13026            let tab_size = language_settings.tab_size;
13027
13028            let (line_prefix, inside_comment) = match &comment_prefix {
13029                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13030                    (Some(prefix.as_str()), true)
13031                }
13032                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13033                    (Some(prefix.as_ref()), true)
13034                }
13035                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13036                    start: _,
13037                    end: _,
13038                    prefix,
13039                    tab_size,
13040                })) => {
13041                    indent_size.len += tab_size;
13042                    (Some(prefix.as_ref()), true)
13043                }
13044                None => (None, false),
13045            };
13046            let indent_prefix = indent_size.chars().collect::<String>();
13047            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13048
13049            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13050                RewrapBehavior::InComments => inside_comment,
13051                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13052                RewrapBehavior::Anywhere => true,
13053            };
13054
13055            let should_rewrap = options.override_language_settings
13056                || allow_rewrap_based_on_language
13057                || self.hard_wrap.is_some();
13058            if !should_rewrap {
13059                continue;
13060            }
13061
13062            if from_empty_selection {
13063                'expand_upwards: while start_row > 0 {
13064                    let prev_row = start_row - 1;
13065                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13066                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13067                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13068                    {
13069                        start_row = prev_row;
13070                    } else {
13071                        break 'expand_upwards;
13072                    }
13073                }
13074
13075                'expand_downwards: while end_row < buffer.max_point().row {
13076                    let next_row = end_row + 1;
13077                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13078                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13079                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13080                    {
13081                        end_row = next_row;
13082                    } else {
13083                        break 'expand_downwards;
13084                    }
13085                }
13086            }
13087
13088            let start = Point::new(start_row, 0);
13089            let start_offset = ToOffset::to_offset(&start, &buffer);
13090            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13091            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13092            let mut first_line_delimiter = None;
13093            let mut last_line_delimiter = None;
13094            let Some(lines_without_prefixes) = selection_text
13095                .lines()
13096                .enumerate()
13097                .map(|(ix, line)| {
13098                    let line_trimmed = line.trim_start();
13099                    if rewrap_prefix.is_some() && ix > 0 {
13100                        Ok(line_trimmed)
13101                    } else if let Some(
13102                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13103                            start,
13104                            prefix,
13105                            end,
13106                            tab_size,
13107                        })
13108                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13109                            start,
13110                            prefix,
13111                            end,
13112                            tab_size,
13113                        }),
13114                    ) = &comment_prefix
13115                    {
13116                        let line_trimmed = line_trimmed
13117                            .strip_prefix(start.as_ref())
13118                            .map(|s| {
13119                                let mut indent_size = indent_size;
13120                                indent_size.len -= tab_size;
13121                                let indent_prefix: String = indent_size.chars().collect();
13122                                first_line_delimiter = Some((indent_prefix, start));
13123                                s.trim_start()
13124                            })
13125                            .unwrap_or(line_trimmed);
13126                        let line_trimmed = line_trimmed
13127                            .strip_suffix(end.as_ref())
13128                            .map(|s| {
13129                                last_line_delimiter = Some(end);
13130                                s.trim_end()
13131                            })
13132                            .unwrap_or(line_trimmed);
13133                        let line_trimmed = line_trimmed
13134                            .strip_prefix(prefix.as_ref())
13135                            .unwrap_or(line_trimmed);
13136                        Ok(line_trimmed)
13137                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13138                        line_trimmed.strip_prefix(prefix).with_context(|| {
13139                            format!("line did not start with prefix {prefix:?}: {line:?}")
13140                        })
13141                    } else {
13142                        line_trimmed
13143                            .strip_prefix(&line_prefix.trim_start())
13144                            .with_context(|| {
13145                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13146                            })
13147                    }
13148                })
13149                .collect::<Result<Vec<_>, _>>()
13150                .log_err()
13151            else {
13152                continue;
13153            };
13154
13155            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13156                buffer
13157                    .language_settings_at(Point::new(start_row, 0), cx)
13158                    .preferred_line_length as usize
13159            });
13160
13161            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13162                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13163            } else {
13164                line_prefix.clone()
13165            };
13166
13167            let wrapped_text = {
13168                let mut wrapped_text = wrap_with_prefix(
13169                    line_prefix,
13170                    subsequent_lines_prefix,
13171                    lines_without_prefixes.join("\n"),
13172                    wrap_column,
13173                    tab_size,
13174                    options.preserve_existing_whitespace,
13175                );
13176
13177                if let Some((indent, delimiter)) = first_line_delimiter {
13178                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13179                }
13180                if let Some(last_line) = last_line_delimiter {
13181                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13182                }
13183
13184                wrapped_text
13185            };
13186
13187            // TODO: should always use char-based diff while still supporting cursor behavior that
13188            // matches vim.
13189            let mut diff_options = DiffOptions::default();
13190            if options.override_language_settings {
13191                diff_options.max_word_diff_len = 0;
13192                diff_options.max_word_diff_line_count = 0;
13193            } else {
13194                diff_options.max_word_diff_len = usize::MAX;
13195                diff_options.max_word_diff_line_count = usize::MAX;
13196            }
13197
13198            for (old_range, new_text) in
13199                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13200            {
13201                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13202                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13203                edits.push((edit_start..edit_end, new_text));
13204            }
13205
13206            rewrapped_row_ranges.push(start_row..=end_row);
13207        }
13208
13209        self.buffer
13210            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13211    }
13212
13213    pub fn cut_common(
13214        &mut self,
13215        cut_no_selection_line: bool,
13216        window: &mut Window,
13217        cx: &mut Context<Self>,
13218    ) -> ClipboardItem {
13219        let mut text = String::new();
13220        let buffer = self.buffer.read(cx).snapshot(cx);
13221        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13222        let mut clipboard_selections = Vec::with_capacity(selections.len());
13223        {
13224            let max_point = buffer.max_point();
13225            let mut is_first = true;
13226            let mut prev_selection_was_entire_line = false;
13227            for selection in &mut selections {
13228                let is_entire_line =
13229                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13230                if is_entire_line {
13231                    selection.start = Point::new(selection.start.row, 0);
13232                    if !selection.is_empty() && selection.end.column == 0 {
13233                        selection.end = cmp::min(max_point, selection.end);
13234                    } else {
13235                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13236                    }
13237                    selection.goal = SelectionGoal::None;
13238                }
13239                if is_first {
13240                    is_first = false;
13241                } else if !prev_selection_was_entire_line {
13242                    text += "\n";
13243                }
13244                prev_selection_was_entire_line = is_entire_line;
13245                let mut len = 0;
13246                for chunk in buffer.text_for_range(selection.start..selection.end) {
13247                    text.push_str(chunk);
13248                    len += chunk.len();
13249                }
13250
13251                clipboard_selections.push(ClipboardSelection::for_buffer(
13252                    len,
13253                    is_entire_line,
13254                    selection.range(),
13255                    &buffer,
13256                    self.project.as_ref(),
13257                    cx,
13258                ));
13259            }
13260        }
13261
13262        self.transact(window, cx, |this, window, cx| {
13263            this.change_selections(Default::default(), window, cx, |s| {
13264                s.select(selections);
13265            });
13266            this.insert("", window, cx);
13267        });
13268        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13269    }
13270
13271    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13272        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13273        let item = self.cut_common(true, window, cx);
13274        cx.write_to_clipboard(item);
13275    }
13276
13277    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13278        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13279        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13280            s.move_with(|snapshot, sel| {
13281                if sel.is_empty() {
13282                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13283                }
13284                if sel.is_empty() {
13285                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13286                }
13287            });
13288        });
13289        let item = self.cut_common(false, window, cx);
13290        cx.set_global(KillRing(item))
13291    }
13292
13293    pub fn kill_ring_yank(
13294        &mut self,
13295        _: &KillRingYank,
13296        window: &mut Window,
13297        cx: &mut Context<Self>,
13298    ) {
13299        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13300        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13301            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13302                (kill_ring.text().to_string(), kill_ring.metadata_json())
13303            } else {
13304                return;
13305            }
13306        } else {
13307            return;
13308        };
13309        self.do_paste(&text, metadata, false, window, cx);
13310    }
13311
13312    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13313        self.do_copy(true, cx);
13314    }
13315
13316    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13317        self.do_copy(false, cx);
13318    }
13319
13320    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13321        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13322        let buffer = self.buffer.read(cx).read(cx);
13323        let mut text = String::new();
13324
13325        let mut clipboard_selections = Vec::with_capacity(selections.len());
13326        {
13327            let max_point = buffer.max_point();
13328            let mut is_first = true;
13329            let mut prev_selection_was_entire_line = false;
13330            for selection in &selections {
13331                let mut start = selection.start;
13332                let mut end = selection.end;
13333                let is_entire_line = selection.is_empty() || self.selections.line_mode();
13334                let mut add_trailing_newline = false;
13335                if is_entire_line {
13336                    start = Point::new(start.row, 0);
13337                    let next_line_start = Point::new(end.row + 1, 0);
13338                    if next_line_start <= max_point {
13339                        end = next_line_start;
13340                    } else {
13341                        // We're on the last line without a trailing newline.
13342                        // Copy to the end of the line and add a newline afterwards.
13343                        end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13344                        add_trailing_newline = true;
13345                    }
13346                }
13347
13348                let mut trimmed_selections = Vec::new();
13349                if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13350                    let row = MultiBufferRow(start.row);
13351                    let first_indent = buffer.indent_size_for_line(row);
13352                    if first_indent.len == 0 || start.column > first_indent.len {
13353                        trimmed_selections.push(start..end);
13354                    } else {
13355                        trimmed_selections.push(
13356                            Point::new(row.0, first_indent.len)
13357                                ..Point::new(row.0, buffer.line_len(row)),
13358                        );
13359                        for row in start.row + 1..=end.row {
13360                            let mut line_len = buffer.line_len(MultiBufferRow(row));
13361                            if row == end.row {
13362                                line_len = end.column;
13363                            }
13364                            if line_len == 0 {
13365                                trimmed_selections
13366                                    .push(Point::new(row, 0)..Point::new(row, line_len));
13367                                continue;
13368                            }
13369                            let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13370                            if row_indent_size.len >= first_indent.len {
13371                                trimmed_selections.push(
13372                                    Point::new(row, first_indent.len)..Point::new(row, line_len),
13373                                );
13374                            } else {
13375                                trimmed_selections.clear();
13376                                trimmed_selections.push(start..end);
13377                                break;
13378                            }
13379                        }
13380                    }
13381                } else {
13382                    trimmed_selections.push(start..end);
13383                }
13384
13385                let is_multiline_trim = trimmed_selections.len() > 1;
13386                for trimmed_range in trimmed_selections {
13387                    if is_first {
13388                        is_first = false;
13389                    } else if is_multiline_trim || !prev_selection_was_entire_line {
13390                        text += "\n";
13391                    }
13392                    prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13393                    let mut len = 0;
13394                    for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13395                        text.push_str(chunk);
13396                        len += chunk.len();
13397                    }
13398                    if add_trailing_newline {
13399                        text.push('\n');
13400                        len += 1;
13401                    }
13402                    clipboard_selections.push(ClipboardSelection::for_buffer(
13403                        len,
13404                        is_entire_line,
13405                        trimmed_range,
13406                        &buffer,
13407                        self.project.as_ref(),
13408                        cx,
13409                    ));
13410                }
13411            }
13412        }
13413
13414        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13415            text,
13416            clipboard_selections,
13417        ));
13418    }
13419
13420    pub fn do_paste(
13421        &mut self,
13422        text: &String,
13423        clipboard_selections: Option<Vec<ClipboardSelection>>,
13424        handle_entire_lines: bool,
13425        window: &mut Window,
13426        cx: &mut Context<Self>,
13427    ) {
13428        if self.read_only(cx) {
13429            return;
13430        }
13431
13432        let clipboard_text = Cow::Borrowed(text.as_str());
13433
13434        self.transact(window, cx, |this, window, cx| {
13435            let had_active_edit_prediction = this.has_active_edit_prediction();
13436            let display_map = this.display_snapshot(cx);
13437            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13438            let cursor_offset = this
13439                .selections
13440                .last::<MultiBufferOffset>(&display_map)
13441                .head();
13442
13443            if let Some(mut clipboard_selections) = clipboard_selections {
13444                let all_selections_were_entire_line =
13445                    clipboard_selections.iter().all(|s| s.is_entire_line);
13446                let first_selection_indent_column =
13447                    clipboard_selections.first().map(|s| s.first_line_indent);
13448                if clipboard_selections.len() != old_selections.len() {
13449                    clipboard_selections.drain(..);
13450                }
13451                let mut auto_indent_on_paste = true;
13452
13453                this.buffer.update(cx, |buffer, cx| {
13454                    let snapshot = buffer.read(cx);
13455                    auto_indent_on_paste = snapshot
13456                        .language_settings_at(cursor_offset, cx)
13457                        .auto_indent_on_paste;
13458
13459                    let mut start_offset = 0;
13460                    let mut edits = Vec::new();
13461                    let mut original_indent_columns = Vec::new();
13462                    for (ix, selection) in old_selections.iter().enumerate() {
13463                        let to_insert;
13464                        let entire_line;
13465                        let original_indent_column;
13466                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13467                            let end_offset = start_offset + clipboard_selection.len;
13468                            to_insert = &clipboard_text[start_offset..end_offset];
13469                            entire_line = clipboard_selection.is_entire_line;
13470                            start_offset = if entire_line {
13471                                end_offset
13472                            } else {
13473                                end_offset + 1
13474                            };
13475                            original_indent_column = Some(clipboard_selection.first_line_indent);
13476                        } else {
13477                            to_insert = &*clipboard_text;
13478                            entire_line = all_selections_were_entire_line;
13479                            original_indent_column = first_selection_indent_column
13480                        }
13481
13482                        let (range, to_insert) =
13483                            if selection.is_empty() && handle_entire_lines && entire_line {
13484                                // If the corresponding selection was empty when this slice of the
13485                                // clipboard text was written, then the entire line containing the
13486                                // selection was copied. If this selection is also currently empty,
13487                                // then paste the line before the current line of the buffer.
13488                                let column = selection.start.to_point(&snapshot).column as usize;
13489                                let line_start = selection.start - column;
13490                                (line_start..line_start, Cow::Borrowed(to_insert))
13491                            } else {
13492                                let language = snapshot.language_at(selection.head());
13493                                let range = selection.range();
13494                                if let Some(language) = language
13495                                    && language.name() == "Markdown".into()
13496                                {
13497                                    edit_for_markdown_paste(
13498                                        &snapshot,
13499                                        range,
13500                                        to_insert,
13501                                        url::Url::parse(to_insert).ok(),
13502                                    )
13503                                } else {
13504                                    (range, Cow::Borrowed(to_insert))
13505                                }
13506                            };
13507
13508                        edits.push((range, to_insert));
13509                        original_indent_columns.push(original_indent_column);
13510                    }
13511                    drop(snapshot);
13512
13513                    buffer.edit(
13514                        edits,
13515                        if auto_indent_on_paste {
13516                            Some(AutoindentMode::Block {
13517                                original_indent_columns,
13518                            })
13519                        } else {
13520                            None
13521                        },
13522                        cx,
13523                    );
13524                });
13525
13526                let selections = this
13527                    .selections
13528                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13529                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13530            } else {
13531                let url = url::Url::parse(&clipboard_text).ok();
13532
13533                let auto_indent_mode = if !clipboard_text.is_empty() {
13534                    Some(AutoindentMode::Block {
13535                        original_indent_columns: Vec::new(),
13536                    })
13537                } else {
13538                    None
13539                };
13540
13541                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13542                    let snapshot = buffer.snapshot(cx);
13543
13544                    let anchors = old_selections
13545                        .iter()
13546                        .map(|s| {
13547                            let anchor = snapshot.anchor_after(s.head());
13548                            s.map(|_| anchor)
13549                        })
13550                        .collect::<Vec<_>>();
13551
13552                    let mut edits = Vec::new();
13553
13554                    for selection in old_selections.iter() {
13555                        let language = snapshot.language_at(selection.head());
13556                        let range = selection.range();
13557
13558                        let (edit_range, edit_text) = if let Some(language) = language
13559                            && language.name() == "Markdown".into()
13560                        {
13561                            edit_for_markdown_paste(&snapshot, range, &clipboard_text, url.clone())
13562                        } else {
13563                            (range, clipboard_text.clone())
13564                        };
13565
13566                        edits.push((edit_range, edit_text));
13567                    }
13568
13569                    drop(snapshot);
13570                    buffer.edit(edits, auto_indent_mode, cx);
13571
13572                    anchors
13573                });
13574
13575                this.change_selections(Default::default(), window, cx, |s| {
13576                    s.select_anchors(selection_anchors);
13577                });
13578            }
13579
13580            //   🤔                 |    ..     | show_in_menu |
13581            // | ..                  |   true        true
13582            // | had_edit_prediction |   false       true
13583
13584            let trigger_in_words =
13585                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13586
13587            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13588        });
13589    }
13590
13591    pub fn diff_clipboard_with_selection(
13592        &mut self,
13593        _: &DiffClipboardWithSelection,
13594        window: &mut Window,
13595        cx: &mut Context<Self>,
13596    ) {
13597        let selections = self
13598            .selections
13599            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13600
13601        if selections.is_empty() {
13602            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13603            return;
13604        };
13605
13606        let clipboard_text = match cx.read_from_clipboard() {
13607            Some(item) => match item.entries().first() {
13608                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13609                _ => None,
13610            },
13611            None => None,
13612        };
13613
13614        let Some(clipboard_text) = clipboard_text else {
13615            log::warn!("Clipboard doesn't contain text.");
13616            return;
13617        };
13618
13619        window.dispatch_action(
13620            Box::new(DiffClipboardWithSelectionData {
13621                clipboard_text,
13622                editor: cx.entity(),
13623            }),
13624            cx,
13625        );
13626    }
13627
13628    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13629        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13630        if let Some(item) = cx.read_from_clipboard() {
13631            let entries = item.entries();
13632
13633            match entries.first() {
13634                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13635                // of all the pasted entries.
13636                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13637                    .do_paste(
13638                        clipboard_string.text(),
13639                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13640                        true,
13641                        window,
13642                        cx,
13643                    ),
13644                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
13645            }
13646        }
13647    }
13648
13649    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
13650        if self.read_only(cx) {
13651            return;
13652        }
13653
13654        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13655
13656        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
13657            if let Some((selections, _)) =
13658                self.selection_history.transaction(transaction_id).cloned()
13659            {
13660                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13661                    s.select_anchors(selections.to_vec());
13662                });
13663            } else {
13664                log::error!(
13665                    "No entry in selection_history found for undo. \
13666                     This may correspond to a bug where undo does not update the selection. \
13667                     If this is occurring, please add details to \
13668                     https://github.com/zed-industries/zed/issues/22692"
13669                );
13670            }
13671            self.request_autoscroll(Autoscroll::fit(), cx);
13672            self.unmark_text(window, cx);
13673            self.refresh_edit_prediction(true, false, window, cx);
13674            cx.emit(EditorEvent::Edited { transaction_id });
13675            cx.emit(EditorEvent::TransactionUndone { transaction_id });
13676        }
13677    }
13678
13679    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
13680        if self.read_only(cx) {
13681            return;
13682        }
13683
13684        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13685
13686        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
13687            if let Some((_, Some(selections))) =
13688                self.selection_history.transaction(transaction_id).cloned()
13689            {
13690                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13691                    s.select_anchors(selections.to_vec());
13692                });
13693            } else {
13694                log::error!(
13695                    "No entry in selection_history found for redo. \
13696                     This may correspond to a bug where undo does not update the selection. \
13697                     If this is occurring, please add details to \
13698                     https://github.com/zed-industries/zed/issues/22692"
13699                );
13700            }
13701            self.request_autoscroll(Autoscroll::fit(), cx);
13702            self.unmark_text(window, cx);
13703            self.refresh_edit_prediction(true, false, window, cx);
13704            cx.emit(EditorEvent::Edited { transaction_id });
13705        }
13706    }
13707
13708    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
13709        self.buffer
13710            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
13711    }
13712
13713    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
13714        self.buffer
13715            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
13716    }
13717
13718    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
13719        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13720        self.change_selections(Default::default(), window, cx, |s| {
13721            s.move_with(|map, selection| {
13722                let cursor = if selection.is_empty() {
13723                    movement::left(map, selection.start)
13724                } else {
13725                    selection.start
13726                };
13727                selection.collapse_to(cursor, SelectionGoal::None);
13728            });
13729        })
13730    }
13731
13732    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
13733        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13734        self.change_selections(Default::default(), window, cx, |s| {
13735            s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
13736        })
13737    }
13738
13739    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
13740        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13741        self.change_selections(Default::default(), window, cx, |s| {
13742            s.move_with(|map, selection| {
13743                let cursor = if selection.is_empty() {
13744                    movement::right(map, selection.end)
13745                } else {
13746                    selection.end
13747                };
13748                selection.collapse_to(cursor, SelectionGoal::None)
13749            });
13750        })
13751    }
13752
13753    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
13754        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13755        self.change_selections(Default::default(), window, cx, |s| {
13756            s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
13757        });
13758    }
13759
13760    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
13761        if self.take_rename(true, window, cx).is_some() {
13762            return;
13763        }
13764
13765        if self.mode.is_single_line() {
13766            cx.propagate();
13767            return;
13768        }
13769
13770        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13771
13772        let text_layout_details = &self.text_layout_details(window);
13773        let selection_count = self.selections.count();
13774        let first_selection = self.selections.first_anchor();
13775
13776        self.change_selections(Default::default(), window, cx, |s| {
13777            s.move_with(|map, selection| {
13778                if !selection.is_empty() {
13779                    selection.goal = SelectionGoal::None;
13780                }
13781                let (cursor, goal) = movement::up(
13782                    map,
13783                    selection.start,
13784                    selection.goal,
13785                    false,
13786                    text_layout_details,
13787                );
13788                selection.collapse_to(cursor, goal);
13789            });
13790        });
13791
13792        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
13793        {
13794            cx.propagate();
13795        }
13796    }
13797
13798    pub fn move_up_by_lines(
13799        &mut self,
13800        action: &MoveUpByLines,
13801        window: &mut Window,
13802        cx: &mut Context<Self>,
13803    ) {
13804        if self.take_rename(true, window, cx).is_some() {
13805            return;
13806        }
13807
13808        if self.mode.is_single_line() {
13809            cx.propagate();
13810            return;
13811        }
13812
13813        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13814
13815        let text_layout_details = &self.text_layout_details(window);
13816
13817        self.change_selections(Default::default(), window, cx, |s| {
13818            s.move_with(|map, selection| {
13819                if !selection.is_empty() {
13820                    selection.goal = SelectionGoal::None;
13821                }
13822                let (cursor, goal) = movement::up_by_rows(
13823                    map,
13824                    selection.start,
13825                    action.lines,
13826                    selection.goal,
13827                    false,
13828                    text_layout_details,
13829                );
13830                selection.collapse_to(cursor, goal);
13831            });
13832        })
13833    }
13834
13835    pub fn move_down_by_lines(
13836        &mut self,
13837        action: &MoveDownByLines,
13838        window: &mut Window,
13839        cx: &mut Context<Self>,
13840    ) {
13841        if self.take_rename(true, window, cx).is_some() {
13842            return;
13843        }
13844
13845        if self.mode.is_single_line() {
13846            cx.propagate();
13847            return;
13848        }
13849
13850        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13851
13852        let text_layout_details = &self.text_layout_details(window);
13853
13854        self.change_selections(Default::default(), window, cx, |s| {
13855            s.move_with(|map, selection| {
13856                if !selection.is_empty() {
13857                    selection.goal = SelectionGoal::None;
13858                }
13859                let (cursor, goal) = movement::down_by_rows(
13860                    map,
13861                    selection.start,
13862                    action.lines,
13863                    selection.goal,
13864                    false,
13865                    text_layout_details,
13866                );
13867                selection.collapse_to(cursor, goal);
13868            });
13869        })
13870    }
13871
13872    pub fn select_down_by_lines(
13873        &mut self,
13874        action: &SelectDownByLines,
13875        window: &mut Window,
13876        cx: &mut Context<Self>,
13877    ) {
13878        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13879        let text_layout_details = &self.text_layout_details(window);
13880        self.change_selections(Default::default(), window, cx, |s| {
13881            s.move_heads_with(|map, head, goal| {
13882                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
13883            })
13884        })
13885    }
13886
13887    pub fn select_up_by_lines(
13888        &mut self,
13889        action: &SelectUpByLines,
13890        window: &mut Window,
13891        cx: &mut Context<Self>,
13892    ) {
13893        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13894        let text_layout_details = &self.text_layout_details(window);
13895        self.change_selections(Default::default(), window, cx, |s| {
13896            s.move_heads_with(|map, head, goal| {
13897                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
13898            })
13899        })
13900    }
13901
13902    pub fn select_page_up(
13903        &mut self,
13904        _: &SelectPageUp,
13905        window: &mut Window,
13906        cx: &mut Context<Self>,
13907    ) {
13908        let Some(row_count) = self.visible_row_count() else {
13909            return;
13910        };
13911
13912        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13913
13914        let text_layout_details = &self.text_layout_details(window);
13915
13916        self.change_selections(Default::default(), window, cx, |s| {
13917            s.move_heads_with(|map, head, goal| {
13918                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
13919            })
13920        })
13921    }
13922
13923    pub fn move_page_up(
13924        &mut self,
13925        action: &MovePageUp,
13926        window: &mut Window,
13927        cx: &mut Context<Self>,
13928    ) {
13929        if self.take_rename(true, window, cx).is_some() {
13930            return;
13931        }
13932
13933        if self
13934            .context_menu
13935            .borrow_mut()
13936            .as_mut()
13937            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
13938            .unwrap_or(false)
13939        {
13940            return;
13941        }
13942
13943        if matches!(self.mode, EditorMode::SingleLine) {
13944            cx.propagate();
13945            return;
13946        }
13947
13948        let Some(row_count) = self.visible_row_count() else {
13949            return;
13950        };
13951
13952        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13953
13954        let effects = if action.center_cursor {
13955            SelectionEffects::scroll(Autoscroll::center())
13956        } else {
13957            SelectionEffects::default()
13958        };
13959
13960        let text_layout_details = &self.text_layout_details(window);
13961
13962        self.change_selections(effects, window, cx, |s| {
13963            s.move_with(|map, selection| {
13964                if !selection.is_empty() {
13965                    selection.goal = SelectionGoal::None;
13966                }
13967                let (cursor, goal) = movement::up_by_rows(
13968                    map,
13969                    selection.end,
13970                    row_count,
13971                    selection.goal,
13972                    false,
13973                    text_layout_details,
13974                );
13975                selection.collapse_to(cursor, goal);
13976            });
13977        });
13978    }
13979
13980    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
13981        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13982        let text_layout_details = &self.text_layout_details(window);
13983        self.change_selections(Default::default(), window, cx, |s| {
13984            s.move_heads_with(|map, head, goal| {
13985                movement::up(map, head, goal, false, text_layout_details)
13986            })
13987        })
13988    }
13989
13990    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
13991        self.take_rename(true, window, cx);
13992
13993        if self.mode.is_single_line() {
13994            cx.propagate();
13995            return;
13996        }
13997
13998        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13999
14000        let text_layout_details = &self.text_layout_details(window);
14001        let selection_count = self.selections.count();
14002        let first_selection = self.selections.first_anchor();
14003
14004        self.change_selections(Default::default(), window, cx, |s| {
14005            s.move_with(|map, selection| {
14006                if !selection.is_empty() {
14007                    selection.goal = SelectionGoal::None;
14008                }
14009                let (cursor, goal) = movement::down(
14010                    map,
14011                    selection.end,
14012                    selection.goal,
14013                    false,
14014                    text_layout_details,
14015                );
14016                selection.collapse_to(cursor, goal);
14017            });
14018        });
14019
14020        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14021        {
14022            cx.propagate();
14023        }
14024    }
14025
14026    pub fn select_page_down(
14027        &mut self,
14028        _: &SelectPageDown,
14029        window: &mut Window,
14030        cx: &mut Context<Self>,
14031    ) {
14032        let Some(row_count) = self.visible_row_count() else {
14033            return;
14034        };
14035
14036        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14037
14038        let text_layout_details = &self.text_layout_details(window);
14039
14040        self.change_selections(Default::default(), window, cx, |s| {
14041            s.move_heads_with(|map, head, goal| {
14042                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14043            })
14044        })
14045    }
14046
14047    pub fn move_page_down(
14048        &mut self,
14049        action: &MovePageDown,
14050        window: &mut Window,
14051        cx: &mut Context<Self>,
14052    ) {
14053        if self.take_rename(true, window, cx).is_some() {
14054            return;
14055        }
14056
14057        if self
14058            .context_menu
14059            .borrow_mut()
14060            .as_mut()
14061            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14062            .unwrap_or(false)
14063        {
14064            return;
14065        }
14066
14067        if matches!(self.mode, EditorMode::SingleLine) {
14068            cx.propagate();
14069            return;
14070        }
14071
14072        let Some(row_count) = self.visible_row_count() else {
14073            return;
14074        };
14075
14076        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14077
14078        let effects = if action.center_cursor {
14079            SelectionEffects::scroll(Autoscroll::center())
14080        } else {
14081            SelectionEffects::default()
14082        };
14083
14084        let text_layout_details = &self.text_layout_details(window);
14085        self.change_selections(effects, window, cx, |s| {
14086            s.move_with(|map, selection| {
14087                if !selection.is_empty() {
14088                    selection.goal = SelectionGoal::None;
14089                }
14090                let (cursor, goal) = movement::down_by_rows(
14091                    map,
14092                    selection.end,
14093                    row_count,
14094                    selection.goal,
14095                    false,
14096                    text_layout_details,
14097                );
14098                selection.collapse_to(cursor, goal);
14099            });
14100        });
14101    }
14102
14103    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14104        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14105        let text_layout_details = &self.text_layout_details(window);
14106        self.change_selections(Default::default(), window, cx, |s| {
14107            s.move_heads_with(|map, head, goal| {
14108                movement::down(map, head, goal, false, text_layout_details)
14109            })
14110        });
14111    }
14112
14113    pub fn context_menu_first(
14114        &mut self,
14115        _: &ContextMenuFirst,
14116        window: &mut Window,
14117        cx: &mut Context<Self>,
14118    ) {
14119        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14120            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14121        }
14122    }
14123
14124    pub fn context_menu_prev(
14125        &mut self,
14126        _: &ContextMenuPrevious,
14127        window: &mut Window,
14128        cx: &mut Context<Self>,
14129    ) {
14130        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14131            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14132        }
14133    }
14134
14135    pub fn context_menu_next(
14136        &mut self,
14137        _: &ContextMenuNext,
14138        window: &mut Window,
14139        cx: &mut Context<Self>,
14140    ) {
14141        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14142            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14143        }
14144    }
14145
14146    pub fn context_menu_last(
14147        &mut self,
14148        _: &ContextMenuLast,
14149        window: &mut Window,
14150        cx: &mut Context<Self>,
14151    ) {
14152        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14153            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14154        }
14155    }
14156
14157    pub fn signature_help_prev(
14158        &mut self,
14159        _: &SignatureHelpPrevious,
14160        _: &mut Window,
14161        cx: &mut Context<Self>,
14162    ) {
14163        if let Some(popover) = self.signature_help_state.popover_mut() {
14164            if popover.current_signature == 0 {
14165                popover.current_signature = popover.signatures.len() - 1;
14166            } else {
14167                popover.current_signature -= 1;
14168            }
14169            cx.notify();
14170        }
14171    }
14172
14173    pub fn signature_help_next(
14174        &mut self,
14175        _: &SignatureHelpNext,
14176        _: &mut Window,
14177        cx: &mut Context<Self>,
14178    ) {
14179        if let Some(popover) = self.signature_help_state.popover_mut() {
14180            if popover.current_signature + 1 == popover.signatures.len() {
14181                popover.current_signature = 0;
14182            } else {
14183                popover.current_signature += 1;
14184            }
14185            cx.notify();
14186        }
14187    }
14188
14189    pub fn move_to_previous_word_start(
14190        &mut self,
14191        _: &MoveToPreviousWordStart,
14192        window: &mut Window,
14193        cx: &mut Context<Self>,
14194    ) {
14195        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14196        self.change_selections(Default::default(), window, cx, |s| {
14197            s.move_cursors_with(|map, head, _| {
14198                (
14199                    movement::previous_word_start(map, head),
14200                    SelectionGoal::None,
14201                )
14202            });
14203        })
14204    }
14205
14206    pub fn move_to_previous_subword_start(
14207        &mut self,
14208        _: &MoveToPreviousSubwordStart,
14209        window: &mut Window,
14210        cx: &mut Context<Self>,
14211    ) {
14212        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14213        self.change_selections(Default::default(), window, cx, |s| {
14214            s.move_cursors_with(|map, head, _| {
14215                (
14216                    movement::previous_subword_start(map, head),
14217                    SelectionGoal::None,
14218                )
14219            });
14220        })
14221    }
14222
14223    pub fn select_to_previous_word_start(
14224        &mut self,
14225        _: &SelectToPreviousWordStart,
14226        window: &mut Window,
14227        cx: &mut Context<Self>,
14228    ) {
14229        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14230        self.change_selections(Default::default(), window, cx, |s| {
14231            s.move_heads_with(|map, head, _| {
14232                (
14233                    movement::previous_word_start(map, head),
14234                    SelectionGoal::None,
14235                )
14236            });
14237        })
14238    }
14239
14240    pub fn select_to_previous_subword_start(
14241        &mut self,
14242        _: &SelectToPreviousSubwordStart,
14243        window: &mut Window,
14244        cx: &mut Context<Self>,
14245    ) {
14246        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14247        self.change_selections(Default::default(), window, cx, |s| {
14248            s.move_heads_with(|map, head, _| {
14249                (
14250                    movement::previous_subword_start(map, head),
14251                    SelectionGoal::None,
14252                )
14253            });
14254        })
14255    }
14256
14257    pub fn delete_to_previous_word_start(
14258        &mut self,
14259        action: &DeleteToPreviousWordStart,
14260        window: &mut Window,
14261        cx: &mut Context<Self>,
14262    ) {
14263        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14264        self.transact(window, cx, |this, window, cx| {
14265            this.select_autoclose_pair(window, cx);
14266            this.change_selections(Default::default(), window, cx, |s| {
14267                s.move_with(|map, selection| {
14268                    if selection.is_empty() {
14269                        let mut cursor = if action.ignore_newlines {
14270                            movement::previous_word_start(map, selection.head())
14271                        } else {
14272                            movement::previous_word_start_or_newline(map, selection.head())
14273                        };
14274                        cursor = movement::adjust_greedy_deletion(
14275                            map,
14276                            selection.head(),
14277                            cursor,
14278                            action.ignore_brackets,
14279                        );
14280                        selection.set_head(cursor, SelectionGoal::None);
14281                    }
14282                });
14283            });
14284            this.insert("", window, cx);
14285        });
14286    }
14287
14288    pub fn delete_to_previous_subword_start(
14289        &mut self,
14290        action: &DeleteToPreviousSubwordStart,
14291        window: &mut Window,
14292        cx: &mut Context<Self>,
14293    ) {
14294        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14295        self.transact(window, cx, |this, window, cx| {
14296            this.select_autoclose_pair(window, cx);
14297            this.change_selections(Default::default(), window, cx, |s| {
14298                s.move_with(|map, selection| {
14299                    if selection.is_empty() {
14300                        let mut cursor = if action.ignore_newlines {
14301                            movement::previous_subword_start(map, selection.head())
14302                        } else {
14303                            movement::previous_subword_start_or_newline(map, selection.head())
14304                        };
14305                        cursor = movement::adjust_greedy_deletion(
14306                            map,
14307                            selection.head(),
14308                            cursor,
14309                            action.ignore_brackets,
14310                        );
14311                        selection.set_head(cursor, SelectionGoal::None);
14312                    }
14313                });
14314            });
14315            this.insert("", window, cx);
14316        });
14317    }
14318
14319    pub fn move_to_next_word_end(
14320        &mut self,
14321        _: &MoveToNextWordEnd,
14322        window: &mut Window,
14323        cx: &mut Context<Self>,
14324    ) {
14325        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14326        self.change_selections(Default::default(), window, cx, |s| {
14327            s.move_cursors_with(|map, head, _| {
14328                (movement::next_word_end(map, head), SelectionGoal::None)
14329            });
14330        })
14331    }
14332
14333    pub fn move_to_next_subword_end(
14334        &mut self,
14335        _: &MoveToNextSubwordEnd,
14336        window: &mut Window,
14337        cx: &mut Context<Self>,
14338    ) {
14339        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14340        self.change_selections(Default::default(), window, cx, |s| {
14341            s.move_cursors_with(|map, head, _| {
14342                (movement::next_subword_end(map, head), SelectionGoal::None)
14343            });
14344        })
14345    }
14346
14347    pub fn select_to_next_word_end(
14348        &mut self,
14349        _: &SelectToNextWordEnd,
14350        window: &mut Window,
14351        cx: &mut Context<Self>,
14352    ) {
14353        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14354        self.change_selections(Default::default(), window, cx, |s| {
14355            s.move_heads_with(|map, head, _| {
14356                (movement::next_word_end(map, head), SelectionGoal::None)
14357            });
14358        })
14359    }
14360
14361    pub fn select_to_next_subword_end(
14362        &mut self,
14363        _: &SelectToNextSubwordEnd,
14364        window: &mut Window,
14365        cx: &mut Context<Self>,
14366    ) {
14367        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14368        self.change_selections(Default::default(), window, cx, |s| {
14369            s.move_heads_with(|map, head, _| {
14370                (movement::next_subword_end(map, head), SelectionGoal::None)
14371            });
14372        })
14373    }
14374
14375    pub fn delete_to_next_word_end(
14376        &mut self,
14377        action: &DeleteToNextWordEnd,
14378        window: &mut Window,
14379        cx: &mut Context<Self>,
14380    ) {
14381        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14382        self.transact(window, cx, |this, window, cx| {
14383            this.change_selections(Default::default(), window, cx, |s| {
14384                s.move_with(|map, selection| {
14385                    if selection.is_empty() {
14386                        let mut cursor = if action.ignore_newlines {
14387                            movement::next_word_end(map, selection.head())
14388                        } else {
14389                            movement::next_word_end_or_newline(map, selection.head())
14390                        };
14391                        cursor = movement::adjust_greedy_deletion(
14392                            map,
14393                            selection.head(),
14394                            cursor,
14395                            action.ignore_brackets,
14396                        );
14397                        selection.set_head(cursor, SelectionGoal::None);
14398                    }
14399                });
14400            });
14401            this.insert("", window, cx);
14402        });
14403    }
14404
14405    pub fn delete_to_next_subword_end(
14406        &mut self,
14407        action: &DeleteToNextSubwordEnd,
14408        window: &mut Window,
14409        cx: &mut Context<Self>,
14410    ) {
14411        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14412        self.transact(window, cx, |this, 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::next_subword_end(map, selection.head())
14418                        } else {
14419                            movement::next_subword_end_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_beginning_of_line(
14436        &mut self,
14437        action: &MoveToBeginningOfLine,
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                (
14445                    movement::indented_line_beginning(
14446                        map,
14447                        head,
14448                        action.stop_at_soft_wraps,
14449                        action.stop_at_indent,
14450                    ),
14451                    SelectionGoal::None,
14452                )
14453            });
14454        })
14455    }
14456
14457    pub fn select_to_beginning_of_line(
14458        &mut self,
14459        action: &SelectToBeginningOfLine,
14460        window: &mut Window,
14461        cx: &mut Context<Self>,
14462    ) {
14463        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14464        self.change_selections(Default::default(), window, cx, |s| {
14465            s.move_heads_with(|map, head, _| {
14466                (
14467                    movement::indented_line_beginning(
14468                        map,
14469                        head,
14470                        action.stop_at_soft_wraps,
14471                        action.stop_at_indent,
14472                    ),
14473                    SelectionGoal::None,
14474                )
14475            });
14476        });
14477    }
14478
14479    pub fn delete_to_beginning_of_line(
14480        &mut self,
14481        action: &DeleteToBeginningOfLine,
14482        window: &mut Window,
14483        cx: &mut Context<Self>,
14484    ) {
14485        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14486        self.transact(window, cx, |this, window, cx| {
14487            this.change_selections(Default::default(), window, cx, |s| {
14488                s.move_with(|_, selection| {
14489                    selection.reversed = true;
14490                });
14491            });
14492
14493            this.select_to_beginning_of_line(
14494                &SelectToBeginningOfLine {
14495                    stop_at_soft_wraps: false,
14496                    stop_at_indent: action.stop_at_indent,
14497                },
14498                window,
14499                cx,
14500            );
14501            this.backspace(&Backspace, window, cx);
14502        });
14503    }
14504
14505    pub fn move_to_end_of_line(
14506        &mut self,
14507        action: &MoveToEndOfLine,
14508        window: &mut Window,
14509        cx: &mut Context<Self>,
14510    ) {
14511        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14512        self.change_selections(Default::default(), window, cx, |s| {
14513            s.move_cursors_with(|map, head, _| {
14514                (
14515                    movement::line_end(map, head, action.stop_at_soft_wraps),
14516                    SelectionGoal::None,
14517                )
14518            });
14519        })
14520    }
14521
14522    pub fn select_to_end_of_line(
14523        &mut self,
14524        action: &SelectToEndOfLine,
14525        window: &mut Window,
14526        cx: &mut Context<Self>,
14527    ) {
14528        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14529        self.change_selections(Default::default(), window, cx, |s| {
14530            s.move_heads_with(|map, head, _| {
14531                (
14532                    movement::line_end(map, head, action.stop_at_soft_wraps),
14533                    SelectionGoal::None,
14534                )
14535            });
14536        })
14537    }
14538
14539    pub fn delete_to_end_of_line(
14540        &mut self,
14541        _: &DeleteToEndOfLine,
14542        window: &mut Window,
14543        cx: &mut Context<Self>,
14544    ) {
14545        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14546        self.transact(window, cx, |this, window, cx| {
14547            this.select_to_end_of_line(
14548                &SelectToEndOfLine {
14549                    stop_at_soft_wraps: false,
14550                },
14551                window,
14552                cx,
14553            );
14554            this.delete(&Delete, window, cx);
14555        });
14556    }
14557
14558    pub fn cut_to_end_of_line(
14559        &mut self,
14560        action: &CutToEndOfLine,
14561        window: &mut Window,
14562        cx: &mut Context<Self>,
14563    ) {
14564        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14565        self.transact(window, cx, |this, window, cx| {
14566            this.select_to_end_of_line(
14567                &SelectToEndOfLine {
14568                    stop_at_soft_wraps: false,
14569                },
14570                window,
14571                cx,
14572            );
14573            if !action.stop_at_newlines {
14574                this.change_selections(Default::default(), window, cx, |s| {
14575                    s.move_with(|_, sel| {
14576                        if sel.is_empty() {
14577                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14578                        }
14579                    });
14580                });
14581            }
14582            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14583            let item = this.cut_common(false, window, cx);
14584            cx.write_to_clipboard(item);
14585        });
14586    }
14587
14588    pub fn move_to_start_of_paragraph(
14589        &mut self,
14590        _: &MoveToStartOfParagraph,
14591        window: &mut Window,
14592        cx: &mut Context<Self>,
14593    ) {
14594        if matches!(self.mode, EditorMode::SingleLine) {
14595            cx.propagate();
14596            return;
14597        }
14598        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14599        self.change_selections(Default::default(), window, cx, |s| {
14600            s.move_with(|map, selection| {
14601                selection.collapse_to(
14602                    movement::start_of_paragraph(map, selection.head(), 1),
14603                    SelectionGoal::None,
14604                )
14605            });
14606        })
14607    }
14608
14609    pub fn move_to_end_of_paragraph(
14610        &mut self,
14611        _: &MoveToEndOfParagraph,
14612        window: &mut Window,
14613        cx: &mut Context<Self>,
14614    ) {
14615        if matches!(self.mode, EditorMode::SingleLine) {
14616            cx.propagate();
14617            return;
14618        }
14619        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14620        self.change_selections(Default::default(), window, cx, |s| {
14621            s.move_with(|map, selection| {
14622                selection.collapse_to(
14623                    movement::end_of_paragraph(map, selection.head(), 1),
14624                    SelectionGoal::None,
14625                )
14626            });
14627        })
14628    }
14629
14630    pub fn select_to_start_of_paragraph(
14631        &mut self,
14632        _: &SelectToStartOfParagraph,
14633        window: &mut Window,
14634        cx: &mut Context<Self>,
14635    ) {
14636        if matches!(self.mode, EditorMode::SingleLine) {
14637            cx.propagate();
14638            return;
14639        }
14640        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14641        self.change_selections(Default::default(), window, cx, |s| {
14642            s.move_heads_with(|map, head, _| {
14643                (
14644                    movement::start_of_paragraph(map, head, 1),
14645                    SelectionGoal::None,
14646                )
14647            });
14648        })
14649    }
14650
14651    pub fn select_to_end_of_paragraph(
14652        &mut self,
14653        _: &SelectToEndOfParagraph,
14654        window: &mut Window,
14655        cx: &mut Context<Self>,
14656    ) {
14657        if matches!(self.mode, EditorMode::SingleLine) {
14658            cx.propagate();
14659            return;
14660        }
14661        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14662        self.change_selections(Default::default(), window, cx, |s| {
14663            s.move_heads_with(|map, head, _| {
14664                (
14665                    movement::end_of_paragraph(map, head, 1),
14666                    SelectionGoal::None,
14667                )
14668            });
14669        })
14670    }
14671
14672    pub fn move_to_start_of_excerpt(
14673        &mut self,
14674        _: &MoveToStartOfExcerpt,
14675        window: &mut Window,
14676        cx: &mut Context<Self>,
14677    ) {
14678        if matches!(self.mode, EditorMode::SingleLine) {
14679            cx.propagate();
14680            return;
14681        }
14682        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14683        self.change_selections(Default::default(), window, cx, |s| {
14684            s.move_with(|map, selection| {
14685                selection.collapse_to(
14686                    movement::start_of_excerpt(
14687                        map,
14688                        selection.head(),
14689                        workspace::searchable::Direction::Prev,
14690                    ),
14691                    SelectionGoal::None,
14692                )
14693            });
14694        })
14695    }
14696
14697    pub fn move_to_start_of_next_excerpt(
14698        &mut self,
14699        _: &MoveToStartOfNextExcerpt,
14700        window: &mut Window,
14701        cx: &mut Context<Self>,
14702    ) {
14703        if matches!(self.mode, EditorMode::SingleLine) {
14704            cx.propagate();
14705            return;
14706        }
14707
14708        self.change_selections(Default::default(), window, cx, |s| {
14709            s.move_with(|map, selection| {
14710                selection.collapse_to(
14711                    movement::start_of_excerpt(
14712                        map,
14713                        selection.head(),
14714                        workspace::searchable::Direction::Next,
14715                    ),
14716                    SelectionGoal::None,
14717                )
14718            });
14719        })
14720    }
14721
14722    pub fn move_to_end_of_excerpt(
14723        &mut self,
14724        _: &MoveToEndOfExcerpt,
14725        window: &mut Window,
14726        cx: &mut Context<Self>,
14727    ) {
14728        if matches!(self.mode, EditorMode::SingleLine) {
14729            cx.propagate();
14730            return;
14731        }
14732        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14733        self.change_selections(Default::default(), window, cx, |s| {
14734            s.move_with(|map, selection| {
14735                selection.collapse_to(
14736                    movement::end_of_excerpt(
14737                        map,
14738                        selection.head(),
14739                        workspace::searchable::Direction::Next,
14740                    ),
14741                    SelectionGoal::None,
14742                )
14743            });
14744        })
14745    }
14746
14747    pub fn move_to_end_of_previous_excerpt(
14748        &mut self,
14749        _: &MoveToEndOfPreviousExcerpt,
14750        window: &mut Window,
14751        cx: &mut Context<Self>,
14752    ) {
14753        if matches!(self.mode, EditorMode::SingleLine) {
14754            cx.propagate();
14755            return;
14756        }
14757        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14758        self.change_selections(Default::default(), window, cx, |s| {
14759            s.move_with(|map, selection| {
14760                selection.collapse_to(
14761                    movement::end_of_excerpt(
14762                        map,
14763                        selection.head(),
14764                        workspace::searchable::Direction::Prev,
14765                    ),
14766                    SelectionGoal::None,
14767                )
14768            });
14769        })
14770    }
14771
14772    pub fn select_to_start_of_excerpt(
14773        &mut self,
14774        _: &SelectToStartOfExcerpt,
14775        window: &mut Window,
14776        cx: &mut Context<Self>,
14777    ) {
14778        if matches!(self.mode, EditorMode::SingleLine) {
14779            cx.propagate();
14780            return;
14781        }
14782        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14783        self.change_selections(Default::default(), window, cx, |s| {
14784            s.move_heads_with(|map, head, _| {
14785                (
14786                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
14787                    SelectionGoal::None,
14788                )
14789            });
14790        })
14791    }
14792
14793    pub fn select_to_start_of_next_excerpt(
14794        &mut self,
14795        _: &SelectToStartOfNextExcerpt,
14796        window: &mut Window,
14797        cx: &mut Context<Self>,
14798    ) {
14799        if matches!(self.mode, EditorMode::SingleLine) {
14800            cx.propagate();
14801            return;
14802        }
14803        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14804        self.change_selections(Default::default(), window, cx, |s| {
14805            s.move_heads_with(|map, head, _| {
14806                (
14807                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
14808                    SelectionGoal::None,
14809                )
14810            });
14811        })
14812    }
14813
14814    pub fn select_to_end_of_excerpt(
14815        &mut self,
14816        _: &SelectToEndOfExcerpt,
14817        window: &mut Window,
14818        cx: &mut Context<Self>,
14819    ) {
14820        if matches!(self.mode, EditorMode::SingleLine) {
14821            cx.propagate();
14822            return;
14823        }
14824        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14825        self.change_selections(Default::default(), window, cx, |s| {
14826            s.move_heads_with(|map, head, _| {
14827                (
14828                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
14829                    SelectionGoal::None,
14830                )
14831            });
14832        })
14833    }
14834
14835    pub fn select_to_end_of_previous_excerpt(
14836        &mut self,
14837        _: &SelectToEndOfPreviousExcerpt,
14838        window: &mut Window,
14839        cx: &mut Context<Self>,
14840    ) {
14841        if matches!(self.mode, EditorMode::SingleLine) {
14842            cx.propagate();
14843            return;
14844        }
14845        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14846        self.change_selections(Default::default(), window, cx, |s| {
14847            s.move_heads_with(|map, head, _| {
14848                (
14849                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
14850                    SelectionGoal::None,
14851                )
14852            });
14853        })
14854    }
14855
14856    pub fn move_to_beginning(
14857        &mut self,
14858        _: &MoveToBeginning,
14859        window: &mut Window,
14860        cx: &mut Context<Self>,
14861    ) {
14862        if matches!(self.mode, EditorMode::SingleLine) {
14863            cx.propagate();
14864            return;
14865        }
14866        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14867        self.change_selections(Default::default(), window, cx, |s| {
14868            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
14869        });
14870    }
14871
14872    pub fn select_to_beginning(
14873        &mut self,
14874        _: &SelectToBeginning,
14875        window: &mut Window,
14876        cx: &mut Context<Self>,
14877    ) {
14878        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
14879        selection.set_head(Point::zero(), SelectionGoal::None);
14880        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14881        self.change_selections(Default::default(), window, cx, |s| {
14882            s.select(vec![selection]);
14883        });
14884    }
14885
14886    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
14887        if matches!(self.mode, EditorMode::SingleLine) {
14888            cx.propagate();
14889            return;
14890        }
14891        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14892        let cursor = self.buffer.read(cx).read(cx).len();
14893        self.change_selections(Default::default(), window, cx, |s| {
14894            s.select_ranges(vec![cursor..cursor])
14895        });
14896    }
14897
14898    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
14899        self.nav_history = nav_history;
14900    }
14901
14902    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
14903        self.nav_history.as_ref()
14904    }
14905
14906    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
14907        self.push_to_nav_history(
14908            self.selections.newest_anchor().head(),
14909            None,
14910            false,
14911            true,
14912            cx,
14913        );
14914    }
14915
14916    fn push_to_nav_history(
14917        &mut self,
14918        cursor_anchor: Anchor,
14919        new_position: Option<Point>,
14920        is_deactivate: bool,
14921        always: bool,
14922        cx: &mut Context<Self>,
14923    ) {
14924        if let Some(nav_history) = self.nav_history.as_mut() {
14925            let buffer = self.buffer.read(cx).read(cx);
14926            let cursor_position = cursor_anchor.to_point(&buffer);
14927            let scroll_state = self.scroll_manager.anchor();
14928            let scroll_top_row = scroll_state.top_row(&buffer);
14929            drop(buffer);
14930
14931            if let Some(new_position) = new_position {
14932                let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
14933                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
14934                    return;
14935                }
14936            }
14937
14938            nav_history.push(
14939                Some(NavigationData {
14940                    cursor_anchor,
14941                    cursor_position,
14942                    scroll_anchor: scroll_state,
14943                    scroll_top_row,
14944                }),
14945                cx,
14946            );
14947            cx.emit(EditorEvent::PushedToNavHistory {
14948                anchor: cursor_anchor,
14949                is_deactivate,
14950            })
14951        }
14952    }
14953
14954    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
14955        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14956        let buffer = self.buffer.read(cx).snapshot(cx);
14957        let mut selection = self
14958            .selections
14959            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
14960        selection.set_head(buffer.len(), SelectionGoal::None);
14961        self.change_selections(Default::default(), window, cx, |s| {
14962            s.select(vec![selection]);
14963        });
14964    }
14965
14966    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
14967        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14968        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14969            s.select_ranges(vec![Anchor::min()..Anchor::max()]);
14970        });
14971    }
14972
14973    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
14974        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14975        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14976        let mut selections = self.selections.all::<Point>(&display_map);
14977        let max_point = display_map.buffer_snapshot().max_point();
14978        for selection in &mut selections {
14979            let rows = selection.spanned_rows(true, &display_map);
14980            selection.start = Point::new(rows.start.0, 0);
14981            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
14982            selection.reversed = false;
14983        }
14984        self.change_selections(Default::default(), window, cx, |s| {
14985            s.select(selections);
14986        });
14987    }
14988
14989    pub fn split_selection_into_lines(
14990        &mut self,
14991        action: &SplitSelectionIntoLines,
14992        window: &mut Window,
14993        cx: &mut Context<Self>,
14994    ) {
14995        let selections = self
14996            .selections
14997            .all::<Point>(&self.display_snapshot(cx))
14998            .into_iter()
14999            .map(|selection| selection.start..selection.end)
15000            .collect::<Vec<_>>();
15001        self.unfold_ranges(&selections, true, true, cx);
15002
15003        let mut new_selection_ranges = Vec::new();
15004        {
15005            let buffer = self.buffer.read(cx).read(cx);
15006            for selection in selections {
15007                for row in selection.start.row..selection.end.row {
15008                    let line_start = Point::new(row, 0);
15009                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15010
15011                    if action.keep_selections {
15012                        // Keep the selection range for each line
15013                        let selection_start = if row == selection.start.row {
15014                            selection.start
15015                        } else {
15016                            line_start
15017                        };
15018                        new_selection_ranges.push(selection_start..line_end);
15019                    } else {
15020                        // Collapse to cursor at end of line
15021                        new_selection_ranges.push(line_end..line_end);
15022                    }
15023                }
15024
15025                let is_multiline_selection = selection.start.row != selection.end.row;
15026                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15027                // so this action feels more ergonomic when paired with other selection operations
15028                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15029                if !should_skip_last {
15030                    if action.keep_selections {
15031                        if is_multiline_selection {
15032                            let line_start = Point::new(selection.end.row, 0);
15033                            new_selection_ranges.push(line_start..selection.end);
15034                        } else {
15035                            new_selection_ranges.push(selection.start..selection.end);
15036                        }
15037                    } else {
15038                        new_selection_ranges.push(selection.end..selection.end);
15039                    }
15040                }
15041            }
15042        }
15043        self.change_selections(Default::default(), window, cx, |s| {
15044            s.select_ranges(new_selection_ranges);
15045        });
15046    }
15047
15048    pub fn add_selection_above(
15049        &mut self,
15050        action: &AddSelectionAbove,
15051        window: &mut Window,
15052        cx: &mut Context<Self>,
15053    ) {
15054        self.add_selection(true, action.skip_soft_wrap, window, cx);
15055    }
15056
15057    pub fn add_selection_below(
15058        &mut self,
15059        action: &AddSelectionBelow,
15060        window: &mut Window,
15061        cx: &mut Context<Self>,
15062    ) {
15063        self.add_selection(false, action.skip_soft_wrap, window, cx);
15064    }
15065
15066    fn add_selection(
15067        &mut self,
15068        above: bool,
15069        skip_soft_wrap: bool,
15070        window: &mut Window,
15071        cx: &mut Context<Self>,
15072    ) {
15073        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15074
15075        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15076        let all_selections = self.selections.all::<Point>(&display_map);
15077        let text_layout_details = self.text_layout_details(window);
15078
15079        let (mut columnar_selections, new_selections_to_columnarize) = {
15080            if let Some(state) = self.add_selections_state.as_ref() {
15081                let columnar_selection_ids: HashSet<_> = state
15082                    .groups
15083                    .iter()
15084                    .flat_map(|group| group.stack.iter())
15085                    .copied()
15086                    .collect();
15087
15088                all_selections
15089                    .into_iter()
15090                    .partition(|s| columnar_selection_ids.contains(&s.id))
15091            } else {
15092                (Vec::new(), all_selections)
15093            }
15094        };
15095
15096        let mut state = self
15097            .add_selections_state
15098            .take()
15099            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15100
15101        for selection in new_selections_to_columnarize {
15102            let range = selection.display_range(&display_map).sorted();
15103            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15104            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15105            let positions = start_x.min(end_x)..start_x.max(end_x);
15106            let mut stack = Vec::new();
15107            for row in range.start.row().0..=range.end.row().0 {
15108                if let Some(selection) = self.selections.build_columnar_selection(
15109                    &display_map,
15110                    DisplayRow(row),
15111                    &positions,
15112                    selection.reversed,
15113                    &text_layout_details,
15114                ) {
15115                    stack.push(selection.id);
15116                    columnar_selections.push(selection);
15117                }
15118            }
15119            if !stack.is_empty() {
15120                if above {
15121                    stack.reverse();
15122                }
15123                state.groups.push(AddSelectionsGroup { above, stack });
15124            }
15125        }
15126
15127        let mut final_selections = Vec::new();
15128        let end_row = if above {
15129            DisplayRow(0)
15130        } else {
15131            display_map.max_point().row()
15132        };
15133
15134        let mut last_added_item_per_group = HashMap::default();
15135        for group in state.groups.iter_mut() {
15136            if let Some(last_id) = group.stack.last() {
15137                last_added_item_per_group.insert(*last_id, group);
15138            }
15139        }
15140
15141        for selection in columnar_selections {
15142            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15143                if above == group.above {
15144                    let range = selection.display_range(&display_map).sorted();
15145                    debug_assert_eq!(range.start.row(), range.end.row());
15146                    let mut row = range.start.row();
15147                    let positions =
15148                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15149                            Pixels::from(start)..Pixels::from(end)
15150                        } else {
15151                            let start_x =
15152                                display_map.x_for_display_point(range.start, &text_layout_details);
15153                            let end_x =
15154                                display_map.x_for_display_point(range.end, &text_layout_details);
15155                            start_x.min(end_x)..start_x.max(end_x)
15156                        };
15157
15158                    let mut maybe_new_selection = None;
15159                    let direction = if above { -1 } else { 1 };
15160
15161                    while row != end_row {
15162                        let new_buffer_row = if skip_soft_wrap {
15163                            let new_row = display_map
15164                                .start_of_relative_buffer_row(DisplayPoint::new(row, 0), direction);
15165                            row = new_row.row();
15166                            Some(new_row.to_point(&display_map).row)
15167                        } else {
15168                            if above {
15169                                row.0 -= 1;
15170                            } else {
15171                                row.0 += 1;
15172                            }
15173                            None
15174                        };
15175
15176                        let new_selection = if let Some(buffer_row) = new_buffer_row {
15177                            let start_col = selection.start.column;
15178                            let end_col = selection.end.column;
15179                            let buffer_columns = start_col.min(end_col)..start_col.max(end_col);
15180
15181                            self.selections
15182                                .build_columnar_selection_from_buffer_columns(
15183                                    &display_map,
15184                                    buffer_row,
15185                                    &buffer_columns,
15186                                    selection.reversed,
15187                                    &text_layout_details,
15188                                )
15189                        } else {
15190                            self.selections.build_columnar_selection(
15191                                &display_map,
15192                                row,
15193                                &positions,
15194                                selection.reversed,
15195                                &text_layout_details,
15196                            )
15197                        };
15198
15199                        if let Some(new_selection) = new_selection {
15200                            maybe_new_selection = Some(new_selection);
15201                            break;
15202                        }
15203                    }
15204
15205                    if let Some(new_selection) = maybe_new_selection {
15206                        group.stack.push(new_selection.id);
15207                        if above {
15208                            final_selections.push(new_selection);
15209                            final_selections.push(selection);
15210                        } else {
15211                            final_selections.push(selection);
15212                            final_selections.push(new_selection);
15213                        }
15214                    } else {
15215                        final_selections.push(selection);
15216                    }
15217                } else {
15218                    group.stack.pop();
15219                }
15220            } else {
15221                final_selections.push(selection);
15222            }
15223        }
15224
15225        self.change_selections(Default::default(), window, cx, |s| {
15226            s.select(final_selections);
15227        });
15228
15229        let final_selection_ids: HashSet<_> = self
15230            .selections
15231            .all::<Point>(&display_map)
15232            .iter()
15233            .map(|s| s.id)
15234            .collect();
15235        state.groups.retain_mut(|group| {
15236            // selections might get merged above so we remove invalid items from stacks
15237            group.stack.retain(|id| final_selection_ids.contains(id));
15238
15239            // single selection in stack can be treated as initial state
15240            group.stack.len() > 1
15241        });
15242
15243        if !state.groups.is_empty() {
15244            self.add_selections_state = Some(state);
15245        }
15246    }
15247
15248    pub fn insert_snippet_at_selections(
15249        &mut self,
15250        action: &InsertSnippet,
15251        window: &mut Window,
15252        cx: &mut Context<Self>,
15253    ) {
15254        self.try_insert_snippet_at_selections(action, window, cx)
15255            .log_err();
15256    }
15257
15258    fn try_insert_snippet_at_selections(
15259        &mut self,
15260        action: &InsertSnippet,
15261        window: &mut Window,
15262        cx: &mut Context<Self>,
15263    ) -> Result<()> {
15264        let insertion_ranges = self
15265            .selections
15266            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15267            .into_iter()
15268            .map(|selection| selection.range())
15269            .collect_vec();
15270
15271        let snippet = if let Some(snippet_body) = &action.snippet {
15272            if action.language.is_none() && action.name.is_none() {
15273                Snippet::parse(snippet_body)?
15274            } else {
15275                bail!("`snippet` is mutually exclusive with `language` and `name`")
15276            }
15277        } else if let Some(name) = &action.name {
15278            let project = self.project().context("no project")?;
15279            let snippet_store = project.read(cx).snippets().read(cx);
15280            let snippet = snippet_store
15281                .snippets_for(action.language.clone(), cx)
15282                .into_iter()
15283                .find(|snippet| snippet.name == *name)
15284                .context("snippet not found")?;
15285            Snippet::parse(&snippet.body)?
15286        } else {
15287            // todo(andrew): open modal to select snippet
15288            bail!("`name` or `snippet` is required")
15289        };
15290
15291        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15292    }
15293
15294    fn select_match_ranges(
15295        &mut self,
15296        range: Range<MultiBufferOffset>,
15297        reversed: bool,
15298        replace_newest: bool,
15299        auto_scroll: Option<Autoscroll>,
15300        window: &mut Window,
15301        cx: &mut Context<Editor>,
15302    ) {
15303        self.unfold_ranges(
15304            std::slice::from_ref(&range),
15305            false,
15306            auto_scroll.is_some(),
15307            cx,
15308        );
15309        let effects = if let Some(scroll) = auto_scroll {
15310            SelectionEffects::scroll(scroll)
15311        } else {
15312            SelectionEffects::no_scroll()
15313        };
15314        self.change_selections(effects, window, cx, |s| {
15315            if replace_newest {
15316                s.delete(s.newest_anchor().id);
15317            }
15318            if reversed {
15319                s.insert_range(range.end..range.start);
15320            } else {
15321                s.insert_range(range);
15322            }
15323        });
15324    }
15325
15326    pub fn select_next_match_internal(
15327        &mut self,
15328        display_map: &DisplaySnapshot,
15329        replace_newest: bool,
15330        autoscroll: Option<Autoscroll>,
15331        window: &mut Window,
15332        cx: &mut Context<Self>,
15333    ) -> Result<()> {
15334        let buffer = display_map.buffer_snapshot();
15335        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15336        if let Some(mut select_next_state) = self.select_next_state.take() {
15337            let query = &select_next_state.query;
15338            if !select_next_state.done {
15339                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15340                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15341                let mut next_selected_range = None;
15342
15343                let bytes_after_last_selection =
15344                    buffer.bytes_in_range(last_selection.end..buffer.len());
15345                let bytes_before_first_selection =
15346                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15347                let query_matches = query
15348                    .stream_find_iter(bytes_after_last_selection)
15349                    .map(|result| (last_selection.end, result))
15350                    .chain(
15351                        query
15352                            .stream_find_iter(bytes_before_first_selection)
15353                            .map(|result| (MultiBufferOffset(0), result)),
15354                    );
15355
15356                for (start_offset, query_match) in query_matches {
15357                    let query_match = query_match.unwrap(); // can only fail due to I/O
15358                    let offset_range =
15359                        start_offset + query_match.start()..start_offset + query_match.end();
15360
15361                    if !select_next_state.wordwise
15362                        || (!buffer.is_inside_word(offset_range.start, None)
15363                            && !buffer.is_inside_word(offset_range.end, None))
15364                    {
15365                        let idx = selections
15366                            .partition_point(|selection| selection.end <= offset_range.start);
15367                        let overlaps = selections
15368                            .get(idx)
15369                            .map_or(false, |selection| selection.start < offset_range.end);
15370
15371                        if !overlaps {
15372                            next_selected_range = Some(offset_range);
15373                            break;
15374                        }
15375                    }
15376                }
15377
15378                if let Some(next_selected_range) = next_selected_range {
15379                    self.select_match_ranges(
15380                        next_selected_range,
15381                        last_selection.reversed,
15382                        replace_newest,
15383                        autoscroll,
15384                        window,
15385                        cx,
15386                    );
15387                } else {
15388                    select_next_state.done = true;
15389                }
15390            }
15391
15392            self.select_next_state = Some(select_next_state);
15393        } else {
15394            let mut only_carets = true;
15395            let mut same_text_selected = true;
15396            let mut selected_text = None;
15397
15398            let mut selections_iter = selections.iter().peekable();
15399            while let Some(selection) = selections_iter.next() {
15400                if selection.start != selection.end {
15401                    only_carets = false;
15402                }
15403
15404                if same_text_selected {
15405                    if selected_text.is_none() {
15406                        selected_text =
15407                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15408                    }
15409
15410                    if let Some(next_selection) = selections_iter.peek() {
15411                        if next_selection.len() == selection.len() {
15412                            let next_selected_text = buffer
15413                                .text_for_range(next_selection.range())
15414                                .collect::<String>();
15415                            if Some(next_selected_text) != selected_text {
15416                                same_text_selected = false;
15417                                selected_text = None;
15418                            }
15419                        } else {
15420                            same_text_selected = false;
15421                            selected_text = None;
15422                        }
15423                    }
15424                }
15425            }
15426
15427            if only_carets {
15428                for selection in &mut selections {
15429                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15430                    selection.start = word_range.start;
15431                    selection.end = word_range.end;
15432                    selection.goal = SelectionGoal::None;
15433                    selection.reversed = false;
15434                    self.select_match_ranges(
15435                        selection.start..selection.end,
15436                        selection.reversed,
15437                        replace_newest,
15438                        autoscroll,
15439                        window,
15440                        cx,
15441                    );
15442                }
15443
15444                if selections.len() == 1 {
15445                    let selection = selections
15446                        .last()
15447                        .expect("ensured that there's only one selection");
15448                    let query = buffer
15449                        .text_for_range(selection.start..selection.end)
15450                        .collect::<String>();
15451                    let is_empty = query.is_empty();
15452                    let select_state = SelectNextState {
15453                        query: self.build_query(&[query], cx)?,
15454                        wordwise: true,
15455                        done: is_empty,
15456                    };
15457                    self.select_next_state = Some(select_state);
15458                } else {
15459                    self.select_next_state = None;
15460                }
15461            } else if let Some(selected_text) = selected_text {
15462                self.select_next_state = Some(SelectNextState {
15463                    query: self.build_query(&[selected_text], cx)?,
15464                    wordwise: false,
15465                    done: false,
15466                });
15467                self.select_next_match_internal(
15468                    display_map,
15469                    replace_newest,
15470                    autoscroll,
15471                    window,
15472                    cx,
15473                )?;
15474            }
15475        }
15476        Ok(())
15477    }
15478
15479    pub fn select_all_matches(
15480        &mut self,
15481        _action: &SelectAllMatches,
15482        window: &mut Window,
15483        cx: &mut Context<Self>,
15484    ) -> Result<()> {
15485        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15486
15487        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15488
15489        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15490        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15491        else {
15492            return Ok(());
15493        };
15494
15495        let mut new_selections = Vec::new();
15496
15497        let reversed = self
15498            .selections
15499            .oldest::<MultiBufferOffset>(&display_map)
15500            .reversed;
15501        let buffer = display_map.buffer_snapshot();
15502        let query_matches = select_next_state
15503            .query
15504            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15505
15506        for query_match in query_matches.into_iter() {
15507            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15508            let offset_range = if reversed {
15509                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15510            } else {
15511                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15512            };
15513
15514            if !select_next_state.wordwise
15515                || (!buffer.is_inside_word(offset_range.start, None)
15516                    && !buffer.is_inside_word(offset_range.end, None))
15517            {
15518                new_selections.push(offset_range.start..offset_range.end);
15519            }
15520        }
15521
15522        select_next_state.done = true;
15523
15524        if new_selections.is_empty() {
15525            log::error!("bug: new_selections is empty in select_all_matches");
15526            return Ok(());
15527        }
15528
15529        self.unfold_ranges(&new_selections, false, false, cx);
15530        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15531            selections.select_ranges(new_selections)
15532        });
15533
15534        Ok(())
15535    }
15536
15537    pub fn select_next(
15538        &mut self,
15539        action: &SelectNext,
15540        window: &mut Window,
15541        cx: &mut Context<Self>,
15542    ) -> Result<()> {
15543        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15544        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15545        self.select_next_match_internal(
15546            &display_map,
15547            action.replace_newest,
15548            Some(Autoscroll::newest()),
15549            window,
15550            cx,
15551        )
15552    }
15553
15554    pub fn select_previous(
15555        &mut self,
15556        action: &SelectPrevious,
15557        window: &mut Window,
15558        cx: &mut Context<Self>,
15559    ) -> Result<()> {
15560        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15561        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15562        let buffer = display_map.buffer_snapshot();
15563        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15564        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15565            let query = &select_prev_state.query;
15566            if !select_prev_state.done {
15567                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15568                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15569                let mut next_selected_range = None;
15570                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15571                let bytes_before_last_selection =
15572                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15573                let bytes_after_first_selection =
15574                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15575                let query_matches = query
15576                    .stream_find_iter(bytes_before_last_selection)
15577                    .map(|result| (last_selection.start, result))
15578                    .chain(
15579                        query
15580                            .stream_find_iter(bytes_after_first_selection)
15581                            .map(|result| (buffer.len(), result)),
15582                    );
15583                for (end_offset, query_match) in query_matches {
15584                    let query_match = query_match.unwrap(); // can only fail due to I/O
15585                    let offset_range =
15586                        end_offset - query_match.end()..end_offset - query_match.start();
15587
15588                    if !select_prev_state.wordwise
15589                        || (!buffer.is_inside_word(offset_range.start, None)
15590                            && !buffer.is_inside_word(offset_range.end, None))
15591                    {
15592                        next_selected_range = Some(offset_range);
15593                        break;
15594                    }
15595                }
15596
15597                if let Some(next_selected_range) = next_selected_range {
15598                    self.select_match_ranges(
15599                        next_selected_range,
15600                        last_selection.reversed,
15601                        action.replace_newest,
15602                        Some(Autoscroll::newest()),
15603                        window,
15604                        cx,
15605                    );
15606                } else {
15607                    select_prev_state.done = true;
15608                }
15609            }
15610
15611            self.select_prev_state = Some(select_prev_state);
15612        } else {
15613            let mut only_carets = true;
15614            let mut same_text_selected = true;
15615            let mut selected_text = None;
15616
15617            let mut selections_iter = selections.iter().peekable();
15618            while let Some(selection) = selections_iter.next() {
15619                if selection.start != selection.end {
15620                    only_carets = false;
15621                }
15622
15623                if same_text_selected {
15624                    if selected_text.is_none() {
15625                        selected_text =
15626                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15627                    }
15628
15629                    if let Some(next_selection) = selections_iter.peek() {
15630                        if next_selection.len() == selection.len() {
15631                            let next_selected_text = buffer
15632                                .text_for_range(next_selection.range())
15633                                .collect::<String>();
15634                            if Some(next_selected_text) != selected_text {
15635                                same_text_selected = false;
15636                                selected_text = None;
15637                            }
15638                        } else {
15639                            same_text_selected = false;
15640                            selected_text = None;
15641                        }
15642                    }
15643                }
15644            }
15645
15646            if only_carets {
15647                for selection in &mut selections {
15648                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15649                    selection.start = word_range.start;
15650                    selection.end = word_range.end;
15651                    selection.goal = SelectionGoal::None;
15652                    selection.reversed = false;
15653                    self.select_match_ranges(
15654                        selection.start..selection.end,
15655                        selection.reversed,
15656                        action.replace_newest,
15657                        Some(Autoscroll::newest()),
15658                        window,
15659                        cx,
15660                    );
15661                }
15662                if selections.len() == 1 {
15663                    let selection = selections
15664                        .last()
15665                        .expect("ensured that there's only one selection");
15666                    let query = buffer
15667                        .text_for_range(selection.start..selection.end)
15668                        .collect::<String>();
15669                    let is_empty = query.is_empty();
15670                    let select_state = SelectNextState {
15671                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
15672                        wordwise: true,
15673                        done: is_empty,
15674                    };
15675                    self.select_prev_state = Some(select_state);
15676                } else {
15677                    self.select_prev_state = None;
15678                }
15679            } else if let Some(selected_text) = selected_text {
15680                self.select_prev_state = Some(SelectNextState {
15681                    query: self
15682                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
15683                    wordwise: false,
15684                    done: false,
15685                });
15686                self.select_previous(action, window, cx)?;
15687            }
15688        }
15689        Ok(())
15690    }
15691
15692    /// Builds an `AhoCorasick` automaton from the provided patterns, while
15693    /// setting the case sensitivity based on the global
15694    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
15695    /// editor's settings.
15696    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
15697    where
15698        I: IntoIterator<Item = P>,
15699        P: AsRef<[u8]>,
15700    {
15701        let case_sensitive = self
15702            .select_next_is_case_sensitive
15703            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
15704
15705        let mut builder = AhoCorasickBuilder::new();
15706        builder.ascii_case_insensitive(!case_sensitive);
15707        builder.build(patterns)
15708    }
15709
15710    pub fn find_next_match(
15711        &mut self,
15712        _: &FindNextMatch,
15713        window: &mut Window,
15714        cx: &mut Context<Self>,
15715    ) -> Result<()> {
15716        let selections = self.selections.disjoint_anchors_arc();
15717        match selections.first() {
15718            Some(first) if selections.len() >= 2 => {
15719                self.change_selections(Default::default(), window, cx, |s| {
15720                    s.select_ranges([first.range()]);
15721                });
15722            }
15723            _ => self.select_next(
15724                &SelectNext {
15725                    replace_newest: true,
15726                },
15727                window,
15728                cx,
15729            )?,
15730        }
15731        Ok(())
15732    }
15733
15734    pub fn find_previous_match(
15735        &mut self,
15736        _: &FindPreviousMatch,
15737        window: &mut Window,
15738        cx: &mut Context<Self>,
15739    ) -> Result<()> {
15740        let selections = self.selections.disjoint_anchors_arc();
15741        match selections.last() {
15742            Some(last) if selections.len() >= 2 => {
15743                self.change_selections(Default::default(), window, cx, |s| {
15744                    s.select_ranges([last.range()]);
15745                });
15746            }
15747            _ => self.select_previous(
15748                &SelectPrevious {
15749                    replace_newest: true,
15750                },
15751                window,
15752                cx,
15753            )?,
15754        }
15755        Ok(())
15756    }
15757
15758    pub fn toggle_comments(
15759        &mut self,
15760        action: &ToggleComments,
15761        window: &mut Window,
15762        cx: &mut Context<Self>,
15763    ) {
15764        if self.read_only(cx) {
15765            return;
15766        }
15767        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15768        let text_layout_details = &self.text_layout_details(window);
15769        self.transact(window, cx, |this, window, cx| {
15770            let mut selections = this
15771                .selections
15772                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
15773            let mut edits = Vec::new();
15774            let mut selection_edit_ranges = Vec::new();
15775            let mut last_toggled_row = None;
15776            let snapshot = this.buffer.read(cx).read(cx);
15777            let empty_str: Arc<str> = Arc::default();
15778            let mut suffixes_inserted = Vec::new();
15779            let ignore_indent = action.ignore_indent;
15780
15781            fn comment_prefix_range(
15782                snapshot: &MultiBufferSnapshot,
15783                row: MultiBufferRow,
15784                comment_prefix: &str,
15785                comment_prefix_whitespace: &str,
15786                ignore_indent: bool,
15787            ) -> Range<Point> {
15788                let indent_size = if ignore_indent {
15789                    0
15790                } else {
15791                    snapshot.indent_size_for_line(row).len
15792                };
15793
15794                let start = Point::new(row.0, indent_size);
15795
15796                let mut line_bytes = snapshot
15797                    .bytes_in_range(start..snapshot.max_point())
15798                    .flatten()
15799                    .copied();
15800
15801                // If this line currently begins with the line comment prefix, then record
15802                // the range containing the prefix.
15803                if line_bytes
15804                    .by_ref()
15805                    .take(comment_prefix.len())
15806                    .eq(comment_prefix.bytes())
15807                {
15808                    // Include any whitespace that matches the comment prefix.
15809                    let matching_whitespace_len = line_bytes
15810                        .zip(comment_prefix_whitespace.bytes())
15811                        .take_while(|(a, b)| a == b)
15812                        .count() as u32;
15813                    let end = Point::new(
15814                        start.row,
15815                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
15816                    );
15817                    start..end
15818                } else {
15819                    start..start
15820                }
15821            }
15822
15823            fn comment_suffix_range(
15824                snapshot: &MultiBufferSnapshot,
15825                row: MultiBufferRow,
15826                comment_suffix: &str,
15827                comment_suffix_has_leading_space: bool,
15828            ) -> Range<Point> {
15829                let end = Point::new(row.0, snapshot.line_len(row));
15830                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
15831
15832                let mut line_end_bytes = snapshot
15833                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
15834                    .flatten()
15835                    .copied();
15836
15837                let leading_space_len = if suffix_start_column > 0
15838                    && line_end_bytes.next() == Some(b' ')
15839                    && comment_suffix_has_leading_space
15840                {
15841                    1
15842                } else {
15843                    0
15844                };
15845
15846                // If this line currently begins with the line comment prefix, then record
15847                // the range containing the prefix.
15848                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
15849                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
15850                    start..end
15851                } else {
15852                    end..end
15853                }
15854            }
15855
15856            // TODO: Handle selections that cross excerpts
15857            for selection in &mut selections {
15858                let start_column = snapshot
15859                    .indent_size_for_line(MultiBufferRow(selection.start.row))
15860                    .len;
15861                let language = if let Some(language) =
15862                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
15863                {
15864                    language
15865                } else {
15866                    continue;
15867                };
15868
15869                selection_edit_ranges.clear();
15870
15871                // If multiple selections contain a given row, avoid processing that
15872                // row more than once.
15873                let mut start_row = MultiBufferRow(selection.start.row);
15874                if last_toggled_row == Some(start_row) {
15875                    start_row = start_row.next_row();
15876                }
15877                let end_row =
15878                    if selection.end.row > selection.start.row && selection.end.column == 0 {
15879                        MultiBufferRow(selection.end.row - 1)
15880                    } else {
15881                        MultiBufferRow(selection.end.row)
15882                    };
15883                last_toggled_row = Some(end_row);
15884
15885                if start_row > end_row {
15886                    continue;
15887                }
15888
15889                // If the language has line comments, toggle those.
15890                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
15891
15892                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
15893                if ignore_indent {
15894                    full_comment_prefixes = full_comment_prefixes
15895                        .into_iter()
15896                        .map(|s| Arc::from(s.trim_end()))
15897                        .collect();
15898                }
15899
15900                if !full_comment_prefixes.is_empty() {
15901                    let first_prefix = full_comment_prefixes
15902                        .first()
15903                        .expect("prefixes is non-empty");
15904                    let prefix_trimmed_lengths = full_comment_prefixes
15905                        .iter()
15906                        .map(|p| p.trim_end_matches(' ').len())
15907                        .collect::<SmallVec<[usize; 4]>>();
15908
15909                    let mut all_selection_lines_are_comments = true;
15910
15911                    for row in start_row.0..=end_row.0 {
15912                        let row = MultiBufferRow(row);
15913                        if start_row < end_row && snapshot.is_line_blank(row) {
15914                            continue;
15915                        }
15916
15917                        let prefix_range = full_comment_prefixes
15918                            .iter()
15919                            .zip(prefix_trimmed_lengths.iter().copied())
15920                            .map(|(prefix, trimmed_prefix_len)| {
15921                                comment_prefix_range(
15922                                    snapshot.deref(),
15923                                    row,
15924                                    &prefix[..trimmed_prefix_len],
15925                                    &prefix[trimmed_prefix_len..],
15926                                    ignore_indent,
15927                                )
15928                            })
15929                            .max_by_key(|range| range.end.column - range.start.column)
15930                            .expect("prefixes is non-empty");
15931
15932                        if prefix_range.is_empty() {
15933                            all_selection_lines_are_comments = false;
15934                        }
15935
15936                        selection_edit_ranges.push(prefix_range);
15937                    }
15938
15939                    if all_selection_lines_are_comments {
15940                        edits.extend(
15941                            selection_edit_ranges
15942                                .iter()
15943                                .cloned()
15944                                .map(|range| (range, empty_str.clone())),
15945                        );
15946                    } else {
15947                        let min_column = selection_edit_ranges
15948                            .iter()
15949                            .map(|range| range.start.column)
15950                            .min()
15951                            .unwrap_or(0);
15952                        edits.extend(selection_edit_ranges.iter().map(|range| {
15953                            let position = Point::new(range.start.row, min_column);
15954                            (position..position, first_prefix.clone())
15955                        }));
15956                    }
15957                } else if let Some(BlockCommentConfig {
15958                    start: full_comment_prefix,
15959                    end: comment_suffix,
15960                    ..
15961                }) = language.block_comment()
15962                {
15963                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
15964                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
15965                    let prefix_range = comment_prefix_range(
15966                        snapshot.deref(),
15967                        start_row,
15968                        comment_prefix,
15969                        comment_prefix_whitespace,
15970                        ignore_indent,
15971                    );
15972                    let suffix_range = comment_suffix_range(
15973                        snapshot.deref(),
15974                        end_row,
15975                        comment_suffix.trim_start_matches(' '),
15976                        comment_suffix.starts_with(' '),
15977                    );
15978
15979                    if prefix_range.is_empty() || suffix_range.is_empty() {
15980                        edits.push((
15981                            prefix_range.start..prefix_range.start,
15982                            full_comment_prefix.clone(),
15983                        ));
15984                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
15985                        suffixes_inserted.push((end_row, comment_suffix.len()));
15986                    } else {
15987                        edits.push((prefix_range, empty_str.clone()));
15988                        edits.push((suffix_range, empty_str.clone()));
15989                    }
15990                } else {
15991                    continue;
15992                }
15993            }
15994
15995            drop(snapshot);
15996            this.buffer.update(cx, |buffer, cx| {
15997                buffer.edit(edits, None, cx);
15998            });
15999
16000            // Adjust selections so that they end before any comment suffixes that
16001            // were inserted.
16002            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16003            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16004            let snapshot = this.buffer.read(cx).read(cx);
16005            for selection in &mut selections {
16006                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16007                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16008                        Ordering::Less => {
16009                            suffixes_inserted.next();
16010                            continue;
16011                        }
16012                        Ordering::Greater => break,
16013                        Ordering::Equal => {
16014                            if selection.end.column == snapshot.line_len(row) {
16015                                if selection.is_empty() {
16016                                    selection.start.column -= suffix_len as u32;
16017                                }
16018                                selection.end.column -= suffix_len as u32;
16019                            }
16020                            break;
16021                        }
16022                    }
16023                }
16024            }
16025
16026            drop(snapshot);
16027            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16028
16029            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16030            let selections_on_single_row = selections.windows(2).all(|selections| {
16031                selections[0].start.row == selections[1].start.row
16032                    && selections[0].end.row == selections[1].end.row
16033                    && selections[0].start.row == selections[0].end.row
16034            });
16035            let selections_selecting = selections
16036                .iter()
16037                .any(|selection| selection.start != selection.end);
16038            let advance_downwards = action.advance_downwards
16039                && selections_on_single_row
16040                && !selections_selecting
16041                && !matches!(this.mode, EditorMode::SingleLine);
16042
16043            if advance_downwards {
16044                let snapshot = this.buffer.read(cx).snapshot(cx);
16045
16046                this.change_selections(Default::default(), window, cx, |s| {
16047                    s.move_cursors_with(|display_snapshot, display_point, _| {
16048                        let mut point = display_point.to_point(display_snapshot);
16049                        point.row += 1;
16050                        point = snapshot.clip_point(point, Bias::Left);
16051                        let display_point = point.to_display_point(display_snapshot);
16052                        let goal = SelectionGoal::HorizontalPosition(
16053                            display_snapshot
16054                                .x_for_display_point(display_point, text_layout_details)
16055                                .into(),
16056                        );
16057                        (display_point, goal)
16058                    })
16059                });
16060            }
16061        });
16062    }
16063
16064    pub fn select_enclosing_symbol(
16065        &mut self,
16066        _: &SelectEnclosingSymbol,
16067        window: &mut Window,
16068        cx: &mut Context<Self>,
16069    ) {
16070        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16071
16072        let buffer = self.buffer.read(cx).snapshot(cx);
16073        let old_selections = self
16074            .selections
16075            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16076            .into_boxed_slice();
16077
16078        fn update_selection(
16079            selection: &Selection<MultiBufferOffset>,
16080            buffer_snap: &MultiBufferSnapshot,
16081        ) -> Option<Selection<MultiBufferOffset>> {
16082            let cursor = selection.head();
16083            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16084            for symbol in symbols.iter().rev() {
16085                let start = symbol.range.start.to_offset(buffer_snap);
16086                let end = symbol.range.end.to_offset(buffer_snap);
16087                let new_range = start..end;
16088                if start < selection.start || end > selection.end {
16089                    return Some(Selection {
16090                        id: selection.id,
16091                        start: new_range.start,
16092                        end: new_range.end,
16093                        goal: SelectionGoal::None,
16094                        reversed: selection.reversed,
16095                    });
16096                }
16097            }
16098            None
16099        }
16100
16101        let mut selected_larger_symbol = false;
16102        let new_selections = old_selections
16103            .iter()
16104            .map(|selection| match update_selection(selection, &buffer) {
16105                Some(new_selection) => {
16106                    if new_selection.range() != selection.range() {
16107                        selected_larger_symbol = true;
16108                    }
16109                    new_selection
16110                }
16111                None => selection.clone(),
16112            })
16113            .collect::<Vec<_>>();
16114
16115        if selected_larger_symbol {
16116            self.change_selections(Default::default(), window, cx, |s| {
16117                s.select(new_selections);
16118            });
16119        }
16120    }
16121
16122    pub fn select_larger_syntax_node(
16123        &mut self,
16124        _: &SelectLargerSyntaxNode,
16125        window: &mut Window,
16126        cx: &mut Context<Self>,
16127    ) {
16128        let Some(visible_row_count) = self.visible_row_count() else {
16129            return;
16130        };
16131        let old_selections: Box<[_]> = self
16132            .selections
16133            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16134            .into();
16135        if old_selections.is_empty() {
16136            return;
16137        }
16138
16139        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16140
16141        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16142        let buffer = self.buffer.read(cx).snapshot(cx);
16143
16144        let mut selected_larger_node = false;
16145        let mut new_selections = old_selections
16146            .iter()
16147            .map(|selection| {
16148                let old_range = selection.start..selection.end;
16149
16150                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16151                    // manually select word at selection
16152                    if ["string_content", "inline"].contains(&node.kind()) {
16153                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16154                        // ignore if word is already selected
16155                        if !word_range.is_empty() && old_range != word_range {
16156                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16157                            // only select word if start and end point belongs to same word
16158                            if word_range == last_word_range {
16159                                selected_larger_node = true;
16160                                return Selection {
16161                                    id: selection.id,
16162                                    start: word_range.start,
16163                                    end: word_range.end,
16164                                    goal: SelectionGoal::None,
16165                                    reversed: selection.reversed,
16166                                };
16167                            }
16168                        }
16169                    }
16170                }
16171
16172                let mut new_range = old_range.clone();
16173                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16174                    new_range = range;
16175                    if !node.is_named() {
16176                        continue;
16177                    }
16178                    if !display_map.intersects_fold(new_range.start)
16179                        && !display_map.intersects_fold(new_range.end)
16180                    {
16181                        break;
16182                    }
16183                }
16184
16185                selected_larger_node |= new_range != old_range;
16186                Selection {
16187                    id: selection.id,
16188                    start: new_range.start,
16189                    end: new_range.end,
16190                    goal: SelectionGoal::None,
16191                    reversed: selection.reversed,
16192                }
16193            })
16194            .collect::<Vec<_>>();
16195
16196        if !selected_larger_node {
16197            return; // don't put this call in the history
16198        }
16199
16200        // scroll based on transformation done to the last selection created by the user
16201        let (last_old, last_new) = old_selections
16202            .last()
16203            .zip(new_selections.last().cloned())
16204            .expect("old_selections isn't empty");
16205
16206        // revert selection
16207        let is_selection_reversed = {
16208            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16209            new_selections.last_mut().expect("checked above").reversed =
16210                should_newest_selection_be_reversed;
16211            should_newest_selection_be_reversed
16212        };
16213
16214        if selected_larger_node {
16215            self.select_syntax_node_history.disable_clearing = true;
16216            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16217                s.select(new_selections.clone());
16218            });
16219            self.select_syntax_node_history.disable_clearing = false;
16220        }
16221
16222        let start_row = last_new.start.to_display_point(&display_map).row().0;
16223        let end_row = last_new.end.to_display_point(&display_map).row().0;
16224        let selection_height = end_row - start_row + 1;
16225        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16226
16227        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16228        let scroll_behavior = if fits_on_the_screen {
16229            self.request_autoscroll(Autoscroll::fit(), cx);
16230            SelectSyntaxNodeScrollBehavior::FitSelection
16231        } else if is_selection_reversed {
16232            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16233            SelectSyntaxNodeScrollBehavior::CursorTop
16234        } else {
16235            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16236            SelectSyntaxNodeScrollBehavior::CursorBottom
16237        };
16238
16239        self.select_syntax_node_history.push((
16240            old_selections,
16241            scroll_behavior,
16242            is_selection_reversed,
16243        ));
16244    }
16245
16246    pub fn select_smaller_syntax_node(
16247        &mut self,
16248        _: &SelectSmallerSyntaxNode,
16249        window: &mut Window,
16250        cx: &mut Context<Self>,
16251    ) {
16252        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16253
16254        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16255            self.select_syntax_node_history.pop()
16256        {
16257            if let Some(selection) = selections.last_mut() {
16258                selection.reversed = is_selection_reversed;
16259            }
16260
16261            self.select_syntax_node_history.disable_clearing = true;
16262            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16263                s.select(selections.to_vec());
16264            });
16265            self.select_syntax_node_history.disable_clearing = false;
16266
16267            match scroll_behavior {
16268                SelectSyntaxNodeScrollBehavior::CursorTop => {
16269                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16270                }
16271                SelectSyntaxNodeScrollBehavior::FitSelection => {
16272                    self.request_autoscroll(Autoscroll::fit(), cx);
16273                }
16274                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16275                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16276                }
16277            }
16278        }
16279    }
16280
16281    pub fn unwrap_syntax_node(
16282        &mut self,
16283        _: &UnwrapSyntaxNode,
16284        window: &mut Window,
16285        cx: &mut Context<Self>,
16286    ) {
16287        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16288
16289        let buffer = self.buffer.read(cx).snapshot(cx);
16290        let selections = self
16291            .selections
16292            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16293            .into_iter()
16294            // subtracting the offset requires sorting
16295            .sorted_by_key(|i| i.start);
16296
16297        let full_edits = selections
16298            .into_iter()
16299            .filter_map(|selection| {
16300                let child = if selection.is_empty()
16301                    && let Some((_, ancestor_range)) =
16302                        buffer.syntax_ancestor(selection.start..selection.end)
16303                {
16304                    ancestor_range
16305                } else {
16306                    selection.range()
16307                };
16308
16309                let mut parent = child.clone();
16310                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16311                    parent = ancestor_range;
16312                    if parent.start < child.start || parent.end > child.end {
16313                        break;
16314                    }
16315                }
16316
16317                if parent == child {
16318                    return None;
16319                }
16320                let text = buffer.text_for_range(child).collect::<String>();
16321                Some((selection.id, parent, text))
16322            })
16323            .collect::<Vec<_>>();
16324        if full_edits.is_empty() {
16325            return;
16326        }
16327
16328        self.transact(window, cx, |this, window, cx| {
16329            this.buffer.update(cx, |buffer, cx| {
16330                buffer.edit(
16331                    full_edits
16332                        .iter()
16333                        .map(|(_, p, t)| (p.clone(), t.clone()))
16334                        .collect::<Vec<_>>(),
16335                    None,
16336                    cx,
16337                );
16338            });
16339            this.change_selections(Default::default(), window, cx, |s| {
16340                let mut offset = 0;
16341                let mut selections = vec![];
16342                for (id, parent, text) in full_edits {
16343                    let start = parent.start - offset;
16344                    offset += (parent.end - parent.start) - text.len();
16345                    selections.push(Selection {
16346                        id,
16347                        start,
16348                        end: start + text.len(),
16349                        reversed: false,
16350                        goal: Default::default(),
16351                    });
16352                }
16353                s.select(selections);
16354            });
16355        });
16356    }
16357
16358    pub fn select_next_syntax_node(
16359        &mut self,
16360        _: &SelectNextSyntaxNode,
16361        window: &mut Window,
16362        cx: &mut Context<Self>,
16363    ) {
16364        let old_selections: Box<[_]> = self
16365            .selections
16366            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16367            .into();
16368        if old_selections.is_empty() {
16369            return;
16370        }
16371
16372        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16373
16374        let buffer = self.buffer.read(cx).snapshot(cx);
16375        let mut selected_sibling = false;
16376
16377        let new_selections = old_selections
16378            .iter()
16379            .map(|selection| {
16380                let old_range = selection.start..selection.end;
16381
16382                let old_range =
16383                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16384                let excerpt = buffer.excerpt_containing(old_range.clone());
16385
16386                if let Some(mut excerpt) = excerpt
16387                    && let Some(node) = excerpt
16388                        .buffer()
16389                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16390                {
16391                    let new_range = excerpt.map_range_from_buffer(
16392                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16393                    );
16394                    selected_sibling = true;
16395                    Selection {
16396                        id: selection.id,
16397                        start: new_range.start,
16398                        end: new_range.end,
16399                        goal: SelectionGoal::None,
16400                        reversed: selection.reversed,
16401                    }
16402                } else {
16403                    selection.clone()
16404                }
16405            })
16406            .collect::<Vec<_>>();
16407
16408        if selected_sibling {
16409            self.change_selections(
16410                SelectionEffects::scroll(Autoscroll::fit()),
16411                window,
16412                cx,
16413                |s| {
16414                    s.select(new_selections);
16415                },
16416            );
16417        }
16418    }
16419
16420    pub fn select_prev_syntax_node(
16421        &mut self,
16422        _: &SelectPreviousSyntaxNode,
16423        window: &mut Window,
16424        cx: &mut Context<Self>,
16425    ) {
16426        let old_selections: Box<[_]> = self
16427            .selections
16428            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16429            .into();
16430        if old_selections.is_empty() {
16431            return;
16432        }
16433
16434        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16435
16436        let buffer = self.buffer.read(cx).snapshot(cx);
16437        let mut selected_sibling = false;
16438
16439        let new_selections = old_selections
16440            .iter()
16441            .map(|selection| {
16442                let old_range = selection.start..selection.end;
16443                let old_range =
16444                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16445                let excerpt = buffer.excerpt_containing(old_range.clone());
16446
16447                if let Some(mut excerpt) = excerpt
16448                    && let Some(node) = excerpt
16449                        .buffer()
16450                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16451                {
16452                    let new_range = excerpt.map_range_from_buffer(
16453                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16454                    );
16455                    selected_sibling = true;
16456                    Selection {
16457                        id: selection.id,
16458                        start: new_range.start,
16459                        end: new_range.end,
16460                        goal: SelectionGoal::None,
16461                        reversed: selection.reversed,
16462                    }
16463                } else {
16464                    selection.clone()
16465                }
16466            })
16467            .collect::<Vec<_>>();
16468
16469        if selected_sibling {
16470            self.change_selections(
16471                SelectionEffects::scroll(Autoscroll::fit()),
16472                window,
16473                cx,
16474                |s| {
16475                    s.select(new_selections);
16476                },
16477            );
16478        }
16479    }
16480
16481    pub fn move_to_start_of_larger_syntax_node(
16482        &mut self,
16483        _: &MoveToStartOfLargerSyntaxNode,
16484        window: &mut Window,
16485        cx: &mut Context<Self>,
16486    ) {
16487        self.move_cursors_to_syntax_nodes(window, cx, false);
16488    }
16489
16490    pub fn move_to_end_of_larger_syntax_node(
16491        &mut self,
16492        _: &MoveToEndOfLargerSyntaxNode,
16493        window: &mut Window,
16494        cx: &mut Context<Self>,
16495    ) {
16496        self.move_cursors_to_syntax_nodes(window, cx, true);
16497    }
16498
16499    fn move_cursors_to_syntax_nodes(
16500        &mut self,
16501        window: &mut Window,
16502        cx: &mut Context<Self>,
16503        move_to_end: bool,
16504    ) -> bool {
16505        let old_selections: Box<[_]> = self
16506            .selections
16507            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16508            .into();
16509        if old_selections.is_empty() {
16510            return false;
16511        }
16512
16513        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16514
16515        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16516        let buffer = self.buffer.read(cx).snapshot(cx);
16517
16518        let mut any_cursor_moved = false;
16519        let new_selections = old_selections
16520            .iter()
16521            .map(|selection| {
16522                if !selection.is_empty() {
16523                    return selection.clone();
16524                }
16525
16526                let selection_pos = selection.head();
16527                let old_range = selection_pos..selection_pos;
16528
16529                let mut new_pos = selection_pos;
16530                let mut search_range = old_range;
16531                while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16532                    search_range = range.clone();
16533                    if !node.is_named()
16534                        || display_map.intersects_fold(range.start)
16535                        || display_map.intersects_fold(range.end)
16536                        // If cursor is already at the end of the syntax node, continue searching
16537                        || (move_to_end && range.end == selection_pos)
16538                        // If cursor is already at the start of the syntax node, continue searching
16539                        || (!move_to_end && range.start == selection_pos)
16540                    {
16541                        continue;
16542                    }
16543
16544                    // If we found a string_content node, find the largest parent that is still string_content
16545                    // Enables us to skip to the end of strings without taking multiple steps inside the string
16546                    let (_, final_range) = if node.kind() == "string_content" {
16547                        let mut current_node = node;
16548                        let mut current_range = range;
16549                        while let Some((parent, parent_range)) =
16550                            buffer.syntax_ancestor(current_range.clone())
16551                        {
16552                            if parent.kind() == "string_content" {
16553                                current_node = parent;
16554                                current_range = parent_range;
16555                            } else {
16556                                break;
16557                            }
16558                        }
16559
16560                        (current_node, current_range)
16561                    } else {
16562                        (node, range)
16563                    };
16564
16565                    new_pos = if move_to_end {
16566                        final_range.end
16567                    } else {
16568                        final_range.start
16569                    };
16570
16571                    break;
16572                }
16573
16574                any_cursor_moved |= new_pos != selection_pos;
16575
16576                Selection {
16577                    id: selection.id,
16578                    start: new_pos,
16579                    end: new_pos,
16580                    goal: SelectionGoal::None,
16581                    reversed: false,
16582                }
16583            })
16584            .collect::<Vec<_>>();
16585
16586        self.change_selections(Default::default(), window, cx, |s| {
16587            s.select(new_selections);
16588        });
16589        self.request_autoscroll(Autoscroll::newest(), cx);
16590
16591        any_cursor_moved
16592    }
16593
16594    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
16595        if !EditorSettings::get_global(cx).gutter.runnables {
16596            self.clear_tasks();
16597            return Task::ready(());
16598        }
16599        let project = self.project().map(Entity::downgrade);
16600        let task_sources = self.lsp_task_sources(cx);
16601        let multi_buffer = self.buffer.downgrade();
16602        cx.spawn_in(window, async move |editor, cx| {
16603            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
16604            let Some(project) = project.and_then(|p| p.upgrade()) else {
16605                return;
16606            };
16607            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
16608                this.display_map.update(cx, |map, cx| map.snapshot(cx))
16609            }) else {
16610                return;
16611            };
16612
16613            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
16614            if hide_runnables {
16615                return;
16616            }
16617            let new_rows =
16618                cx.background_spawn({
16619                    let snapshot = display_snapshot.clone();
16620                    async move {
16621                        Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
16622                    }
16623                })
16624                    .await;
16625            let Ok(lsp_tasks) =
16626                cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
16627            else {
16628                return;
16629            };
16630            let lsp_tasks = lsp_tasks.await;
16631
16632            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
16633                lsp_tasks
16634                    .into_iter()
16635                    .flat_map(|(kind, tasks)| {
16636                        tasks.into_iter().filter_map(move |(location, task)| {
16637                            Some((kind.clone(), location?, task))
16638                        })
16639                    })
16640                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
16641                        let buffer = location.target.buffer;
16642                        let buffer_snapshot = buffer.read(cx).snapshot();
16643                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
16644                            |(excerpt_id, snapshot, _)| {
16645                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
16646                                    display_snapshot
16647                                        .buffer_snapshot()
16648                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
16649                                } else {
16650                                    None
16651                                }
16652                            },
16653                        );
16654                        if let Some(offset) = offset {
16655                            let task_buffer_range =
16656                                location.target.range.to_point(&buffer_snapshot);
16657                            let context_buffer_range =
16658                                task_buffer_range.to_offset(&buffer_snapshot);
16659                            let context_range = BufferOffset(context_buffer_range.start)
16660                                ..BufferOffset(context_buffer_range.end);
16661
16662                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
16663                                .or_insert_with(|| RunnableTasks {
16664                                    templates: Vec::new(),
16665                                    offset,
16666                                    column: task_buffer_range.start.column,
16667                                    extra_variables: HashMap::default(),
16668                                    context_range,
16669                                })
16670                                .templates
16671                                .push((kind, task.original_task().clone()));
16672                        }
16673
16674                        acc
16675                    })
16676            }) else {
16677                return;
16678            };
16679
16680            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
16681                buffer.language_settings(cx).tasks.prefer_lsp
16682            }) else {
16683                return;
16684            };
16685
16686            let rows = Self::runnable_rows(
16687                project,
16688                display_snapshot,
16689                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
16690                new_rows,
16691                cx.clone(),
16692            )
16693            .await;
16694            editor
16695                .update(cx, |editor, _| {
16696                    editor.clear_tasks();
16697                    for (key, mut value) in rows {
16698                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
16699                            value.templates.extend(lsp_tasks.templates);
16700                        }
16701
16702                        editor.insert_tasks(key, value);
16703                    }
16704                    for (key, value) in lsp_tasks_by_rows {
16705                        editor.insert_tasks(key, value);
16706                    }
16707                })
16708                .ok();
16709        })
16710    }
16711    fn fetch_runnable_ranges(
16712        snapshot: &DisplaySnapshot,
16713        range: Range<Anchor>,
16714    ) -> Vec<(Range<MultiBufferOffset>, language::RunnableRange)> {
16715        snapshot.buffer_snapshot().runnable_ranges(range).collect()
16716    }
16717
16718    fn runnable_rows(
16719        project: Entity<Project>,
16720        snapshot: DisplaySnapshot,
16721        prefer_lsp: bool,
16722        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
16723        cx: AsyncWindowContext,
16724    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
16725        cx.spawn(async move |cx| {
16726            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
16727            for (run_range, mut runnable) in runnable_ranges {
16728                let Some(tasks) = cx
16729                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
16730                    .ok()
16731                else {
16732                    continue;
16733                };
16734                let mut tasks = tasks.await;
16735
16736                if prefer_lsp {
16737                    tasks.retain(|(task_kind, _)| {
16738                        !matches!(task_kind, TaskSourceKind::Language { .. })
16739                    });
16740                }
16741                if tasks.is_empty() {
16742                    continue;
16743                }
16744
16745                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
16746                let Some(row) = snapshot
16747                    .buffer_snapshot()
16748                    .buffer_line_for_row(MultiBufferRow(point.row))
16749                    .map(|(_, range)| range.start.row)
16750                else {
16751                    continue;
16752                };
16753
16754                let context_range =
16755                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
16756                runnable_rows.push((
16757                    (runnable.buffer_id, row),
16758                    RunnableTasks {
16759                        templates: tasks,
16760                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
16761                        context_range,
16762                        column: point.column,
16763                        extra_variables: runnable.extra_captures,
16764                    },
16765                ));
16766            }
16767            runnable_rows
16768        })
16769    }
16770
16771    fn templates_with_tags(
16772        project: &Entity<Project>,
16773        runnable: &mut Runnable,
16774        cx: &mut App,
16775    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
16776        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
16777            let (worktree_id, file) = project
16778                .buffer_for_id(runnable.buffer, cx)
16779                .and_then(|buffer| buffer.read(cx).file())
16780                .map(|file| (file.worktree_id(cx), file.clone()))
16781                .unzip();
16782
16783            (
16784                project.task_store().read(cx).task_inventory().cloned(),
16785                worktree_id,
16786                file,
16787            )
16788        });
16789
16790        let tags = mem::take(&mut runnable.tags);
16791        let language = runnable.language.clone();
16792        cx.spawn(async move |cx| {
16793            let mut templates_with_tags = Vec::new();
16794            if let Some(inventory) = inventory {
16795                for RunnableTag(tag) in tags {
16796                    let new_tasks = inventory.update(cx, |inventory, cx| {
16797                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
16798                    });
16799                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
16800                        move |(_, template)| {
16801                            template.tags.iter().any(|source_tag| source_tag == &tag)
16802                        },
16803                    ));
16804                }
16805            }
16806            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
16807
16808            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
16809                // Strongest source wins; if we have worktree tag binding, prefer that to
16810                // global and language bindings;
16811                // if we have a global binding, prefer that to language binding.
16812                let first_mismatch = templates_with_tags
16813                    .iter()
16814                    .position(|(tag_source, _)| tag_source != leading_tag_source);
16815                if let Some(index) = first_mismatch {
16816                    templates_with_tags.truncate(index);
16817                }
16818            }
16819
16820            templates_with_tags
16821        })
16822    }
16823
16824    pub fn move_to_enclosing_bracket(
16825        &mut self,
16826        _: &MoveToEnclosingBracket,
16827        window: &mut Window,
16828        cx: &mut Context<Self>,
16829    ) {
16830        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16831        self.change_selections(Default::default(), window, cx, |s| {
16832            s.move_offsets_with(|snapshot, selection| {
16833                let Some(enclosing_bracket_ranges) =
16834                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
16835                else {
16836                    return;
16837                };
16838
16839                let mut best_length = usize::MAX;
16840                let mut best_inside = false;
16841                let mut best_in_bracket_range = false;
16842                let mut best_destination = None;
16843                for (open, close) in enclosing_bracket_ranges {
16844                    let close = close.to_inclusive();
16845                    let length = *close.end() - open.start;
16846                    let inside = selection.start >= open.end && selection.end <= *close.start();
16847                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
16848                        || close.contains(&selection.head());
16849
16850                    // If best is next to a bracket and current isn't, skip
16851                    if !in_bracket_range && best_in_bracket_range {
16852                        continue;
16853                    }
16854
16855                    // Prefer smaller lengths unless best is inside and current isn't
16856                    if length > best_length && (best_inside || !inside) {
16857                        continue;
16858                    }
16859
16860                    best_length = length;
16861                    best_inside = inside;
16862                    best_in_bracket_range = in_bracket_range;
16863                    best_destination = Some(
16864                        if close.contains(&selection.start) && close.contains(&selection.end) {
16865                            if inside { open.end } else { open.start }
16866                        } else if inside {
16867                            *close.start()
16868                        } else {
16869                            *close.end()
16870                        },
16871                    );
16872                }
16873
16874                if let Some(destination) = best_destination {
16875                    selection.collapse_to(destination, SelectionGoal::None);
16876                }
16877            })
16878        });
16879    }
16880
16881    pub fn undo_selection(
16882        &mut self,
16883        _: &UndoSelection,
16884        window: &mut Window,
16885        cx: &mut Context<Self>,
16886    ) {
16887        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16888        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
16889            self.selection_history.mode = SelectionHistoryMode::Undoing;
16890            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
16891                this.end_selection(window, cx);
16892                this.change_selections(
16893                    SelectionEffects::scroll(Autoscroll::newest()),
16894                    window,
16895                    cx,
16896                    |s| s.select_anchors(entry.selections.to_vec()),
16897                );
16898            });
16899            self.selection_history.mode = SelectionHistoryMode::Normal;
16900
16901            self.select_next_state = entry.select_next_state;
16902            self.select_prev_state = entry.select_prev_state;
16903            self.add_selections_state = entry.add_selections_state;
16904        }
16905    }
16906
16907    pub fn redo_selection(
16908        &mut self,
16909        _: &RedoSelection,
16910        window: &mut Window,
16911        cx: &mut Context<Self>,
16912    ) {
16913        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16914        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
16915            self.selection_history.mode = SelectionHistoryMode::Redoing;
16916            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
16917                this.end_selection(window, cx);
16918                this.change_selections(
16919                    SelectionEffects::scroll(Autoscroll::newest()),
16920                    window,
16921                    cx,
16922                    |s| s.select_anchors(entry.selections.to_vec()),
16923                );
16924            });
16925            self.selection_history.mode = SelectionHistoryMode::Normal;
16926
16927            self.select_next_state = entry.select_next_state;
16928            self.select_prev_state = entry.select_prev_state;
16929            self.add_selections_state = entry.add_selections_state;
16930        }
16931    }
16932
16933    pub fn expand_excerpts(
16934        &mut self,
16935        action: &ExpandExcerpts,
16936        _: &mut Window,
16937        cx: &mut Context<Self>,
16938    ) {
16939        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
16940    }
16941
16942    pub fn expand_excerpts_down(
16943        &mut self,
16944        action: &ExpandExcerptsDown,
16945        _: &mut Window,
16946        cx: &mut Context<Self>,
16947    ) {
16948        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
16949    }
16950
16951    pub fn expand_excerpts_up(
16952        &mut self,
16953        action: &ExpandExcerptsUp,
16954        _: &mut Window,
16955        cx: &mut Context<Self>,
16956    ) {
16957        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
16958    }
16959
16960    pub fn expand_excerpts_for_direction(
16961        &mut self,
16962        lines: u32,
16963        direction: ExpandExcerptDirection,
16964        cx: &mut Context<Self>,
16965    ) {
16966        let selections = self.selections.disjoint_anchors_arc();
16967
16968        let lines = if lines == 0 {
16969            EditorSettings::get_global(cx).expand_excerpt_lines
16970        } else {
16971            lines
16972        };
16973
16974        let snapshot = self.buffer.read(cx).snapshot(cx);
16975        let mut excerpt_ids = selections
16976            .iter()
16977            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
16978            .collect::<Vec<_>>();
16979        excerpt_ids.sort();
16980        excerpt_ids.dedup();
16981
16982        if self.delegate_expand_excerpts {
16983            cx.emit(EditorEvent::ExpandExcerptsRequested {
16984                excerpt_ids,
16985                lines,
16986                direction,
16987            });
16988            return;
16989        }
16990
16991        self.buffer.update(cx, |buffer, cx| {
16992            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
16993        })
16994    }
16995
16996    pub fn expand_excerpt(
16997        &mut self,
16998        excerpt: ExcerptId,
16999        direction: ExpandExcerptDirection,
17000        window: &mut Window,
17001        cx: &mut Context<Self>,
17002    ) {
17003        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17004
17005        if self.delegate_expand_excerpts {
17006            cx.emit(EditorEvent::ExpandExcerptsRequested {
17007                excerpt_ids: vec![excerpt],
17008                lines: lines_to_expand,
17009                direction,
17010            });
17011            return;
17012        }
17013
17014        let current_scroll_position = self.scroll_position(cx);
17015        let mut scroll = None;
17016
17017        if direction == ExpandExcerptDirection::Down {
17018            let multi_buffer = self.buffer.read(cx);
17019            let snapshot = multi_buffer.snapshot(cx);
17020            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17021                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17022                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17023            {
17024                let buffer_snapshot = buffer.read(cx).snapshot();
17025                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17026                let last_row = buffer_snapshot.max_point().row;
17027                let lines_below = last_row.saturating_sub(excerpt_end_row);
17028                if lines_below >= lines_to_expand {
17029                    scroll = Some(
17030                        current_scroll_position
17031                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17032                    );
17033                }
17034            }
17035        }
17036        if direction == ExpandExcerptDirection::Up
17037            && self
17038                .buffer
17039                .read(cx)
17040                .snapshot(cx)
17041                .excerpt_before(excerpt)
17042                .is_none()
17043        {
17044            scroll = Some(current_scroll_position);
17045        }
17046
17047        self.buffer.update(cx, |buffer, cx| {
17048            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17049        });
17050
17051        if let Some(new_scroll_position) = scroll {
17052            self.set_scroll_position(new_scroll_position, window, cx);
17053        }
17054    }
17055
17056    pub fn go_to_singleton_buffer_point(
17057        &mut self,
17058        point: Point,
17059        window: &mut Window,
17060        cx: &mut Context<Self>,
17061    ) {
17062        self.go_to_singleton_buffer_range(point..point, window, cx);
17063    }
17064
17065    pub fn go_to_singleton_buffer_range(
17066        &mut self,
17067        range: Range<Point>,
17068        window: &mut Window,
17069        cx: &mut Context<Self>,
17070    ) {
17071        let multibuffer = self.buffer().read(cx);
17072        let Some(buffer) = multibuffer.as_singleton() else {
17073            return;
17074        };
17075        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17076            return;
17077        };
17078        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17079            return;
17080        };
17081        self.change_selections(
17082            SelectionEffects::default().nav_history(true),
17083            window,
17084            cx,
17085            |s| s.select_anchor_ranges([start..end]),
17086        );
17087    }
17088
17089    pub fn go_to_diagnostic(
17090        &mut self,
17091        action: &GoToDiagnostic,
17092        window: &mut Window,
17093        cx: &mut Context<Self>,
17094    ) {
17095        if !self.diagnostics_enabled() {
17096            return;
17097        }
17098        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17099        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17100    }
17101
17102    pub fn go_to_prev_diagnostic(
17103        &mut self,
17104        action: &GoToPreviousDiagnostic,
17105        window: &mut Window,
17106        cx: &mut Context<Self>,
17107    ) {
17108        if !self.diagnostics_enabled() {
17109            return;
17110        }
17111        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17112        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17113    }
17114
17115    pub fn go_to_diagnostic_impl(
17116        &mut self,
17117        direction: Direction,
17118        severity: GoToDiagnosticSeverityFilter,
17119        window: &mut Window,
17120        cx: &mut Context<Self>,
17121    ) {
17122        let buffer = self.buffer.read(cx).snapshot(cx);
17123        let selection = self
17124            .selections
17125            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17126
17127        let mut active_group_id = None;
17128        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17129            && active_group.active_range.start.to_offset(&buffer) == selection.start
17130        {
17131            active_group_id = Some(active_group.group_id);
17132        }
17133
17134        fn filtered<'a>(
17135            severity: GoToDiagnosticSeverityFilter,
17136            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17137        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17138            diagnostics
17139                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17140                .filter(|entry| entry.range.start != entry.range.end)
17141                .filter(|entry| !entry.diagnostic.is_unnecessary)
17142        }
17143
17144        let before = filtered(
17145            severity,
17146            buffer
17147                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17148                .filter(|entry| entry.range.start <= selection.start),
17149        );
17150        let after = filtered(
17151            severity,
17152            buffer
17153                .diagnostics_in_range(selection.start..buffer.len())
17154                .filter(|entry| entry.range.start >= selection.start),
17155        );
17156
17157        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17158        if direction == Direction::Prev {
17159            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17160            {
17161                for diagnostic in prev_diagnostics.into_iter().rev() {
17162                    if diagnostic.range.start != selection.start
17163                        || active_group_id
17164                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17165                    {
17166                        found = Some(diagnostic);
17167                        break 'outer;
17168                    }
17169                }
17170            }
17171        } else {
17172            for diagnostic in after.chain(before) {
17173                if diagnostic.range.start != selection.start
17174                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17175                {
17176                    found = Some(diagnostic);
17177                    break;
17178                }
17179            }
17180        }
17181        let Some(next_diagnostic) = found else {
17182            return;
17183        };
17184
17185        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17186        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17187            return;
17188        };
17189        let snapshot = self.snapshot(window, cx);
17190        if snapshot.intersects_fold(next_diagnostic.range.start) {
17191            self.unfold_ranges(
17192                std::slice::from_ref(&next_diagnostic.range),
17193                true,
17194                false,
17195                cx,
17196            );
17197        }
17198        self.change_selections(Default::default(), window, cx, |s| {
17199            s.select_ranges(vec![
17200                next_diagnostic.range.start..next_diagnostic.range.start,
17201            ])
17202        });
17203        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17204        self.refresh_edit_prediction(false, true, window, cx);
17205    }
17206
17207    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17208        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17209        let snapshot = self.snapshot(window, cx);
17210        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17211        self.go_to_hunk_before_or_after_position(
17212            &snapshot,
17213            selection.head(),
17214            Direction::Next,
17215            window,
17216            cx,
17217        );
17218    }
17219
17220    pub fn go_to_hunk_before_or_after_position(
17221        &mut self,
17222        snapshot: &EditorSnapshot,
17223        position: Point,
17224        direction: Direction,
17225        window: &mut Window,
17226        cx: &mut Context<Editor>,
17227    ) {
17228        let row = if direction == Direction::Next {
17229            self.hunk_after_position(snapshot, position)
17230                .map(|hunk| hunk.row_range.start)
17231        } else {
17232            self.hunk_before_position(snapshot, position)
17233        };
17234
17235        if let Some(row) = row {
17236            let destination = Point::new(row.0, 0);
17237            let autoscroll = Autoscroll::center();
17238
17239            self.unfold_ranges(&[destination..destination], false, false, cx);
17240            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17241                s.select_ranges([destination..destination]);
17242            });
17243        }
17244    }
17245
17246    fn hunk_after_position(
17247        &mut self,
17248        snapshot: &EditorSnapshot,
17249        position: Point,
17250    ) -> Option<MultiBufferDiffHunk> {
17251        snapshot
17252            .buffer_snapshot()
17253            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17254            .find(|hunk| hunk.row_range.start.0 > position.row)
17255            .or_else(|| {
17256                snapshot
17257                    .buffer_snapshot()
17258                    .diff_hunks_in_range(Point::zero()..position)
17259                    .find(|hunk| hunk.row_range.end.0 < position.row)
17260            })
17261    }
17262
17263    fn go_to_prev_hunk(
17264        &mut self,
17265        _: &GoToPreviousHunk,
17266        window: &mut Window,
17267        cx: &mut Context<Self>,
17268    ) {
17269        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17270        let snapshot = self.snapshot(window, cx);
17271        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17272        self.go_to_hunk_before_or_after_position(
17273            &snapshot,
17274            selection.head(),
17275            Direction::Prev,
17276            window,
17277            cx,
17278        );
17279    }
17280
17281    fn hunk_before_position(
17282        &mut self,
17283        snapshot: &EditorSnapshot,
17284        position: Point,
17285    ) -> Option<MultiBufferRow> {
17286        snapshot
17287            .buffer_snapshot()
17288            .diff_hunk_before(position)
17289            .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17290    }
17291
17292    fn go_to_next_change(
17293        &mut self,
17294        _: &GoToNextChange,
17295        window: &mut Window,
17296        cx: &mut Context<Self>,
17297    ) {
17298        if let Some(selections) = self
17299            .change_list
17300            .next_change(1, Direction::Next)
17301            .map(|s| s.to_vec())
17302        {
17303            self.change_selections(Default::default(), window, cx, |s| {
17304                let map = s.display_snapshot();
17305                s.select_display_ranges(selections.iter().map(|a| {
17306                    let point = a.to_display_point(&map);
17307                    point..point
17308                }))
17309            })
17310        }
17311    }
17312
17313    fn go_to_previous_change(
17314        &mut self,
17315        _: &GoToPreviousChange,
17316        window: &mut Window,
17317        cx: &mut Context<Self>,
17318    ) {
17319        if let Some(selections) = self
17320            .change_list
17321            .next_change(1, Direction::Prev)
17322            .map(|s| s.to_vec())
17323        {
17324            self.change_selections(Default::default(), window, cx, |s| {
17325                let map = s.display_snapshot();
17326                s.select_display_ranges(selections.iter().map(|a| {
17327                    let point = a.to_display_point(&map);
17328                    point..point
17329                }))
17330            })
17331        }
17332    }
17333
17334    pub fn go_to_next_document_highlight(
17335        &mut self,
17336        _: &GoToNextDocumentHighlight,
17337        window: &mut Window,
17338        cx: &mut Context<Self>,
17339    ) {
17340        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17341    }
17342
17343    pub fn go_to_prev_document_highlight(
17344        &mut self,
17345        _: &GoToPreviousDocumentHighlight,
17346        window: &mut Window,
17347        cx: &mut Context<Self>,
17348    ) {
17349        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17350    }
17351
17352    pub fn go_to_document_highlight_before_or_after_position(
17353        &mut self,
17354        direction: Direction,
17355        window: &mut Window,
17356        cx: &mut Context<Editor>,
17357    ) {
17358        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17359        let snapshot = self.snapshot(window, cx);
17360        let buffer = &snapshot.buffer_snapshot();
17361        let position = self
17362            .selections
17363            .newest::<Point>(&snapshot.display_snapshot)
17364            .head();
17365        let anchor_position = buffer.anchor_after(position);
17366
17367        // Get all document highlights (both read and write)
17368        let mut all_highlights = Vec::new();
17369
17370        if let Some((_, read_highlights)) = self
17371            .background_highlights
17372            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
17373        {
17374            all_highlights.extend(read_highlights.iter());
17375        }
17376
17377        if let Some((_, write_highlights)) = self
17378            .background_highlights
17379            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
17380        {
17381            all_highlights.extend(write_highlights.iter());
17382        }
17383
17384        if all_highlights.is_empty() {
17385            return;
17386        }
17387
17388        // Sort highlights by position
17389        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17390
17391        let target_highlight = match direction {
17392            Direction::Next => {
17393                // Find the first highlight after the current position
17394                all_highlights
17395                    .iter()
17396                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17397            }
17398            Direction::Prev => {
17399                // Find the last highlight before the current position
17400                all_highlights
17401                    .iter()
17402                    .rev()
17403                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17404            }
17405        };
17406
17407        if let Some(highlight) = target_highlight {
17408            let destination = highlight.start.to_point(buffer);
17409            let autoscroll = Autoscroll::center();
17410
17411            self.unfold_ranges(&[destination..destination], false, false, cx);
17412            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17413                s.select_ranges([destination..destination]);
17414            });
17415        }
17416    }
17417
17418    fn go_to_line<T: 'static>(
17419        &mut self,
17420        position: Anchor,
17421        highlight_color: Option<Hsla>,
17422        window: &mut Window,
17423        cx: &mut Context<Self>,
17424    ) {
17425        let snapshot = self.snapshot(window, cx).display_snapshot;
17426        let position = position.to_point(&snapshot.buffer_snapshot());
17427        let start = snapshot
17428            .buffer_snapshot()
17429            .clip_point(Point::new(position.row, 0), Bias::Left);
17430        let end = start + Point::new(1, 0);
17431        let start = snapshot.buffer_snapshot().anchor_before(start);
17432        let end = snapshot.buffer_snapshot().anchor_before(end);
17433
17434        self.highlight_rows::<T>(
17435            start..end,
17436            highlight_color
17437                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17438            Default::default(),
17439            cx,
17440        );
17441
17442        if self.buffer.read(cx).is_singleton() {
17443            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17444        }
17445    }
17446
17447    pub fn go_to_definition(
17448        &mut self,
17449        _: &GoToDefinition,
17450        window: &mut Window,
17451        cx: &mut Context<Self>,
17452    ) -> Task<Result<Navigated>> {
17453        let definition =
17454            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17455        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17456        cx.spawn_in(window, async move |editor, cx| {
17457            if definition.await? == Navigated::Yes {
17458                return Ok(Navigated::Yes);
17459            }
17460            match fallback_strategy {
17461                GoToDefinitionFallback::None => Ok(Navigated::No),
17462                GoToDefinitionFallback::FindAllReferences => {
17463                    match editor.update_in(cx, |editor, window, cx| {
17464                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17465                    })? {
17466                        Some(references) => references.await,
17467                        None => Ok(Navigated::No),
17468                    }
17469                }
17470            }
17471        })
17472    }
17473
17474    pub fn go_to_declaration(
17475        &mut self,
17476        _: &GoToDeclaration,
17477        window: &mut Window,
17478        cx: &mut Context<Self>,
17479    ) -> Task<Result<Navigated>> {
17480        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17481    }
17482
17483    pub fn go_to_declaration_split(
17484        &mut self,
17485        _: &GoToDeclaration,
17486        window: &mut Window,
17487        cx: &mut Context<Self>,
17488    ) -> Task<Result<Navigated>> {
17489        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17490    }
17491
17492    pub fn go_to_implementation(
17493        &mut self,
17494        _: &GoToImplementation,
17495        window: &mut Window,
17496        cx: &mut Context<Self>,
17497    ) -> Task<Result<Navigated>> {
17498        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17499    }
17500
17501    pub fn go_to_implementation_split(
17502        &mut self,
17503        _: &GoToImplementationSplit,
17504        window: &mut Window,
17505        cx: &mut Context<Self>,
17506    ) -> Task<Result<Navigated>> {
17507        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17508    }
17509
17510    pub fn go_to_type_definition(
17511        &mut self,
17512        _: &GoToTypeDefinition,
17513        window: &mut Window,
17514        cx: &mut Context<Self>,
17515    ) -> Task<Result<Navigated>> {
17516        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17517    }
17518
17519    pub fn go_to_definition_split(
17520        &mut self,
17521        _: &GoToDefinitionSplit,
17522        window: &mut Window,
17523        cx: &mut Context<Self>,
17524    ) -> Task<Result<Navigated>> {
17525        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17526    }
17527
17528    pub fn go_to_type_definition_split(
17529        &mut self,
17530        _: &GoToTypeDefinitionSplit,
17531        window: &mut Window,
17532        cx: &mut Context<Self>,
17533    ) -> Task<Result<Navigated>> {
17534        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17535    }
17536
17537    fn go_to_definition_of_kind(
17538        &mut self,
17539        kind: GotoDefinitionKind,
17540        split: bool,
17541        window: &mut Window,
17542        cx: &mut Context<Self>,
17543    ) -> Task<Result<Navigated>> {
17544        let Some(provider) = self.semantics_provider.clone() else {
17545            return Task::ready(Ok(Navigated::No));
17546        };
17547        let head = self
17548            .selections
17549            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
17550            .head();
17551        let buffer = self.buffer.read(cx);
17552        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
17553            return Task::ready(Ok(Navigated::No));
17554        };
17555        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
17556            return Task::ready(Ok(Navigated::No));
17557        };
17558
17559        cx.spawn_in(window, async move |editor, cx| {
17560            let Some(definitions) = definitions.await? else {
17561                return Ok(Navigated::No);
17562            };
17563            let navigated = editor
17564                .update_in(cx, |editor, window, cx| {
17565                    editor.navigate_to_hover_links(
17566                        Some(kind),
17567                        definitions
17568                            .into_iter()
17569                            .filter(|location| {
17570                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
17571                            })
17572                            .map(HoverLink::Text)
17573                            .collect::<Vec<_>>(),
17574                        split,
17575                        window,
17576                        cx,
17577                    )
17578                })?
17579                .await?;
17580            anyhow::Ok(navigated)
17581        })
17582    }
17583
17584    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
17585        let selection = self.selections.newest_anchor();
17586        let head = selection.head();
17587        let tail = selection.tail();
17588
17589        let Some((buffer, start_position)) =
17590            self.buffer.read(cx).text_anchor_for_position(head, cx)
17591        else {
17592            return;
17593        };
17594
17595        let end_position = if head != tail {
17596            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
17597                return;
17598            };
17599            Some(pos)
17600        } else {
17601            None
17602        };
17603
17604        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
17605            let url = if let Some(end_pos) = end_position {
17606                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
17607            } else {
17608                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
17609            };
17610
17611            if let Some(url) = url {
17612                cx.update(|window, cx| {
17613                    if parse_zed_link(&url, cx).is_some() {
17614                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
17615                    } else {
17616                        cx.open_url(&url);
17617                    }
17618                })?;
17619            }
17620
17621            anyhow::Ok(())
17622        });
17623
17624        url_finder.detach();
17625    }
17626
17627    pub fn open_selected_filename(
17628        &mut self,
17629        _: &OpenSelectedFilename,
17630        window: &mut Window,
17631        cx: &mut Context<Self>,
17632    ) {
17633        let Some(workspace) = self.workspace() else {
17634            return;
17635        };
17636
17637        let position = self.selections.newest_anchor().head();
17638
17639        let Some((buffer, buffer_position)) =
17640            self.buffer.read(cx).text_anchor_for_position(position, cx)
17641        else {
17642            return;
17643        };
17644
17645        let project = self.project.clone();
17646
17647        cx.spawn_in(window, async move |_, cx| {
17648            let result = find_file(&buffer, project, buffer_position, cx).await;
17649
17650            if let Some((_, path)) = result {
17651                workspace
17652                    .update_in(cx, |workspace, window, cx| {
17653                        workspace.open_resolved_path(path, window, cx)
17654                    })?
17655                    .await?;
17656            }
17657            anyhow::Ok(())
17658        })
17659        .detach();
17660    }
17661
17662    pub(crate) fn navigate_to_hover_links(
17663        &mut self,
17664        kind: Option<GotoDefinitionKind>,
17665        definitions: Vec<HoverLink>,
17666        split: bool,
17667        window: &mut Window,
17668        cx: &mut Context<Editor>,
17669    ) -> Task<Result<Navigated>> {
17670        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
17671        let mut first_url_or_file = None;
17672        let definitions: Vec<_> = definitions
17673            .into_iter()
17674            .filter_map(|def| match def {
17675                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
17676                HoverLink::InlayHint(lsp_location, server_id) => {
17677                    let computation =
17678                        self.compute_target_location(lsp_location, server_id, window, cx);
17679                    Some(cx.background_spawn(computation))
17680                }
17681                HoverLink::Url(url) => {
17682                    first_url_or_file = Some(Either::Left(url));
17683                    None
17684                }
17685                HoverLink::File(path) => {
17686                    first_url_or_file = Some(Either::Right(path));
17687                    None
17688                }
17689            })
17690            .collect();
17691
17692        let workspace = self.workspace();
17693
17694        cx.spawn_in(window, async move |editor, cx| {
17695            let locations: Vec<Location> = future::join_all(definitions)
17696                .await
17697                .into_iter()
17698                .filter_map(|location| location.transpose())
17699                .collect::<Result<_>>()
17700                .context("location tasks")?;
17701            let mut locations = cx.update(|_, cx| {
17702                locations
17703                    .into_iter()
17704                    .map(|location| {
17705                        let buffer = location.buffer.read(cx);
17706                        (location.buffer, location.range.to_point(buffer))
17707                    })
17708                    .into_group_map()
17709            })?;
17710            let mut num_locations = 0;
17711            for ranges in locations.values_mut() {
17712                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
17713                ranges.dedup();
17714                num_locations += ranges.len();
17715            }
17716
17717            if num_locations > 1 {
17718                let tab_kind = match kind {
17719                    Some(GotoDefinitionKind::Implementation) => "Implementations",
17720                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
17721                    Some(GotoDefinitionKind::Declaration) => "Declarations",
17722                    Some(GotoDefinitionKind::Type) => "Types",
17723                };
17724                let title = editor
17725                    .update_in(cx, |_, _, cx| {
17726                        let target = locations
17727                            .iter()
17728                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
17729                            .map(|(buffer, location)| {
17730                                buffer
17731                                    .read(cx)
17732                                    .text_for_range(location.clone())
17733                                    .collect::<String>()
17734                            })
17735                            .filter(|text| !text.contains('\n'))
17736                            .unique()
17737                            .take(3)
17738                            .join(", ");
17739                        if target.is_empty() {
17740                            tab_kind.to_owned()
17741                        } else {
17742                            format!("{tab_kind} for {target}")
17743                        }
17744                    })
17745                    .context("buffer title")?;
17746
17747                let Some(workspace) = workspace else {
17748                    return Ok(Navigated::No);
17749                };
17750
17751                let opened = workspace
17752                    .update_in(cx, |workspace, window, cx| {
17753                        let allow_preview = PreviewTabsSettings::get_global(cx)
17754                            .enable_preview_multibuffer_from_code_navigation;
17755                        Self::open_locations_in_multibuffer(
17756                            workspace,
17757                            locations,
17758                            title,
17759                            split,
17760                            allow_preview,
17761                            MultibufferSelectionMode::First,
17762                            window,
17763                            cx,
17764                        )
17765                    })
17766                    .is_ok();
17767
17768                anyhow::Ok(Navigated::from_bool(opened))
17769            } else if num_locations == 0 {
17770                // If there is one url or file, open it directly
17771                match first_url_or_file {
17772                    Some(Either::Left(url)) => {
17773                        cx.update(|window, cx| {
17774                            if parse_zed_link(&url, cx).is_some() {
17775                                window
17776                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
17777                            } else {
17778                                cx.open_url(&url);
17779                            }
17780                        })?;
17781                        Ok(Navigated::Yes)
17782                    }
17783                    Some(Either::Right(path)) => {
17784                        // TODO(andrew): respect preview tab settings
17785                        //               `enable_keep_preview_on_code_navigation` and
17786                        //               `enable_preview_file_from_code_navigation`
17787                        let Some(workspace) = workspace else {
17788                            return Ok(Navigated::No);
17789                        };
17790                        workspace
17791                            .update_in(cx, |workspace, window, cx| {
17792                                workspace.open_resolved_path(path, window, cx)
17793                            })?
17794                            .await?;
17795                        Ok(Navigated::Yes)
17796                    }
17797                    None => Ok(Navigated::No),
17798                }
17799            } else {
17800                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
17801                let target_range = target_ranges.first().unwrap().clone();
17802
17803                editor.update_in(cx, |editor, window, cx| {
17804                    let range = target_range.to_point(target_buffer.read(cx));
17805                    let range = editor.range_for_match(&range);
17806                    let range = collapse_multiline_range(range);
17807
17808                    if !split
17809                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
17810                    {
17811                        editor.go_to_singleton_buffer_range(range, window, cx);
17812                    } else {
17813                        let Some(workspace) = workspace else {
17814                            return Navigated::No;
17815                        };
17816                        let pane = workspace.read(cx).active_pane().clone();
17817                        window.defer(cx, move |window, cx| {
17818                            let target_editor: Entity<Self> =
17819                                workspace.update(cx, |workspace, cx| {
17820                                    let pane = if split {
17821                                        workspace.adjacent_pane(window, cx)
17822                                    } else {
17823                                        workspace.active_pane().clone()
17824                                    };
17825
17826                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
17827                                    let keep_old_preview = preview_tabs_settings
17828                                        .enable_keep_preview_on_code_navigation;
17829                                    let allow_new_preview = preview_tabs_settings
17830                                        .enable_preview_file_from_code_navigation;
17831
17832                                    workspace.open_project_item(
17833                                        pane,
17834                                        target_buffer.clone(),
17835                                        true,
17836                                        true,
17837                                        keep_old_preview,
17838                                        allow_new_preview,
17839                                        window,
17840                                        cx,
17841                                    )
17842                                });
17843                            target_editor.update(cx, |target_editor, cx| {
17844                                // When selecting a definition in a different buffer, disable the nav history
17845                                // to avoid creating a history entry at the previous cursor location.
17846                                pane.update(cx, |pane, _| pane.disable_history());
17847                                target_editor.go_to_singleton_buffer_range(range, window, cx);
17848                                pane.update(cx, |pane, _| pane.enable_history());
17849                            });
17850                        });
17851                    }
17852                    Navigated::Yes
17853                })
17854            }
17855        })
17856    }
17857
17858    fn compute_target_location(
17859        &self,
17860        lsp_location: lsp::Location,
17861        server_id: LanguageServerId,
17862        window: &mut Window,
17863        cx: &mut Context<Self>,
17864    ) -> Task<anyhow::Result<Option<Location>>> {
17865        let Some(project) = self.project.clone() else {
17866            return Task::ready(Ok(None));
17867        };
17868
17869        cx.spawn_in(window, async move |editor, cx| {
17870            let location_task = editor.update(cx, |_, cx| {
17871                project.update(cx, |project, cx| {
17872                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
17873                })
17874            })?;
17875            let location = Some({
17876                let target_buffer_handle = location_task.await.context("open local buffer")?;
17877                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
17878                    let target_start = target_buffer
17879                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
17880                    let target_end = target_buffer
17881                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
17882                    target_buffer.anchor_after(target_start)
17883                        ..target_buffer.anchor_before(target_end)
17884                });
17885                Location {
17886                    buffer: target_buffer_handle,
17887                    range,
17888                }
17889            });
17890            Ok(location)
17891        })
17892    }
17893
17894    fn go_to_next_reference(
17895        &mut self,
17896        _: &GoToNextReference,
17897        window: &mut Window,
17898        cx: &mut Context<Self>,
17899    ) {
17900        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
17901        if let Some(task) = task {
17902            task.detach();
17903        };
17904    }
17905
17906    fn go_to_prev_reference(
17907        &mut self,
17908        _: &GoToPreviousReference,
17909        window: &mut Window,
17910        cx: &mut Context<Self>,
17911    ) {
17912        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
17913        if let Some(task) = task {
17914            task.detach();
17915        };
17916    }
17917
17918    pub fn go_to_reference_before_or_after_position(
17919        &mut self,
17920        direction: Direction,
17921        count: usize,
17922        window: &mut Window,
17923        cx: &mut Context<Self>,
17924    ) -> Option<Task<Result<()>>> {
17925        let selection = self.selections.newest_anchor();
17926        let head = selection.head();
17927
17928        let multi_buffer = self.buffer.read(cx);
17929
17930        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
17931        let workspace = self.workspace()?;
17932        let project = workspace.read(cx).project().clone();
17933        let references =
17934            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
17935        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
17936            let Some(locations) = references.await? else {
17937                return Ok(());
17938            };
17939
17940            if locations.is_empty() {
17941                // totally normal - the cursor may be on something which is not
17942                // a symbol (e.g. a keyword)
17943                log::info!("no references found under cursor");
17944                return Ok(());
17945            }
17946
17947            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
17948
17949            let (locations, current_location_index) =
17950                multi_buffer.update(cx, |multi_buffer, cx| {
17951                    let mut locations = locations
17952                        .into_iter()
17953                        .filter_map(|loc| {
17954                            let start = multi_buffer.buffer_anchor_to_anchor(
17955                                &loc.buffer,
17956                                loc.range.start,
17957                                cx,
17958                            )?;
17959                            let end = multi_buffer.buffer_anchor_to_anchor(
17960                                &loc.buffer,
17961                                loc.range.end,
17962                                cx,
17963                            )?;
17964                            Some(start..end)
17965                        })
17966                        .collect::<Vec<_>>();
17967
17968                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
17969                    // There is an O(n) implementation, but given this list will be
17970                    // small (usually <100 items), the extra O(log(n)) factor isn't
17971                    // worth the (surprisingly large amount of) extra complexity.
17972                    locations
17973                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
17974
17975                    let head_offset = head.to_offset(&multi_buffer_snapshot);
17976
17977                    let current_location_index = locations.iter().position(|loc| {
17978                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
17979                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
17980                    });
17981
17982                    (locations, current_location_index)
17983                });
17984
17985            let Some(current_location_index) = current_location_index else {
17986                // This indicates something has gone wrong, because we already
17987                // handle the "no references" case above
17988                log::error!(
17989                    "failed to find current reference under cursor. Total references: {}",
17990                    locations.len()
17991                );
17992                return Ok(());
17993            };
17994
17995            let destination_location_index = match direction {
17996                Direction::Next => (current_location_index + count) % locations.len(),
17997                Direction::Prev => {
17998                    (current_location_index + locations.len() - count % locations.len())
17999                        % locations.len()
18000                }
18001            };
18002
18003            // TODO(cameron): is this needed?
18004            // the thinking is to avoid "jumping to the current location" (avoid
18005            // polluting "jumplist" in vim terms)
18006            if current_location_index == destination_location_index {
18007                return Ok(());
18008            }
18009
18010            let Range { start, end } = locations[destination_location_index];
18011
18012            editor.update_in(cx, |editor, window, cx| {
18013                let effects = SelectionEffects::default();
18014
18015                editor.unfold_ranges(&[start..end], false, false, cx);
18016                editor.change_selections(effects, window, cx, |s| {
18017                    s.select_ranges([start..start]);
18018                });
18019            })?;
18020
18021            Ok(())
18022        }))
18023    }
18024
18025    pub fn find_all_references(
18026        &mut self,
18027        action: &FindAllReferences,
18028        window: &mut Window,
18029        cx: &mut Context<Self>,
18030    ) -> Option<Task<Result<Navigated>>> {
18031        let always_open_multibuffer = action.always_open_multibuffer;
18032        let selection = self.selections.newest_anchor();
18033        let multi_buffer = self.buffer.read(cx);
18034        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18035        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18036        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18037        let head = selection_offset.head();
18038
18039        let head_anchor = multi_buffer_snapshot.anchor_at(
18040            head,
18041            if head < selection_offset.tail() {
18042                Bias::Right
18043            } else {
18044                Bias::Left
18045            },
18046        );
18047
18048        match self
18049            .find_all_references_task_sources
18050            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18051        {
18052            Ok(_) => {
18053                log::info!(
18054                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18055                );
18056                return None;
18057            }
18058            Err(i) => {
18059                self.find_all_references_task_sources.insert(i, head_anchor);
18060            }
18061        }
18062
18063        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18064        let workspace = self.workspace()?;
18065        let project = workspace.read(cx).project().clone();
18066        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18067        Some(cx.spawn_in(window, async move |editor, cx| {
18068            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18069                if let Ok(i) = editor
18070                    .find_all_references_task_sources
18071                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18072                {
18073                    editor.find_all_references_task_sources.remove(i);
18074                }
18075            });
18076
18077            let Some(locations) = references.await? else {
18078                return anyhow::Ok(Navigated::No);
18079            };
18080            let mut locations = cx.update(|_, cx| {
18081                locations
18082                    .into_iter()
18083                    .map(|location| {
18084                        let buffer = location.buffer.read(cx);
18085                        (location.buffer, location.range.to_point(buffer))
18086                    })
18087                    // if special-casing the single-match case, remove ranges
18088                    // that intersect current selection
18089                    .filter(|(location_buffer, location)| {
18090                        if always_open_multibuffer || &buffer != location_buffer {
18091                            return true;
18092                        }
18093
18094                        !location.contains_inclusive(&selection_point.range())
18095                    })
18096                    .into_group_map()
18097            })?;
18098            if locations.is_empty() {
18099                return anyhow::Ok(Navigated::No);
18100            }
18101            for ranges in locations.values_mut() {
18102                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18103                ranges.dedup();
18104            }
18105            let mut num_locations = 0;
18106            for ranges in locations.values_mut() {
18107                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18108                ranges.dedup();
18109                num_locations += ranges.len();
18110            }
18111
18112            if num_locations == 1 && !always_open_multibuffer {
18113                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18114                let target_range = target_ranges.first().unwrap().clone();
18115
18116                return editor.update_in(cx, |editor, window, cx| {
18117                    let range = target_range.to_point(target_buffer.read(cx));
18118                    let range = editor.range_for_match(&range);
18119                    let range = range.start..range.start;
18120
18121                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18122                        editor.go_to_singleton_buffer_range(range, window, cx);
18123                    } else {
18124                        let pane = workspace.read(cx).active_pane().clone();
18125                        window.defer(cx, move |window, cx| {
18126                            let target_editor: Entity<Self> =
18127                                workspace.update(cx, |workspace, cx| {
18128                                    let pane = workspace.active_pane().clone();
18129
18130                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18131                                    let keep_old_preview = preview_tabs_settings
18132                                        .enable_keep_preview_on_code_navigation;
18133                                    let allow_new_preview = preview_tabs_settings
18134                                        .enable_preview_file_from_code_navigation;
18135
18136                                    workspace.open_project_item(
18137                                        pane,
18138                                        target_buffer.clone(),
18139                                        true,
18140                                        true,
18141                                        keep_old_preview,
18142                                        allow_new_preview,
18143                                        window,
18144                                        cx,
18145                                    )
18146                                });
18147                            target_editor.update(cx, |target_editor, cx| {
18148                                // When selecting a definition in a different buffer, disable the nav history
18149                                // to avoid creating a history entry at the previous cursor location.
18150                                pane.update(cx, |pane, _| pane.disable_history());
18151                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18152                                pane.update(cx, |pane, _| pane.enable_history());
18153                            });
18154                        });
18155                    }
18156                    Navigated::No
18157                });
18158            }
18159
18160            workspace.update_in(cx, |workspace, window, cx| {
18161                let target = locations
18162                    .iter()
18163                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18164                    .map(|(buffer, location)| {
18165                        buffer
18166                            .read(cx)
18167                            .text_for_range(location.clone())
18168                            .collect::<String>()
18169                    })
18170                    .filter(|text| !text.contains('\n'))
18171                    .unique()
18172                    .take(3)
18173                    .join(", ");
18174                let title = if target.is_empty() {
18175                    "References".to_owned()
18176                } else {
18177                    format!("References to {target}")
18178                };
18179                let allow_preview = PreviewTabsSettings::get_global(cx)
18180                    .enable_preview_multibuffer_from_code_navigation;
18181                Self::open_locations_in_multibuffer(
18182                    workspace,
18183                    locations,
18184                    title,
18185                    false,
18186                    allow_preview,
18187                    MultibufferSelectionMode::First,
18188                    window,
18189                    cx,
18190                );
18191                Navigated::Yes
18192            })
18193        }))
18194    }
18195
18196    /// Opens a multibuffer with the given project locations in it.
18197    pub fn open_locations_in_multibuffer(
18198        workspace: &mut Workspace,
18199        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18200        title: String,
18201        split: bool,
18202        allow_preview: bool,
18203        multibuffer_selection_mode: MultibufferSelectionMode,
18204        window: &mut Window,
18205        cx: &mut Context<Workspace>,
18206    ) {
18207        if locations.is_empty() {
18208            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18209            return;
18210        }
18211
18212        let capability = workspace.project().read(cx).capability();
18213        let mut ranges = <Vec<Range<Anchor>>>::new();
18214
18215        // a key to find existing multibuffer editors with the same set of locations
18216        // to prevent us from opening more and more multibuffer tabs for searches and the like
18217        let mut key = (title.clone(), vec![]);
18218        let excerpt_buffer = cx.new(|cx| {
18219            let key = &mut key.1;
18220            let mut multibuffer = MultiBuffer::new(capability);
18221            for (buffer, mut ranges_for_buffer) in locations {
18222                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18223                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18224                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18225                    PathKey::for_buffer(&buffer, cx),
18226                    buffer.clone(),
18227                    ranges_for_buffer,
18228                    multibuffer_context_lines(cx),
18229                    cx,
18230                );
18231                ranges.extend(new_ranges)
18232            }
18233
18234            multibuffer.with_title(title)
18235        });
18236        let existing = workspace.active_pane().update(cx, |pane, cx| {
18237            pane.items()
18238                .filter_map(|item| item.downcast::<Editor>())
18239                .find(|editor| {
18240                    editor
18241                        .read(cx)
18242                        .lookup_key
18243                        .as_ref()
18244                        .and_then(|it| {
18245                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18246                        })
18247                        .is_some_and(|it| *it == key)
18248                })
18249        });
18250        let was_existing = existing.is_some();
18251        let editor = existing.unwrap_or_else(|| {
18252            cx.new(|cx| {
18253                let mut editor = Editor::for_multibuffer(
18254                    excerpt_buffer,
18255                    Some(workspace.project().clone()),
18256                    window,
18257                    cx,
18258                );
18259                editor.lookup_key = Some(Box::new(key));
18260                editor
18261            })
18262        });
18263        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18264            MultibufferSelectionMode::First => {
18265                if let Some(first_range) = ranges.first() {
18266                    editor.change_selections(
18267                        SelectionEffects::no_scroll(),
18268                        window,
18269                        cx,
18270                        |selections| {
18271                            selections.clear_disjoint();
18272                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18273                        },
18274                    );
18275                }
18276                editor.highlight_background::<Self>(
18277                    &ranges,
18278                    |_, theme| theme.colors().editor_highlighted_line_background,
18279                    cx,
18280                );
18281            }
18282            MultibufferSelectionMode::All => {
18283                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18284                    selections.clear_disjoint();
18285                    selections.select_anchor_ranges(ranges);
18286                });
18287            }
18288        });
18289
18290        let item = Box::new(editor);
18291
18292        let pane = if split {
18293            workspace.adjacent_pane(window, cx)
18294        } else {
18295            workspace.active_pane().clone()
18296        };
18297        let activate_pane = split;
18298
18299        let mut destination_index = None;
18300        pane.update(cx, |pane, cx| {
18301            if allow_preview && !was_existing {
18302                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18303            }
18304            if was_existing && !allow_preview {
18305                pane.unpreview_item_if_preview(item.item_id());
18306            }
18307            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18308        });
18309    }
18310
18311    pub fn rename(
18312        &mut self,
18313        _: &Rename,
18314        window: &mut Window,
18315        cx: &mut Context<Self>,
18316    ) -> Option<Task<Result<()>>> {
18317        use language::ToOffset as _;
18318
18319        let provider = self.semantics_provider.clone()?;
18320        let selection = self.selections.newest_anchor().clone();
18321        let (cursor_buffer, cursor_buffer_position) = self
18322            .buffer
18323            .read(cx)
18324            .text_anchor_for_position(selection.head(), cx)?;
18325        let (tail_buffer, cursor_buffer_position_end) = self
18326            .buffer
18327            .read(cx)
18328            .text_anchor_for_position(selection.tail(), cx)?;
18329        if tail_buffer != cursor_buffer {
18330            return None;
18331        }
18332
18333        let snapshot = cursor_buffer.read(cx).snapshot();
18334        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18335        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18336        let prepare_rename = provider
18337            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18338            .unwrap_or_else(|| Task::ready(Ok(None)));
18339        drop(snapshot);
18340
18341        Some(cx.spawn_in(window, async move |this, cx| {
18342            let rename_range = if let Some(range) = prepare_rename.await? {
18343                Some(range)
18344            } else {
18345                this.update(cx, |this, cx| {
18346                    let buffer = this.buffer.read(cx).snapshot(cx);
18347                    let mut buffer_highlights = this
18348                        .document_highlights_for_position(selection.head(), &buffer)
18349                        .filter(|highlight| {
18350                            highlight.start.excerpt_id == selection.head().excerpt_id
18351                                && highlight.end.excerpt_id == selection.head().excerpt_id
18352                        });
18353                    buffer_highlights
18354                        .next()
18355                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18356                })?
18357            };
18358            if let Some(rename_range) = rename_range {
18359                this.update_in(cx, |this, window, cx| {
18360                    let snapshot = cursor_buffer.read(cx).snapshot();
18361                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18362                    let cursor_offset_in_rename_range =
18363                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18364                    let cursor_offset_in_rename_range_end =
18365                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18366
18367                    this.take_rename(false, window, cx);
18368                    let buffer = this.buffer.read(cx).read(cx);
18369                    let cursor_offset = selection.head().to_offset(&buffer);
18370                    let rename_start =
18371                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18372                    let rename_end = rename_start + rename_buffer_range.len();
18373                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18374                    let mut old_highlight_id = None;
18375                    let old_name: Arc<str> = buffer
18376                        .chunks(rename_start..rename_end, true)
18377                        .map(|chunk| {
18378                            if old_highlight_id.is_none() {
18379                                old_highlight_id = chunk.syntax_highlight_id;
18380                            }
18381                            chunk.text
18382                        })
18383                        .collect::<String>()
18384                        .into();
18385
18386                    drop(buffer);
18387
18388                    // Position the selection in the rename editor so that it matches the current selection.
18389                    this.show_local_selections = false;
18390                    let rename_editor = cx.new(|cx| {
18391                        let mut editor = Editor::single_line(window, cx);
18392                        editor.buffer.update(cx, |buffer, cx| {
18393                            buffer.edit(
18394                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18395                                None,
18396                                cx,
18397                            )
18398                        });
18399                        let cursor_offset_in_rename_range =
18400                            MultiBufferOffset(cursor_offset_in_rename_range);
18401                        let cursor_offset_in_rename_range_end =
18402                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18403                        let rename_selection_range = match cursor_offset_in_rename_range
18404                            .cmp(&cursor_offset_in_rename_range_end)
18405                        {
18406                            Ordering::Equal => {
18407                                editor.select_all(&SelectAll, window, cx);
18408                                return editor;
18409                            }
18410                            Ordering::Less => {
18411                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18412                            }
18413                            Ordering::Greater => {
18414                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18415                            }
18416                        };
18417                        if rename_selection_range.end.0 > old_name.len() {
18418                            editor.select_all(&SelectAll, window, cx);
18419                        } else {
18420                            editor.change_selections(Default::default(), window, cx, |s| {
18421                                s.select_ranges([rename_selection_range]);
18422                            });
18423                        }
18424                        editor
18425                    });
18426                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18427                        if e == &EditorEvent::Focused {
18428                            cx.emit(EditorEvent::FocusedIn)
18429                        }
18430                    })
18431                    .detach();
18432
18433                    let write_highlights =
18434                        this.clear_background_highlights::<DocumentHighlightWrite>(cx);
18435                    let read_highlights =
18436                        this.clear_background_highlights::<DocumentHighlightRead>(cx);
18437                    let ranges = write_highlights
18438                        .iter()
18439                        .flat_map(|(_, ranges)| ranges.iter())
18440                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18441                        .cloned()
18442                        .collect();
18443
18444                    this.highlight_text::<Rename>(
18445                        ranges,
18446                        HighlightStyle {
18447                            fade_out: Some(0.6),
18448                            ..Default::default()
18449                        },
18450                        cx,
18451                    );
18452                    let rename_focus_handle = rename_editor.focus_handle(cx);
18453                    window.focus(&rename_focus_handle, cx);
18454                    let block_id = this.insert_blocks(
18455                        [BlockProperties {
18456                            style: BlockStyle::Flex,
18457                            placement: BlockPlacement::Below(range.start),
18458                            height: Some(1),
18459                            render: Arc::new({
18460                                let rename_editor = rename_editor.clone();
18461                                move |cx: &mut BlockContext| {
18462                                    let mut text_style = cx.editor_style.text.clone();
18463                                    if let Some(highlight_style) = old_highlight_id
18464                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18465                                    {
18466                                        text_style = text_style.highlight(highlight_style);
18467                                    }
18468                                    div()
18469                                        .block_mouse_except_scroll()
18470                                        .pl(cx.anchor_x)
18471                                        .child(EditorElement::new(
18472                                            &rename_editor,
18473                                            EditorStyle {
18474                                                background: cx.theme().system().transparent,
18475                                                local_player: cx.editor_style.local_player,
18476                                                text: text_style,
18477                                                scrollbar_width: cx.editor_style.scrollbar_width,
18478                                                syntax: cx.editor_style.syntax.clone(),
18479                                                status: cx.editor_style.status.clone(),
18480                                                inlay_hints_style: HighlightStyle {
18481                                                    font_weight: Some(FontWeight::BOLD),
18482                                                    ..make_inlay_hints_style(cx.app)
18483                                                },
18484                                                edit_prediction_styles: make_suggestion_styles(
18485                                                    cx.app,
18486                                                ),
18487                                                ..EditorStyle::default()
18488                                            },
18489                                        ))
18490                                        .into_any_element()
18491                                }
18492                            }),
18493                            priority: 0,
18494                        }],
18495                        Some(Autoscroll::fit()),
18496                        cx,
18497                    )[0];
18498                    this.pending_rename = Some(RenameState {
18499                        range,
18500                        old_name,
18501                        editor: rename_editor,
18502                        block_id,
18503                    });
18504                })?;
18505            }
18506
18507            Ok(())
18508        }))
18509    }
18510
18511    pub fn confirm_rename(
18512        &mut self,
18513        _: &ConfirmRename,
18514        window: &mut Window,
18515        cx: &mut Context<Self>,
18516    ) -> Option<Task<Result<()>>> {
18517        let rename = self.take_rename(false, window, cx)?;
18518        let workspace = self.workspace()?.downgrade();
18519        let (buffer, start) = self
18520            .buffer
18521            .read(cx)
18522            .text_anchor_for_position(rename.range.start, cx)?;
18523        let (end_buffer, _) = self
18524            .buffer
18525            .read(cx)
18526            .text_anchor_for_position(rename.range.end, cx)?;
18527        if buffer != end_buffer {
18528            return None;
18529        }
18530
18531        let old_name = rename.old_name;
18532        let new_name = rename.editor.read(cx).text(cx);
18533
18534        let rename = self.semantics_provider.as_ref()?.perform_rename(
18535            &buffer,
18536            start,
18537            new_name.clone(),
18538            cx,
18539        )?;
18540
18541        Some(cx.spawn_in(window, async move |editor, cx| {
18542            let project_transaction = rename.await?;
18543            Self::open_project_transaction(
18544                &editor,
18545                workspace,
18546                project_transaction,
18547                format!("Rename: {}{}", old_name, new_name),
18548                cx,
18549            )
18550            .await?;
18551
18552            editor.update(cx, |editor, cx| {
18553                editor.refresh_document_highlights(cx);
18554            })?;
18555            Ok(())
18556        }))
18557    }
18558
18559    fn take_rename(
18560        &mut self,
18561        moving_cursor: bool,
18562        window: &mut Window,
18563        cx: &mut Context<Self>,
18564    ) -> Option<RenameState> {
18565        let rename = self.pending_rename.take()?;
18566        if rename.editor.focus_handle(cx).is_focused(window) {
18567            window.focus(&self.focus_handle, cx);
18568        }
18569
18570        self.remove_blocks(
18571            [rename.block_id].into_iter().collect(),
18572            Some(Autoscroll::fit()),
18573            cx,
18574        );
18575        self.clear_highlights::<Rename>(cx);
18576        self.show_local_selections = true;
18577
18578        if moving_cursor {
18579            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
18580                editor
18581                    .selections
18582                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
18583                    .head()
18584            });
18585
18586            // Update the selection to match the position of the selection inside
18587            // the rename editor.
18588            let snapshot = self.buffer.read(cx).read(cx);
18589            let rename_range = rename.range.to_offset(&snapshot);
18590            let cursor_in_editor = snapshot
18591                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
18592                .min(rename_range.end);
18593            drop(snapshot);
18594
18595            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
18596                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
18597            });
18598        } else {
18599            self.refresh_document_highlights(cx);
18600        }
18601
18602        Some(rename)
18603    }
18604
18605    pub fn pending_rename(&self) -> Option<&RenameState> {
18606        self.pending_rename.as_ref()
18607    }
18608
18609    fn format(
18610        &mut self,
18611        _: &Format,
18612        window: &mut Window,
18613        cx: &mut Context<Self>,
18614    ) -> Option<Task<Result<()>>> {
18615        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18616
18617        let project = match &self.project {
18618            Some(project) => project.clone(),
18619            None => return None,
18620        };
18621
18622        Some(self.perform_format(
18623            project,
18624            FormatTrigger::Manual,
18625            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
18626            window,
18627            cx,
18628        ))
18629    }
18630
18631    fn format_selections(
18632        &mut self,
18633        _: &FormatSelections,
18634        window: &mut Window,
18635        cx: &mut Context<Self>,
18636    ) -> Option<Task<Result<()>>> {
18637        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18638
18639        let project = match &self.project {
18640            Some(project) => project.clone(),
18641            None => return None,
18642        };
18643
18644        let ranges = self
18645            .selections
18646            .all_adjusted(&self.display_snapshot(cx))
18647            .into_iter()
18648            .map(|selection| selection.range())
18649            .collect_vec();
18650
18651        Some(self.perform_format(
18652            project,
18653            FormatTrigger::Manual,
18654            FormatTarget::Ranges(ranges),
18655            window,
18656            cx,
18657        ))
18658    }
18659
18660    fn perform_format(
18661        &mut self,
18662        project: Entity<Project>,
18663        trigger: FormatTrigger,
18664        target: FormatTarget,
18665        window: &mut Window,
18666        cx: &mut Context<Self>,
18667    ) -> Task<Result<()>> {
18668        let buffer = self.buffer.clone();
18669        let (buffers, target) = match target {
18670            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
18671            FormatTarget::Ranges(selection_ranges) => {
18672                let multi_buffer = buffer.read(cx);
18673                let snapshot = multi_buffer.read(cx);
18674                let mut buffers = HashSet::default();
18675                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
18676                    BTreeMap::new();
18677                for selection_range in selection_ranges {
18678                    for (buffer, buffer_range, _) in
18679                        snapshot.range_to_buffer_ranges(selection_range)
18680                    {
18681                        let buffer_id = buffer.remote_id();
18682                        let start = buffer.anchor_before(buffer_range.start);
18683                        let end = buffer.anchor_after(buffer_range.end);
18684                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
18685                        buffer_id_to_ranges
18686                            .entry(buffer_id)
18687                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
18688                            .or_insert_with(|| vec![start..end]);
18689                    }
18690                }
18691                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
18692            }
18693        };
18694
18695        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
18696        let selections_prev = transaction_id_prev
18697            .and_then(|transaction_id_prev| {
18698                // default to selections as they were after the last edit, if we have them,
18699                // instead of how they are now.
18700                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
18701                // will take you back to where you made the last edit, instead of staying where you scrolled
18702                self.selection_history
18703                    .transaction(transaction_id_prev)
18704                    .map(|t| t.0.clone())
18705            })
18706            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
18707
18708        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
18709        let format = project.update(cx, |project, cx| {
18710            project.format(buffers, target, true, trigger, cx)
18711        });
18712
18713        cx.spawn_in(window, async move |editor, cx| {
18714            let transaction = futures::select_biased! {
18715                transaction = format.log_err().fuse() => transaction,
18716                () = timeout => {
18717                    log::warn!("timed out waiting for formatting");
18718                    None
18719                }
18720            };
18721
18722            buffer.update(cx, |buffer, cx| {
18723                if let Some(transaction) = transaction
18724                    && !buffer.is_singleton()
18725                {
18726                    buffer.push_transaction(&transaction.0, cx);
18727                }
18728                cx.notify();
18729            });
18730
18731            if let Some(transaction_id_now) =
18732                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
18733            {
18734                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
18735                if has_new_transaction {
18736                    editor
18737                        .update(cx, |editor, _| {
18738                            editor
18739                                .selection_history
18740                                .insert_transaction(transaction_id_now, selections_prev);
18741                        })
18742                        .ok();
18743                }
18744            }
18745
18746            Ok(())
18747        })
18748    }
18749
18750    fn organize_imports(
18751        &mut self,
18752        _: &OrganizeImports,
18753        window: &mut Window,
18754        cx: &mut Context<Self>,
18755    ) -> Option<Task<Result<()>>> {
18756        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18757        let project = match &self.project {
18758            Some(project) => project.clone(),
18759            None => return None,
18760        };
18761        Some(self.perform_code_action_kind(
18762            project,
18763            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
18764            window,
18765            cx,
18766        ))
18767    }
18768
18769    fn perform_code_action_kind(
18770        &mut self,
18771        project: Entity<Project>,
18772        kind: CodeActionKind,
18773        window: &mut Window,
18774        cx: &mut Context<Self>,
18775    ) -> Task<Result<()>> {
18776        let buffer = self.buffer.clone();
18777        let buffers = buffer.read(cx).all_buffers();
18778        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
18779        let apply_action = project.update(cx, |project, cx| {
18780            project.apply_code_action_kind(buffers, kind, true, cx)
18781        });
18782        cx.spawn_in(window, async move |_, cx| {
18783            let transaction = futures::select_biased! {
18784                () = timeout => {
18785                    log::warn!("timed out waiting for executing code action");
18786                    None
18787                }
18788                transaction = apply_action.log_err().fuse() => transaction,
18789            };
18790            buffer.update(cx, |buffer, cx| {
18791                // check if we need this
18792                if let Some(transaction) = transaction
18793                    && !buffer.is_singleton()
18794                {
18795                    buffer.push_transaction(&transaction.0, cx);
18796                }
18797                cx.notify();
18798            });
18799            Ok(())
18800        })
18801    }
18802
18803    pub fn restart_language_server(
18804        &mut self,
18805        _: &RestartLanguageServer,
18806        _: &mut Window,
18807        cx: &mut Context<Self>,
18808    ) {
18809        if let Some(project) = self.project.clone() {
18810            self.buffer.update(cx, |multi_buffer, cx| {
18811                project.update(cx, |project, cx| {
18812                    project.restart_language_servers_for_buffers(
18813                        multi_buffer.all_buffers().into_iter().collect(),
18814                        HashSet::default(),
18815                        cx,
18816                    );
18817                });
18818            })
18819        }
18820    }
18821
18822    pub fn stop_language_server(
18823        &mut self,
18824        _: &StopLanguageServer,
18825        _: &mut Window,
18826        cx: &mut Context<Self>,
18827    ) {
18828        if let Some(project) = self.project.clone() {
18829            self.buffer.update(cx, |multi_buffer, cx| {
18830                project.update(cx, |project, cx| {
18831                    project.stop_language_servers_for_buffers(
18832                        multi_buffer.all_buffers().into_iter().collect(),
18833                        HashSet::default(),
18834                        cx,
18835                    );
18836                });
18837            });
18838            self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
18839        }
18840    }
18841
18842    fn cancel_language_server_work(
18843        workspace: &mut Workspace,
18844        _: &actions::CancelLanguageServerWork,
18845        _: &mut Window,
18846        cx: &mut Context<Workspace>,
18847    ) {
18848        let project = workspace.project();
18849        let buffers = workspace
18850            .active_item(cx)
18851            .and_then(|item| item.act_as::<Editor>(cx))
18852            .map_or(HashSet::default(), |editor| {
18853                editor.read(cx).buffer.read(cx).all_buffers()
18854            });
18855        project.update(cx, |project, cx| {
18856            project.cancel_language_server_work_for_buffers(buffers, cx);
18857        });
18858    }
18859
18860    fn show_character_palette(
18861        &mut self,
18862        _: &ShowCharacterPalette,
18863        window: &mut Window,
18864        _: &mut Context<Self>,
18865    ) {
18866        window.show_character_palette();
18867    }
18868
18869    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
18870        if !self.diagnostics_enabled() {
18871            return;
18872        }
18873
18874        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
18875            let buffer = self.buffer.read(cx).snapshot(cx);
18876            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
18877            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
18878            let is_valid = buffer
18879                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
18880                .any(|entry| {
18881                    entry.diagnostic.is_primary
18882                        && !entry.range.is_empty()
18883                        && entry.range.start == primary_range_start
18884                        && entry.diagnostic.message == active_diagnostics.active_message
18885                });
18886
18887            if !is_valid {
18888                self.dismiss_diagnostics(cx);
18889            }
18890        }
18891    }
18892
18893    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
18894        match &self.active_diagnostics {
18895            ActiveDiagnostic::Group(group) => Some(group),
18896            _ => None,
18897        }
18898    }
18899
18900    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
18901        if !self.diagnostics_enabled() {
18902            return;
18903        }
18904        self.dismiss_diagnostics(cx);
18905        self.active_diagnostics = ActiveDiagnostic::All;
18906    }
18907
18908    fn activate_diagnostics(
18909        &mut self,
18910        buffer_id: BufferId,
18911        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
18912        window: &mut Window,
18913        cx: &mut Context<Self>,
18914    ) {
18915        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
18916            return;
18917        }
18918        self.dismiss_diagnostics(cx);
18919        let snapshot = self.snapshot(window, cx);
18920        let buffer = self.buffer.read(cx).snapshot(cx);
18921        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
18922            return;
18923        };
18924
18925        let diagnostic_group = buffer
18926            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
18927            .collect::<Vec<_>>();
18928
18929        let language_registry = self
18930            .project()
18931            .map(|project| project.read(cx).languages().clone());
18932
18933        let blocks = renderer.render_group(
18934            diagnostic_group,
18935            buffer_id,
18936            snapshot,
18937            cx.weak_entity(),
18938            language_registry,
18939            cx,
18940        );
18941
18942        let blocks = self.display_map.update(cx, |display_map, cx| {
18943            display_map.insert_blocks(blocks, cx).into_iter().collect()
18944        });
18945        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
18946            active_range: buffer.anchor_before(diagnostic.range.start)
18947                ..buffer.anchor_after(diagnostic.range.end),
18948            active_message: diagnostic.diagnostic.message.clone(),
18949            group_id: diagnostic.diagnostic.group_id,
18950            blocks,
18951        });
18952        cx.notify();
18953    }
18954
18955    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
18956        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
18957            return;
18958        };
18959
18960        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
18961        if let ActiveDiagnostic::Group(group) = prev {
18962            self.display_map.update(cx, |display_map, cx| {
18963                display_map.remove_blocks(group.blocks, cx);
18964            });
18965            cx.notify();
18966        }
18967    }
18968
18969    /// Disable inline diagnostics rendering for this editor.
18970    pub fn disable_inline_diagnostics(&mut self) {
18971        self.inline_diagnostics_enabled = false;
18972        self.inline_diagnostics_update = Task::ready(());
18973        self.inline_diagnostics.clear();
18974    }
18975
18976    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
18977        self.diagnostics_enabled = false;
18978        self.dismiss_diagnostics(cx);
18979        self.inline_diagnostics_update = Task::ready(());
18980        self.inline_diagnostics.clear();
18981    }
18982
18983    pub fn disable_word_completions(&mut self) {
18984        self.word_completions_enabled = false;
18985    }
18986
18987    pub fn diagnostics_enabled(&self) -> bool {
18988        self.diagnostics_enabled && self.mode.is_full()
18989    }
18990
18991    pub fn inline_diagnostics_enabled(&self) -> bool {
18992        self.inline_diagnostics_enabled && self.diagnostics_enabled()
18993    }
18994
18995    pub fn show_inline_diagnostics(&self) -> bool {
18996        self.show_inline_diagnostics
18997    }
18998
18999    pub fn toggle_inline_diagnostics(
19000        &mut self,
19001        _: &ToggleInlineDiagnostics,
19002        window: &mut Window,
19003        cx: &mut Context<Editor>,
19004    ) {
19005        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19006        self.refresh_inline_diagnostics(false, window, cx);
19007    }
19008
19009    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19010        self.diagnostics_max_severity = severity;
19011        self.display_map.update(cx, |display_map, _| {
19012            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19013        });
19014    }
19015
19016    pub fn toggle_diagnostics(
19017        &mut self,
19018        _: &ToggleDiagnostics,
19019        window: &mut Window,
19020        cx: &mut Context<Editor>,
19021    ) {
19022        if !self.diagnostics_enabled() {
19023            return;
19024        }
19025
19026        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19027            EditorSettings::get_global(cx)
19028                .diagnostics_max_severity
19029                .filter(|severity| severity != &DiagnosticSeverity::Off)
19030                .unwrap_or(DiagnosticSeverity::Hint)
19031        } else {
19032            DiagnosticSeverity::Off
19033        };
19034        self.set_max_diagnostics_severity(new_severity, cx);
19035        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19036            self.active_diagnostics = ActiveDiagnostic::None;
19037            self.inline_diagnostics_update = Task::ready(());
19038            self.inline_diagnostics.clear();
19039        } else {
19040            self.refresh_inline_diagnostics(false, window, cx);
19041        }
19042
19043        cx.notify();
19044    }
19045
19046    pub fn toggle_minimap(
19047        &mut self,
19048        _: &ToggleMinimap,
19049        window: &mut Window,
19050        cx: &mut Context<Editor>,
19051    ) {
19052        if self.supports_minimap(cx) {
19053            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19054        }
19055    }
19056
19057    fn refresh_inline_diagnostics(
19058        &mut self,
19059        debounce: bool,
19060        window: &mut Window,
19061        cx: &mut Context<Self>,
19062    ) {
19063        let max_severity = ProjectSettings::get_global(cx)
19064            .diagnostics
19065            .inline
19066            .max_severity
19067            .unwrap_or(self.diagnostics_max_severity);
19068
19069        if !self.inline_diagnostics_enabled()
19070            || !self.diagnostics_enabled()
19071            || !self.show_inline_diagnostics
19072            || max_severity == DiagnosticSeverity::Off
19073        {
19074            self.inline_diagnostics_update = Task::ready(());
19075            self.inline_diagnostics.clear();
19076            return;
19077        }
19078
19079        let debounce_ms = ProjectSettings::get_global(cx)
19080            .diagnostics
19081            .inline
19082            .update_debounce_ms;
19083        let debounce = if debounce && debounce_ms > 0 {
19084            Some(Duration::from_millis(debounce_ms))
19085        } else {
19086            None
19087        };
19088        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19089            if let Some(debounce) = debounce {
19090                cx.background_executor().timer(debounce).await;
19091            }
19092            let Some(snapshot) = editor.upgrade().map(|editor| {
19093                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19094            }) else {
19095                return;
19096            };
19097
19098            let new_inline_diagnostics = cx
19099                .background_spawn(async move {
19100                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19101                    for diagnostic_entry in
19102                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19103                    {
19104                        let message = diagnostic_entry
19105                            .diagnostic
19106                            .message
19107                            .split_once('\n')
19108                            .map(|(line, _)| line)
19109                            .map(SharedString::new)
19110                            .unwrap_or_else(|| {
19111                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19112                            });
19113                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19114                        let (Ok(i) | Err(i)) = inline_diagnostics
19115                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19116                        inline_diagnostics.insert(
19117                            i,
19118                            (
19119                                start_anchor,
19120                                InlineDiagnostic {
19121                                    message,
19122                                    group_id: diagnostic_entry.diagnostic.group_id,
19123                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19124                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19125                                    severity: diagnostic_entry.diagnostic.severity,
19126                                },
19127                            ),
19128                        );
19129                    }
19130                    inline_diagnostics
19131                })
19132                .await;
19133
19134            editor
19135                .update(cx, |editor, cx| {
19136                    editor.inline_diagnostics = new_inline_diagnostics;
19137                    cx.notify();
19138                })
19139                .ok();
19140        });
19141    }
19142
19143    fn pull_diagnostics(
19144        &mut self,
19145        buffer_id: Option<BufferId>,
19146        window: &Window,
19147        cx: &mut Context<Self>,
19148    ) -> Option<()> {
19149        if self.ignore_lsp_data() || !self.diagnostics_enabled() {
19150            return None;
19151        }
19152        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19153            .diagnostics
19154            .lsp_pull_diagnostics;
19155        if !pull_diagnostics_settings.enabled {
19156            return None;
19157        }
19158        let project = self.project()?.downgrade();
19159
19160        let mut edited_buffer_ids = HashSet::default();
19161        let mut edited_worktree_ids = HashSet::default();
19162        let edited_buffers = match buffer_id {
19163            Some(buffer_id) => {
19164                let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19165                let worktree_id = buffer.read(cx).file().map(|f| f.worktree_id(cx))?;
19166                edited_buffer_ids.insert(buffer.read(cx).remote_id());
19167                edited_worktree_ids.insert(worktree_id);
19168                vec![buffer]
19169            }
19170            None => self
19171                .buffer()
19172                .read(cx)
19173                .all_buffers()
19174                .into_iter()
19175                .filter(|buffer| {
19176                    let buffer = buffer.read(cx);
19177                    match buffer.file().map(|f| f.worktree_id(cx)) {
19178                        Some(worktree_id) => {
19179                            edited_buffer_ids.insert(buffer.remote_id());
19180                            edited_worktree_ids.insert(worktree_id);
19181                            true
19182                        }
19183                        None => false,
19184                    }
19185                })
19186                .collect::<Vec<_>>(),
19187        };
19188
19189        if edited_buffers.is_empty() {
19190            self.pull_diagnostics_task = Task::ready(());
19191            self.pull_diagnostics_background_task = Task::ready(());
19192            return None;
19193        }
19194
19195        let mut already_used_buffers = HashSet::default();
19196        let related_open_buffers = self
19197            .workspace
19198            .as_ref()
19199            .and_then(|(workspace, _)| workspace.upgrade())
19200            .into_iter()
19201            .flat_map(|workspace| workspace.read(cx).panes())
19202            .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
19203            .filter(|editor| editor != &cx.entity())
19204            .flat_map(|editor| editor.read(cx).buffer().read(cx).all_buffers())
19205            .filter(|buffer| {
19206                let buffer = buffer.read(cx);
19207                let buffer_id = buffer.remote_id();
19208                if already_used_buffers.insert(buffer_id) {
19209                    if let Some(worktree_id) = buffer.file().map(|f| f.worktree_id(cx)) {
19210                        return !edited_buffer_ids.contains(&buffer_id)
19211                            && edited_worktree_ids.contains(&worktree_id);
19212                    }
19213                }
19214                false
19215            })
19216            .collect::<Vec<_>>();
19217
19218        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19219        let make_spawn = |buffers: Vec<Entity<Buffer>>, delay: Duration| {
19220            if buffers.is_empty() {
19221                return Task::ready(());
19222            }
19223            let project_weak = project.clone();
19224            cx.spawn_in(window, async move |_, cx| {
19225                cx.background_executor().timer(delay).await;
19226
19227                let Ok(mut pull_diagnostics_tasks) = cx.update(|_, cx| {
19228                    buffers
19229                        .into_iter()
19230                        .filter_map(|buffer| {
19231                            project_weak
19232                                .update(cx, |project, cx| {
19233                                    project.lsp_store().update(cx, |lsp_store, cx| {
19234                                        lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19235                                    })
19236                                })
19237                                .ok()
19238                        })
19239                        .collect::<FuturesUnordered<_>>()
19240                }) else {
19241                    return;
19242                };
19243
19244                while let Some(pull_task) = pull_diagnostics_tasks.next().await {
19245                    if let Err(e) = pull_task {
19246                        log::error!("Failed to update project diagnostics: {e:#}");
19247                    }
19248                }
19249            })
19250        };
19251
19252        self.pull_diagnostics_task = make_spawn(edited_buffers, debounce);
19253        self.pull_diagnostics_background_task = make_spawn(related_open_buffers, debounce * 2);
19254
19255        Some(())
19256    }
19257
19258    pub fn set_selections_from_remote(
19259        &mut self,
19260        selections: Vec<Selection<Anchor>>,
19261        pending_selection: Option<Selection<Anchor>>,
19262        window: &mut Window,
19263        cx: &mut Context<Self>,
19264    ) {
19265        let old_cursor_position = self.selections.newest_anchor().head();
19266        self.selections
19267            .change_with(&self.display_snapshot(cx), |s| {
19268                s.select_anchors(selections);
19269                if let Some(pending_selection) = pending_selection {
19270                    s.set_pending(pending_selection, SelectMode::Character);
19271                } else {
19272                    s.clear_pending();
19273                }
19274            });
19275        self.selections_did_change(
19276            false,
19277            &old_cursor_position,
19278            SelectionEffects::default(),
19279            window,
19280            cx,
19281        );
19282    }
19283
19284    pub fn transact(
19285        &mut self,
19286        window: &mut Window,
19287        cx: &mut Context<Self>,
19288        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19289    ) -> Option<TransactionId> {
19290        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19291            this.start_transaction_at(Instant::now(), window, cx);
19292            update(this, window, cx);
19293            this.end_transaction_at(Instant::now(), cx)
19294        })
19295    }
19296
19297    pub fn start_transaction_at(
19298        &mut self,
19299        now: Instant,
19300        window: &mut Window,
19301        cx: &mut Context<Self>,
19302    ) -> Option<TransactionId> {
19303        self.end_selection(window, cx);
19304        if let Some(tx_id) = self
19305            .buffer
19306            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19307        {
19308            self.selection_history
19309                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19310            cx.emit(EditorEvent::TransactionBegun {
19311                transaction_id: tx_id,
19312            });
19313            Some(tx_id)
19314        } else {
19315            None
19316        }
19317    }
19318
19319    pub fn end_transaction_at(
19320        &mut self,
19321        now: Instant,
19322        cx: &mut Context<Self>,
19323    ) -> Option<TransactionId> {
19324        if let Some(transaction_id) = self
19325            .buffer
19326            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19327        {
19328            if let Some((_, end_selections)) =
19329                self.selection_history.transaction_mut(transaction_id)
19330            {
19331                *end_selections = Some(self.selections.disjoint_anchors_arc());
19332            } else {
19333                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19334            }
19335
19336            cx.emit(EditorEvent::Edited { transaction_id });
19337            Some(transaction_id)
19338        } else {
19339            None
19340        }
19341    }
19342
19343    pub fn modify_transaction_selection_history(
19344        &mut self,
19345        transaction_id: TransactionId,
19346        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19347    ) -> bool {
19348        self.selection_history
19349            .transaction_mut(transaction_id)
19350            .map(modify)
19351            .is_some()
19352    }
19353
19354    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19355        if self.selection_mark_mode {
19356            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19357                s.move_with(|_, sel| {
19358                    sel.collapse_to(sel.head(), SelectionGoal::None);
19359                });
19360            })
19361        }
19362        self.selection_mark_mode = true;
19363        cx.notify();
19364    }
19365
19366    pub fn swap_selection_ends(
19367        &mut self,
19368        _: &actions::SwapSelectionEnds,
19369        window: &mut Window,
19370        cx: &mut Context<Self>,
19371    ) {
19372        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19373            s.move_with(|_, sel| {
19374                if sel.start != sel.end {
19375                    sel.reversed = !sel.reversed
19376                }
19377            });
19378        });
19379        self.request_autoscroll(Autoscroll::newest(), cx);
19380        cx.notify();
19381    }
19382
19383    pub fn toggle_focus(
19384        workspace: &mut Workspace,
19385        _: &actions::ToggleFocus,
19386        window: &mut Window,
19387        cx: &mut Context<Workspace>,
19388    ) {
19389        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19390            return;
19391        };
19392        workspace.activate_item(&item, true, true, window, cx);
19393    }
19394
19395    pub fn toggle_fold(
19396        &mut self,
19397        _: &actions::ToggleFold,
19398        window: &mut Window,
19399        cx: &mut Context<Self>,
19400    ) {
19401        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19402            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19403            let selection = self.selections.newest::<Point>(&display_map);
19404
19405            let range = if selection.is_empty() {
19406                let point = selection.head().to_display_point(&display_map);
19407                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19408                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19409                    .to_point(&display_map);
19410                start..end
19411            } else {
19412                selection.range()
19413            };
19414            if display_map.folds_in_range(range).next().is_some() {
19415                self.unfold_lines(&Default::default(), window, cx)
19416            } else {
19417                self.fold(&Default::default(), window, cx)
19418            }
19419        } else {
19420            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19421            let buffer_ids: HashSet<_> = self
19422                .selections
19423                .disjoint_anchor_ranges()
19424                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19425                .collect();
19426
19427            let should_unfold = buffer_ids
19428                .iter()
19429                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19430
19431            for buffer_id in buffer_ids {
19432                if should_unfold {
19433                    self.unfold_buffer(buffer_id, cx);
19434                } else {
19435                    self.fold_buffer(buffer_id, cx);
19436                }
19437            }
19438        }
19439    }
19440
19441    pub fn toggle_fold_recursive(
19442        &mut self,
19443        _: &actions::ToggleFoldRecursive,
19444        window: &mut Window,
19445        cx: &mut Context<Self>,
19446    ) {
19447        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19448
19449        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19450        let range = if selection.is_empty() {
19451            let point = selection.head().to_display_point(&display_map);
19452            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19453            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19454                .to_point(&display_map);
19455            start..end
19456        } else {
19457            selection.range()
19458        };
19459        if display_map.folds_in_range(range).next().is_some() {
19460            self.unfold_recursive(&Default::default(), window, cx)
19461        } else {
19462            self.fold_recursive(&Default::default(), window, cx)
19463        }
19464    }
19465
19466    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19467        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19468            let mut to_fold = Vec::new();
19469            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19470            let selections = self.selections.all_adjusted(&display_map);
19471
19472            for selection in selections {
19473                let range = selection.range().sorted();
19474                let buffer_start_row = range.start.row;
19475
19476                if range.start.row != range.end.row {
19477                    let mut found = false;
19478                    let mut row = range.start.row;
19479                    while row <= range.end.row {
19480                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19481                        {
19482                            found = true;
19483                            row = crease.range().end.row + 1;
19484                            to_fold.push(crease);
19485                        } else {
19486                            row += 1
19487                        }
19488                    }
19489                    if found {
19490                        continue;
19491                    }
19492                }
19493
19494                for row in (0..=range.start.row).rev() {
19495                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19496                        && crease.range().end.row >= buffer_start_row
19497                    {
19498                        to_fold.push(crease);
19499                        if row <= range.start.row {
19500                            break;
19501                        }
19502                    }
19503                }
19504            }
19505
19506            self.fold_creases(to_fold, true, window, cx);
19507        } else {
19508            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19509            let buffer_ids = self
19510                .selections
19511                .disjoint_anchor_ranges()
19512                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19513                .collect::<HashSet<_>>();
19514            for buffer_id in buffer_ids {
19515                self.fold_buffer(buffer_id, cx);
19516            }
19517        }
19518    }
19519
19520    pub fn toggle_fold_all(
19521        &mut self,
19522        _: &actions::ToggleFoldAll,
19523        window: &mut Window,
19524        cx: &mut Context<Self>,
19525    ) {
19526        let has_folds = if self.buffer.read(cx).is_singleton() {
19527            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19528            let has_folds = display_map
19529                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19530                .next()
19531                .is_some();
19532            has_folds
19533        } else {
19534            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19535            let has_folds = buffer_ids
19536                .iter()
19537                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19538            has_folds
19539        };
19540
19541        if has_folds {
19542            self.unfold_all(&actions::UnfoldAll, window, cx);
19543        } else {
19544            self.fold_all(&actions::FoldAll, window, cx);
19545        }
19546    }
19547
19548    fn fold_at_level(
19549        &mut self,
19550        fold_at: &FoldAtLevel,
19551        window: &mut Window,
19552        cx: &mut Context<Self>,
19553    ) {
19554        if !self.buffer.read(cx).is_singleton() {
19555            return;
19556        }
19557
19558        let fold_at_level = fold_at.0;
19559        let snapshot = self.buffer.read(cx).snapshot(cx);
19560        let mut to_fold = Vec::new();
19561        let mut stack = vec![(0, snapshot.max_row().0, 1)];
19562
19563        let row_ranges_to_keep: Vec<Range<u32>> = self
19564            .selections
19565            .all::<Point>(&self.display_snapshot(cx))
19566            .into_iter()
19567            .map(|sel| sel.start.row..sel.end.row)
19568            .collect();
19569
19570        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
19571            while start_row < end_row {
19572                match self
19573                    .snapshot(window, cx)
19574                    .crease_for_buffer_row(MultiBufferRow(start_row))
19575                {
19576                    Some(crease) => {
19577                        let nested_start_row = crease.range().start.row + 1;
19578                        let nested_end_row = crease.range().end.row;
19579
19580                        if current_level < fold_at_level {
19581                            stack.push((nested_start_row, nested_end_row, current_level + 1));
19582                        } else if current_level == fold_at_level {
19583                            // Fold iff there is no selection completely contained within the fold region
19584                            if !row_ranges_to_keep.iter().any(|selection| {
19585                                selection.end >= nested_start_row
19586                                    && selection.start <= nested_end_row
19587                            }) {
19588                                to_fold.push(crease);
19589                            }
19590                        }
19591
19592                        start_row = nested_end_row + 1;
19593                    }
19594                    None => start_row += 1,
19595                }
19596            }
19597        }
19598
19599        self.fold_creases(to_fold, true, window, cx);
19600    }
19601
19602    pub fn fold_at_level_1(
19603        &mut self,
19604        _: &actions::FoldAtLevel1,
19605        window: &mut Window,
19606        cx: &mut Context<Self>,
19607    ) {
19608        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
19609    }
19610
19611    pub fn fold_at_level_2(
19612        &mut self,
19613        _: &actions::FoldAtLevel2,
19614        window: &mut Window,
19615        cx: &mut Context<Self>,
19616    ) {
19617        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
19618    }
19619
19620    pub fn fold_at_level_3(
19621        &mut self,
19622        _: &actions::FoldAtLevel3,
19623        window: &mut Window,
19624        cx: &mut Context<Self>,
19625    ) {
19626        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
19627    }
19628
19629    pub fn fold_at_level_4(
19630        &mut self,
19631        _: &actions::FoldAtLevel4,
19632        window: &mut Window,
19633        cx: &mut Context<Self>,
19634    ) {
19635        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
19636    }
19637
19638    pub fn fold_at_level_5(
19639        &mut self,
19640        _: &actions::FoldAtLevel5,
19641        window: &mut Window,
19642        cx: &mut Context<Self>,
19643    ) {
19644        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
19645    }
19646
19647    pub fn fold_at_level_6(
19648        &mut self,
19649        _: &actions::FoldAtLevel6,
19650        window: &mut Window,
19651        cx: &mut Context<Self>,
19652    ) {
19653        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
19654    }
19655
19656    pub fn fold_at_level_7(
19657        &mut self,
19658        _: &actions::FoldAtLevel7,
19659        window: &mut Window,
19660        cx: &mut Context<Self>,
19661    ) {
19662        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
19663    }
19664
19665    pub fn fold_at_level_8(
19666        &mut self,
19667        _: &actions::FoldAtLevel8,
19668        window: &mut Window,
19669        cx: &mut Context<Self>,
19670    ) {
19671        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
19672    }
19673
19674    pub fn fold_at_level_9(
19675        &mut self,
19676        _: &actions::FoldAtLevel9,
19677        window: &mut Window,
19678        cx: &mut Context<Self>,
19679    ) {
19680        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
19681    }
19682
19683    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
19684        if self.buffer.read(cx).is_singleton() {
19685            let mut fold_ranges = Vec::new();
19686            let snapshot = self.buffer.read(cx).snapshot(cx);
19687
19688            for row in 0..snapshot.max_row().0 {
19689                if let Some(foldable_range) = self
19690                    .snapshot(window, cx)
19691                    .crease_for_buffer_row(MultiBufferRow(row))
19692                {
19693                    fold_ranges.push(foldable_range);
19694                }
19695            }
19696
19697            self.fold_creases(fold_ranges, true, window, cx);
19698        } else {
19699            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
19700                editor
19701                    .update_in(cx, |editor, _, cx| {
19702                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
19703                            editor.fold_buffer(buffer_id, cx);
19704                        }
19705                    })
19706                    .ok();
19707            });
19708        }
19709        cx.emit(SearchEvent::ResultsCollapsedChanged(
19710            CollapseDirection::Collapsed,
19711        ));
19712    }
19713
19714    pub fn fold_function_bodies(
19715        &mut self,
19716        _: &actions::FoldFunctionBodies,
19717        window: &mut Window,
19718        cx: &mut Context<Self>,
19719    ) {
19720        let snapshot = self.buffer.read(cx).snapshot(cx);
19721
19722        let ranges = snapshot
19723            .text_object_ranges(
19724                MultiBufferOffset(0)..snapshot.len(),
19725                TreeSitterOptions::default(),
19726            )
19727            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
19728            .collect::<Vec<_>>();
19729
19730        let creases = ranges
19731            .into_iter()
19732            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
19733            .collect();
19734
19735        self.fold_creases(creases, true, window, cx);
19736    }
19737
19738    pub fn fold_recursive(
19739        &mut self,
19740        _: &actions::FoldRecursive,
19741        window: &mut Window,
19742        cx: &mut Context<Self>,
19743    ) {
19744        let mut to_fold = Vec::new();
19745        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19746        let selections = self.selections.all_adjusted(&display_map);
19747
19748        for selection in selections {
19749            let range = selection.range().sorted();
19750            let buffer_start_row = range.start.row;
19751
19752            if range.start.row != range.end.row {
19753                let mut found = false;
19754                for row in range.start.row..=range.end.row {
19755                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19756                        found = true;
19757                        to_fold.push(crease);
19758                    }
19759                }
19760                if found {
19761                    continue;
19762                }
19763            }
19764
19765            for row in (0..=range.start.row).rev() {
19766                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19767                    if crease.range().end.row >= buffer_start_row {
19768                        to_fold.push(crease);
19769                    } else {
19770                        break;
19771                    }
19772                }
19773            }
19774        }
19775
19776        self.fold_creases(to_fold, true, window, cx);
19777    }
19778
19779    pub fn fold_at(
19780        &mut self,
19781        buffer_row: MultiBufferRow,
19782        window: &mut Window,
19783        cx: &mut Context<Self>,
19784    ) {
19785        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19786
19787        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
19788            let autoscroll = self
19789                .selections
19790                .all::<Point>(&display_map)
19791                .iter()
19792                .any(|selection| crease.range().overlaps(&selection.range()));
19793
19794            self.fold_creases(vec![crease], autoscroll, window, cx);
19795        }
19796    }
19797
19798    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
19799        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19800            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19801            let buffer = display_map.buffer_snapshot();
19802            let selections = self.selections.all::<Point>(&display_map);
19803            let ranges = selections
19804                .iter()
19805                .map(|s| {
19806                    let range = s.display_range(&display_map).sorted();
19807                    let mut start = range.start.to_point(&display_map);
19808                    let mut end = range.end.to_point(&display_map);
19809                    start.column = 0;
19810                    end.column = buffer.line_len(MultiBufferRow(end.row));
19811                    start..end
19812                })
19813                .collect::<Vec<_>>();
19814
19815            self.unfold_ranges(&ranges, true, true, cx);
19816        } else {
19817            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19818            let buffer_ids = self
19819                .selections
19820                .disjoint_anchor_ranges()
19821                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19822                .collect::<HashSet<_>>();
19823            for buffer_id in buffer_ids {
19824                self.unfold_buffer(buffer_id, cx);
19825            }
19826        }
19827    }
19828
19829    pub fn unfold_recursive(
19830        &mut self,
19831        _: &UnfoldRecursive,
19832        _window: &mut Window,
19833        cx: &mut Context<Self>,
19834    ) {
19835        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19836        let selections = self.selections.all::<Point>(&display_map);
19837        let ranges = selections
19838            .iter()
19839            .map(|s| {
19840                let mut range = s.display_range(&display_map).sorted();
19841                *range.start.column_mut() = 0;
19842                *range.end.column_mut() = display_map.line_len(range.end.row());
19843                let start = range.start.to_point(&display_map);
19844                let end = range.end.to_point(&display_map);
19845                start..end
19846            })
19847            .collect::<Vec<_>>();
19848
19849        self.unfold_ranges(&ranges, true, true, cx);
19850    }
19851
19852    pub fn unfold_at(
19853        &mut self,
19854        buffer_row: MultiBufferRow,
19855        _window: &mut Window,
19856        cx: &mut Context<Self>,
19857    ) {
19858        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19859
19860        let intersection_range = Point::new(buffer_row.0, 0)
19861            ..Point::new(
19862                buffer_row.0,
19863                display_map.buffer_snapshot().line_len(buffer_row),
19864            );
19865
19866        let autoscroll = self
19867            .selections
19868            .all::<Point>(&display_map)
19869            .iter()
19870            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
19871
19872        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
19873    }
19874
19875    pub fn unfold_all(
19876        &mut self,
19877        _: &actions::UnfoldAll,
19878        _window: &mut Window,
19879        cx: &mut Context<Self>,
19880    ) {
19881        if self.buffer.read(cx).is_singleton() {
19882            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19883            self.unfold_ranges(
19884                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
19885                true,
19886                true,
19887                cx,
19888            );
19889        } else {
19890            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
19891                editor
19892                    .update(cx, |editor, cx| {
19893                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
19894                            editor.unfold_buffer(buffer_id, cx);
19895                        }
19896                    })
19897                    .ok();
19898            });
19899        }
19900        cx.emit(SearchEvent::ResultsCollapsedChanged(
19901            CollapseDirection::Expanded,
19902        ));
19903    }
19904
19905    pub fn fold_selected_ranges(
19906        &mut self,
19907        _: &FoldSelectedRanges,
19908        window: &mut Window,
19909        cx: &mut Context<Self>,
19910    ) {
19911        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19912        let selections = self.selections.all_adjusted(&display_map);
19913        let ranges = selections
19914            .into_iter()
19915            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
19916            .collect::<Vec<_>>();
19917        self.fold_creases(ranges, true, window, cx);
19918    }
19919
19920    pub fn fold_ranges<T: ToOffset + Clone>(
19921        &mut self,
19922        ranges: Vec<Range<T>>,
19923        auto_scroll: bool,
19924        window: &mut Window,
19925        cx: &mut Context<Self>,
19926    ) {
19927        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19928        let ranges = ranges
19929            .into_iter()
19930            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
19931            .collect::<Vec<_>>();
19932        self.fold_creases(ranges, auto_scroll, window, cx);
19933    }
19934
19935    pub fn fold_creases<T: ToOffset + Clone>(
19936        &mut self,
19937        creases: Vec<Crease<T>>,
19938        auto_scroll: bool,
19939        _window: &mut Window,
19940        cx: &mut Context<Self>,
19941    ) {
19942        if creases.is_empty() {
19943            return;
19944        }
19945
19946        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
19947
19948        if auto_scroll {
19949            self.request_autoscroll(Autoscroll::fit(), cx);
19950        }
19951
19952        cx.notify();
19953
19954        self.scrollbar_marker_state.dirty = true;
19955        self.folds_did_change(cx);
19956    }
19957
19958    /// Removes any folds whose ranges intersect any of the given ranges.
19959    pub fn unfold_ranges<T: ToOffset + Clone>(
19960        &mut self,
19961        ranges: &[Range<T>],
19962        inclusive: bool,
19963        auto_scroll: bool,
19964        cx: &mut Context<Self>,
19965    ) {
19966        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
19967            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
19968        });
19969        self.folds_did_change(cx);
19970    }
19971
19972    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
19973        if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
19974            return;
19975        }
19976
19977        let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
19978        self.display_map.update(cx, |display_map, cx| {
19979            display_map.fold_buffers([buffer_id], cx)
19980        });
19981
19982        let snapshot = self.display_snapshot(cx);
19983        self.selections.change_with(&snapshot, |selections| {
19984            selections.remove_selections_from_buffer(buffer_id);
19985        });
19986
19987        cx.emit(EditorEvent::BufferFoldToggled {
19988            ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
19989            folded: true,
19990        });
19991        cx.notify();
19992    }
19993
19994    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
19995        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
19996            return;
19997        }
19998        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
19999        self.display_map.update(cx, |display_map, cx| {
20000            display_map.unfold_buffers([buffer_id], cx);
20001        });
20002        cx.emit(EditorEvent::BufferFoldToggled {
20003            ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
20004            folded: false,
20005        });
20006        cx.notify();
20007    }
20008
20009    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20010        self.display_map.read(cx).is_buffer_folded(buffer)
20011    }
20012
20013    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20014        self.display_map.read(cx).folded_buffers()
20015    }
20016
20017    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20018        self.display_map.update(cx, |display_map, cx| {
20019            display_map.disable_header_for_buffer(buffer_id, cx);
20020        });
20021        cx.notify();
20022    }
20023
20024    /// Removes any folds with the given ranges.
20025    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20026        &mut self,
20027        ranges: &[Range<T>],
20028        type_id: TypeId,
20029        auto_scroll: bool,
20030        cx: &mut Context<Self>,
20031    ) {
20032        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20033            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20034        });
20035        self.folds_did_change(cx);
20036    }
20037
20038    fn remove_folds_with<T: ToOffset + Clone>(
20039        &mut self,
20040        ranges: &[Range<T>],
20041        auto_scroll: bool,
20042        cx: &mut Context<Self>,
20043        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20044    ) {
20045        if ranges.is_empty() {
20046            return;
20047        }
20048
20049        let mut buffers_affected = HashSet::default();
20050        let multi_buffer = self.buffer().read(cx);
20051        for range in ranges {
20052            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20053                buffers_affected.insert(buffer.read(cx).remote_id());
20054            };
20055        }
20056
20057        self.display_map.update(cx, update);
20058
20059        if auto_scroll {
20060            self.request_autoscroll(Autoscroll::fit(), cx);
20061        }
20062
20063        cx.notify();
20064        self.scrollbar_marker_state.dirty = true;
20065        self.active_indent_guides_state.dirty = true;
20066    }
20067
20068    pub fn update_renderer_widths(
20069        &mut self,
20070        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20071        cx: &mut Context<Self>,
20072    ) -> bool {
20073        self.display_map
20074            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20075    }
20076
20077    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20078        self.display_map.read(cx).fold_placeholder.clone()
20079    }
20080
20081    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20082        self.buffer.update(cx, |buffer, cx| {
20083            buffer.set_all_diff_hunks_expanded(cx);
20084        });
20085    }
20086
20087    pub fn expand_all_diff_hunks(
20088        &mut self,
20089        _: &ExpandAllDiffHunks,
20090        _window: &mut Window,
20091        cx: &mut Context<Self>,
20092    ) {
20093        self.buffer.update(cx, |buffer, cx| {
20094            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20095        });
20096    }
20097
20098    pub fn collapse_all_diff_hunks(
20099        &mut self,
20100        _: &CollapseAllDiffHunks,
20101        _window: &mut Window,
20102        cx: &mut Context<Self>,
20103    ) {
20104        self.buffer.update(cx, |buffer, cx| {
20105            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20106        });
20107    }
20108
20109    pub fn toggle_selected_diff_hunks(
20110        &mut self,
20111        _: &ToggleSelectedDiffHunks,
20112        _window: &mut Window,
20113        cx: &mut Context<Self>,
20114    ) {
20115        let ranges: Vec<_> = self
20116            .selections
20117            .disjoint_anchors()
20118            .iter()
20119            .map(|s| s.range())
20120            .collect();
20121        self.toggle_diff_hunks_in_ranges(ranges, cx);
20122    }
20123
20124    pub fn diff_hunks_in_ranges<'a>(
20125        &'a self,
20126        ranges: &'a [Range<Anchor>],
20127        buffer: &'a MultiBufferSnapshot,
20128    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20129        ranges.iter().flat_map(move |range| {
20130            let end_excerpt_id = range.end.excerpt_id;
20131            let range = range.to_point(buffer);
20132            let mut peek_end = range.end;
20133            if range.end.row < buffer.max_row().0 {
20134                peek_end = Point::new(range.end.row + 1, 0);
20135            }
20136            buffer
20137                .diff_hunks_in_range(range.start..peek_end)
20138                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20139        })
20140    }
20141
20142    pub fn has_stageable_diff_hunks_in_ranges(
20143        &self,
20144        ranges: &[Range<Anchor>],
20145        snapshot: &MultiBufferSnapshot,
20146    ) -> bool {
20147        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20148        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20149    }
20150
20151    pub fn toggle_staged_selected_diff_hunks(
20152        &mut self,
20153        _: &::git::ToggleStaged,
20154        _: &mut Window,
20155        cx: &mut Context<Self>,
20156    ) {
20157        let snapshot = self.buffer.read(cx).snapshot(cx);
20158        let ranges: Vec<_> = self
20159            .selections
20160            .disjoint_anchors()
20161            .iter()
20162            .map(|s| s.range())
20163            .collect();
20164        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20165        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20166    }
20167
20168    pub fn set_render_diff_hunk_controls(
20169        &mut self,
20170        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20171        cx: &mut Context<Self>,
20172    ) {
20173        self.render_diff_hunk_controls = render_diff_hunk_controls;
20174        cx.notify();
20175    }
20176
20177    pub fn stage_and_next(
20178        &mut self,
20179        _: &::git::StageAndNext,
20180        window: &mut Window,
20181        cx: &mut Context<Self>,
20182    ) {
20183        self.do_stage_or_unstage_and_next(true, window, cx);
20184    }
20185
20186    pub fn unstage_and_next(
20187        &mut self,
20188        _: &::git::UnstageAndNext,
20189        window: &mut Window,
20190        cx: &mut Context<Self>,
20191    ) {
20192        self.do_stage_or_unstage_and_next(false, window, cx);
20193    }
20194
20195    pub fn stage_or_unstage_diff_hunks(
20196        &mut self,
20197        stage: bool,
20198        ranges: Vec<Range<Anchor>>,
20199        cx: &mut Context<Self>,
20200    ) {
20201        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20202        cx.spawn(async move |this, cx| {
20203            task.await?;
20204            this.update(cx, |this, cx| {
20205                let snapshot = this.buffer.read(cx).snapshot(cx);
20206                let chunk_by = this
20207                    .diff_hunks_in_ranges(&ranges, &snapshot)
20208                    .chunk_by(|hunk| hunk.buffer_id);
20209                for (buffer_id, hunks) in &chunk_by {
20210                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20211                }
20212            })
20213        })
20214        .detach_and_log_err(cx);
20215    }
20216
20217    fn save_buffers_for_ranges_if_needed(
20218        &mut self,
20219        ranges: &[Range<Anchor>],
20220        cx: &mut Context<Editor>,
20221    ) -> Task<Result<()>> {
20222        let multibuffer = self.buffer.read(cx);
20223        let snapshot = multibuffer.read(cx);
20224        let buffer_ids: HashSet<_> = ranges
20225            .iter()
20226            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20227            .collect();
20228        drop(snapshot);
20229
20230        let mut buffers = HashSet::default();
20231        for buffer_id in buffer_ids {
20232            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20233                let buffer = buffer_entity.read(cx);
20234                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20235                {
20236                    buffers.insert(buffer_entity);
20237                }
20238            }
20239        }
20240
20241        if let Some(project) = &self.project {
20242            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20243        } else {
20244            Task::ready(Ok(()))
20245        }
20246    }
20247
20248    fn do_stage_or_unstage_and_next(
20249        &mut self,
20250        stage: bool,
20251        window: &mut Window,
20252        cx: &mut Context<Self>,
20253    ) {
20254        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20255
20256        if ranges.iter().any(|range| range.start != range.end) {
20257            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20258            return;
20259        }
20260
20261        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20262        let snapshot = self.snapshot(window, cx);
20263        let position = self
20264            .selections
20265            .newest::<Point>(&snapshot.display_snapshot)
20266            .head();
20267        let mut row = snapshot
20268            .buffer_snapshot()
20269            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
20270            .find(|hunk| hunk.row_range.start.0 > position.row)
20271            .map(|hunk| hunk.row_range.start);
20272
20273        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20274        // Outside of the project diff editor, wrap around to the beginning.
20275        if !all_diff_hunks_expanded {
20276            row = row.or_else(|| {
20277                snapshot
20278                    .buffer_snapshot()
20279                    .diff_hunks_in_range(Point::zero()..position)
20280                    .find(|hunk| hunk.row_range.end.0 < position.row)
20281                    .map(|hunk| hunk.row_range.start)
20282            });
20283        }
20284
20285        if let Some(row) = row {
20286            let destination = Point::new(row.0, 0);
20287            let autoscroll = Autoscroll::center();
20288
20289            self.unfold_ranges(&[destination..destination], false, false, cx);
20290            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
20291                s.select_ranges([destination..destination]);
20292            });
20293        }
20294    }
20295
20296    fn do_stage_or_unstage(
20297        &self,
20298        stage: bool,
20299        buffer_id: BufferId,
20300        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20301        cx: &mut App,
20302    ) -> Option<()> {
20303        let project = self.project()?;
20304        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20305        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20306        let buffer_snapshot = buffer.read(cx).snapshot();
20307        let file_exists = buffer_snapshot
20308            .file()
20309            .is_some_and(|file| file.disk_state().exists());
20310        diff.update(cx, |diff, cx| {
20311            diff.stage_or_unstage_hunks(
20312                stage,
20313                &hunks
20314                    .map(|hunk| buffer_diff::DiffHunk {
20315                        buffer_range: hunk.buffer_range,
20316                        // We don't need to pass in word diffs here because they're only used for rendering and
20317                        // this function changes internal state
20318                        base_word_diffs: Vec::default(),
20319                        buffer_word_diffs: Vec::default(),
20320                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20321                            ..hunk.diff_base_byte_range.end.0,
20322                        secondary_status: hunk.status.secondary,
20323                        range: Point::zero()..Point::zero(), // unused
20324                    })
20325                    .collect::<Vec<_>>(),
20326                &buffer_snapshot,
20327                file_exists,
20328                cx,
20329            )
20330        });
20331        None
20332    }
20333
20334    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20335        let ranges: Vec<_> = self
20336            .selections
20337            .disjoint_anchors()
20338            .iter()
20339            .map(|s| s.range())
20340            .collect();
20341        self.buffer
20342            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20343    }
20344
20345    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20346        self.buffer.update(cx, |buffer, cx| {
20347            let ranges = vec![Anchor::min()..Anchor::max()];
20348            if !buffer.all_diff_hunks_expanded()
20349                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20350            {
20351                buffer.collapse_diff_hunks(ranges, cx);
20352                true
20353            } else {
20354                false
20355            }
20356        })
20357    }
20358
20359    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20360        if self.buffer.read(cx).all_diff_hunks_expanded() {
20361            return true;
20362        }
20363        let ranges = vec![Anchor::min()..Anchor::max()];
20364        self.buffer
20365            .read(cx)
20366            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20367    }
20368
20369    fn toggle_diff_hunks_in_ranges(
20370        &mut self,
20371        ranges: Vec<Range<Anchor>>,
20372        cx: &mut Context<Editor>,
20373    ) {
20374        self.buffer.update(cx, |buffer, cx| {
20375            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20376            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20377        })
20378    }
20379
20380    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20381        self.buffer.update(cx, |buffer, cx| {
20382            let snapshot = buffer.snapshot(cx);
20383            let excerpt_id = range.end.excerpt_id;
20384            let point_range = range.to_point(&snapshot);
20385            let expand = !buffer.single_hunk_is_expanded(range, cx);
20386            buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
20387        })
20388    }
20389
20390    pub(crate) fn apply_all_diff_hunks(
20391        &mut self,
20392        _: &ApplyAllDiffHunks,
20393        window: &mut Window,
20394        cx: &mut Context<Self>,
20395    ) {
20396        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20397
20398        let buffers = self.buffer.read(cx).all_buffers();
20399        for branch_buffer in buffers {
20400            branch_buffer.update(cx, |branch_buffer, cx| {
20401                branch_buffer.merge_into_base(Vec::new(), cx);
20402            });
20403        }
20404
20405        if let Some(project) = self.project.clone() {
20406            self.save(
20407                SaveOptions {
20408                    format: true,
20409                    autosave: false,
20410                },
20411                project,
20412                window,
20413                cx,
20414            )
20415            .detach_and_log_err(cx);
20416        }
20417    }
20418
20419    pub(crate) fn apply_selected_diff_hunks(
20420        &mut self,
20421        _: &ApplyDiffHunk,
20422        window: &mut Window,
20423        cx: &mut Context<Self>,
20424    ) {
20425        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20426        let snapshot = self.snapshot(window, cx);
20427        let hunks = snapshot.hunks_for_ranges(
20428            self.selections
20429                .all(&snapshot.display_snapshot)
20430                .into_iter()
20431                .map(|selection| selection.range()),
20432        );
20433        let mut ranges_by_buffer = HashMap::default();
20434        self.transact(window, cx, |editor, _window, cx| {
20435            for hunk in hunks {
20436                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20437                    ranges_by_buffer
20438                        .entry(buffer.clone())
20439                        .or_insert_with(Vec::new)
20440                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20441                }
20442            }
20443
20444            for (buffer, ranges) in ranges_by_buffer {
20445                buffer.update(cx, |buffer, cx| {
20446                    buffer.merge_into_base(ranges, cx);
20447                });
20448            }
20449        });
20450
20451        if let Some(project) = self.project.clone() {
20452            self.save(
20453                SaveOptions {
20454                    format: true,
20455                    autosave: false,
20456                },
20457                project,
20458                window,
20459                cx,
20460            )
20461            .detach_and_log_err(cx);
20462        }
20463    }
20464
20465    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20466        if hovered != self.gutter_hovered {
20467            self.gutter_hovered = hovered;
20468            cx.notify();
20469        }
20470    }
20471
20472    pub fn insert_blocks(
20473        &mut self,
20474        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20475        autoscroll: Option<Autoscroll>,
20476        cx: &mut Context<Self>,
20477    ) -> Vec<CustomBlockId> {
20478        let blocks = self
20479            .display_map
20480            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20481        if let Some(autoscroll) = autoscroll {
20482            self.request_autoscroll(autoscroll, cx);
20483        }
20484        cx.notify();
20485        blocks
20486    }
20487
20488    pub fn resize_blocks(
20489        &mut self,
20490        heights: HashMap<CustomBlockId, u32>,
20491        autoscroll: Option<Autoscroll>,
20492        cx: &mut Context<Self>,
20493    ) {
20494        self.display_map
20495            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20496        if let Some(autoscroll) = autoscroll {
20497            self.request_autoscroll(autoscroll, cx);
20498        }
20499        cx.notify();
20500    }
20501
20502    pub fn replace_blocks(
20503        &mut self,
20504        renderers: HashMap<CustomBlockId, RenderBlock>,
20505        autoscroll: Option<Autoscroll>,
20506        cx: &mut Context<Self>,
20507    ) {
20508        self.display_map
20509            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20510        if let Some(autoscroll) = autoscroll {
20511            self.request_autoscroll(autoscroll, cx);
20512        }
20513        cx.notify();
20514    }
20515
20516    pub fn remove_blocks(
20517        &mut self,
20518        block_ids: HashSet<CustomBlockId>,
20519        autoscroll: Option<Autoscroll>,
20520        cx: &mut Context<Self>,
20521    ) {
20522        self.display_map.update(cx, |display_map, cx| {
20523            display_map.remove_blocks(block_ids, cx)
20524        });
20525        if let Some(autoscroll) = autoscroll {
20526            self.request_autoscroll(autoscroll, cx);
20527        }
20528        cx.notify();
20529    }
20530
20531    pub fn row_for_block(
20532        &self,
20533        block_id: CustomBlockId,
20534        cx: &mut Context<Self>,
20535    ) -> Option<DisplayRow> {
20536        self.display_map
20537            .update(cx, |map, cx| map.row_for_block(block_id, cx))
20538    }
20539
20540    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
20541        self.focused_block = Some(focused_block);
20542    }
20543
20544    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
20545        self.focused_block.take()
20546    }
20547
20548    pub fn insert_creases(
20549        &mut self,
20550        creases: impl IntoIterator<Item = Crease<Anchor>>,
20551        cx: &mut Context<Self>,
20552    ) -> Vec<CreaseId> {
20553        self.display_map
20554            .update(cx, |map, cx| map.insert_creases(creases, cx))
20555    }
20556
20557    pub fn remove_creases(
20558        &mut self,
20559        ids: impl IntoIterator<Item = CreaseId>,
20560        cx: &mut Context<Self>,
20561    ) -> Vec<(CreaseId, Range<Anchor>)> {
20562        self.display_map
20563            .update(cx, |map, cx| map.remove_creases(ids, cx))
20564    }
20565
20566    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
20567        self.display_map
20568            .update(cx, |map, cx| map.snapshot(cx))
20569            .longest_row()
20570    }
20571
20572    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
20573        self.display_map
20574            .update(cx, |map, cx| map.snapshot(cx))
20575            .max_point()
20576    }
20577
20578    pub fn text(&self, cx: &App) -> String {
20579        self.buffer.read(cx).read(cx).text()
20580    }
20581
20582    pub fn is_empty(&self, cx: &App) -> bool {
20583        self.buffer.read(cx).read(cx).is_empty()
20584    }
20585
20586    pub fn text_option(&self, cx: &App) -> Option<String> {
20587        let text = self.text(cx);
20588        let text = text.trim();
20589
20590        if text.is_empty() {
20591            return None;
20592        }
20593
20594        Some(text.to_string())
20595    }
20596
20597    pub fn set_text(
20598        &mut self,
20599        text: impl Into<Arc<str>>,
20600        window: &mut Window,
20601        cx: &mut Context<Self>,
20602    ) {
20603        self.transact(window, cx, |this, _, cx| {
20604            this.buffer
20605                .read(cx)
20606                .as_singleton()
20607                .expect("you can only call set_text on editors for singleton buffers")
20608                .update(cx, |buffer, cx| buffer.set_text(text, cx));
20609        });
20610    }
20611
20612    pub fn display_text(&self, cx: &mut App) -> String {
20613        self.display_map
20614            .update(cx, |map, cx| map.snapshot(cx))
20615            .text()
20616    }
20617
20618    fn create_minimap(
20619        &self,
20620        minimap_settings: MinimapSettings,
20621        window: &mut Window,
20622        cx: &mut Context<Self>,
20623    ) -> Option<Entity<Self>> {
20624        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
20625            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
20626    }
20627
20628    fn initialize_new_minimap(
20629        &self,
20630        minimap_settings: MinimapSettings,
20631        window: &mut Window,
20632        cx: &mut Context<Self>,
20633    ) -> Entity<Self> {
20634        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
20635        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
20636
20637        let mut minimap = Editor::new_internal(
20638            EditorMode::Minimap {
20639                parent: cx.weak_entity(),
20640            },
20641            self.buffer.clone(),
20642            None,
20643            Some(self.display_map.clone()),
20644            window,
20645            cx,
20646        );
20647        minimap.scroll_manager.clone_state(&self.scroll_manager);
20648        minimap.set_text_style_refinement(TextStyleRefinement {
20649            font_size: Some(MINIMAP_FONT_SIZE),
20650            font_weight: Some(MINIMAP_FONT_WEIGHT),
20651            font_family: Some(MINIMAP_FONT_FAMILY),
20652            ..Default::default()
20653        });
20654        minimap.update_minimap_configuration(minimap_settings, cx);
20655        cx.new(|_| minimap)
20656    }
20657
20658    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
20659        let current_line_highlight = minimap_settings
20660            .current_line_highlight
20661            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
20662        self.set_current_line_highlight(Some(current_line_highlight));
20663    }
20664
20665    pub fn minimap(&self) -> Option<&Entity<Self>> {
20666        self.minimap
20667            .as_ref()
20668            .filter(|_| self.minimap_visibility.visible())
20669    }
20670
20671    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
20672        let mut wrap_guides = smallvec![];
20673
20674        if self.show_wrap_guides == Some(false) {
20675            return wrap_guides;
20676        }
20677
20678        let settings = self.buffer.read(cx).language_settings(cx);
20679        if settings.show_wrap_guides {
20680            match self.soft_wrap_mode(cx) {
20681                SoftWrap::Column(soft_wrap) => {
20682                    wrap_guides.push((soft_wrap as usize, true));
20683                }
20684                SoftWrap::Bounded(soft_wrap) => {
20685                    wrap_guides.push((soft_wrap as usize, true));
20686                }
20687                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
20688            }
20689            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
20690        }
20691
20692        wrap_guides
20693    }
20694
20695    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
20696        let settings = self.buffer.read(cx).language_settings(cx);
20697        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
20698        match mode {
20699            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
20700                SoftWrap::None
20701            }
20702            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
20703            language_settings::SoftWrap::PreferredLineLength => {
20704                SoftWrap::Column(settings.preferred_line_length)
20705            }
20706            language_settings::SoftWrap::Bounded => {
20707                SoftWrap::Bounded(settings.preferred_line_length)
20708            }
20709        }
20710    }
20711
20712    pub fn set_soft_wrap_mode(
20713        &mut self,
20714        mode: language_settings::SoftWrap,
20715
20716        cx: &mut Context<Self>,
20717    ) {
20718        self.soft_wrap_mode_override = Some(mode);
20719        cx.notify();
20720    }
20721
20722    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
20723        self.hard_wrap = hard_wrap;
20724        cx.notify();
20725    }
20726
20727    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
20728        self.text_style_refinement = Some(style);
20729    }
20730
20731    /// called by the Element so we know what style we were most recently rendered with.
20732    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
20733        // We intentionally do not inform the display map about the minimap style
20734        // so that wrapping is not recalculated and stays consistent for the editor
20735        // and its linked minimap.
20736        if !self.mode.is_minimap() {
20737            let font = style.text.font();
20738            let font_size = style.text.font_size.to_pixels(window.rem_size());
20739            let display_map = self
20740                .placeholder_display_map
20741                .as_ref()
20742                .filter(|_| self.is_empty(cx))
20743                .unwrap_or(&self.display_map);
20744
20745            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
20746        }
20747        self.style = Some(style);
20748    }
20749
20750    pub fn style(&mut self, cx: &App) -> &EditorStyle {
20751        if self.style.is_none() {
20752            self.style = Some(self.create_style(cx));
20753        }
20754        self.style.as_ref().unwrap()
20755    }
20756
20757    // Called by the element. This method is not designed to be called outside of the editor
20758    // element's layout code because it does not notify when rewrapping is computed synchronously.
20759    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
20760        if self.is_empty(cx) {
20761            self.placeholder_display_map
20762                .as_ref()
20763                .map_or(false, |display_map| {
20764                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
20765                })
20766        } else {
20767            self.display_map
20768                .update(cx, |map, cx| map.set_wrap_width(width, cx))
20769        }
20770    }
20771
20772    pub fn set_soft_wrap(&mut self) {
20773        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
20774    }
20775
20776    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
20777        if self.soft_wrap_mode_override.is_some() {
20778            self.soft_wrap_mode_override.take();
20779        } else {
20780            let soft_wrap = match self.soft_wrap_mode(cx) {
20781                SoftWrap::GitDiff => return,
20782                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
20783                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
20784                    language_settings::SoftWrap::None
20785                }
20786            };
20787            self.soft_wrap_mode_override = Some(soft_wrap);
20788        }
20789        cx.notify();
20790    }
20791
20792    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
20793        let Some(workspace) = self.workspace() else {
20794            return;
20795        };
20796        let fs = workspace.read(cx).app_state().fs.clone();
20797        let current_show = TabBarSettings::get_global(cx).show;
20798        update_settings_file(fs, cx, move |setting, _| {
20799            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
20800        });
20801    }
20802
20803    pub fn toggle_indent_guides(
20804        &mut self,
20805        _: &ToggleIndentGuides,
20806        _: &mut Window,
20807        cx: &mut Context<Self>,
20808    ) {
20809        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
20810            self.buffer
20811                .read(cx)
20812                .language_settings(cx)
20813                .indent_guides
20814                .enabled
20815        });
20816        self.show_indent_guides = Some(!currently_enabled);
20817        cx.notify();
20818    }
20819
20820    fn should_show_indent_guides(&self) -> Option<bool> {
20821        self.show_indent_guides
20822    }
20823
20824    pub fn disable_indent_guides_for_buffer(
20825        &mut self,
20826        buffer_id: BufferId,
20827        cx: &mut Context<Self>,
20828    ) {
20829        self.buffers_with_disabled_indent_guides.insert(buffer_id);
20830        cx.notify();
20831    }
20832
20833    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
20834        self.buffers_with_disabled_indent_guides
20835            .contains(&buffer_id)
20836    }
20837
20838    pub fn toggle_line_numbers(
20839        &mut self,
20840        _: &ToggleLineNumbers,
20841        _: &mut Window,
20842        cx: &mut Context<Self>,
20843    ) {
20844        let mut editor_settings = EditorSettings::get_global(cx).clone();
20845        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
20846        EditorSettings::override_global(editor_settings, cx);
20847    }
20848
20849    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
20850        if let Some(show_line_numbers) = self.show_line_numbers {
20851            return show_line_numbers;
20852        }
20853        EditorSettings::get_global(cx).gutter.line_numbers
20854    }
20855
20856    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
20857        match (
20858            self.use_relative_line_numbers,
20859            EditorSettings::get_global(cx).relative_line_numbers,
20860        ) {
20861            (None, setting) => setting,
20862            (Some(false), _) => RelativeLineNumbers::Disabled,
20863            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
20864            (Some(true), _) => RelativeLineNumbers::Enabled,
20865        }
20866    }
20867
20868    pub fn toggle_relative_line_numbers(
20869        &mut self,
20870        _: &ToggleRelativeLineNumbers,
20871        _: &mut Window,
20872        cx: &mut Context<Self>,
20873    ) {
20874        let is_relative = self.relative_line_numbers(cx);
20875        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
20876    }
20877
20878    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
20879        self.use_relative_line_numbers = is_relative;
20880        cx.notify();
20881    }
20882
20883    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
20884        self.show_gutter = show_gutter;
20885        cx.notify();
20886    }
20887
20888    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
20889        self.show_scrollbars = ScrollbarAxes {
20890            horizontal: show,
20891            vertical: show,
20892        };
20893        cx.notify();
20894    }
20895
20896    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
20897        self.show_scrollbars.vertical = show;
20898        cx.notify();
20899    }
20900
20901    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
20902        self.show_scrollbars.horizontal = show;
20903        cx.notify();
20904    }
20905
20906    pub fn set_minimap_visibility(
20907        &mut self,
20908        minimap_visibility: MinimapVisibility,
20909        window: &mut Window,
20910        cx: &mut Context<Self>,
20911    ) {
20912        if self.minimap_visibility != minimap_visibility {
20913            if minimap_visibility.visible() && self.minimap.is_none() {
20914                let minimap_settings = EditorSettings::get_global(cx).minimap;
20915                self.minimap =
20916                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
20917            }
20918            self.minimap_visibility = minimap_visibility;
20919            cx.notify();
20920        }
20921    }
20922
20923    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20924        self.set_show_scrollbars(false, cx);
20925        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
20926    }
20927
20928    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20929        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
20930    }
20931
20932    /// Normally the text in full mode and auto height editors is padded on the
20933    /// left side by roughly half a character width for improved hit testing.
20934    ///
20935    /// Use this method to disable this for cases where this is not wanted (e.g.
20936    /// if you want to align the editor text with some other text above or below)
20937    /// or if you want to add this padding to single-line editors.
20938    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
20939        self.offset_content = offset_content;
20940        cx.notify();
20941    }
20942
20943    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
20944        self.show_line_numbers = Some(show_line_numbers);
20945        cx.notify();
20946    }
20947
20948    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
20949        self.disable_expand_excerpt_buttons = true;
20950        cx.notify();
20951    }
20952
20953    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
20954        self.delegate_expand_excerpts = delegate;
20955    }
20956
20957    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
20958        self.show_git_diff_gutter = Some(show_git_diff_gutter);
20959        cx.notify();
20960    }
20961
20962    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
20963        self.show_code_actions = Some(show_code_actions);
20964        cx.notify();
20965    }
20966
20967    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
20968        self.show_runnables = Some(show_runnables);
20969        cx.notify();
20970    }
20971
20972    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
20973        self.show_breakpoints = Some(show_breakpoints);
20974        cx.notify();
20975    }
20976
20977    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
20978        self.show_diff_review_button = show;
20979        cx.notify();
20980    }
20981
20982    pub fn show_diff_review_button(&self) -> bool {
20983        self.show_diff_review_button
20984    }
20985
20986    pub fn render_diff_review_button(
20987        &self,
20988        display_row: DisplayRow,
20989        width: Pixels,
20990        cx: &mut Context<Self>,
20991    ) -> impl IntoElement {
20992        let text_color = cx.theme().colors().text;
20993        let icon_color = cx.theme().colors().icon_accent;
20994
20995        h_flex()
20996            .id("diff_review_button")
20997            .cursor_pointer()
20998            .w(width - px(1.))
20999            .h(relative(0.9))
21000            .justify_center()
21001            .rounded_sm()
21002            .border_1()
21003            .border_color(text_color.opacity(0.1))
21004            .bg(text_color.opacity(0.15))
21005            .hover(|s| {
21006                s.bg(icon_color.opacity(0.4))
21007                    .border_color(icon_color.opacity(0.5))
21008            })
21009            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21010            .tooltip(Tooltip::text("Add Review"))
21011            .on_click(cx.listener(move |editor, _event: &ClickEvent, window, cx| {
21012                editor.show_diff_review_overlay(display_row, window, cx);
21013            }))
21014    }
21015
21016    /// Calculates the appropriate block height for the diff review overlay.
21017    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21018    /// and 2 lines per comment when expanded.
21019    fn calculate_overlay_height(
21020        &self,
21021        hunk_key: &DiffHunkKey,
21022        comments_expanded: bool,
21023        snapshot: &MultiBufferSnapshot,
21024    ) -> u32 {
21025        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21026        let base_height: u32 = 2; // Input row with avatar and buttons
21027
21028        if comment_count == 0 {
21029            base_height
21030        } else if comments_expanded {
21031            // Header (1 line) + 2 lines per comment
21032            base_height + 1 + (comment_count as u32 * 2)
21033        } else {
21034            // Just header when collapsed
21035            base_height + 1
21036        }
21037    }
21038
21039    pub fn show_diff_review_overlay(
21040        &mut self,
21041        display_row: DisplayRow,
21042        window: &mut Window,
21043        cx: &mut Context<Self>,
21044    ) {
21045        // Check if there's already an overlay for the same hunk - if so, just focus it
21046        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21047        let editor_snapshot = self.snapshot(window, cx);
21048        let display_point = DisplayPoint::new(display_row, 0);
21049        let buffer_point = editor_snapshot
21050            .display_snapshot
21051            .display_point_to_point(display_point, Bias::Left);
21052
21053        // Compute the hunk key for this display row
21054        let file_path = buffer_snapshot
21055            .file_at(Point::new(buffer_point.row, 0))
21056            .map(|file: &Arc<dyn language::File>| file.path().clone())
21057            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21058        let hunk_start_anchor = buffer_snapshot.anchor_before(Point::new(buffer_point.row, 0));
21059        let new_hunk_key = DiffHunkKey {
21060            file_path,
21061            hunk_start_anchor,
21062        };
21063
21064        // Check if we already have an overlay for this hunk
21065        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21066            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21067        }) {
21068            // Just focus the existing overlay's prompt editor
21069            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21070            window.focus(&focus_handle, cx);
21071            return;
21072        }
21073
21074        // Dismiss overlays that have no comments for their hunks
21075        self.dismiss_overlays_without_comments(cx);
21076
21077        // Get the current user's avatar URI from the project's user_store
21078        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21079            let user_store = project.read(cx).user_store();
21080            user_store
21081                .read(cx)
21082                .current_user()
21083                .map(|user| user.avatar_uri.clone())
21084        });
21085
21086        // Create anchor at the end of the row so the block appears immediately below it
21087        let line_len = buffer_snapshot.line_len(MultiBufferRow(buffer_point.row));
21088        let anchor = buffer_snapshot.anchor_after(Point::new(buffer_point.row, line_len));
21089
21090        // Use the hunk key we already computed
21091        let hunk_key = new_hunk_key;
21092
21093        // Create the prompt editor for the review input
21094        let prompt_editor = cx.new(|cx| {
21095            let mut editor = Editor::single_line(window, cx);
21096            editor.set_placeholder_text("Add a review comment...", window, cx);
21097            editor
21098        });
21099
21100        // Register the Newline action on the prompt editor to submit the review
21101        let parent_editor = cx.entity().downgrade();
21102        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21103            prompt_editor.register_action({
21104                let parent_editor = parent_editor.clone();
21105                move |_: &crate::actions::Newline, window, cx| {
21106                    if let Some(editor) = parent_editor.upgrade() {
21107                        editor.update(cx, |editor, cx| {
21108                            editor.submit_diff_review_comment(window, cx);
21109                        });
21110                    }
21111                }
21112            })
21113        });
21114
21115        // Calculate initial height based on existing comments for this hunk
21116        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21117
21118        // Create the overlay block
21119        let prompt_editor_for_render = prompt_editor.clone();
21120        let hunk_key_for_render = hunk_key.clone();
21121        let editor_handle = cx.entity().downgrade();
21122        let block = BlockProperties {
21123            style: BlockStyle::Sticky,
21124            placement: BlockPlacement::Below(anchor),
21125            height: Some(initial_height),
21126            render: Arc::new(move |cx| {
21127                Self::render_diff_review_overlay(
21128                    &prompt_editor_for_render,
21129                    &hunk_key_for_render,
21130                    &editor_handle,
21131                    cx,
21132                )
21133            }),
21134            priority: 0,
21135        };
21136
21137        let block_ids = self.insert_blocks([block], None, cx);
21138        let Some(block_id) = block_ids.into_iter().next() else {
21139            log::error!("Failed to insert diff review overlay block");
21140            return;
21141        };
21142
21143        self.diff_review_overlays.push(DiffReviewOverlay {
21144            display_row,
21145            block_id,
21146            prompt_editor: prompt_editor.clone(),
21147            hunk_key,
21148            comments_expanded: true,
21149            inline_edit_editors: HashMap::default(),
21150            inline_edit_subscriptions: HashMap::default(),
21151            user_avatar_uri,
21152            _subscription: subscription,
21153        });
21154
21155        // Focus the prompt editor
21156        let focus_handle = prompt_editor.focus_handle(cx);
21157        window.focus(&focus_handle, cx);
21158
21159        cx.notify();
21160    }
21161
21162    /// Dismisses all diff review overlays.
21163    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21164        if self.diff_review_overlays.is_empty() {
21165            return;
21166        }
21167        let block_ids: HashSet<_> = self
21168            .diff_review_overlays
21169            .drain(..)
21170            .map(|overlay| overlay.block_id)
21171            .collect();
21172        self.remove_blocks(block_ids, None, cx);
21173        cx.notify();
21174    }
21175
21176    /// Dismisses overlays that have no comments stored for their hunks.
21177    /// Keeps overlays that have at least one comment.
21178    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21179        let snapshot = self.buffer.read(cx).snapshot(cx);
21180
21181        // First, compute which overlays have comments (to avoid borrow issues with retain)
21182        let overlays_with_comments: Vec<bool> = self
21183            .diff_review_overlays
21184            .iter()
21185            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21186            .collect();
21187
21188        // Now collect block IDs to remove and retain overlays
21189        let mut block_ids_to_remove = HashSet::default();
21190        let mut index = 0;
21191        self.diff_review_overlays.retain(|overlay| {
21192            let has_comments = overlays_with_comments[index];
21193            index += 1;
21194            if !has_comments {
21195                block_ids_to_remove.insert(overlay.block_id);
21196            }
21197            has_comments
21198        });
21199
21200        if !block_ids_to_remove.is_empty() {
21201            self.remove_blocks(block_ids_to_remove, None, cx);
21202            cx.notify();
21203        }
21204    }
21205
21206    /// Refreshes the diff review overlay block to update its height and render function.
21207    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21208    fn refresh_diff_review_overlay_height(
21209        &mut self,
21210        hunk_key: &DiffHunkKey,
21211        _window: &mut Window,
21212        cx: &mut Context<Self>,
21213    ) {
21214        // Extract all needed data from overlay first to avoid borrow conflicts
21215        let snapshot = self.buffer.read(cx).snapshot(cx);
21216        let (comments_expanded, block_id, prompt_editor) = {
21217            let Some(overlay) = self
21218                .diff_review_overlays
21219                .iter()
21220                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21221            else {
21222                return;
21223            };
21224
21225            (
21226                overlay.comments_expanded,
21227                overlay.block_id,
21228                overlay.prompt_editor.clone(),
21229            )
21230        };
21231
21232        // Calculate new height
21233        let snapshot = self.buffer.read(cx).snapshot(cx);
21234        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21235
21236        // Update the block height using resize_blocks (avoids flicker)
21237        let mut heights = HashMap::default();
21238        heights.insert(block_id, new_height);
21239        self.resize_blocks(heights, None, cx);
21240
21241        // Update the render function using replace_blocks (avoids flicker)
21242        let hunk_key_for_render = hunk_key.clone();
21243        let editor_handle = cx.entity().downgrade();
21244        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21245            Arc::new(move |cx| {
21246                Self::render_diff_review_overlay(
21247                    &prompt_editor,
21248                    &hunk_key_for_render,
21249                    &editor_handle,
21250                    cx,
21251                )
21252            });
21253
21254        let mut renderers = HashMap::default();
21255        renderers.insert(block_id, render);
21256        self.replace_blocks(renderers, None, cx);
21257    }
21258
21259    /// Action handler for SubmitDiffReviewComment.
21260    pub fn submit_diff_review_comment_action(
21261        &mut self,
21262        _: &SubmitDiffReviewComment,
21263        window: &mut Window,
21264        cx: &mut Context<Self>,
21265    ) {
21266        self.submit_diff_review_comment(window, cx);
21267    }
21268
21269    /// Stores the diff review comment locally.
21270    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21271    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21272        // Find the overlay that currently has focus
21273        let overlay_index = self
21274            .diff_review_overlays
21275            .iter()
21276            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21277        let Some(overlay_index) = overlay_index else {
21278            return;
21279        };
21280        let overlay = &self.diff_review_overlays[overlay_index];
21281
21282        // Get the comment text from the prompt editor
21283        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21284
21285        // Don't submit if the comment is empty
21286        if comment_text.is_empty() {
21287            return;
21288        }
21289
21290        // Get the display row and hunk key
21291        let display_row = overlay.display_row;
21292        let hunk_key = overlay.hunk_key.clone();
21293
21294        // Convert to buffer position for anchors
21295        let snapshot = self.snapshot(window, cx);
21296        let display_point = DisplayPoint::new(display_row, 0);
21297        let buffer_point = snapshot
21298            .display_snapshot
21299            .display_point_to_point(display_point, Bias::Left);
21300
21301        // Get the line range
21302        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21303        let line_start = Point::new(buffer_point.row, 0);
21304        let line_end = Point::new(
21305            buffer_point.row,
21306            buffer_snapshot.line_len(MultiBufferRow(buffer_point.row)),
21307        );
21308
21309        // Create anchors for the selection
21310        let anchor_start = buffer_snapshot.anchor_after(line_start);
21311        let anchor_end = buffer_snapshot.anchor_before(line_end);
21312
21313        // Store the comment locally
21314        self.add_review_comment(
21315            hunk_key.clone(),
21316            comment_text,
21317            display_row,
21318            anchor_start..anchor_end,
21319            cx,
21320        );
21321
21322        // Clear the prompt editor but keep the overlay open
21323        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21324            overlay.prompt_editor.update(cx, |editor, cx| {
21325                editor.clear(window, cx);
21326            });
21327        }
21328
21329        // Refresh the overlay to update the block height for the new comment
21330        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21331
21332        cx.notify();
21333    }
21334
21335    /// Returns the prompt editor for the diff review overlay, if one is active.
21336    /// This is primarily used for testing.
21337    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21338        self.diff_review_overlays
21339            .first()
21340            .map(|overlay| &overlay.prompt_editor)
21341    }
21342
21343    /// Returns the display row for the first diff review overlay, if one is active.
21344    pub fn diff_review_display_row(&self) -> Option<DisplayRow> {
21345        self.diff_review_overlays
21346            .first()
21347            .map(|overlay| overlay.display_row)
21348    }
21349
21350    /// Sets whether the comments section is expanded in the diff review overlay.
21351    /// This is primarily used for testing.
21352    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21353        for overlay in &mut self.diff_review_overlays {
21354            overlay.comments_expanded = expanded;
21355        }
21356        cx.notify();
21357    }
21358
21359    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21360    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21361        a.file_path == b.file_path
21362            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21363    }
21364
21365    /// Returns comments for a specific hunk, ordered by creation time.
21366    pub fn comments_for_hunk<'a>(
21367        &'a self,
21368        key: &DiffHunkKey,
21369        snapshot: &MultiBufferSnapshot,
21370    ) -> &'a [StoredReviewComment] {
21371        let key_point = key.hunk_start_anchor.to_point(snapshot);
21372        self.stored_review_comments
21373            .iter()
21374            .find(|(k, _)| {
21375                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21376            })
21377            .map(|(_, comments)| comments.as_slice())
21378            .unwrap_or(&[])
21379    }
21380
21381    /// Returns the total count of stored review comments across all hunks.
21382    pub fn total_review_comment_count(&self) -> usize {
21383        self.stored_review_comments
21384            .iter()
21385            .map(|(_, v)| v.len())
21386            .sum()
21387    }
21388
21389    /// Returns the count of comments for a specific hunk.
21390    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21391        let key_point = key.hunk_start_anchor.to_point(snapshot);
21392        self.stored_review_comments
21393            .iter()
21394            .find(|(k, _)| {
21395                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21396            })
21397            .map(|(_, v)| v.len())
21398            .unwrap_or(0)
21399    }
21400
21401    /// Adds a new review comment to a specific hunk.
21402    pub fn add_review_comment(
21403        &mut self,
21404        hunk_key: DiffHunkKey,
21405        comment: String,
21406        display_row: DisplayRow,
21407        anchor_range: Range<Anchor>,
21408        cx: &mut Context<Self>,
21409    ) -> usize {
21410        let id = self.next_review_comment_id;
21411        self.next_review_comment_id += 1;
21412
21413        let stored_comment = StoredReviewComment::new(id, comment, display_row, anchor_range);
21414
21415        let snapshot = self.buffer.read(cx).snapshot(cx);
21416        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21417
21418        // Find existing entry for this hunk or add a new one
21419        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21420            k.file_path == hunk_key.file_path
21421                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21422        }) {
21423            comments.push(stored_comment);
21424        } else {
21425            self.stored_review_comments
21426                .push((hunk_key, vec![stored_comment]));
21427        }
21428
21429        cx.emit(EditorEvent::ReviewCommentsChanged {
21430            total_count: self.total_review_comment_count(),
21431        });
21432        cx.notify();
21433        id
21434    }
21435
21436    /// Removes a review comment by ID from any hunk.
21437    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21438        for (_, comments) in self.stored_review_comments.iter_mut() {
21439            if let Some(index) = comments.iter().position(|c| c.id == id) {
21440                comments.remove(index);
21441                cx.emit(EditorEvent::ReviewCommentsChanged {
21442                    total_count: self.total_review_comment_count(),
21443                });
21444                cx.notify();
21445                return true;
21446            }
21447        }
21448        false
21449    }
21450
21451    /// Updates a review comment's text by ID.
21452    pub fn update_review_comment(
21453        &mut self,
21454        id: usize,
21455        new_comment: String,
21456        cx: &mut Context<Self>,
21457    ) -> bool {
21458        for (_, comments) in self.stored_review_comments.iter_mut() {
21459            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21460                comment.comment = new_comment;
21461                comment.is_editing = false;
21462                cx.emit(EditorEvent::ReviewCommentsChanged {
21463                    total_count: self.total_review_comment_count(),
21464                });
21465                cx.notify();
21466                return true;
21467            }
21468        }
21469        false
21470    }
21471
21472    /// Sets a comment's editing state.
21473    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
21474        for (_, comments) in self.stored_review_comments.iter_mut() {
21475            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21476                comment.is_editing = is_editing;
21477                cx.notify();
21478                return;
21479            }
21480        }
21481    }
21482
21483    /// Takes all stored comments from all hunks, clearing the storage.
21484    /// Returns a Vec of (hunk_key, comments) pairs.
21485    pub fn take_all_review_comments(
21486        &mut self,
21487        cx: &mut Context<Self>,
21488    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
21489        // Dismiss all overlays when taking comments (e.g., when sending to agent)
21490        self.dismiss_all_diff_review_overlays(cx);
21491        let comments = std::mem::take(&mut self.stored_review_comments);
21492        // Reset the ID counter since all comments have been taken
21493        self.next_review_comment_id = 0;
21494        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
21495        cx.notify();
21496        comments
21497    }
21498
21499    /// Removes review comments whose anchors are no longer valid or whose
21500    /// associated diff hunks no longer exist.
21501    ///
21502    /// This should be called when the buffer changes to prevent orphaned comments
21503    /// from accumulating.
21504    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
21505        let snapshot = self.buffer.read(cx).snapshot(cx);
21506        let original_count = self.total_review_comment_count();
21507
21508        // Remove comments with invalid hunk anchors
21509        self.stored_review_comments
21510            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
21511
21512        // Also clean up individual comments with invalid anchor ranges
21513        for (_, comments) in &mut self.stored_review_comments {
21514            comments.retain(|comment| {
21515                comment.anchor_range.start.is_valid(&snapshot)
21516                    && comment.anchor_range.end.is_valid(&snapshot)
21517            });
21518        }
21519
21520        // Remove empty hunk entries
21521        self.stored_review_comments
21522            .retain(|(_, comments)| !comments.is_empty());
21523
21524        let new_count = self.total_review_comment_count();
21525        if new_count != original_count {
21526            cx.emit(EditorEvent::ReviewCommentsChanged {
21527                total_count: new_count,
21528            });
21529            cx.notify();
21530        }
21531    }
21532
21533    /// Toggles the expanded state of the comments section in the overlay.
21534    pub fn toggle_review_comments_expanded(
21535        &mut self,
21536        _: &ToggleReviewCommentsExpanded,
21537        window: &mut Window,
21538        cx: &mut Context<Self>,
21539    ) {
21540        // Find the overlay that currently has focus, or use the first one
21541        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
21542            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
21543                overlay.comments_expanded = !overlay.comments_expanded;
21544                Some(overlay.hunk_key.clone())
21545            } else {
21546                None
21547            }
21548        });
21549
21550        // If no focused overlay found, toggle the first one
21551        let hunk_key = overlay_info.or_else(|| {
21552            self.diff_review_overlays.first_mut().map(|overlay| {
21553                overlay.comments_expanded = !overlay.comments_expanded;
21554                overlay.hunk_key.clone()
21555            })
21556        });
21557
21558        if let Some(hunk_key) = hunk_key {
21559            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21560            cx.notify();
21561        }
21562    }
21563
21564    /// Handles the EditReviewComment action - sets a comment into editing mode.
21565    pub fn edit_review_comment(
21566        &mut self,
21567        action: &EditReviewComment,
21568        window: &mut Window,
21569        cx: &mut Context<Self>,
21570    ) {
21571        let comment_id = action.id;
21572
21573        // Set the comment to editing mode
21574        self.set_comment_editing(comment_id, true, cx);
21575
21576        // Find the overlay that contains this comment and create an inline editor if needed
21577        // First, find which hunk this comment belongs to
21578        let hunk_key = self
21579            .stored_review_comments
21580            .iter()
21581            .find_map(|(key, comments)| {
21582                if comments.iter().any(|c| c.id == comment_id) {
21583                    Some(key.clone())
21584                } else {
21585                    None
21586                }
21587            });
21588
21589        let snapshot = self.buffer.read(cx).snapshot(cx);
21590        if let Some(hunk_key) = hunk_key {
21591            if let Some(overlay) = self
21592                .diff_review_overlays
21593                .iter_mut()
21594                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21595            {
21596                if let std::collections::hash_map::Entry::Vacant(entry) =
21597                    overlay.inline_edit_editors.entry(comment_id)
21598                {
21599                    // Find the comment text
21600                    let comment_text = self
21601                        .stored_review_comments
21602                        .iter()
21603                        .flat_map(|(_, comments)| comments)
21604                        .find(|c| c.id == comment_id)
21605                        .map(|c| c.comment.clone())
21606                        .unwrap_or_default();
21607
21608                    // Create inline editor
21609                    let parent_editor = cx.entity().downgrade();
21610                    let inline_editor = cx.new(|cx| {
21611                        let mut editor = Editor::single_line(window, cx);
21612                        editor.set_text(&*comment_text, window, cx);
21613                        // Select all text for easy replacement
21614                        editor.select_all(&crate::actions::SelectAll, window, cx);
21615                        editor
21616                    });
21617
21618                    // Register the Newline action to confirm the edit
21619                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
21620                        inline_editor.register_action({
21621                            let parent_editor = parent_editor.clone();
21622                            move |_: &crate::actions::Newline, window, cx| {
21623                                if let Some(editor) = parent_editor.upgrade() {
21624                                    editor.update(cx, |editor, cx| {
21625                                        editor.confirm_edit_review_comment(comment_id, window, cx);
21626                                    });
21627                                }
21628                            }
21629                        })
21630                    });
21631
21632                    // Store the subscription to keep the action handler alive
21633                    overlay
21634                        .inline_edit_subscriptions
21635                        .insert(comment_id, subscription);
21636
21637                    // Focus the inline editor
21638                    let focus_handle = inline_editor.focus_handle(cx);
21639                    window.focus(&focus_handle, cx);
21640
21641                    entry.insert(inline_editor);
21642                }
21643            }
21644        }
21645
21646        cx.notify();
21647    }
21648
21649    /// Confirms an inline edit of a review comment.
21650    pub fn confirm_edit_review_comment(
21651        &mut self,
21652        comment_id: usize,
21653        _window: &mut Window,
21654        cx: &mut Context<Self>,
21655    ) {
21656        // Get the new text from the inline editor
21657        // Find the overlay containing this comment's inline editor
21658        let snapshot = self.buffer.read(cx).snapshot(cx);
21659        let hunk_key = self
21660            .stored_review_comments
21661            .iter()
21662            .find_map(|(key, comments)| {
21663                if comments.iter().any(|c| c.id == comment_id) {
21664                    Some(key.clone())
21665                } else {
21666                    None
21667                }
21668            });
21669
21670        let new_text = hunk_key
21671            .as_ref()
21672            .and_then(|hunk_key| {
21673                self.diff_review_overlays
21674                    .iter()
21675                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21676            })
21677            .as_ref()
21678            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
21679            .map(|editor| editor.read(cx).text(cx).trim().to_string());
21680
21681        if let Some(new_text) = new_text {
21682            if !new_text.is_empty() {
21683                self.update_review_comment(comment_id, new_text, cx);
21684            }
21685        }
21686
21687        // Remove the inline editor and its subscription
21688        if let Some(hunk_key) = hunk_key {
21689            if let Some(overlay) = self
21690                .diff_review_overlays
21691                .iter_mut()
21692                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21693            {
21694                overlay.inline_edit_editors.remove(&comment_id);
21695                overlay.inline_edit_subscriptions.remove(&comment_id);
21696            }
21697        }
21698
21699        // Clear editing state
21700        self.set_comment_editing(comment_id, false, cx);
21701    }
21702
21703    /// Cancels an inline edit of a review comment.
21704    pub fn cancel_edit_review_comment(
21705        &mut self,
21706        comment_id: usize,
21707        _window: &mut Window,
21708        cx: &mut Context<Self>,
21709    ) {
21710        // Find which hunk this comment belongs to
21711        let hunk_key = self
21712            .stored_review_comments
21713            .iter()
21714            .find_map(|(key, comments)| {
21715                if comments.iter().any(|c| c.id == comment_id) {
21716                    Some(key.clone())
21717                } else {
21718                    None
21719                }
21720            });
21721
21722        // Remove the inline editor and its subscription
21723        if let Some(hunk_key) = hunk_key {
21724            let snapshot = self.buffer.read(cx).snapshot(cx);
21725            if let Some(overlay) = self
21726                .diff_review_overlays
21727                .iter_mut()
21728                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21729            {
21730                overlay.inline_edit_editors.remove(&comment_id);
21731                overlay.inline_edit_subscriptions.remove(&comment_id);
21732            }
21733        }
21734
21735        // Clear editing state
21736        self.set_comment_editing(comment_id, false, cx);
21737    }
21738
21739    /// Action handler for ConfirmEditReviewComment.
21740    pub fn confirm_edit_review_comment_action(
21741        &mut self,
21742        action: &ConfirmEditReviewComment,
21743        window: &mut Window,
21744        cx: &mut Context<Self>,
21745    ) {
21746        self.confirm_edit_review_comment(action.id, window, cx);
21747    }
21748
21749    /// Action handler for CancelEditReviewComment.
21750    pub fn cancel_edit_review_comment_action(
21751        &mut self,
21752        action: &CancelEditReviewComment,
21753        window: &mut Window,
21754        cx: &mut Context<Self>,
21755    ) {
21756        self.cancel_edit_review_comment(action.id, window, cx);
21757    }
21758
21759    /// Handles the DeleteReviewComment action - removes a comment.
21760    pub fn delete_review_comment(
21761        &mut self,
21762        action: &DeleteReviewComment,
21763        window: &mut Window,
21764        cx: &mut Context<Self>,
21765    ) {
21766        // Get the hunk key before removing the comment
21767        // Find the hunk key from the comment itself
21768        let comment_id = action.id;
21769        let hunk_key = self
21770            .stored_review_comments
21771            .iter()
21772            .find_map(|(key, comments)| {
21773                if comments.iter().any(|c| c.id == comment_id) {
21774                    Some(key.clone())
21775                } else {
21776                    None
21777                }
21778            });
21779
21780        // Also get it from the overlay for refresh purposes
21781        let overlay_hunk_key = self
21782            .diff_review_overlays
21783            .first()
21784            .map(|o| o.hunk_key.clone());
21785
21786        self.remove_review_comment(action.id, cx);
21787
21788        // Refresh the overlay height after removing a comment
21789        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
21790            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21791        }
21792    }
21793
21794    fn render_diff_review_overlay(
21795        prompt_editor: &Entity<Editor>,
21796        hunk_key: &DiffHunkKey,
21797        editor_handle: &WeakEntity<Editor>,
21798        cx: &mut BlockContext,
21799    ) -> AnyElement {
21800        let theme = cx.theme();
21801        let colors = theme.colors();
21802
21803        // Get stored comments, expanded state, inline editors, and user avatar from the editor
21804        let (comments, comments_expanded, inline_editors, user_avatar_uri) = editor_handle
21805            .upgrade()
21806            .map(|editor| {
21807                let editor = editor.read(cx);
21808                let snapshot = editor.buffer().read(cx).snapshot(cx);
21809                let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
21810                let snapshot = editor.buffer.read(cx).snapshot(cx);
21811                let (expanded, editors, avatar_uri) = editor
21812                    .diff_review_overlays
21813                    .iter()
21814                    .find(|overlay| Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21815                    .as_ref()
21816                    .map(|o| {
21817                        (
21818                            o.comments_expanded,
21819                            o.inline_edit_editors.clone(),
21820                            o.user_avatar_uri.clone(),
21821                        )
21822                    })
21823                    .unwrap_or((true, HashMap::default(), None));
21824                (comments, expanded, editors, avatar_uri)
21825            })
21826            .unwrap_or((Vec::new(), true, HashMap::default(), None));
21827
21828        let comment_count = comments.len();
21829        let avatar_size = px(20.);
21830        let action_icon_size = IconSize::XSmall;
21831
21832        v_flex()
21833            .w_full()
21834            .bg(colors.editor_background)
21835            .border_b_1()
21836            .border_color(colors.border)
21837            .px_2()
21838            .pb_2()
21839            .gap_2()
21840            // Top row: editable input with user's avatar
21841            .child(
21842                h_flex()
21843                    .w_full()
21844                    .items_center()
21845                    .gap_2()
21846                    .px_2()
21847                    .py_1p5()
21848                    .rounded_md()
21849                    .bg(colors.surface_background)
21850                    .child(
21851                        div()
21852                            .size(avatar_size)
21853                            .flex_shrink_0()
21854                            .rounded_full()
21855                            .overflow_hidden()
21856                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
21857                                Avatar::new(avatar_uri.clone())
21858                                    .size(avatar_size)
21859                                    .into_any_element()
21860                            } else {
21861                                Icon::new(IconName::Person)
21862                                    .size(IconSize::Small)
21863                                    .color(ui::Color::Muted)
21864                                    .into_any_element()
21865                            }),
21866                    )
21867                    .child(
21868                        div()
21869                            .flex_1()
21870                            .border_1()
21871                            .border_color(colors.border)
21872                            .rounded_md()
21873                            .bg(colors.editor_background)
21874                            .px_2()
21875                            .py_1()
21876                            .child(prompt_editor.clone()),
21877                    )
21878                    .child(
21879                        h_flex()
21880                            .flex_shrink_0()
21881                            .gap_1()
21882                            .child(
21883                                IconButton::new("diff-review-close", IconName::Close)
21884                                    .icon_color(ui::Color::Muted)
21885                                    .icon_size(action_icon_size)
21886                                    .tooltip(Tooltip::text("Close"))
21887                                    .on_click(|_, window, cx| {
21888                                        window
21889                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
21890                                    }),
21891                            )
21892                            .child(
21893                                IconButton::new("diff-review-add", IconName::Return)
21894                                    .icon_color(ui::Color::Muted)
21895                                    .icon_size(action_icon_size)
21896                                    .tooltip(Tooltip::text("Add comment"))
21897                                    .on_click(|_, window, cx| {
21898                                        window.dispatch_action(
21899                                            Box::new(crate::actions::SubmitDiffReviewComment),
21900                                            cx,
21901                                        );
21902                                    }),
21903                            ),
21904                    ),
21905            )
21906            // Expandable comments section (only shown when there are comments)
21907            .when(comment_count > 0, |el| {
21908                el.child(Self::render_comments_section(
21909                    comments,
21910                    comments_expanded,
21911                    inline_editors,
21912                    user_avatar_uri,
21913                    avatar_size,
21914                    action_icon_size,
21915                    colors,
21916                ))
21917            })
21918            .into_any_element()
21919    }
21920
21921    fn render_comments_section(
21922        comments: Vec<StoredReviewComment>,
21923        expanded: bool,
21924        inline_editors: HashMap<usize, Entity<Editor>>,
21925        user_avatar_uri: Option<SharedUri>,
21926        avatar_size: Pixels,
21927        action_icon_size: IconSize,
21928        colors: &theme::ThemeColors,
21929    ) -> impl IntoElement {
21930        let comment_count = comments.len();
21931
21932        v_flex()
21933            .w_full()
21934            .gap_1()
21935            // Header with expand/collapse toggle
21936            .child(
21937                h_flex()
21938                    .id("review-comments-header")
21939                    .w_full()
21940                    .items_center()
21941                    .gap_1()
21942                    .px_2()
21943                    .py_1()
21944                    .cursor_pointer()
21945                    .rounded_md()
21946                    .hover(|style| style.bg(colors.ghost_element_hover))
21947                    .on_click(|_, window: &mut Window, cx| {
21948                        window.dispatch_action(
21949                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
21950                            cx,
21951                        );
21952                    })
21953                    .child(
21954                        Icon::new(if expanded {
21955                            IconName::ChevronDown
21956                        } else {
21957                            IconName::ChevronRight
21958                        })
21959                        .size(IconSize::Small)
21960                        .color(ui::Color::Muted),
21961                    )
21962                    .child(
21963                        Label::new(format!(
21964                            "{} Comment{}",
21965                            comment_count,
21966                            if comment_count == 1 { "" } else { "s" }
21967                        ))
21968                        .size(LabelSize::Small)
21969                        .color(Color::Muted),
21970                    ),
21971            )
21972            // Comments list (when expanded)
21973            .when(expanded, |el| {
21974                el.children(comments.into_iter().map(|comment| {
21975                    let inline_editor = inline_editors.get(&comment.id).cloned();
21976                    Self::render_comment_row(
21977                        comment,
21978                        inline_editor,
21979                        user_avatar_uri.clone(),
21980                        avatar_size,
21981                        action_icon_size,
21982                        colors,
21983                    )
21984                }))
21985            })
21986    }
21987
21988    fn render_comment_row(
21989        comment: StoredReviewComment,
21990        inline_editor: Option<Entity<Editor>>,
21991        user_avatar_uri: Option<SharedUri>,
21992        avatar_size: Pixels,
21993        action_icon_size: IconSize,
21994        colors: &theme::ThemeColors,
21995    ) -> impl IntoElement {
21996        let comment_id = comment.id;
21997        let is_editing = inline_editor.is_some();
21998
21999        h_flex()
22000            .w_full()
22001            .items_center()
22002            .gap_2()
22003            .px_2()
22004            .py_1p5()
22005            .rounded_md()
22006            .bg(colors.surface_background)
22007            .child(
22008                div()
22009                    .size(avatar_size)
22010                    .flex_shrink_0()
22011                    .rounded_full()
22012                    .overflow_hidden()
22013                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22014                        Avatar::new(avatar_uri.clone())
22015                            .size(avatar_size)
22016                            .into_any_element()
22017                    } else {
22018                        Icon::new(IconName::Person)
22019                            .size(IconSize::Small)
22020                            .color(ui::Color::Muted)
22021                            .into_any_element()
22022                    }),
22023            )
22024            .child(if let Some(editor) = inline_editor {
22025                // Inline edit mode: show an editable text field
22026                div()
22027                    .flex_1()
22028                    .border_1()
22029                    .border_color(colors.border)
22030                    .rounded_md()
22031                    .bg(colors.editor_background)
22032                    .px_2()
22033                    .py_1()
22034                    .child(editor)
22035                    .into_any_element()
22036            } else {
22037                // Display mode: show the comment text
22038                div()
22039                    .flex_1()
22040                    .text_sm()
22041                    .text_color(colors.text)
22042                    .child(comment.comment)
22043                    .into_any_element()
22044            })
22045            .child(if is_editing {
22046                // Editing mode: show close and confirm buttons
22047                h_flex()
22048                    .gap_1()
22049                    .child(
22050                        IconButton::new(
22051                            format!("diff-review-cancel-edit-{comment_id}"),
22052                            IconName::Close,
22053                        )
22054                        .icon_color(ui::Color::Muted)
22055                        .icon_size(action_icon_size)
22056                        .tooltip(Tooltip::text("Cancel"))
22057                        .on_click(move |_, window, cx| {
22058                            window.dispatch_action(
22059                                Box::new(crate::actions::CancelEditReviewComment {
22060                                    id: comment_id,
22061                                }),
22062                                cx,
22063                            );
22064                        }),
22065                    )
22066                    .child(
22067                        IconButton::new(
22068                            format!("diff-review-confirm-edit-{comment_id}"),
22069                            IconName::Return,
22070                        )
22071                        .icon_color(ui::Color::Muted)
22072                        .icon_size(action_icon_size)
22073                        .tooltip(Tooltip::text("Confirm"))
22074                        .on_click(move |_, window, cx| {
22075                            window.dispatch_action(
22076                                Box::new(crate::actions::ConfirmEditReviewComment {
22077                                    id: comment_id,
22078                                }),
22079                                cx,
22080                            );
22081                        }),
22082                    )
22083                    .into_any_element()
22084            } else {
22085                // Display mode: no action buttons for now (edit/delete not yet implemented)
22086                gpui::Empty.into_any_element()
22087            })
22088    }
22089
22090    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22091        if self.display_map.read(cx).masked != masked {
22092            self.display_map.update(cx, |map, _| map.masked = masked);
22093        }
22094        cx.notify()
22095    }
22096
22097    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22098        self.show_wrap_guides = Some(show_wrap_guides);
22099        cx.notify();
22100    }
22101
22102    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22103        self.show_indent_guides = Some(show_indent_guides);
22104        cx.notify();
22105    }
22106
22107    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22108        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22109            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22110                && let Some(dir) = file.abs_path(cx).parent()
22111            {
22112                return Some(dir.to_owned());
22113            }
22114        }
22115
22116        None
22117    }
22118
22119    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22120        self.active_excerpt(cx)?
22121            .1
22122            .read(cx)
22123            .file()
22124            .and_then(|f| f.as_local())
22125    }
22126
22127    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22128        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22129            let buffer = buffer.read(cx);
22130            if let Some(project_path) = buffer.project_path(cx) {
22131                let project = self.project()?.read(cx);
22132                project.absolute_path(&project_path, cx)
22133            } else {
22134                buffer
22135                    .file()
22136                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22137            }
22138        })
22139    }
22140
22141    pub fn reveal_in_finder(
22142        &mut self,
22143        _: &RevealInFileManager,
22144        _window: &mut Window,
22145        cx: &mut Context<Self>,
22146    ) {
22147        if let Some(target) = self.target_file(cx) {
22148            cx.reveal_path(&target.abs_path(cx));
22149        }
22150    }
22151
22152    pub fn copy_path(
22153        &mut self,
22154        _: &zed_actions::workspace::CopyPath,
22155        _window: &mut Window,
22156        cx: &mut Context<Self>,
22157    ) {
22158        if let Some(path) = self.target_file_abs_path(cx)
22159            && let Some(path) = path.to_str()
22160        {
22161            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22162        } else {
22163            cx.propagate();
22164        }
22165    }
22166
22167    pub fn copy_relative_path(
22168        &mut self,
22169        _: &zed_actions::workspace::CopyRelativePath,
22170        _window: &mut Window,
22171        cx: &mut Context<Self>,
22172    ) {
22173        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22174            let project = self.project()?.read(cx);
22175            let path = buffer.read(cx).file()?.path();
22176            let path = path.display(project.path_style(cx));
22177            Some(path)
22178        }) {
22179            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22180        } else {
22181            cx.propagate();
22182        }
22183    }
22184
22185    /// Returns the project path for the editor's buffer, if any buffer is
22186    /// opened in the editor.
22187    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22188        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22189            buffer.read(cx).project_path(cx)
22190        } else {
22191            None
22192        }
22193    }
22194
22195    // Returns true if the editor handled a go-to-line request
22196    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22197        maybe!({
22198            let breakpoint_store = self.breakpoint_store.as_ref()?;
22199
22200            let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
22201            else {
22202                self.clear_row_highlights::<ActiveDebugLine>();
22203                return None;
22204            };
22205
22206            let position = active_stack_frame.position;
22207            let buffer_id = position.buffer_id?;
22208            let snapshot = self
22209                .project
22210                .as_ref()?
22211                .read(cx)
22212                .buffer_for_id(buffer_id, cx)?
22213                .read(cx)
22214                .snapshot();
22215
22216            let mut handled = false;
22217            for (id, ExcerptRange { context, .. }) in
22218                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22219            {
22220                if context.start.cmp(&position, &snapshot).is_ge()
22221                    || context.end.cmp(&position, &snapshot).is_lt()
22222                {
22223                    continue;
22224                }
22225                let snapshot = self.buffer.read(cx).snapshot(cx);
22226                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22227
22228                handled = true;
22229                self.clear_row_highlights::<ActiveDebugLine>();
22230
22231                self.go_to_line::<ActiveDebugLine>(
22232                    multibuffer_anchor,
22233                    Some(cx.theme().colors().editor_debugger_active_line_background),
22234                    window,
22235                    cx,
22236                );
22237
22238                cx.notify();
22239            }
22240
22241            handled.then_some(())
22242        })
22243        .is_some()
22244    }
22245
22246    pub fn copy_file_name_without_extension(
22247        &mut self,
22248        _: &CopyFileNameWithoutExtension,
22249        _: &mut Window,
22250        cx: &mut Context<Self>,
22251    ) {
22252        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22253            let file = buffer.read(cx).file()?;
22254            file.path().file_stem()
22255        }) {
22256            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22257        }
22258    }
22259
22260    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22261        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22262            let file = buffer.read(cx).file()?;
22263            Some(file.file_name(cx))
22264        }) {
22265            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22266        }
22267    }
22268
22269    pub fn toggle_git_blame(
22270        &mut self,
22271        _: &::git::Blame,
22272        window: &mut Window,
22273        cx: &mut Context<Self>,
22274    ) {
22275        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22276
22277        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22278            self.start_git_blame(true, window, cx);
22279        }
22280
22281        cx.notify();
22282    }
22283
22284    pub fn toggle_git_blame_inline(
22285        &mut self,
22286        _: &ToggleGitBlameInline,
22287        window: &mut Window,
22288        cx: &mut Context<Self>,
22289    ) {
22290        self.toggle_git_blame_inline_internal(true, window, cx);
22291        cx.notify();
22292    }
22293
22294    pub fn open_git_blame_commit(
22295        &mut self,
22296        _: &OpenGitBlameCommit,
22297        window: &mut Window,
22298        cx: &mut Context<Self>,
22299    ) {
22300        self.open_git_blame_commit_internal(window, cx);
22301    }
22302
22303    fn open_git_blame_commit_internal(
22304        &mut self,
22305        window: &mut Window,
22306        cx: &mut Context<Self>,
22307    ) -> Option<()> {
22308        let blame = self.blame.as_ref()?;
22309        let snapshot = self.snapshot(window, cx);
22310        let cursor = self
22311            .selections
22312            .newest::<Point>(&snapshot.display_snapshot)
22313            .head();
22314        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22315        let (_, blame_entry) = blame
22316            .update(cx, |blame, cx| {
22317                blame
22318                    .blame_for_rows(
22319                        &[RowInfo {
22320                            buffer_id: Some(buffer.remote_id()),
22321                            buffer_row: Some(point.row),
22322                            ..Default::default()
22323                        }],
22324                        cx,
22325                    )
22326                    .next()
22327            })
22328            .flatten()?;
22329        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22330        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22331        let workspace = self.workspace()?.downgrade();
22332        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22333        None
22334    }
22335
22336    pub fn git_blame_inline_enabled(&self) -> bool {
22337        self.git_blame_inline_enabled
22338    }
22339
22340    pub fn toggle_selection_menu(
22341        &mut self,
22342        _: &ToggleSelectionMenu,
22343        _: &mut Window,
22344        cx: &mut Context<Self>,
22345    ) {
22346        self.show_selection_menu = self
22347            .show_selection_menu
22348            .map(|show_selections_menu| !show_selections_menu)
22349            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22350
22351        cx.notify();
22352    }
22353
22354    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22355        self.show_selection_menu
22356            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22357    }
22358
22359    fn start_git_blame(
22360        &mut self,
22361        user_triggered: bool,
22362        window: &mut Window,
22363        cx: &mut Context<Self>,
22364    ) {
22365        if let Some(project) = self.project() {
22366            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22367                && buffer.read(cx).file().is_none()
22368            {
22369                return;
22370            }
22371
22372            let focused = self.focus_handle(cx).contains_focused(window, cx);
22373
22374            let project = project.clone();
22375            let blame = cx
22376                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
22377            self.blame_subscription =
22378                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
22379            self.blame = Some(blame);
22380        }
22381    }
22382
22383    fn toggle_git_blame_inline_internal(
22384        &mut self,
22385        user_triggered: bool,
22386        window: &mut Window,
22387        cx: &mut Context<Self>,
22388    ) {
22389        if self.git_blame_inline_enabled {
22390            self.git_blame_inline_enabled = false;
22391            self.show_git_blame_inline = false;
22392            self.show_git_blame_inline_delay_task.take();
22393        } else {
22394            self.git_blame_inline_enabled = true;
22395            self.start_git_blame_inline(user_triggered, window, cx);
22396        }
22397
22398        cx.notify();
22399    }
22400
22401    fn start_git_blame_inline(
22402        &mut self,
22403        user_triggered: bool,
22404        window: &mut Window,
22405        cx: &mut Context<Self>,
22406    ) {
22407        self.start_git_blame(user_triggered, window, cx);
22408
22409        if ProjectSettings::get_global(cx)
22410            .git
22411            .inline_blame_delay()
22412            .is_some()
22413        {
22414            self.start_inline_blame_timer(window, cx);
22415        } else {
22416            self.show_git_blame_inline = true
22417        }
22418    }
22419
22420    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
22421        self.blame.as_ref()
22422    }
22423
22424    pub fn show_git_blame_gutter(&self) -> bool {
22425        self.show_git_blame_gutter
22426    }
22427
22428    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
22429        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
22430    }
22431
22432    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
22433        self.show_git_blame_inline
22434            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
22435            && !self.newest_selection_head_on_empty_line(cx)
22436            && self.has_blame_entries(cx)
22437    }
22438
22439    fn has_blame_entries(&self, cx: &App) -> bool {
22440        self.blame()
22441            .is_some_and(|blame| blame.read(cx).has_generated_entries())
22442    }
22443
22444    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
22445        let cursor_anchor = self.selections.newest_anchor().head();
22446
22447        let snapshot = self.buffer.read(cx).snapshot(cx);
22448        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
22449
22450        snapshot.line_len(buffer_row) == 0
22451    }
22452
22453    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
22454        let buffer_and_selection = maybe!({
22455            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
22456            let selection_range = selection.range();
22457
22458            let multi_buffer = self.buffer().read(cx);
22459            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
22460            let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
22461
22462            let (buffer, range, _) = if selection.reversed {
22463                buffer_ranges.first()
22464            } else {
22465                buffer_ranges.last()
22466            }?;
22467
22468            let start_row_in_buffer = text::ToPoint::to_point(&range.start, buffer).row;
22469            let end_row_in_buffer = text::ToPoint::to_point(&range.end, buffer).row;
22470
22471            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
22472                let selection = start_row_in_buffer..end_row_in_buffer;
22473
22474                return Some((multi_buffer.buffer(buffer.remote_id()).unwrap(), selection));
22475            };
22476
22477            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
22478
22479            Some((
22480                multi_buffer.buffer(buffer.remote_id()).unwrap(),
22481                buffer_diff_snapshot.row_to_base_text_row(start_row_in_buffer, Bias::Left, buffer)
22482                    ..buffer_diff_snapshot.row_to_base_text_row(
22483                        end_row_in_buffer,
22484                        Bias::Left,
22485                        buffer,
22486                    ),
22487            ))
22488        });
22489
22490        let Some((buffer, selection)) = buffer_and_selection else {
22491            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
22492        };
22493
22494        let Some(project) = self.project() else {
22495            return Task::ready(Err(anyhow!("editor does not have project")));
22496        };
22497
22498        project.update(cx, |project, cx| {
22499            project.get_permalink_to_line(&buffer, selection, cx)
22500        })
22501    }
22502
22503    pub fn copy_permalink_to_line(
22504        &mut self,
22505        _: &CopyPermalinkToLine,
22506        window: &mut Window,
22507        cx: &mut Context<Self>,
22508    ) {
22509        let permalink_task = self.get_permalink_to_line(cx);
22510        let workspace = self.workspace();
22511
22512        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22513            Ok(permalink) => {
22514                cx.update(|_, cx| {
22515                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
22516                })
22517                .ok();
22518            }
22519            Err(err) => {
22520                let message = format!("Failed to copy permalink: {err}");
22521
22522                anyhow::Result::<()>::Err(err).log_err();
22523
22524                if let Some(workspace) = workspace {
22525                    workspace
22526                        .update_in(cx, |workspace, _, cx| {
22527                            struct CopyPermalinkToLine;
22528
22529                            workspace.show_toast(
22530                                Toast::new(
22531                                    NotificationId::unique::<CopyPermalinkToLine>(),
22532                                    message,
22533                                ),
22534                                cx,
22535                            )
22536                        })
22537                        .ok();
22538                }
22539            }
22540        })
22541        .detach();
22542    }
22543
22544    pub fn copy_file_location(
22545        &mut self,
22546        _: &CopyFileLocation,
22547        _: &mut Window,
22548        cx: &mut Context<Self>,
22549    ) {
22550        let selection = self
22551            .selections
22552            .newest::<Point>(&self.display_snapshot(cx))
22553            .start
22554            .row
22555            + 1;
22556        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22557            let project = self.project()?.read(cx);
22558            let file = buffer.read(cx).file()?;
22559            let path = file.path().display(project.path_style(cx));
22560
22561            Some(format!("{path}:{selection}"))
22562        }) {
22563            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
22564        }
22565    }
22566
22567    pub fn open_permalink_to_line(
22568        &mut self,
22569        _: &OpenPermalinkToLine,
22570        window: &mut Window,
22571        cx: &mut Context<Self>,
22572    ) {
22573        let permalink_task = self.get_permalink_to_line(cx);
22574        let workspace = self.workspace();
22575
22576        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22577            Ok(permalink) => {
22578                cx.update(|_, cx| {
22579                    cx.open_url(permalink.as_ref());
22580                })
22581                .ok();
22582            }
22583            Err(err) => {
22584                let message = format!("Failed to open permalink: {err}");
22585
22586                anyhow::Result::<()>::Err(err).log_err();
22587
22588                if let Some(workspace) = workspace {
22589                    workspace.update(cx, |workspace, cx| {
22590                        struct OpenPermalinkToLine;
22591
22592                        workspace.show_toast(
22593                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
22594                            cx,
22595                        )
22596                    });
22597                }
22598            }
22599        })
22600        .detach();
22601    }
22602
22603    pub fn insert_uuid_v4(
22604        &mut self,
22605        _: &InsertUuidV4,
22606        window: &mut Window,
22607        cx: &mut Context<Self>,
22608    ) {
22609        self.insert_uuid(UuidVersion::V4, window, cx);
22610    }
22611
22612    pub fn insert_uuid_v7(
22613        &mut self,
22614        _: &InsertUuidV7,
22615        window: &mut Window,
22616        cx: &mut Context<Self>,
22617    ) {
22618        self.insert_uuid(UuidVersion::V7, window, cx);
22619    }
22620
22621    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
22622        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
22623        self.transact(window, cx, |this, window, cx| {
22624            let edits = this
22625                .selections
22626                .all::<Point>(&this.display_snapshot(cx))
22627                .into_iter()
22628                .map(|selection| {
22629                    let uuid = match version {
22630                        UuidVersion::V4 => uuid::Uuid::new_v4(),
22631                        UuidVersion::V7 => uuid::Uuid::now_v7(),
22632                    };
22633
22634                    (selection.range(), uuid.to_string())
22635                });
22636            this.edit(edits, cx);
22637            this.refresh_edit_prediction(true, false, window, cx);
22638        });
22639    }
22640
22641    pub fn open_selections_in_multibuffer(
22642        &mut self,
22643        _: &OpenSelectionsInMultibuffer,
22644        window: &mut Window,
22645        cx: &mut Context<Self>,
22646    ) {
22647        let multibuffer = self.buffer.read(cx);
22648
22649        let Some(buffer) = multibuffer.as_singleton() else {
22650            return;
22651        };
22652
22653        let Some(workspace) = self.workspace() else {
22654            return;
22655        };
22656
22657        let title = multibuffer.title(cx).to_string();
22658
22659        let locations = self
22660            .selections
22661            .all_anchors(&self.display_snapshot(cx))
22662            .iter()
22663            .map(|selection| {
22664                (
22665                    buffer.clone(),
22666                    (selection.start.text_anchor..selection.end.text_anchor)
22667                        .to_point(buffer.read(cx)),
22668                )
22669            })
22670            .into_group_map();
22671
22672        cx.spawn_in(window, async move |_, cx| {
22673            workspace.update_in(cx, |workspace, window, cx| {
22674                Self::open_locations_in_multibuffer(
22675                    workspace,
22676                    locations,
22677                    format!("Selections for '{title}'"),
22678                    false,
22679                    false,
22680                    MultibufferSelectionMode::All,
22681                    window,
22682                    cx,
22683                );
22684            })
22685        })
22686        .detach();
22687    }
22688
22689    /// Adds a row highlight for the given range. If a row has multiple highlights, the
22690    /// last highlight added will be used.
22691    ///
22692    /// If the range ends at the beginning of a line, then that line will not be highlighted.
22693    pub fn highlight_rows<T: 'static>(
22694        &mut self,
22695        range: Range<Anchor>,
22696        color: Hsla,
22697        options: RowHighlightOptions,
22698        cx: &mut Context<Self>,
22699    ) {
22700        let snapshot = self.buffer().read(cx).snapshot(cx);
22701        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
22702        let ix = row_highlights.binary_search_by(|highlight| {
22703            Ordering::Equal
22704                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
22705                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
22706        });
22707
22708        if let Err(mut ix) = ix {
22709            let index = post_inc(&mut self.highlight_order);
22710
22711            // If this range intersects with the preceding highlight, then merge it with
22712            // the preceding highlight. Otherwise insert a new highlight.
22713            let mut merged = false;
22714            if ix > 0 {
22715                let prev_highlight = &mut row_highlights[ix - 1];
22716                if prev_highlight
22717                    .range
22718                    .end
22719                    .cmp(&range.start, &snapshot)
22720                    .is_ge()
22721                {
22722                    ix -= 1;
22723                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
22724                        prev_highlight.range.end = range.end;
22725                    }
22726                    merged = true;
22727                    prev_highlight.index = index;
22728                    prev_highlight.color = color;
22729                    prev_highlight.options = options;
22730                }
22731            }
22732
22733            if !merged {
22734                row_highlights.insert(
22735                    ix,
22736                    RowHighlight {
22737                        range,
22738                        index,
22739                        color,
22740                        options,
22741                        type_id: TypeId::of::<T>(),
22742                    },
22743                );
22744            }
22745
22746            // If any of the following highlights intersect with this one, merge them.
22747            while let Some(next_highlight) = row_highlights.get(ix + 1) {
22748                let highlight = &row_highlights[ix];
22749                if next_highlight
22750                    .range
22751                    .start
22752                    .cmp(&highlight.range.end, &snapshot)
22753                    .is_le()
22754                {
22755                    if next_highlight
22756                        .range
22757                        .end
22758                        .cmp(&highlight.range.end, &snapshot)
22759                        .is_gt()
22760                    {
22761                        row_highlights[ix].range.end = next_highlight.range.end;
22762                    }
22763                    row_highlights.remove(ix + 1);
22764                } else {
22765                    break;
22766                }
22767            }
22768        }
22769    }
22770
22771    /// Remove any highlighted row ranges of the given type that intersect the
22772    /// given ranges.
22773    pub fn remove_highlighted_rows<T: 'static>(
22774        &mut self,
22775        ranges_to_remove: Vec<Range<Anchor>>,
22776        cx: &mut Context<Self>,
22777    ) {
22778        let snapshot = self.buffer().read(cx).snapshot(cx);
22779        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
22780        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
22781        row_highlights.retain(|highlight| {
22782            while let Some(range_to_remove) = ranges_to_remove.peek() {
22783                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
22784                    Ordering::Less | Ordering::Equal => {
22785                        ranges_to_remove.next();
22786                    }
22787                    Ordering::Greater => {
22788                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
22789                            Ordering::Less | Ordering::Equal => {
22790                                return false;
22791                            }
22792                            Ordering::Greater => break,
22793                        }
22794                    }
22795                }
22796            }
22797
22798            true
22799        })
22800    }
22801
22802    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
22803    pub fn clear_row_highlights<T: 'static>(&mut self) {
22804        self.highlighted_rows.remove(&TypeId::of::<T>());
22805    }
22806
22807    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
22808    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
22809        self.highlighted_rows
22810            .get(&TypeId::of::<T>())
22811            .map_or(&[] as &[_], |vec| vec.as_slice())
22812            .iter()
22813            .map(|highlight| (highlight.range.clone(), highlight.color))
22814    }
22815
22816    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
22817    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
22818    /// Allows to ignore certain kinds of highlights.
22819    pub fn highlighted_display_rows(
22820        &self,
22821        window: &mut Window,
22822        cx: &mut App,
22823    ) -> BTreeMap<DisplayRow, LineHighlight> {
22824        let snapshot = self.snapshot(window, cx);
22825        let mut used_highlight_orders = HashMap::default();
22826        self.highlighted_rows
22827            .iter()
22828            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
22829            .fold(
22830                BTreeMap::<DisplayRow, LineHighlight>::new(),
22831                |mut unique_rows, highlight| {
22832                    let start = highlight.range.start.to_display_point(&snapshot);
22833                    let end = highlight.range.end.to_display_point(&snapshot);
22834                    let start_row = start.row().0;
22835                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
22836                    {
22837                        end.row().0.saturating_sub(1)
22838                    } else {
22839                        end.row().0
22840                    };
22841                    for row in start_row..=end_row {
22842                        let used_index =
22843                            used_highlight_orders.entry(row).or_insert(highlight.index);
22844                        if highlight.index >= *used_index {
22845                            *used_index = highlight.index;
22846                            unique_rows.insert(
22847                                DisplayRow(row),
22848                                LineHighlight {
22849                                    include_gutter: highlight.options.include_gutter,
22850                                    border: None,
22851                                    background: highlight.color.into(),
22852                                    type_id: Some(highlight.type_id),
22853                                },
22854                            );
22855                        }
22856                    }
22857                    unique_rows
22858                },
22859            )
22860    }
22861
22862    pub fn highlighted_display_row_for_autoscroll(
22863        &self,
22864        snapshot: &DisplaySnapshot,
22865    ) -> Option<DisplayRow> {
22866        self.highlighted_rows
22867            .values()
22868            .flat_map(|highlighted_rows| highlighted_rows.iter())
22869            .filter_map(|highlight| {
22870                if highlight.options.autoscroll {
22871                    Some(highlight.range.start.to_display_point(snapshot).row())
22872                } else {
22873                    None
22874                }
22875            })
22876            .min()
22877    }
22878
22879    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
22880        self.highlight_background::<SearchWithinRange>(
22881            ranges,
22882            |_, colors| colors.colors().editor_document_highlight_read_background,
22883            cx,
22884        )
22885    }
22886
22887    pub fn set_breadcrumb_header(&mut self, new_header: String) {
22888        self.breadcrumb_header = Some(new_header);
22889    }
22890
22891    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
22892        self.clear_background_highlights::<SearchWithinRange>(cx);
22893    }
22894
22895    pub fn highlight_background<T: 'static>(
22896        &mut self,
22897        ranges: &[Range<Anchor>],
22898        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
22899        cx: &mut Context<Self>,
22900    ) {
22901        self.background_highlights.insert(
22902            HighlightKey::Type(TypeId::of::<T>()),
22903            (Arc::new(color_fetcher), Arc::from(ranges)),
22904        );
22905        self.scrollbar_marker_state.dirty = true;
22906        cx.notify();
22907    }
22908
22909    pub fn highlight_background_key<T: 'static>(
22910        &mut self,
22911        key: usize,
22912        ranges: &[Range<Anchor>],
22913        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
22914        cx: &mut Context<Self>,
22915    ) {
22916        self.background_highlights.insert(
22917            HighlightKey::TypePlus(TypeId::of::<T>(), key),
22918            (Arc::new(color_fetcher), Arc::from(ranges)),
22919        );
22920        self.scrollbar_marker_state.dirty = true;
22921        cx.notify();
22922    }
22923
22924    pub fn clear_background_highlights<T: 'static>(
22925        &mut self,
22926        cx: &mut Context<Self>,
22927    ) -> Option<BackgroundHighlight> {
22928        let text_highlights = self
22929            .background_highlights
22930            .remove(&HighlightKey::Type(TypeId::of::<T>()))?;
22931        if !text_highlights.1.is_empty() {
22932            self.scrollbar_marker_state.dirty = true;
22933            cx.notify();
22934        }
22935        Some(text_highlights)
22936    }
22937
22938    pub fn highlight_gutter<T: 'static>(
22939        &mut self,
22940        ranges: impl Into<Vec<Range<Anchor>>>,
22941        color_fetcher: fn(&App) -> Hsla,
22942        cx: &mut Context<Self>,
22943    ) {
22944        self.gutter_highlights
22945            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
22946        cx.notify();
22947    }
22948
22949    pub fn clear_gutter_highlights<T: 'static>(
22950        &mut self,
22951        cx: &mut Context<Self>,
22952    ) -> Option<GutterHighlight> {
22953        cx.notify();
22954        self.gutter_highlights.remove(&TypeId::of::<T>())
22955    }
22956
22957    pub fn insert_gutter_highlight<T: 'static>(
22958        &mut self,
22959        range: Range<Anchor>,
22960        color_fetcher: fn(&App) -> Hsla,
22961        cx: &mut Context<Self>,
22962    ) {
22963        let snapshot = self.buffer().read(cx).snapshot(cx);
22964        let mut highlights = self
22965            .gutter_highlights
22966            .remove(&TypeId::of::<T>())
22967            .map(|(_, highlights)| highlights)
22968            .unwrap_or_default();
22969        let ix = highlights.binary_search_by(|highlight| {
22970            Ordering::Equal
22971                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
22972                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
22973        });
22974        if let Err(ix) = ix {
22975            highlights.insert(ix, range);
22976        }
22977        self.gutter_highlights
22978            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
22979    }
22980
22981    pub fn remove_gutter_highlights<T: 'static>(
22982        &mut self,
22983        ranges_to_remove: Vec<Range<Anchor>>,
22984        cx: &mut Context<Self>,
22985    ) {
22986        let snapshot = self.buffer().read(cx).snapshot(cx);
22987        let Some((color_fetcher, mut gutter_highlights)) =
22988            self.gutter_highlights.remove(&TypeId::of::<T>())
22989        else {
22990            return;
22991        };
22992        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
22993        gutter_highlights.retain(|highlight| {
22994            while let Some(range_to_remove) = ranges_to_remove.peek() {
22995                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
22996                    Ordering::Less | Ordering::Equal => {
22997                        ranges_to_remove.next();
22998                    }
22999                    Ordering::Greater => {
23000                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23001                            Ordering::Less | Ordering::Equal => {
23002                                return false;
23003                            }
23004                            Ordering::Greater => break,
23005                        }
23006                    }
23007                }
23008            }
23009
23010            true
23011        });
23012        self.gutter_highlights
23013            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23014    }
23015
23016    #[cfg(feature = "test-support")]
23017    pub fn all_text_highlights(
23018        &self,
23019        window: &mut Window,
23020        cx: &mut Context<Self>,
23021    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23022        let snapshot = self.snapshot(window, cx);
23023        self.display_map.update(cx, |display_map, _| {
23024            display_map
23025                .all_text_highlights()
23026                .map(|highlight| {
23027                    let (style, ranges) = highlight.as_ref();
23028                    (
23029                        *style,
23030                        ranges
23031                            .iter()
23032                            .map(|range| range.clone().to_display_points(&snapshot))
23033                            .collect(),
23034                    )
23035                })
23036                .collect()
23037        })
23038    }
23039
23040    #[cfg(feature = "test-support")]
23041    pub fn all_text_background_highlights(
23042        &self,
23043        window: &mut Window,
23044        cx: &mut Context<Self>,
23045    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23046        let snapshot = self.snapshot(window, cx);
23047        let buffer = &snapshot.buffer_snapshot();
23048        let start = buffer.anchor_before(MultiBufferOffset(0));
23049        let end = buffer.anchor_after(buffer.len());
23050        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23051    }
23052
23053    #[cfg(any(test, feature = "test-support"))]
23054    pub fn sorted_background_highlights_in_range(
23055        &self,
23056        search_range: Range<Anchor>,
23057        display_snapshot: &DisplaySnapshot,
23058        theme: &Theme,
23059    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23060        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23061        res.sort_by(|a, b| {
23062            a.0.start
23063                .cmp(&b.0.start)
23064                .then_with(|| a.0.end.cmp(&b.0.end))
23065                .then_with(|| a.1.cmp(&b.1))
23066        });
23067        res
23068    }
23069
23070    #[cfg(feature = "test-support")]
23071    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23072        let snapshot = self.buffer().read(cx).snapshot(cx);
23073
23074        let highlights = self
23075            .background_highlights
23076            .get(&HighlightKey::Type(TypeId::of::<
23077                items::BufferSearchHighlights,
23078            >()));
23079
23080        if let Some((_color, ranges)) = highlights {
23081            ranges
23082                .iter()
23083                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23084                .collect_vec()
23085        } else {
23086            vec![]
23087        }
23088    }
23089
23090    fn document_highlights_for_position<'a>(
23091        &'a self,
23092        position: Anchor,
23093        buffer: &'a MultiBufferSnapshot,
23094    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23095        let read_highlights = self
23096            .background_highlights
23097            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
23098            .map(|h| &h.1);
23099        let write_highlights = self
23100            .background_highlights
23101            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
23102            .map(|h| &h.1);
23103        let left_position = position.bias_left(buffer);
23104        let right_position = position.bias_right(buffer);
23105        read_highlights
23106            .into_iter()
23107            .chain(write_highlights)
23108            .flat_map(move |ranges| {
23109                let start_ix = match ranges.binary_search_by(|probe| {
23110                    let cmp = probe.end.cmp(&left_position, buffer);
23111                    if cmp.is_ge() {
23112                        Ordering::Greater
23113                    } else {
23114                        Ordering::Less
23115                    }
23116                }) {
23117                    Ok(i) | Err(i) => i,
23118                };
23119
23120                ranges[start_ix..]
23121                    .iter()
23122                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23123            })
23124    }
23125
23126    pub fn has_background_highlights<T: 'static>(&self) -> bool {
23127        self.background_highlights
23128            .get(&HighlightKey::Type(TypeId::of::<T>()))
23129            .is_some_and(|(_, highlights)| !highlights.is_empty())
23130    }
23131
23132    /// Returns all background highlights for a given range.
23133    ///
23134    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23135    pub fn background_highlights_in_range(
23136        &self,
23137        search_range: Range<Anchor>,
23138        display_snapshot: &DisplaySnapshot,
23139        theme: &Theme,
23140    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23141        let mut results = Vec::new();
23142        for (color_fetcher, ranges) in self.background_highlights.values() {
23143            let start_ix = match ranges.binary_search_by(|probe| {
23144                let cmp = probe
23145                    .end
23146                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23147                if cmp.is_gt() {
23148                    Ordering::Greater
23149                } else {
23150                    Ordering::Less
23151                }
23152            }) {
23153                Ok(i) | Err(i) => i,
23154            };
23155            for (index, range) in ranges[start_ix..].iter().enumerate() {
23156                if range
23157                    .start
23158                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23159                    .is_ge()
23160                {
23161                    break;
23162                }
23163
23164                let color = color_fetcher(&(start_ix + index), theme);
23165                let start = range.start.to_display_point(display_snapshot);
23166                let end = range.end.to_display_point(display_snapshot);
23167                results.push((start..end, color))
23168            }
23169        }
23170        results
23171    }
23172
23173    pub fn gutter_highlights_in_range(
23174        &self,
23175        search_range: Range<Anchor>,
23176        display_snapshot: &DisplaySnapshot,
23177        cx: &App,
23178    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23179        let mut results = Vec::new();
23180        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23181            let color = color_fetcher(cx);
23182            let start_ix = match ranges.binary_search_by(|probe| {
23183                let cmp = probe
23184                    .end
23185                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23186                if cmp.is_gt() {
23187                    Ordering::Greater
23188                } else {
23189                    Ordering::Less
23190                }
23191            }) {
23192                Ok(i) | Err(i) => i,
23193            };
23194            for range in &ranges[start_ix..] {
23195                if range
23196                    .start
23197                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23198                    .is_ge()
23199                {
23200                    break;
23201                }
23202
23203                let start = range.start.to_display_point(display_snapshot);
23204                let end = range.end.to_display_point(display_snapshot);
23205                results.push((start..end, color))
23206            }
23207        }
23208        results
23209    }
23210
23211    /// Get the text ranges corresponding to the redaction query
23212    pub fn redacted_ranges(
23213        &self,
23214        search_range: Range<Anchor>,
23215        display_snapshot: &DisplaySnapshot,
23216        cx: &App,
23217    ) -> Vec<Range<DisplayPoint>> {
23218        display_snapshot
23219            .buffer_snapshot()
23220            .redacted_ranges(search_range, |file| {
23221                if let Some(file) = file {
23222                    file.is_private()
23223                        && EditorSettings::get(
23224                            Some(SettingsLocation {
23225                                worktree_id: file.worktree_id(cx),
23226                                path: file.path().as_ref(),
23227                            }),
23228                            cx,
23229                        )
23230                        .redact_private_values
23231                } else {
23232                    false
23233                }
23234            })
23235            .map(|range| {
23236                range.start.to_display_point(display_snapshot)
23237                    ..range.end.to_display_point(display_snapshot)
23238            })
23239            .collect()
23240    }
23241
23242    pub fn highlight_text_key<T: 'static>(
23243        &mut self,
23244        key: usize,
23245        ranges: Vec<Range<Anchor>>,
23246        style: HighlightStyle,
23247        merge: bool,
23248        cx: &mut Context<Self>,
23249    ) {
23250        self.display_map.update(cx, |map, cx| {
23251            map.highlight_text(
23252                HighlightKey::TypePlus(TypeId::of::<T>(), key),
23253                ranges,
23254                style,
23255                merge,
23256                cx,
23257            );
23258        });
23259        cx.notify();
23260    }
23261
23262    pub fn highlight_text<T: 'static>(
23263        &mut self,
23264        ranges: Vec<Range<Anchor>>,
23265        style: HighlightStyle,
23266        cx: &mut Context<Self>,
23267    ) {
23268        self.display_map.update(cx, |map, cx| {
23269            map.highlight_text(
23270                HighlightKey::Type(TypeId::of::<T>()),
23271                ranges,
23272                style,
23273                false,
23274                cx,
23275            )
23276        });
23277        cx.notify();
23278    }
23279
23280    pub fn text_highlights<'a, T: 'static>(
23281        &'a self,
23282        cx: &'a App,
23283    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23284        self.display_map.read(cx).text_highlights(TypeId::of::<T>())
23285    }
23286
23287    pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
23288        let cleared = self
23289            .display_map
23290            .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
23291        if cleared {
23292            cx.notify();
23293        }
23294    }
23295
23296    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23297        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23298            && self.focus_handle.is_focused(window)
23299    }
23300
23301    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23302        self.show_cursor_when_unfocused = is_enabled;
23303        cx.notify();
23304    }
23305
23306    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23307        cx.notify();
23308    }
23309
23310    fn on_debug_session_event(
23311        &mut self,
23312        _session: Entity<Session>,
23313        event: &SessionEvent,
23314        cx: &mut Context<Self>,
23315    ) {
23316        if let SessionEvent::InvalidateInlineValue = event {
23317            self.refresh_inline_values(cx);
23318        }
23319    }
23320
23321    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23322        let Some(project) = self.project.clone() else {
23323            return;
23324        };
23325
23326        if !self.inline_value_cache.enabled {
23327            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23328            self.splice_inlays(&inlays, Vec::new(), cx);
23329            return;
23330        }
23331
23332        let current_execution_position = self
23333            .highlighted_rows
23334            .get(&TypeId::of::<ActiveDebugLine>())
23335            .and_then(|lines| lines.last().map(|line| line.range.end));
23336
23337        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23338            let inline_values = editor
23339                .update(cx, |editor, cx| {
23340                    let Some(current_execution_position) = current_execution_position else {
23341                        return Some(Task::ready(Ok(Vec::new())));
23342                    };
23343
23344                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23345                        let snapshot = buffer.snapshot(cx);
23346
23347                        let excerpt = snapshot.excerpt_containing(
23348                            current_execution_position..current_execution_position,
23349                        )?;
23350
23351                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23352                    })?;
23353
23354                    let range =
23355                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23356
23357                    project.inline_values(buffer, range, cx)
23358                })
23359                .ok()
23360                .flatten()?
23361                .await
23362                .context("refreshing debugger inlays")
23363                .log_err()?;
23364
23365            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23366
23367            for (buffer_id, inline_value) in inline_values
23368                .into_iter()
23369                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23370            {
23371                buffer_inline_values
23372                    .entry(buffer_id)
23373                    .or_default()
23374                    .push(inline_value);
23375            }
23376
23377            editor
23378                .update(cx, |editor, cx| {
23379                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23380                    let mut new_inlays = Vec::default();
23381
23382                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23383                        let buffer_id = buffer_snapshot.remote_id();
23384                        buffer_inline_values
23385                            .get(&buffer_id)
23386                            .into_iter()
23387                            .flatten()
23388                            .for_each(|hint| {
23389                                let inlay = Inlay::debugger(
23390                                    post_inc(&mut editor.next_inlay_id),
23391                                    Anchor::in_buffer(excerpt_id, hint.position),
23392                                    hint.text(),
23393                                );
23394                                if !inlay.text().chars().contains(&'\n') {
23395                                    new_inlays.push(inlay);
23396                                }
23397                            });
23398                    }
23399
23400                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
23401                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
23402
23403                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
23404                })
23405                .ok()?;
23406            Some(())
23407        });
23408    }
23409
23410    fn on_buffer_event(
23411        &mut self,
23412        multibuffer: &Entity<MultiBuffer>,
23413        event: &multi_buffer::Event,
23414        window: &mut Window,
23415        cx: &mut Context<Self>,
23416    ) {
23417        match event {
23418            multi_buffer::Event::Edited { edited_buffer } => {
23419                self.scrollbar_marker_state.dirty = true;
23420                self.active_indent_guides_state.dirty = true;
23421                self.refresh_active_diagnostics(cx);
23422                self.refresh_code_actions(window, cx);
23423                self.refresh_single_line_folds(window, cx);
23424                self.refresh_matching_bracket_highlights(window, cx);
23425                if self.has_active_edit_prediction() {
23426                    self.update_visible_edit_prediction(window, cx);
23427                }
23428
23429                // Clean up orphaned review comments after edits
23430                self.cleanup_orphaned_review_comments(cx);
23431
23432                if let Some(buffer) = edited_buffer {
23433                    if buffer.read(cx).file().is_none() {
23434                        cx.emit(EditorEvent::TitleChanged);
23435                    }
23436
23437                    if self.project.is_some() {
23438                        let buffer_id = buffer.read(cx).remote_id();
23439                        self.register_buffer(buffer_id, cx);
23440                        self.update_lsp_data(Some(buffer_id), window, cx);
23441                        self.refresh_inlay_hints(
23442                            InlayHintRefreshReason::BufferEdited(buffer_id),
23443                            cx,
23444                        );
23445                    }
23446                }
23447
23448                cx.emit(EditorEvent::BufferEdited);
23449                cx.emit(SearchEvent::MatchesInvalidated);
23450
23451                let Some(project) = &self.project else { return };
23452                let (telemetry, is_via_ssh) = {
23453                    let project = project.read(cx);
23454                    let telemetry = project.client().telemetry().clone();
23455                    let is_via_ssh = project.is_via_remote_server();
23456                    (telemetry, is_via_ssh)
23457                };
23458                telemetry.log_edit_event("editor", is_via_ssh);
23459            }
23460            multi_buffer::Event::ExcerptsAdded {
23461                buffer,
23462                predecessor,
23463                excerpts,
23464            } => {
23465                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23466                let buffer_id = buffer.read(cx).remote_id();
23467                if self.buffer.read(cx).diff_for(buffer_id).is_none()
23468                    && let Some(project) = &self.project
23469                {
23470                    update_uncommitted_diff_for_buffer(
23471                        cx.entity(),
23472                        project,
23473                        [buffer.clone()],
23474                        self.buffer.clone(),
23475                        cx,
23476                    )
23477                    .detach();
23478                }
23479                self.update_lsp_data(Some(buffer_id), window, cx);
23480                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23481                self.colorize_brackets(false, cx);
23482                self.refresh_selected_text_highlights(true, window, cx);
23483                cx.emit(EditorEvent::ExcerptsAdded {
23484                    buffer: buffer.clone(),
23485                    predecessor: *predecessor,
23486                    excerpts: excerpts.clone(),
23487                });
23488            }
23489            multi_buffer::Event::ExcerptsRemoved {
23490                ids,
23491                removed_buffer_ids,
23492            } => {
23493                if let Some(inlay_hints) = &mut self.inlay_hints {
23494                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
23495                }
23496                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
23497                for buffer_id in removed_buffer_ids {
23498                    self.registered_buffers.remove(buffer_id);
23499                }
23500                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23501                cx.emit(EditorEvent::ExcerptsRemoved {
23502                    ids: ids.clone(),
23503                    removed_buffer_ids: removed_buffer_ids.clone(),
23504                });
23505            }
23506            multi_buffer::Event::ExcerptsEdited {
23507                excerpt_ids,
23508                buffer_ids,
23509            } => {
23510                self.display_map.update(cx, |map, cx| {
23511                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
23512                });
23513                cx.emit(EditorEvent::ExcerptsEdited {
23514                    ids: excerpt_ids.clone(),
23515                });
23516            }
23517            multi_buffer::Event::ExcerptsExpanded { ids } => {
23518                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23519                self.refresh_document_highlights(cx);
23520                for id in ids {
23521                    self.fetched_tree_sitter_chunks.remove(id);
23522                }
23523                self.colorize_brackets(false, cx);
23524                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
23525            }
23526            multi_buffer::Event::Reparsed(buffer_id) => {
23527                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23528                self.refresh_selected_text_highlights(true, window, cx);
23529                self.colorize_brackets(true, cx);
23530                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23531
23532                cx.emit(EditorEvent::Reparsed(*buffer_id));
23533            }
23534            multi_buffer::Event::DiffHunksToggled => {
23535                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23536            }
23537            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
23538                if !is_fresh_language {
23539                    self.registered_buffers.remove(&buffer_id);
23540                }
23541                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23542                cx.emit(EditorEvent::Reparsed(*buffer_id));
23543                cx.notify();
23544            }
23545            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
23546            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
23547            multi_buffer::Event::FileHandleChanged
23548            | multi_buffer::Event::Reloaded
23549            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
23550            multi_buffer::Event::DiagnosticsUpdated => {
23551                self.update_diagnostics_state(window, cx);
23552            }
23553            _ => {}
23554        };
23555    }
23556
23557    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
23558        if !self.diagnostics_enabled() {
23559            return;
23560        }
23561        self.refresh_active_diagnostics(cx);
23562        self.refresh_inline_diagnostics(true, window, cx);
23563        self.scrollbar_marker_state.dirty = true;
23564        cx.notify();
23565    }
23566
23567    pub fn start_temporary_diff_override(&mut self) {
23568        self.load_diff_task.take();
23569        self.temporary_diff_override = true;
23570    }
23571
23572    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
23573        self.temporary_diff_override = false;
23574        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
23575        self.buffer.update(cx, |buffer, cx| {
23576            buffer.set_all_diff_hunks_collapsed(cx);
23577        });
23578
23579        if let Some(project) = self.project.clone() {
23580            self.load_diff_task = Some(
23581                update_uncommitted_diff_for_buffer(
23582                    cx.entity(),
23583                    &project,
23584                    self.buffer.read(cx).all_buffers(),
23585                    self.buffer.clone(),
23586                    cx,
23587                )
23588                .shared(),
23589            );
23590        }
23591    }
23592
23593    fn on_display_map_changed(
23594        &mut self,
23595        _: Entity<DisplayMap>,
23596        _: &mut Window,
23597        cx: &mut Context<Self>,
23598    ) {
23599        cx.notify();
23600    }
23601
23602    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
23603        if !self.mode.is_full() {
23604            return None;
23605        }
23606
23607        let theme_settings = theme::ThemeSettings::get_global(cx);
23608        let theme = cx.theme();
23609        let accent_colors = theme.accents().clone();
23610
23611        let accent_overrides = theme_settings
23612            .theme_overrides
23613            .get(theme.name.as_ref())
23614            .map(|theme_style| &theme_style.accents)
23615            .into_iter()
23616            .flatten()
23617            .chain(
23618                theme_settings
23619                    .experimental_theme_overrides
23620                    .as_ref()
23621                    .map(|overrides| &overrides.accents)
23622                    .into_iter()
23623                    .flatten(),
23624            )
23625            .flat_map(|accent| accent.0.clone())
23626            .collect();
23627
23628        Some(AccentData {
23629            colors: accent_colors,
23630            overrides: accent_overrides,
23631        })
23632    }
23633
23634    fn fetch_applicable_language_settings(
23635        &self,
23636        cx: &App,
23637    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
23638        if !self.mode.is_full() {
23639            return HashMap::default();
23640        }
23641
23642        self.buffer().read(cx).all_buffers().into_iter().fold(
23643            HashMap::default(),
23644            |mut acc, buffer| {
23645                let buffer = buffer.read(cx);
23646                let language = buffer.language().map(|language| language.name());
23647                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
23648                    let file = buffer.file();
23649                    v.insert(language_settings(language, file, cx).into_owned());
23650                }
23651                acc
23652            },
23653        )
23654    }
23655
23656    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
23657        let new_language_settings = self.fetch_applicable_language_settings(cx);
23658        let language_settings_changed = new_language_settings != self.applicable_language_settings;
23659        self.applicable_language_settings = new_language_settings;
23660
23661        let new_accents = self.fetch_accent_data(cx);
23662        let accents_changed = new_accents != self.accent_data;
23663        self.accent_data = new_accents;
23664
23665        if self.diagnostics_enabled() {
23666            let new_severity = EditorSettings::get_global(cx)
23667                .diagnostics_max_severity
23668                .unwrap_or(DiagnosticSeverity::Hint);
23669            self.set_max_diagnostics_severity(new_severity, cx);
23670        }
23671        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23672        self.update_edit_prediction_settings(cx);
23673        self.refresh_edit_prediction(true, false, window, cx);
23674        self.refresh_inline_values(cx);
23675        self.refresh_inlay_hints(
23676            InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
23677                self.selections.newest_anchor().head(),
23678                &self.buffer.read(cx).snapshot(cx),
23679                cx,
23680            )),
23681            cx,
23682        );
23683
23684        let old_cursor_shape = self.cursor_shape;
23685        let old_show_breadcrumbs = self.show_breadcrumbs;
23686
23687        {
23688            let editor_settings = EditorSettings::get_global(cx);
23689            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
23690            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
23691            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
23692            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
23693        }
23694
23695        if old_cursor_shape != self.cursor_shape {
23696            cx.emit(EditorEvent::CursorShapeChanged);
23697        }
23698
23699        if old_show_breadcrumbs != self.show_breadcrumbs {
23700            cx.emit(EditorEvent::BreadcrumbsChanged);
23701        }
23702
23703        let project_settings = ProjectSettings::get_global(cx);
23704        self.buffer_serialization = self
23705            .should_serialize_buffer()
23706            .then(|| BufferSerialization::new(project_settings.session.restore_unsaved_buffers));
23707
23708        if self.mode.is_full() {
23709            let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
23710            let inline_blame_enabled = project_settings.git.inline_blame.enabled;
23711            if self.show_inline_diagnostics != show_inline_diagnostics {
23712                self.show_inline_diagnostics = show_inline_diagnostics;
23713                self.refresh_inline_diagnostics(false, window, cx);
23714            }
23715
23716            if self.git_blame_inline_enabled != inline_blame_enabled {
23717                self.toggle_git_blame_inline_internal(false, window, cx);
23718            }
23719
23720            let minimap_settings = EditorSettings::get_global(cx).minimap;
23721            if self.minimap_visibility != MinimapVisibility::Disabled {
23722                if self.minimap_visibility.settings_visibility()
23723                    != minimap_settings.minimap_enabled()
23724                {
23725                    self.set_minimap_visibility(
23726                        MinimapVisibility::for_mode(self.mode(), cx),
23727                        window,
23728                        cx,
23729                    );
23730                } else if let Some(minimap_entity) = self.minimap.as_ref() {
23731                    minimap_entity.update(cx, |minimap_editor, cx| {
23732                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
23733                    })
23734                }
23735            }
23736
23737            if language_settings_changed || accents_changed {
23738                self.colorize_brackets(true, cx);
23739            }
23740
23741            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
23742                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
23743            }) {
23744                if !inlay_splice.is_empty() {
23745                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
23746                }
23747                self.refresh_colors_for_visible_range(None, window, cx);
23748            }
23749        }
23750
23751        cx.notify();
23752    }
23753
23754    pub fn set_searchable(&mut self, searchable: bool) {
23755        self.searchable = searchable;
23756    }
23757
23758    pub fn searchable(&self) -> bool {
23759        self.searchable
23760    }
23761
23762    pub fn open_excerpts_in_split(
23763        &mut self,
23764        _: &OpenExcerptsSplit,
23765        window: &mut Window,
23766        cx: &mut Context<Self>,
23767    ) {
23768        self.open_excerpts_common(None, true, window, cx)
23769    }
23770
23771    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
23772        self.open_excerpts_common(None, false, window, cx)
23773    }
23774
23775    fn open_excerpts_common(
23776        &mut self,
23777        jump_data: Option<JumpData>,
23778        split: bool,
23779        window: &mut Window,
23780        cx: &mut Context<Self>,
23781    ) {
23782        let Some(workspace) = self.workspace() else {
23783            cx.propagate();
23784            return;
23785        };
23786
23787        if self.buffer.read(cx).is_singleton() {
23788            cx.propagate();
23789            return;
23790        }
23791
23792        let mut new_selections_by_buffer = HashMap::default();
23793        match &jump_data {
23794            Some(JumpData::MultiBufferPoint {
23795                excerpt_id,
23796                position,
23797                anchor,
23798                line_offset_from_top,
23799            }) => {
23800                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
23801                if let Some(buffer) = multi_buffer_snapshot
23802                    .buffer_id_for_excerpt(*excerpt_id)
23803                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
23804                {
23805                    let buffer_snapshot = buffer.read(cx).snapshot();
23806                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
23807                        language::ToPoint::to_point(anchor, &buffer_snapshot)
23808                    } else {
23809                        buffer_snapshot.clip_point(*position, Bias::Left)
23810                    };
23811                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
23812                    new_selections_by_buffer.insert(
23813                        buffer,
23814                        (
23815                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
23816                            Some(*line_offset_from_top),
23817                        ),
23818                    );
23819                }
23820            }
23821            Some(JumpData::MultiBufferRow {
23822                row,
23823                line_offset_from_top,
23824            }) => {
23825                let point = MultiBufferPoint::new(row.0, 0);
23826                if let Some((buffer, buffer_point, _)) =
23827                    self.buffer.read(cx).point_to_buffer_point(point, cx)
23828                {
23829                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
23830                    new_selections_by_buffer
23831                        .entry(buffer)
23832                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
23833                        .0
23834                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
23835                }
23836            }
23837            None => {
23838                let selections = self
23839                    .selections
23840                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
23841                let multi_buffer = self.buffer.read(cx);
23842                for selection in selections {
23843                    for (snapshot, range, _, anchor) in multi_buffer
23844                        .snapshot(cx)
23845                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
23846                    {
23847                        if let Some(anchor) = anchor {
23848                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
23849                            else {
23850                                continue;
23851                            };
23852                            let offset = text::ToOffset::to_offset(
23853                                &anchor.text_anchor,
23854                                &buffer_handle.read(cx).snapshot(),
23855                            );
23856                            let range = BufferOffset(offset)..BufferOffset(offset);
23857                            new_selections_by_buffer
23858                                .entry(buffer_handle)
23859                                .or_insert((Vec::new(), None))
23860                                .0
23861                                .push(range)
23862                        } else {
23863                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
23864                            else {
23865                                continue;
23866                            };
23867                            new_selections_by_buffer
23868                                .entry(buffer_handle)
23869                                .or_insert((Vec::new(), None))
23870                                .0
23871                                .push(range)
23872                        }
23873                    }
23874                }
23875            }
23876        }
23877
23878        new_selections_by_buffer
23879            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
23880
23881        if new_selections_by_buffer.is_empty() {
23882            return;
23883        }
23884
23885        // We defer the pane interaction because we ourselves are a workspace item
23886        // and activating a new item causes the pane to call a method on us reentrantly,
23887        // which panics if we're on the stack.
23888        window.defer(cx, move |window, cx| {
23889            workspace.update(cx, |workspace, cx| {
23890                let pane = if split {
23891                    workspace.adjacent_pane(window, cx)
23892                } else {
23893                    workspace.active_pane().clone()
23894                };
23895
23896                for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
23897                    let buffer_read = buffer.read(cx);
23898                    let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
23899                        (true, project::File::from_dyn(Some(file)).is_some())
23900                    } else {
23901                        (false, false)
23902                    };
23903
23904                    // If project file is none workspace.open_project_item will fail to open the excerpt
23905                    // in a pre existing workspace item if one exists, because Buffer entity_id will be None
23906                    // so we check if there's a tab match in that case first
23907                    let editor = (!has_file || !is_project_file)
23908                        .then(|| {
23909                            // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
23910                            // so `workspace.open_project_item` will never find them, always opening a new editor.
23911                            // Instead, we try to activate the existing editor in the pane first.
23912                            let (editor, pane_item_index, pane_item_id) =
23913                                pane.read(cx).items().enumerate().find_map(|(i, item)| {
23914                                    let editor = item.downcast::<Editor>()?;
23915                                    let singleton_buffer =
23916                                        editor.read(cx).buffer().read(cx).as_singleton()?;
23917                                    if singleton_buffer == buffer {
23918                                        Some((editor, i, item.item_id()))
23919                                    } else {
23920                                        None
23921                                    }
23922                                })?;
23923                            pane.update(cx, |pane, cx| {
23924                                pane.activate_item(pane_item_index, true, true, window, cx);
23925                                if !PreviewTabsSettings::get_global(cx)
23926                                    .enable_preview_from_multibuffer
23927                                {
23928                                    pane.unpreview_item_if_preview(pane_item_id);
23929                                }
23930                            });
23931                            Some(editor)
23932                        })
23933                        .flatten()
23934                        .unwrap_or_else(|| {
23935                            let keep_old_preview = PreviewTabsSettings::get_global(cx)
23936                                .enable_keep_preview_on_code_navigation;
23937                            let allow_new_preview =
23938                                PreviewTabsSettings::get_global(cx).enable_preview_from_multibuffer;
23939                            workspace.open_project_item::<Self>(
23940                                pane.clone(),
23941                                buffer,
23942                                true,
23943                                true,
23944                                keep_old_preview,
23945                                allow_new_preview,
23946                                window,
23947                                cx,
23948                            )
23949                        });
23950
23951                    editor.update(cx, |editor, cx| {
23952                        if has_file && !is_project_file {
23953                            editor.set_read_only(true);
23954                        }
23955                        let autoscroll = match scroll_offset {
23956                            Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
23957                            None => Autoscroll::newest(),
23958                        };
23959                        let nav_history = editor.nav_history.take();
23960                        let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
23961                        let Some((&excerpt_id, _, buffer_snapshot)) =
23962                            multibuffer_snapshot.as_singleton()
23963                        else {
23964                            return;
23965                        };
23966                        editor.change_selections(
23967                            SelectionEffects::scroll(autoscroll),
23968                            window,
23969                            cx,
23970                            |s| {
23971                                s.select_ranges(ranges.into_iter().map(|range| {
23972                                    let range = buffer_snapshot.anchor_before(range.start)
23973                                        ..buffer_snapshot.anchor_after(range.end);
23974                                    multibuffer_snapshot
23975                                        .anchor_range_in_excerpt(excerpt_id, range)
23976                                        .unwrap()
23977                                }));
23978                            },
23979                        );
23980                        editor.nav_history = nav_history;
23981                    });
23982                }
23983            })
23984        });
23985    }
23986
23987    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
23988        let snapshot = self.buffer.read(cx).read(cx);
23989        let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
23990        Some(
23991            ranges
23992                .iter()
23993                .map(move |range| {
23994                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
23995                })
23996                .collect(),
23997        )
23998    }
23999
24000    fn selection_replacement_ranges(
24001        &self,
24002        range: Range<MultiBufferOffsetUtf16>,
24003        cx: &mut App,
24004    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24005        let selections = self
24006            .selections
24007            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24008        let newest_selection = selections
24009            .iter()
24010            .max_by_key(|selection| selection.id)
24011            .unwrap();
24012        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24013        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24014        let snapshot = self.buffer.read(cx).read(cx);
24015        selections
24016            .into_iter()
24017            .map(|mut selection| {
24018                selection.start.0.0 =
24019                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24020                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24021                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24022                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24023            })
24024            .collect()
24025    }
24026
24027    fn report_editor_event(
24028        &self,
24029        reported_event: ReportEditorEvent,
24030        file_extension: Option<String>,
24031        cx: &App,
24032    ) {
24033        if cfg!(any(test, feature = "test-support")) {
24034            return;
24035        }
24036
24037        let Some(project) = &self.project else { return };
24038
24039        // If None, we are in a file without an extension
24040        let file = self
24041            .buffer
24042            .read(cx)
24043            .as_singleton()
24044            .and_then(|b| b.read(cx).file());
24045        let file_extension = file_extension.or(file
24046            .as_ref()
24047            .and_then(|file| Path::new(file.file_name(cx)).extension())
24048            .and_then(|e| e.to_str())
24049            .map(|a| a.to_string()));
24050
24051        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24052            .map(|vim_mode| vim_mode.0)
24053            .unwrap_or(false);
24054
24055        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24056        let copilot_enabled = edit_predictions_provider
24057            == language::language_settings::EditPredictionProvider::Copilot;
24058        let copilot_enabled_for_language = self
24059            .buffer
24060            .read(cx)
24061            .language_settings(cx)
24062            .show_edit_predictions;
24063
24064        let project = project.read(cx);
24065        let event_type = reported_event.event_type();
24066
24067        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24068            telemetry::event!(
24069                event_type,
24070                type = if auto_saved {"autosave"} else {"manual"},
24071                file_extension,
24072                vim_mode,
24073                copilot_enabled,
24074                copilot_enabled_for_language,
24075                edit_predictions_provider,
24076                is_via_ssh = project.is_via_remote_server(),
24077            );
24078        } else {
24079            telemetry::event!(
24080                event_type,
24081                file_extension,
24082                vim_mode,
24083                copilot_enabled,
24084                copilot_enabled_for_language,
24085                edit_predictions_provider,
24086                is_via_ssh = project.is_via_remote_server(),
24087            );
24088        };
24089    }
24090
24091    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24092    /// with each line being an array of {text, highlight} objects.
24093    fn copy_highlight_json(
24094        &mut self,
24095        _: &CopyHighlightJson,
24096        window: &mut Window,
24097        cx: &mut Context<Self>,
24098    ) {
24099        #[derive(Serialize)]
24100        struct Chunk<'a> {
24101            text: String,
24102            highlight: Option<&'a str>,
24103        }
24104
24105        let snapshot = self.buffer.read(cx).snapshot(cx);
24106        let range = self
24107            .selected_text_range(false, window, cx)
24108            .and_then(|selection| {
24109                if selection.range.is_empty() {
24110                    None
24111                } else {
24112                    Some(
24113                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24114                            selection.range.start,
24115                        )))
24116                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24117                                selection.range.end,
24118                            ))),
24119                    )
24120                }
24121            })
24122            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24123
24124        let chunks = snapshot.chunks(range, true);
24125        let mut lines = Vec::new();
24126        let mut line: VecDeque<Chunk> = VecDeque::new();
24127
24128        let Some(style) = self.style.as_ref() else {
24129            return;
24130        };
24131
24132        for chunk in chunks {
24133            let highlight = chunk
24134                .syntax_highlight_id
24135                .and_then(|id| id.name(&style.syntax));
24136            let mut chunk_lines = chunk.text.split('\n').peekable();
24137            while let Some(text) = chunk_lines.next() {
24138                let mut merged_with_last_token = false;
24139                if let Some(last_token) = line.back_mut()
24140                    && last_token.highlight == highlight
24141                {
24142                    last_token.text.push_str(text);
24143                    merged_with_last_token = true;
24144                }
24145
24146                if !merged_with_last_token {
24147                    line.push_back(Chunk {
24148                        text: text.into(),
24149                        highlight,
24150                    });
24151                }
24152
24153                if chunk_lines.peek().is_some() {
24154                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24155                        line.pop_front();
24156                    }
24157                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24158                        line.pop_back();
24159                    }
24160
24161                    lines.push(mem::take(&mut line));
24162                }
24163            }
24164        }
24165
24166        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24167            return;
24168        };
24169        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24170    }
24171
24172    pub fn open_context_menu(
24173        &mut self,
24174        _: &OpenContextMenu,
24175        window: &mut Window,
24176        cx: &mut Context<Self>,
24177    ) {
24178        self.request_autoscroll(Autoscroll::newest(), cx);
24179        let position = self
24180            .selections
24181            .newest_display(&self.display_snapshot(cx))
24182            .start;
24183        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24184    }
24185
24186    pub fn replay_insert_event(
24187        &mut self,
24188        text: &str,
24189        relative_utf16_range: Option<Range<isize>>,
24190        window: &mut Window,
24191        cx: &mut Context<Self>,
24192    ) {
24193        if !self.input_enabled {
24194            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24195            return;
24196        }
24197        if let Some(relative_utf16_range) = relative_utf16_range {
24198            let selections = self
24199                .selections
24200                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24201            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24202                let new_ranges = selections.into_iter().map(|range| {
24203                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24204                        range
24205                            .head()
24206                            .0
24207                            .0
24208                            .saturating_add_signed(relative_utf16_range.start),
24209                    ));
24210                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24211                        range
24212                            .head()
24213                            .0
24214                            .0
24215                            .saturating_add_signed(relative_utf16_range.end),
24216                    ));
24217                    start..end
24218                });
24219                s.select_ranges(new_ranges);
24220            });
24221        }
24222
24223        self.handle_input(text, window, cx);
24224    }
24225
24226    pub fn is_focused(&self, window: &Window) -> bool {
24227        self.focus_handle.is_focused(window)
24228    }
24229
24230    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24231        cx.emit(EditorEvent::Focused);
24232
24233        if let Some(descendant) = self
24234            .last_focused_descendant
24235            .take()
24236            .and_then(|descendant| descendant.upgrade())
24237        {
24238            window.focus(&descendant, cx);
24239        } else {
24240            if let Some(blame) = self.blame.as_ref() {
24241                blame.update(cx, GitBlame::focus)
24242            }
24243
24244            self.blink_manager.update(cx, BlinkManager::enable);
24245            self.show_cursor_names(window, cx);
24246            self.buffer.update(cx, |buffer, cx| {
24247                buffer.finalize_last_transaction(cx);
24248                if self.leader_id.is_none() {
24249                    buffer.set_active_selections(
24250                        &self.selections.disjoint_anchors_arc(),
24251                        self.selections.line_mode(),
24252                        self.cursor_shape,
24253                        cx,
24254                    );
24255                }
24256            });
24257
24258            if let Some(position_map) = self.last_position_map.clone() {
24259                EditorElement::mouse_moved(
24260                    self,
24261                    &MouseMoveEvent {
24262                        position: window.mouse_position(),
24263                        pressed_button: None,
24264                        modifiers: window.modifiers(),
24265                    },
24266                    &position_map,
24267                    window,
24268                    cx,
24269                );
24270            }
24271        }
24272    }
24273
24274    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24275        cx.emit(EditorEvent::FocusedIn)
24276    }
24277
24278    fn handle_focus_out(
24279        &mut self,
24280        event: FocusOutEvent,
24281        _window: &mut Window,
24282        cx: &mut Context<Self>,
24283    ) {
24284        if event.blurred != self.focus_handle {
24285            self.last_focused_descendant = Some(event.blurred);
24286        }
24287        self.selection_drag_state = SelectionDragState::None;
24288        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24289    }
24290
24291    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24292        self.blink_manager.update(cx, BlinkManager::disable);
24293        self.buffer
24294            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
24295
24296        if let Some(blame) = self.blame.as_ref() {
24297            blame.update(cx, GitBlame::blur)
24298        }
24299        if !self.hover_state.focused(window, cx) {
24300            hide_hover(self, cx);
24301        }
24302        if !self
24303            .context_menu
24304            .borrow()
24305            .as_ref()
24306            .is_some_and(|context_menu| context_menu.focused(window, cx))
24307        {
24308            self.hide_context_menu(window, cx);
24309        }
24310        self.take_active_edit_prediction(cx);
24311        cx.emit(EditorEvent::Blurred);
24312        cx.notify();
24313    }
24314
24315    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24316        let mut pending: String = window
24317            .pending_input_keystrokes()
24318            .into_iter()
24319            .flatten()
24320            .filter_map(|keystroke| keystroke.key_char.clone())
24321            .collect();
24322
24323        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
24324            pending = "".to_string();
24325        }
24326
24327        let existing_pending = self
24328            .text_highlights::<PendingInput>(cx)
24329            .map(|(_, ranges)| ranges.to_vec());
24330        if existing_pending.is_none() && pending.is_empty() {
24331            return;
24332        }
24333        let transaction =
24334            self.transact(window, cx, |this, window, cx| {
24335                let selections = this
24336                    .selections
24337                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
24338                let edits = selections
24339                    .iter()
24340                    .map(|selection| (selection.end..selection.end, pending.clone()));
24341                this.edit(edits, cx);
24342                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24343                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
24344                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
24345                    }));
24346                });
24347                if let Some(existing_ranges) = existing_pending {
24348                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
24349                    this.edit(edits, cx);
24350                }
24351            });
24352
24353        let snapshot = self.snapshot(window, cx);
24354        let ranges = self
24355            .selections
24356            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
24357            .into_iter()
24358            .map(|selection| {
24359                snapshot.buffer_snapshot().anchor_after(selection.end)
24360                    ..snapshot
24361                        .buffer_snapshot()
24362                        .anchor_before(selection.end + pending.len())
24363            })
24364            .collect();
24365
24366        if pending.is_empty() {
24367            self.clear_highlights::<PendingInput>(cx);
24368        } else {
24369            self.highlight_text::<PendingInput>(
24370                ranges,
24371                HighlightStyle {
24372                    underline: Some(UnderlineStyle {
24373                        thickness: px(1.),
24374                        color: None,
24375                        wavy: false,
24376                    }),
24377                    ..Default::default()
24378                },
24379                cx,
24380            );
24381        }
24382
24383        self.ime_transaction = self.ime_transaction.or(transaction);
24384        if let Some(transaction) = self.ime_transaction {
24385            self.buffer.update(cx, |buffer, cx| {
24386                buffer.group_until_transaction(transaction, cx);
24387            });
24388        }
24389
24390        if self.text_highlights::<PendingInput>(cx).is_none() {
24391            self.ime_transaction.take();
24392        }
24393    }
24394
24395    pub fn register_action_renderer(
24396        &mut self,
24397        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
24398    ) -> Subscription {
24399        let id = self.next_editor_action_id.post_inc();
24400        self.editor_actions
24401            .borrow_mut()
24402            .insert(id, Box::new(listener));
24403
24404        let editor_actions = self.editor_actions.clone();
24405        Subscription::new(move || {
24406            editor_actions.borrow_mut().remove(&id);
24407        })
24408    }
24409
24410    pub fn register_action<A: Action>(
24411        &mut self,
24412        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
24413    ) -> Subscription {
24414        let id = self.next_editor_action_id.post_inc();
24415        let listener = Arc::new(listener);
24416        self.editor_actions.borrow_mut().insert(
24417            id,
24418            Box::new(move |_, window, _| {
24419                let listener = listener.clone();
24420                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
24421                    let action = action.downcast_ref().unwrap();
24422                    if phase == DispatchPhase::Bubble {
24423                        listener(action, window, cx)
24424                    }
24425                })
24426            }),
24427        );
24428
24429        let editor_actions = self.editor_actions.clone();
24430        Subscription::new(move || {
24431            editor_actions.borrow_mut().remove(&id);
24432        })
24433    }
24434
24435    pub fn file_header_size(&self) -> u32 {
24436        FILE_HEADER_HEIGHT
24437    }
24438
24439    pub fn restore(
24440        &mut self,
24441        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
24442        window: &mut Window,
24443        cx: &mut Context<Self>,
24444    ) {
24445        self.buffer().update(cx, |multi_buffer, cx| {
24446            for (buffer_id, changes) in revert_changes {
24447                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
24448                    buffer.update(cx, |buffer, cx| {
24449                        buffer.edit(
24450                            changes
24451                                .into_iter()
24452                                .map(|(range, text)| (range, text.to_string())),
24453                            None,
24454                            cx,
24455                        );
24456                    });
24457                }
24458            }
24459        });
24460        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
24461            selections.refresh()
24462        });
24463    }
24464
24465    pub fn to_pixel_point(
24466        &mut self,
24467        source: multi_buffer::Anchor,
24468        editor_snapshot: &EditorSnapshot,
24469        window: &mut Window,
24470        cx: &App,
24471    ) -> Option<gpui::Point<Pixels>> {
24472        let source_point = source.to_display_point(editor_snapshot);
24473        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
24474    }
24475
24476    pub fn display_to_pixel_point(
24477        &mut self,
24478        source: DisplayPoint,
24479        editor_snapshot: &EditorSnapshot,
24480        window: &mut Window,
24481        cx: &App,
24482    ) -> Option<gpui::Point<Pixels>> {
24483        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
24484        let text_layout_details = self.text_layout_details(window);
24485        let scroll_top = text_layout_details
24486            .scroll_anchor
24487            .scroll_position(editor_snapshot)
24488            .y;
24489
24490        if source.row().as_f64() < scroll_top.floor() {
24491            return None;
24492        }
24493        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
24494        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
24495        Some(gpui::Point::new(source_x, source_y))
24496    }
24497
24498    pub fn has_visible_completions_menu(&self) -> bool {
24499        !self.edit_prediction_preview_is_active()
24500            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
24501                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
24502            })
24503    }
24504
24505    pub fn register_addon<T: Addon>(&mut self, instance: T) {
24506        if self.mode.is_minimap() {
24507            return;
24508        }
24509        self.addons
24510            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
24511    }
24512
24513    pub fn unregister_addon<T: Addon>(&mut self) {
24514        self.addons.remove(&std::any::TypeId::of::<T>());
24515    }
24516
24517    pub fn addon<T: Addon>(&self) -> Option<&T> {
24518        let type_id = std::any::TypeId::of::<T>();
24519        self.addons
24520            .get(&type_id)
24521            .and_then(|item| item.to_any().downcast_ref::<T>())
24522    }
24523
24524    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
24525        let type_id = std::any::TypeId::of::<T>();
24526        self.addons
24527            .get_mut(&type_id)
24528            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
24529    }
24530
24531    fn character_dimensions(&self, window: &mut Window) -> CharacterDimensions {
24532        let text_layout_details = self.text_layout_details(window);
24533        let style = &text_layout_details.editor_style;
24534        let font_id = window.text_system().resolve_font(&style.text.font());
24535        let font_size = style.text.font_size.to_pixels(window.rem_size());
24536        let line_height = style.text.line_height_in_pixels(window.rem_size());
24537        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
24538        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
24539
24540        CharacterDimensions {
24541            em_width,
24542            em_advance,
24543            line_height,
24544        }
24545    }
24546
24547    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
24548        self.load_diff_task.clone()
24549    }
24550
24551    fn read_metadata_from_db(
24552        &mut self,
24553        item_id: u64,
24554        workspace_id: WorkspaceId,
24555        window: &mut Window,
24556        cx: &mut Context<Editor>,
24557    ) {
24558        if self.buffer_kind(cx) == ItemBufferKind::Singleton
24559            && !self.mode.is_minimap()
24560            && WorkspaceSettings::get(None, cx).restore_on_startup
24561                != RestoreOnStartupBehavior::EmptyTab
24562        {
24563            let buffer_snapshot = OnceCell::new();
24564
24565            if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
24566                && !folds.is_empty()
24567            {
24568                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
24569                let snapshot_len = snapshot.len().0;
24570
24571                // Helper: search for fingerprint in buffer, return offset if found
24572                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
24573                    // Ensure we start at a character boundary (defensive)
24574                    let search_start = snapshot
24575                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
24576                        .0;
24577                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
24578
24579                    let mut byte_offset = search_start;
24580                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
24581                        if byte_offset > search_end {
24582                            break;
24583                        }
24584                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
24585                            return Some(byte_offset);
24586                        }
24587                        byte_offset += ch.len_utf8();
24588                    }
24589                    None
24590                };
24591
24592                // Track search position to handle duplicate fingerprints correctly.
24593                // Folds are stored in document order, so we advance after each match.
24594                let mut search_start = 0usize;
24595
24596                let valid_folds: Vec<_> = folds
24597                    .into_iter()
24598                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
24599                        // Skip folds without fingerprints (old data before migration)
24600                        let sfp = start_fp?;
24601                        let efp = end_fp?;
24602                        let efp_len = efp.len();
24603
24604                        // Fast path: check if fingerprints match at stored offsets
24605                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
24606                        let start_matches = stored_start < snapshot_len
24607                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
24608                        let efp_check_pos = stored_end.saturating_sub(efp_len);
24609                        let end_matches = efp_check_pos >= stored_start
24610                            && stored_end <= snapshot_len
24611                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
24612
24613                        let (new_start, new_end) = if start_matches && end_matches {
24614                            // Offsets unchanged, use stored values
24615                            (stored_start, stored_end)
24616                        } else if sfp == efp {
24617                            // Short fold: identical fingerprints can only match once per search
24618                            // Use stored fold length to compute new_end
24619                            let new_start = find_fingerprint(&sfp, search_start)?;
24620                            let fold_len = stored_end - stored_start;
24621                            let new_end = new_start + fold_len;
24622                            (new_start, new_end)
24623                        } else {
24624                            // Slow path: search for fingerprints in buffer
24625                            let new_start = find_fingerprint(&sfp, search_start)?;
24626                            // Search for end_fp after start, then add efp_len to get actual fold end
24627                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
24628                            let new_end = efp_pos + efp_len;
24629                            (new_start, new_end)
24630                        };
24631
24632                        // Advance search position for next fold
24633                        search_start = new_end;
24634
24635                        // Validate fold makes sense (end must be after start)
24636                        if new_end <= new_start {
24637                            return None;
24638                        }
24639
24640                        Some(
24641                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
24642                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
24643                        )
24644                    })
24645                    .collect();
24646
24647                if !valid_folds.is_empty() {
24648                    self.fold_ranges(valid_folds, false, window, cx);
24649
24650                    // Migrate folds to current entity_id before workspace cleanup runs.
24651                    // Entity IDs change between sessions, but workspace cleanup deletes
24652                    // old editor rows (cascading to folds) based on current entity IDs.
24653                    let new_editor_id = cx.entity().entity_id().as_u64() as ItemId;
24654                    if new_editor_id != item_id {
24655                        cx.spawn(async move |_, _| {
24656                            DB.migrate_editor_folds(item_id, new_editor_id, workspace_id)
24657                                .await
24658                                .log_err();
24659                        })
24660                        .detach();
24661                    }
24662                }
24663            }
24664
24665            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
24666                && !selections.is_empty()
24667            {
24668                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
24669                // skip adding the initial selection to selection history
24670                self.selection_history.mode = SelectionHistoryMode::Skipping;
24671                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24672                    s.select_ranges(selections.into_iter().map(|(start, end)| {
24673                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
24674                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
24675                    }));
24676                });
24677                self.selection_history.mode = SelectionHistoryMode::Normal;
24678            };
24679        }
24680
24681        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
24682    }
24683
24684    fn update_lsp_data(
24685        &mut self,
24686        for_buffer: Option<BufferId>,
24687        window: &mut Window,
24688        cx: &mut Context<'_, Self>,
24689    ) {
24690        self.pull_diagnostics(for_buffer, window, cx);
24691        self.refresh_colors_for_visible_range(for_buffer, window, cx);
24692    }
24693
24694    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
24695        if self.ignore_lsp_data() {
24696            return;
24697        }
24698        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
24699            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
24700        }
24701    }
24702
24703    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
24704        if self.ignore_lsp_data() {
24705            return;
24706        }
24707
24708        if !self.registered_buffers.contains_key(&buffer_id)
24709            && let Some(project) = self.project.as_ref()
24710        {
24711            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
24712                project.update(cx, |project, cx| {
24713                    self.registered_buffers.insert(
24714                        buffer_id,
24715                        project.register_buffer_with_language_servers(&buffer, cx),
24716                    );
24717                });
24718            } else {
24719                self.registered_buffers.remove(&buffer_id);
24720            }
24721        }
24722    }
24723
24724    fn ignore_lsp_data(&self) -> bool {
24725        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
24726        // skip any LSP updates for it.
24727        self.active_diagnostics == ActiveDiagnostic::All || !self.mode().is_full()
24728    }
24729
24730    fn create_style(&self, cx: &App) -> EditorStyle {
24731        let settings = ThemeSettings::get_global(cx);
24732
24733        let mut text_style = match self.mode {
24734            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
24735                color: cx.theme().colors().editor_foreground,
24736                font_family: settings.ui_font.family.clone(),
24737                font_features: settings.ui_font.features.clone(),
24738                font_fallbacks: settings.ui_font.fallbacks.clone(),
24739                font_size: rems(0.875).into(),
24740                font_weight: settings.ui_font.weight,
24741                line_height: relative(settings.buffer_line_height.value()),
24742                ..Default::default()
24743            },
24744            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
24745                color: cx.theme().colors().editor_foreground,
24746                font_family: settings.buffer_font.family.clone(),
24747                font_features: settings.buffer_font.features.clone(),
24748                font_fallbacks: settings.buffer_font.fallbacks.clone(),
24749                font_size: settings.buffer_font_size(cx).into(),
24750                font_weight: settings.buffer_font.weight,
24751                line_height: relative(settings.buffer_line_height.value()),
24752                ..Default::default()
24753            },
24754        };
24755        if let Some(text_style_refinement) = &self.text_style_refinement {
24756            text_style.refine(text_style_refinement)
24757        }
24758
24759        let background = match self.mode {
24760            EditorMode::SingleLine => cx.theme().system().transparent,
24761            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
24762            EditorMode::Full { .. } => cx.theme().colors().editor_background,
24763            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
24764        };
24765
24766        EditorStyle {
24767            background,
24768            border: cx.theme().colors().border,
24769            local_player: cx.theme().players().local(),
24770            text: text_style,
24771            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
24772            syntax: cx.theme().syntax().clone(),
24773            status: cx.theme().status().clone(),
24774            inlay_hints_style: make_inlay_hints_style(cx),
24775            edit_prediction_styles: make_suggestion_styles(cx),
24776            unnecessary_code_fade: settings.unnecessary_code_fade,
24777            show_underlines: self.diagnostics_enabled(),
24778        }
24779    }
24780    fn breadcrumbs_inner(&self, variant: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
24781        let cursor = self.selections.newest_anchor().head();
24782        let multibuffer = self.buffer().read(cx);
24783        let is_singleton = multibuffer.is_singleton();
24784        let (buffer_id, symbols) = multibuffer
24785            .read(cx)
24786            .symbols_containing(cursor, Some(variant.syntax()))?;
24787        let buffer = multibuffer.buffer(buffer_id)?;
24788
24789        let buffer = buffer.read(cx);
24790        let settings = ThemeSettings::get_global(cx);
24791        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
24792        let mut breadcrumbs = if is_singleton {
24793            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
24794                buffer
24795                    .snapshot()
24796                    .resolve_file_path(
24797                        self.project
24798                            .as_ref()
24799                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
24800                            .unwrap_or_default(),
24801                        cx,
24802                    )
24803                    .unwrap_or_else(|| {
24804                        if multibuffer.is_singleton() {
24805                            multibuffer.title(cx).to_string()
24806                        } else {
24807                            "untitled".to_string()
24808                        }
24809                    })
24810            });
24811            vec![BreadcrumbText {
24812                text,
24813                highlights: None,
24814                font: Some(settings.buffer_font.clone()),
24815            }]
24816        } else {
24817            vec![]
24818        };
24819
24820        breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText {
24821            text: symbol.text,
24822            highlights: Some(symbol.highlight_ranges),
24823            font: Some(settings.buffer_font.clone()),
24824        }));
24825        Some(breadcrumbs)
24826    }
24827}
24828
24829fn edit_for_markdown_paste<'a>(
24830    buffer: &MultiBufferSnapshot,
24831    range: Range<MultiBufferOffset>,
24832    to_insert: &'a str,
24833    url: Option<url::Url>,
24834) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
24835    if url.is_none() {
24836        return (range, Cow::Borrowed(to_insert));
24837    };
24838
24839    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
24840
24841    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
24842        Cow::Borrowed(to_insert)
24843    } else {
24844        Cow::Owned(format!("[{old_text}]({to_insert})"))
24845    };
24846    (range, new_text)
24847}
24848
24849fn process_completion_for_edit(
24850    completion: &Completion,
24851    intent: CompletionIntent,
24852    buffer: &Entity<Buffer>,
24853    cursor_position: &text::Anchor,
24854    cx: &mut Context<Editor>,
24855) -> CompletionEdit {
24856    let buffer = buffer.read(cx);
24857    let buffer_snapshot = buffer.snapshot();
24858    let (snippet, new_text) = if completion.is_snippet() {
24859        let mut snippet_source = completion.new_text.clone();
24860        // Workaround for typescript language server issues so that methods don't expand within
24861        // strings and functions with type expressions. The previous point is used because the query
24862        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
24863        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
24864        let previous_point = if previous_point.column > 0 {
24865            cursor_position.to_previous_offset(&buffer_snapshot)
24866        } else {
24867            cursor_position.to_offset(&buffer_snapshot)
24868        };
24869        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
24870            && scope.prefers_label_for_snippet_in_completion()
24871            && let Some(label) = completion.label()
24872            && matches!(
24873                completion.kind(),
24874                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
24875            )
24876        {
24877            snippet_source = label;
24878        }
24879        match Snippet::parse(&snippet_source).log_err() {
24880            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
24881            None => (None, completion.new_text.clone()),
24882        }
24883    } else {
24884        (None, completion.new_text.clone())
24885    };
24886
24887    let mut range_to_replace = {
24888        let replace_range = &completion.replace_range;
24889        if let CompletionSource::Lsp {
24890            insert_range: Some(insert_range),
24891            ..
24892        } = &completion.source
24893        {
24894            debug_assert_eq!(
24895                insert_range.start, replace_range.start,
24896                "insert_range and replace_range should start at the same position"
24897            );
24898            debug_assert!(
24899                insert_range
24900                    .start
24901                    .cmp(cursor_position, &buffer_snapshot)
24902                    .is_le(),
24903                "insert_range should start before or at cursor position"
24904            );
24905            debug_assert!(
24906                replace_range
24907                    .start
24908                    .cmp(cursor_position, &buffer_snapshot)
24909                    .is_le(),
24910                "replace_range should start before or at cursor position"
24911            );
24912
24913            let should_replace = match intent {
24914                CompletionIntent::CompleteWithInsert => false,
24915                CompletionIntent::CompleteWithReplace => true,
24916                CompletionIntent::Complete | CompletionIntent::Compose => {
24917                    let insert_mode =
24918                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
24919                            .completions
24920                            .lsp_insert_mode;
24921                    match insert_mode {
24922                        LspInsertMode::Insert => false,
24923                        LspInsertMode::Replace => true,
24924                        LspInsertMode::ReplaceSubsequence => {
24925                            let mut text_to_replace = buffer.chars_for_range(
24926                                buffer.anchor_before(replace_range.start)
24927                                    ..buffer.anchor_after(replace_range.end),
24928                            );
24929                            let mut current_needle = text_to_replace.next();
24930                            for haystack_ch in completion.label.text.chars() {
24931                                if let Some(needle_ch) = current_needle
24932                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
24933                                {
24934                                    current_needle = text_to_replace.next();
24935                                }
24936                            }
24937                            current_needle.is_none()
24938                        }
24939                        LspInsertMode::ReplaceSuffix => {
24940                            if replace_range
24941                                .end
24942                                .cmp(cursor_position, &buffer_snapshot)
24943                                .is_gt()
24944                            {
24945                                let range_after_cursor = *cursor_position..replace_range.end;
24946                                let text_after_cursor = buffer
24947                                    .text_for_range(
24948                                        buffer.anchor_before(range_after_cursor.start)
24949                                            ..buffer.anchor_after(range_after_cursor.end),
24950                                    )
24951                                    .collect::<String>()
24952                                    .to_ascii_lowercase();
24953                                completion
24954                                    .label
24955                                    .text
24956                                    .to_ascii_lowercase()
24957                                    .ends_with(&text_after_cursor)
24958                            } else {
24959                                true
24960                            }
24961                        }
24962                    }
24963                }
24964            };
24965
24966            if should_replace {
24967                replace_range.clone()
24968            } else {
24969                insert_range.clone()
24970            }
24971        } else {
24972            replace_range.clone()
24973        }
24974    };
24975
24976    if range_to_replace
24977        .end
24978        .cmp(cursor_position, &buffer_snapshot)
24979        .is_lt()
24980    {
24981        range_to_replace.end = *cursor_position;
24982    }
24983
24984    let replace_range = range_to_replace.to_offset(buffer);
24985    CompletionEdit {
24986        new_text,
24987        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
24988        snippet,
24989    }
24990}
24991
24992struct CompletionEdit {
24993    new_text: String,
24994    replace_range: Range<BufferOffset>,
24995    snippet: Option<Snippet>,
24996}
24997
24998fn comment_delimiter_for_newline(
24999    start_point: &Point,
25000    buffer: &MultiBufferSnapshot,
25001    language: &LanguageScope,
25002) -> Option<Arc<str>> {
25003    let delimiters = language.line_comment_prefixes();
25004    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25005    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25006
25007    let num_of_whitespaces = snapshot
25008        .chars_for_range(range.clone())
25009        .take_while(|c| c.is_whitespace())
25010        .count();
25011    let comment_candidate = snapshot
25012        .chars_for_range(range.clone())
25013        .skip(num_of_whitespaces)
25014        .take(max_len_of_delimiter)
25015        .collect::<String>();
25016    let (delimiter, trimmed_len) = delimiters
25017        .iter()
25018        .filter_map(|delimiter| {
25019            let prefix = delimiter.trim_end();
25020            if comment_candidate.starts_with(prefix) {
25021                Some((delimiter, prefix.len()))
25022            } else {
25023                None
25024            }
25025        })
25026        .max_by_key(|(_, len)| *len)?;
25027
25028    if let Some(BlockCommentConfig {
25029        start: block_start, ..
25030    }) = language.block_comment()
25031    {
25032        let block_start_trimmed = block_start.trim_end();
25033        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25034            let line_content = snapshot
25035                .chars_for_range(range)
25036                .skip(num_of_whitespaces)
25037                .take(block_start_trimmed.len())
25038                .collect::<String>();
25039
25040            if line_content.starts_with(block_start_trimmed) {
25041                return None;
25042            }
25043        }
25044    }
25045
25046    let cursor_is_placed_after_comment_marker =
25047        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25048    if cursor_is_placed_after_comment_marker {
25049        Some(delimiter.clone())
25050    } else {
25051        None
25052    }
25053}
25054
25055fn documentation_delimiter_for_newline(
25056    start_point: &Point,
25057    buffer: &MultiBufferSnapshot,
25058    language: &LanguageScope,
25059    newline_config: &mut NewlineConfig,
25060) -> Option<Arc<str>> {
25061    let BlockCommentConfig {
25062        start: start_tag,
25063        end: end_tag,
25064        prefix: delimiter,
25065        tab_size: len,
25066    } = language.documentation_comment()?;
25067    let is_within_block_comment = buffer
25068        .language_scope_at(*start_point)
25069        .is_some_and(|scope| scope.override_name() == Some("comment"));
25070    if !is_within_block_comment {
25071        return None;
25072    }
25073
25074    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25075
25076    let num_of_whitespaces = snapshot
25077        .chars_for_range(range.clone())
25078        .take_while(|c| c.is_whitespace())
25079        .count();
25080
25081    // 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.
25082    let column = start_point.column;
25083    let cursor_is_after_start_tag = {
25084        let start_tag_len = start_tag.len();
25085        let start_tag_line = snapshot
25086            .chars_for_range(range.clone())
25087            .skip(num_of_whitespaces)
25088            .take(start_tag_len)
25089            .collect::<String>();
25090        if start_tag_line.starts_with(start_tag.as_ref()) {
25091            num_of_whitespaces + start_tag_len <= column as usize
25092        } else {
25093            false
25094        }
25095    };
25096
25097    let cursor_is_after_delimiter = {
25098        let delimiter_trim = delimiter.trim_end();
25099        let delimiter_line = snapshot
25100            .chars_for_range(range.clone())
25101            .skip(num_of_whitespaces)
25102            .take(delimiter_trim.len())
25103            .collect::<String>();
25104        if delimiter_line.starts_with(delimiter_trim) {
25105            num_of_whitespaces + delimiter_trim.len() <= column as usize
25106        } else {
25107            false
25108        }
25109    };
25110
25111    let mut needs_extra_line = false;
25112    let mut extra_line_additional_indent = IndentSize::spaces(0);
25113
25114    let cursor_is_before_end_tag_if_exists = {
25115        let mut char_position = 0u32;
25116        let mut end_tag_offset = None;
25117
25118        'outer: for chunk in snapshot.text_for_range(range) {
25119            if let Some(byte_pos) = chunk.find(&**end_tag) {
25120                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25121                end_tag_offset = Some(char_position + chars_before_match);
25122                break 'outer;
25123            }
25124            char_position += chunk.chars().count() as u32;
25125        }
25126
25127        if let Some(end_tag_offset) = end_tag_offset {
25128            let cursor_is_before_end_tag = column <= end_tag_offset;
25129            if cursor_is_after_start_tag {
25130                if cursor_is_before_end_tag {
25131                    needs_extra_line = true;
25132                }
25133                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
25134                if cursor_is_at_start_of_end_tag {
25135                    extra_line_additional_indent.len = *len;
25136                }
25137            }
25138            cursor_is_before_end_tag
25139        } else {
25140            true
25141        }
25142    };
25143
25144    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
25145        && cursor_is_before_end_tag_if_exists
25146    {
25147        let additional_indent = if cursor_is_after_start_tag {
25148            IndentSize::spaces(*len)
25149        } else {
25150            IndentSize::spaces(0)
25151        };
25152
25153        *newline_config = NewlineConfig::Newline {
25154            additional_indent,
25155            extra_line_additional_indent: if needs_extra_line {
25156                Some(extra_line_additional_indent)
25157            } else {
25158                None
25159            },
25160            prevent_auto_indent: true,
25161        };
25162        Some(delimiter.clone())
25163    } else {
25164        None
25165    }
25166}
25167
25168const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
25169
25170fn list_delimiter_for_newline(
25171    start_point: &Point,
25172    buffer: &MultiBufferSnapshot,
25173    language: &LanguageScope,
25174    newline_config: &mut NewlineConfig,
25175) -> Option<Arc<str>> {
25176    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25177
25178    let num_of_whitespaces = snapshot
25179        .chars_for_range(range.clone())
25180        .take_while(|c| c.is_whitespace())
25181        .count();
25182
25183    let task_list_entries: Vec<_> = language
25184        .task_list()
25185        .into_iter()
25186        .flat_map(|config| {
25187            config
25188                .prefixes
25189                .iter()
25190                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
25191        })
25192        .collect();
25193    let unordered_list_entries: Vec<_> = language
25194        .unordered_list()
25195        .iter()
25196        .map(|marker| (marker.as_ref(), marker.as_ref()))
25197        .collect();
25198
25199    let all_entries: Vec<_> = task_list_entries
25200        .into_iter()
25201        .chain(unordered_list_entries)
25202        .collect();
25203
25204    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
25205        let candidate: String = snapshot
25206            .chars_for_range(range.clone())
25207            .skip(num_of_whitespaces)
25208            .take(max_prefix_len)
25209            .collect();
25210
25211        if let Some((prefix, continuation)) = all_entries
25212            .iter()
25213            .filter(|(prefix, _)| candidate.starts_with(*prefix))
25214            .max_by_key(|(prefix, _)| prefix.len())
25215        {
25216            let end_of_prefix = num_of_whitespaces + prefix.len();
25217            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25218            let has_content_after_marker = snapshot
25219                .chars_for_range(range)
25220                .skip(end_of_prefix)
25221                .any(|c| !c.is_whitespace());
25222
25223            if has_content_after_marker && cursor_is_after_prefix {
25224                return Some((*continuation).into());
25225            }
25226
25227            if start_point.column as usize == end_of_prefix {
25228                if num_of_whitespaces == 0 {
25229                    *newline_config = NewlineConfig::ClearCurrentLine;
25230                } else {
25231                    *newline_config = NewlineConfig::UnindentCurrentLine {
25232                        continuation: (*continuation).into(),
25233                    };
25234                }
25235            }
25236
25237            return None;
25238        }
25239    }
25240
25241    let candidate: String = snapshot
25242        .chars_for_range(range.clone())
25243        .skip(num_of_whitespaces)
25244        .take(ORDERED_LIST_MAX_MARKER_LEN)
25245        .collect();
25246
25247    for ordered_config in language.ordered_list() {
25248        let regex = match Regex::new(&ordered_config.pattern) {
25249            Ok(r) => r,
25250            Err(_) => continue,
25251        };
25252
25253        if let Some(captures) = regex.captures(&candidate) {
25254            let full_match = captures.get(0)?;
25255            let marker_len = full_match.len();
25256            let end_of_prefix = num_of_whitespaces + marker_len;
25257            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25258
25259            let has_content_after_marker = snapshot
25260                .chars_for_range(range)
25261                .skip(end_of_prefix)
25262                .any(|c| !c.is_whitespace());
25263
25264            if has_content_after_marker && cursor_is_after_prefix {
25265                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
25266                let continuation = ordered_config
25267                    .format
25268                    .replace("{1}", &(number + 1).to_string());
25269                return Some(continuation.into());
25270            }
25271
25272            if start_point.column as usize == end_of_prefix {
25273                let continuation = ordered_config.format.replace("{1}", "1");
25274                if num_of_whitespaces == 0 {
25275                    *newline_config = NewlineConfig::ClearCurrentLine;
25276                } else {
25277                    *newline_config = NewlineConfig::UnindentCurrentLine {
25278                        continuation: continuation.into(),
25279                    };
25280                }
25281            }
25282
25283            return None;
25284        }
25285    }
25286
25287    None
25288}
25289
25290fn is_list_prefix_row(
25291    row: MultiBufferRow,
25292    buffer: &MultiBufferSnapshot,
25293    language: &LanguageScope,
25294) -> bool {
25295    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
25296        return false;
25297    };
25298
25299    let num_of_whitespaces = snapshot
25300        .chars_for_range(range.clone())
25301        .take_while(|c| c.is_whitespace())
25302        .count();
25303
25304    let task_list_prefixes: Vec<_> = language
25305        .task_list()
25306        .into_iter()
25307        .flat_map(|config| {
25308            config
25309                .prefixes
25310                .iter()
25311                .map(|p| p.as_ref())
25312                .collect::<Vec<_>>()
25313        })
25314        .collect();
25315    let unordered_list_markers: Vec<_> = language
25316        .unordered_list()
25317        .iter()
25318        .map(|marker| marker.as_ref())
25319        .collect();
25320    let all_prefixes: Vec<_> = task_list_prefixes
25321        .into_iter()
25322        .chain(unordered_list_markers)
25323        .collect();
25324    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
25325        let candidate: String = snapshot
25326            .chars_for_range(range.clone())
25327            .skip(num_of_whitespaces)
25328            .take(max_prefix_len)
25329            .collect();
25330        if all_prefixes
25331            .iter()
25332            .any(|prefix| candidate.starts_with(*prefix))
25333        {
25334            return true;
25335        }
25336    }
25337
25338    let ordered_list_candidate: String = snapshot
25339        .chars_for_range(range)
25340        .skip(num_of_whitespaces)
25341        .take(ORDERED_LIST_MAX_MARKER_LEN)
25342        .collect();
25343    for ordered_config in language.ordered_list() {
25344        let regex = match Regex::new(&ordered_config.pattern) {
25345            Ok(r) => r,
25346            Err(_) => continue,
25347        };
25348        if let Some(captures) = regex.captures(&ordered_list_candidate) {
25349            return captures.get(0).is_some();
25350        }
25351    }
25352
25353    false
25354}
25355
25356#[derive(Debug)]
25357enum NewlineConfig {
25358    /// Insert newline with optional additional indent and optional extra blank line
25359    Newline {
25360        additional_indent: IndentSize,
25361        extra_line_additional_indent: Option<IndentSize>,
25362        prevent_auto_indent: bool,
25363    },
25364    /// Clear the current line
25365    ClearCurrentLine,
25366    /// Unindent the current line and add continuation
25367    UnindentCurrentLine { continuation: Arc<str> },
25368}
25369
25370impl NewlineConfig {
25371    fn has_extra_line(&self) -> bool {
25372        matches!(
25373            self,
25374            Self::Newline {
25375                extra_line_additional_indent: Some(_),
25376                ..
25377            }
25378        )
25379    }
25380
25381    fn insert_extra_newline_brackets(
25382        buffer: &MultiBufferSnapshot,
25383        range: Range<MultiBufferOffset>,
25384        language: &language::LanguageScope,
25385    ) -> bool {
25386        let leading_whitespace_len = buffer
25387            .reversed_chars_at(range.start)
25388            .take_while(|c| c.is_whitespace() && *c != '\n')
25389            .map(|c| c.len_utf8())
25390            .sum::<usize>();
25391        let trailing_whitespace_len = buffer
25392            .chars_at(range.end)
25393            .take_while(|c| c.is_whitespace() && *c != '\n')
25394            .map(|c| c.len_utf8())
25395            .sum::<usize>();
25396        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
25397
25398        language.brackets().any(|(pair, enabled)| {
25399            let pair_start = pair.start.trim_end();
25400            let pair_end = pair.end.trim_start();
25401
25402            enabled
25403                && pair.newline
25404                && buffer.contains_str_at(range.end, pair_end)
25405                && buffer.contains_str_at(
25406                    range.start.saturating_sub_usize(pair_start.len()),
25407                    pair_start,
25408                )
25409        })
25410    }
25411
25412    fn insert_extra_newline_tree_sitter(
25413        buffer: &MultiBufferSnapshot,
25414        range: Range<MultiBufferOffset>,
25415    ) -> bool {
25416        let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
25417            [(buffer, range, _)] => (*buffer, range.clone()),
25418            _ => return false,
25419        };
25420        let pair = {
25421            let mut result: Option<BracketMatch<usize>> = None;
25422
25423            for pair in buffer
25424                .all_bracket_ranges(range.start.0..range.end.0)
25425                .filter(move |pair| {
25426                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
25427                })
25428            {
25429                let len = pair.close_range.end - pair.open_range.start;
25430
25431                if let Some(existing) = &result {
25432                    let existing_len = existing.close_range.end - existing.open_range.start;
25433                    if len > existing_len {
25434                        continue;
25435                    }
25436                }
25437
25438                result = Some(pair);
25439            }
25440
25441            result
25442        };
25443        let Some(pair) = pair else {
25444            return false;
25445        };
25446        pair.newline_only
25447            && buffer
25448                .chars_for_range(pair.open_range.end..range.start.0)
25449                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
25450                .all(|c| c.is_whitespace() && c != '\n')
25451    }
25452}
25453
25454fn update_uncommitted_diff_for_buffer(
25455    editor: Entity<Editor>,
25456    project: &Entity<Project>,
25457    buffers: impl IntoIterator<Item = Entity<Buffer>>,
25458    buffer: Entity<MultiBuffer>,
25459    cx: &mut App,
25460) -> Task<()> {
25461    let mut tasks = Vec::new();
25462    project.update(cx, |project, cx| {
25463        for buffer in buffers {
25464            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
25465                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
25466            }
25467        }
25468    });
25469    cx.spawn(async move |cx| {
25470        let diffs = future::join_all(tasks).await;
25471        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
25472            return;
25473        }
25474
25475        buffer.update(cx, |buffer, cx| {
25476            for diff in diffs.into_iter().flatten() {
25477                buffer.add_diff(diff, cx);
25478            }
25479        });
25480    })
25481}
25482
25483fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
25484    let tab_size = tab_size.get() as usize;
25485    let mut width = offset;
25486
25487    for ch in text.chars() {
25488        width += if ch == '\t' {
25489            tab_size - (width % tab_size)
25490        } else {
25491            1
25492        };
25493    }
25494
25495    width - offset
25496}
25497
25498#[cfg(test)]
25499mod tests {
25500    use super::*;
25501
25502    #[test]
25503    fn test_string_size_with_expanded_tabs() {
25504        let nz = |val| NonZeroU32::new(val).unwrap();
25505        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
25506        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
25507        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
25508        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
25509        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
25510        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
25511        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
25512        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
25513    }
25514}
25515
25516/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
25517struct WordBreakingTokenizer<'a> {
25518    input: &'a str,
25519}
25520
25521impl<'a> WordBreakingTokenizer<'a> {
25522    fn new(input: &'a str) -> Self {
25523        Self { input }
25524    }
25525}
25526
25527fn is_char_ideographic(ch: char) -> bool {
25528    use unicode_script::Script::*;
25529    use unicode_script::UnicodeScript;
25530    matches!(ch.script(), Han | Tangut | Yi)
25531}
25532
25533fn is_grapheme_ideographic(text: &str) -> bool {
25534    text.chars().any(is_char_ideographic)
25535}
25536
25537fn is_grapheme_whitespace(text: &str) -> bool {
25538    text.chars().any(|x| x.is_whitespace())
25539}
25540
25541fn should_stay_with_preceding_ideograph(text: &str) -> bool {
25542    text.chars()
25543        .next()
25544        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
25545}
25546
25547#[derive(PartialEq, Eq, Debug, Clone, Copy)]
25548enum WordBreakToken<'a> {
25549    Word { token: &'a str, grapheme_len: usize },
25550    InlineWhitespace { token: &'a str, grapheme_len: usize },
25551    Newline,
25552}
25553
25554impl<'a> Iterator for WordBreakingTokenizer<'a> {
25555    /// Yields a span, the count of graphemes in the token, and whether it was
25556    /// whitespace. Note that it also breaks at word boundaries.
25557    type Item = WordBreakToken<'a>;
25558
25559    fn next(&mut self) -> Option<Self::Item> {
25560        use unicode_segmentation::UnicodeSegmentation;
25561        if self.input.is_empty() {
25562            return None;
25563        }
25564
25565        let mut iter = self.input.graphemes(true).peekable();
25566        let mut offset = 0;
25567        let mut grapheme_len = 0;
25568        if let Some(first_grapheme) = iter.next() {
25569            let is_newline = first_grapheme == "\n";
25570            let is_whitespace = is_grapheme_whitespace(first_grapheme);
25571            offset += first_grapheme.len();
25572            grapheme_len += 1;
25573            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
25574                if let Some(grapheme) = iter.peek().copied()
25575                    && should_stay_with_preceding_ideograph(grapheme)
25576                {
25577                    offset += grapheme.len();
25578                    grapheme_len += 1;
25579                }
25580            } else {
25581                let mut words = self.input[offset..].split_word_bound_indices().peekable();
25582                let mut next_word_bound = words.peek().copied();
25583                if next_word_bound.is_some_and(|(i, _)| i == 0) {
25584                    next_word_bound = words.next();
25585                }
25586                while let Some(grapheme) = iter.peek().copied() {
25587                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
25588                        break;
25589                    };
25590                    if is_grapheme_whitespace(grapheme) != is_whitespace
25591                        || (grapheme == "\n") != is_newline
25592                    {
25593                        break;
25594                    };
25595                    offset += grapheme.len();
25596                    grapheme_len += 1;
25597                    iter.next();
25598                }
25599            }
25600            let token = &self.input[..offset];
25601            self.input = &self.input[offset..];
25602            if token == "\n" {
25603                Some(WordBreakToken::Newline)
25604            } else if is_whitespace {
25605                Some(WordBreakToken::InlineWhitespace {
25606                    token,
25607                    grapheme_len,
25608                })
25609            } else {
25610                Some(WordBreakToken::Word {
25611                    token,
25612                    grapheme_len,
25613                })
25614            }
25615        } else {
25616            None
25617        }
25618    }
25619}
25620
25621#[test]
25622fn test_word_breaking_tokenizer() {
25623    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
25624        ("", &[]),
25625        ("  ", &[whitespace("  ", 2)]),
25626        ("Ʒ", &[word("Ʒ", 1)]),
25627        ("Ǽ", &[word("Ǽ", 1)]),
25628        ("", &[word("", 1)]),
25629        ("⋑⋑", &[word("⋑⋑", 2)]),
25630        (
25631            "原理,进而",
25632            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
25633        ),
25634        (
25635            "hello world",
25636            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
25637        ),
25638        (
25639            "hello, world",
25640            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
25641        ),
25642        (
25643            "  hello world",
25644            &[
25645                whitespace("  ", 2),
25646                word("hello", 5),
25647                whitespace(" ", 1),
25648                word("world", 5),
25649            ],
25650        ),
25651        (
25652            "这是什么 \n 钢笔",
25653            &[
25654                word("", 1),
25655                word("", 1),
25656                word("", 1),
25657                word("", 1),
25658                whitespace(" ", 1),
25659                newline(),
25660                whitespace(" ", 1),
25661                word("", 1),
25662                word("", 1),
25663            ],
25664        ),
25665        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
25666    ];
25667
25668    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
25669        WordBreakToken::Word {
25670            token,
25671            grapheme_len,
25672        }
25673    }
25674
25675    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
25676        WordBreakToken::InlineWhitespace {
25677            token,
25678            grapheme_len,
25679        }
25680    }
25681
25682    fn newline() -> WordBreakToken<'static> {
25683        WordBreakToken::Newline
25684    }
25685
25686    for (input, result) in tests {
25687        assert_eq!(
25688            WordBreakingTokenizer::new(input)
25689                .collect::<Vec<_>>()
25690                .as_slice(),
25691            *result,
25692        );
25693    }
25694}
25695
25696fn wrap_with_prefix(
25697    first_line_prefix: String,
25698    subsequent_lines_prefix: String,
25699    unwrapped_text: String,
25700    wrap_column: usize,
25701    tab_size: NonZeroU32,
25702    preserve_existing_whitespace: bool,
25703) -> String {
25704    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
25705    let subsequent_lines_prefix_len =
25706        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
25707    let mut wrapped_text = String::new();
25708    let mut current_line = first_line_prefix;
25709    let mut is_first_line = true;
25710
25711    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
25712    let mut current_line_len = first_line_prefix_len;
25713    let mut in_whitespace = false;
25714    for token in tokenizer {
25715        let have_preceding_whitespace = in_whitespace;
25716        match token {
25717            WordBreakToken::Word {
25718                token,
25719                grapheme_len,
25720            } => {
25721                in_whitespace = false;
25722                let current_prefix_len = if is_first_line {
25723                    first_line_prefix_len
25724                } else {
25725                    subsequent_lines_prefix_len
25726                };
25727                if current_line_len + grapheme_len > wrap_column
25728                    && current_line_len != current_prefix_len
25729                {
25730                    wrapped_text.push_str(current_line.trim_end());
25731                    wrapped_text.push('\n');
25732                    is_first_line = false;
25733                    current_line = subsequent_lines_prefix.clone();
25734                    current_line_len = subsequent_lines_prefix_len;
25735                }
25736                current_line.push_str(token);
25737                current_line_len += grapheme_len;
25738            }
25739            WordBreakToken::InlineWhitespace {
25740                mut token,
25741                mut grapheme_len,
25742            } => {
25743                in_whitespace = true;
25744                if have_preceding_whitespace && !preserve_existing_whitespace {
25745                    continue;
25746                }
25747                if !preserve_existing_whitespace {
25748                    // Keep a single whitespace grapheme as-is
25749                    if let Some(first) =
25750                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
25751                    {
25752                        token = first;
25753                    } else {
25754                        token = " ";
25755                    }
25756                    grapheme_len = 1;
25757                }
25758                let current_prefix_len = if is_first_line {
25759                    first_line_prefix_len
25760                } else {
25761                    subsequent_lines_prefix_len
25762                };
25763                if current_line_len + grapheme_len > wrap_column {
25764                    wrapped_text.push_str(current_line.trim_end());
25765                    wrapped_text.push('\n');
25766                    is_first_line = false;
25767                    current_line = subsequent_lines_prefix.clone();
25768                    current_line_len = subsequent_lines_prefix_len;
25769                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
25770                    current_line.push_str(token);
25771                    current_line_len += grapheme_len;
25772                }
25773            }
25774            WordBreakToken::Newline => {
25775                in_whitespace = true;
25776                let current_prefix_len = if is_first_line {
25777                    first_line_prefix_len
25778                } else {
25779                    subsequent_lines_prefix_len
25780                };
25781                if preserve_existing_whitespace {
25782                    wrapped_text.push_str(current_line.trim_end());
25783                    wrapped_text.push('\n');
25784                    is_first_line = false;
25785                    current_line = subsequent_lines_prefix.clone();
25786                    current_line_len = subsequent_lines_prefix_len;
25787                } else if have_preceding_whitespace {
25788                    continue;
25789                } else if current_line_len + 1 > wrap_column
25790                    && current_line_len != current_prefix_len
25791                {
25792                    wrapped_text.push_str(current_line.trim_end());
25793                    wrapped_text.push('\n');
25794                    is_first_line = false;
25795                    current_line = subsequent_lines_prefix.clone();
25796                    current_line_len = subsequent_lines_prefix_len;
25797                } else if current_line_len != current_prefix_len {
25798                    current_line.push(' ');
25799                    current_line_len += 1;
25800                }
25801            }
25802        }
25803    }
25804
25805    if !current_line.is_empty() {
25806        wrapped_text.push_str(&current_line);
25807    }
25808    wrapped_text
25809}
25810
25811#[test]
25812fn test_wrap_with_prefix() {
25813    assert_eq!(
25814        wrap_with_prefix(
25815            "# ".to_string(),
25816            "# ".to_string(),
25817            "abcdefg".to_string(),
25818            4,
25819            NonZeroU32::new(4).unwrap(),
25820            false,
25821        ),
25822        "# abcdefg"
25823    );
25824    assert_eq!(
25825        wrap_with_prefix(
25826            "".to_string(),
25827            "".to_string(),
25828            "\thello world".to_string(),
25829            8,
25830            NonZeroU32::new(4).unwrap(),
25831            false,
25832        ),
25833        "hello\nworld"
25834    );
25835    assert_eq!(
25836        wrap_with_prefix(
25837            "// ".to_string(),
25838            "// ".to_string(),
25839            "xx \nyy zz aa bb cc".to_string(),
25840            12,
25841            NonZeroU32::new(4).unwrap(),
25842            false,
25843        ),
25844        "// xx yy zz\n// aa bb cc"
25845    );
25846    assert_eq!(
25847        wrap_with_prefix(
25848            String::new(),
25849            String::new(),
25850            "这是什么 \n 钢笔".to_string(),
25851            3,
25852            NonZeroU32::new(4).unwrap(),
25853            false,
25854        ),
25855        "这是什\n么 钢\n"
25856    );
25857    assert_eq!(
25858        wrap_with_prefix(
25859            String::new(),
25860            String::new(),
25861            format!("foo{}bar", '\u{2009}'), // thin space
25862            80,
25863            NonZeroU32::new(4).unwrap(),
25864            false,
25865        ),
25866        format!("foo{}bar", '\u{2009}')
25867    );
25868}
25869
25870pub trait CollaborationHub {
25871    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
25872    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
25873    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
25874}
25875
25876impl CollaborationHub for Entity<Project> {
25877    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
25878        self.read(cx).collaborators()
25879    }
25880
25881    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
25882        self.read(cx).user_store().read(cx).participant_indices()
25883    }
25884
25885    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
25886        let this = self.read(cx);
25887        let user_ids = this.collaborators().values().map(|c| c.user_id);
25888        this.user_store().read(cx).participant_names(user_ids, cx)
25889    }
25890}
25891
25892pub trait SemanticsProvider {
25893    fn hover(
25894        &self,
25895        buffer: &Entity<Buffer>,
25896        position: text::Anchor,
25897        cx: &mut App,
25898    ) -> Option<Task<Option<Vec<project::Hover>>>>;
25899
25900    fn inline_values(
25901        &self,
25902        buffer_handle: Entity<Buffer>,
25903        range: Range<text::Anchor>,
25904        cx: &mut App,
25905    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
25906
25907    fn applicable_inlay_chunks(
25908        &self,
25909        buffer: &Entity<Buffer>,
25910        ranges: &[Range<text::Anchor>],
25911        cx: &mut App,
25912    ) -> Vec<Range<BufferRow>>;
25913
25914    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
25915
25916    fn inlay_hints(
25917        &self,
25918        invalidate: InvalidationStrategy,
25919        buffer: Entity<Buffer>,
25920        ranges: Vec<Range<text::Anchor>>,
25921        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
25922        cx: &mut App,
25923    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
25924
25925    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
25926
25927    fn document_highlights(
25928        &self,
25929        buffer: &Entity<Buffer>,
25930        position: text::Anchor,
25931        cx: &mut App,
25932    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
25933
25934    fn definitions(
25935        &self,
25936        buffer: &Entity<Buffer>,
25937        position: text::Anchor,
25938        kind: GotoDefinitionKind,
25939        cx: &mut App,
25940    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
25941
25942    fn range_for_rename(
25943        &self,
25944        buffer: &Entity<Buffer>,
25945        position: text::Anchor,
25946        cx: &mut App,
25947    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
25948
25949    fn perform_rename(
25950        &self,
25951        buffer: &Entity<Buffer>,
25952        position: text::Anchor,
25953        new_name: String,
25954        cx: &mut App,
25955    ) -> Option<Task<Result<ProjectTransaction>>>;
25956}
25957
25958pub trait CompletionProvider {
25959    fn completions(
25960        &self,
25961        excerpt_id: ExcerptId,
25962        buffer: &Entity<Buffer>,
25963        buffer_position: text::Anchor,
25964        trigger: CompletionContext,
25965        window: &mut Window,
25966        cx: &mut Context<Editor>,
25967    ) -> Task<Result<Vec<CompletionResponse>>>;
25968
25969    fn resolve_completions(
25970        &self,
25971        _buffer: Entity<Buffer>,
25972        _completion_indices: Vec<usize>,
25973        _completions: Rc<RefCell<Box<[Completion]>>>,
25974        _cx: &mut Context<Editor>,
25975    ) -> Task<Result<bool>> {
25976        Task::ready(Ok(false))
25977    }
25978
25979    fn apply_additional_edits_for_completion(
25980        &self,
25981        _buffer: Entity<Buffer>,
25982        _completions: Rc<RefCell<Box<[Completion]>>>,
25983        _completion_index: usize,
25984        _push_to_history: bool,
25985        _cx: &mut Context<Editor>,
25986    ) -> Task<Result<Option<language::Transaction>>> {
25987        Task::ready(Ok(None))
25988    }
25989
25990    fn is_completion_trigger(
25991        &self,
25992        buffer: &Entity<Buffer>,
25993        position: language::Anchor,
25994        text: &str,
25995        trigger_in_words: bool,
25996        cx: &mut Context<Editor>,
25997    ) -> bool;
25998
25999    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26000
26001    fn sort_completions(&self) -> bool {
26002        true
26003    }
26004
26005    fn filter_completions(&self) -> bool {
26006        true
26007    }
26008
26009    fn show_snippets(&self) -> bool {
26010        false
26011    }
26012}
26013
26014pub trait CodeActionProvider {
26015    fn id(&self) -> Arc<str>;
26016
26017    fn code_actions(
26018        &self,
26019        buffer: &Entity<Buffer>,
26020        range: Range<text::Anchor>,
26021        window: &mut Window,
26022        cx: &mut App,
26023    ) -> Task<Result<Vec<CodeAction>>>;
26024
26025    fn apply_code_action(
26026        &self,
26027        buffer_handle: Entity<Buffer>,
26028        action: CodeAction,
26029        excerpt_id: ExcerptId,
26030        push_to_history: bool,
26031        window: &mut Window,
26032        cx: &mut App,
26033    ) -> Task<Result<ProjectTransaction>>;
26034}
26035
26036impl CodeActionProvider for Entity<Project> {
26037    fn id(&self) -> Arc<str> {
26038        "project".into()
26039    }
26040
26041    fn code_actions(
26042        &self,
26043        buffer: &Entity<Buffer>,
26044        range: Range<text::Anchor>,
26045        _window: &mut Window,
26046        cx: &mut App,
26047    ) -> Task<Result<Vec<CodeAction>>> {
26048        self.update(cx, |project, cx| {
26049            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26050            let code_actions = project.code_actions(buffer, range, None, cx);
26051            cx.background_spawn(async move {
26052                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26053                Ok(code_lens_actions
26054                    .context("code lens fetch")?
26055                    .into_iter()
26056                    .flatten()
26057                    .chain(
26058                        code_actions
26059                            .context("code action fetch")?
26060                            .into_iter()
26061                            .flatten(),
26062                    )
26063                    .collect())
26064            })
26065        })
26066    }
26067
26068    fn apply_code_action(
26069        &self,
26070        buffer_handle: Entity<Buffer>,
26071        action: CodeAction,
26072        _excerpt_id: ExcerptId,
26073        push_to_history: bool,
26074        _window: &mut Window,
26075        cx: &mut App,
26076    ) -> Task<Result<ProjectTransaction>> {
26077        self.update(cx, |project, cx| {
26078            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26079        })
26080    }
26081}
26082
26083fn snippet_completions(
26084    project: &Project,
26085    buffer: &Entity<Buffer>,
26086    buffer_anchor: text::Anchor,
26087    classifier: CharClassifier,
26088    cx: &mut App,
26089) -> Task<Result<CompletionResponse>> {
26090    let languages = buffer.read(cx).languages_at(buffer_anchor);
26091    let snippet_store = project.snippets().read(cx);
26092
26093    let scopes: Vec<_> = languages
26094        .iter()
26095        .filter_map(|language| {
26096            let language_name = language.lsp_id();
26097            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26098
26099            if snippets.is_empty() {
26100                None
26101            } else {
26102                Some((language.default_scope(), snippets))
26103            }
26104        })
26105        .collect();
26106
26107    if scopes.is_empty() {
26108        return Task::ready(Ok(CompletionResponse {
26109            completions: vec![],
26110            display_options: CompletionDisplayOptions::default(),
26111            is_incomplete: false,
26112        }));
26113    }
26114
26115    let snapshot = buffer.read(cx).text_snapshot();
26116    let executor = cx.background_executor().clone();
26117
26118    cx.background_spawn(async move {
26119        let is_word_char = |c| classifier.is_word(c);
26120
26121        let mut is_incomplete = false;
26122        let mut completions: Vec<Completion> = Vec::new();
26123
26124        const MAX_PREFIX_LEN: usize = 128;
26125        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
26126        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
26127        let window_start = snapshot.clip_offset(window_start, Bias::Left);
26128
26129        let max_buffer_window: String = snapshot
26130            .text_for_range(window_start..buffer_offset)
26131            .collect();
26132
26133        if max_buffer_window.is_empty() {
26134            return Ok(CompletionResponse {
26135                completions: vec![],
26136                display_options: CompletionDisplayOptions::default(),
26137                is_incomplete: true,
26138            });
26139        }
26140
26141        for (_scope, snippets) in scopes.into_iter() {
26142            // Sort snippets by word count to match longer snippet prefixes first.
26143            let mut sorted_snippet_candidates = snippets
26144                .iter()
26145                .enumerate()
26146                .flat_map(|(snippet_ix, snippet)| {
26147                    snippet
26148                        .prefix
26149                        .iter()
26150                        .enumerate()
26151                        .map(move |(prefix_ix, prefix)| {
26152                            let word_count =
26153                                snippet_candidate_suffixes(prefix, is_word_char).count();
26154                            ((snippet_ix, prefix_ix), prefix, word_count)
26155                        })
26156                })
26157                .collect_vec();
26158            sorted_snippet_candidates
26159                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
26160
26161            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
26162
26163            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, is_word_char)
26164                .take(
26165                    sorted_snippet_candidates
26166                        .first()
26167                        .map(|(_, _, word_count)| *word_count)
26168                        .unwrap_or_default(),
26169                )
26170                .collect_vec();
26171
26172            const MAX_RESULTS: usize = 100;
26173            // Each match also remembers how many characters from the buffer it consumed
26174            let mut matches: Vec<(StringMatch, usize)> = vec![];
26175
26176            let mut snippet_list_cutoff_index = 0;
26177            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
26178                let word_count = buffer_index + 1;
26179                // Increase `snippet_list_cutoff_index` until we have all of the
26180                // snippets with sufficiently many words.
26181                while sorted_snippet_candidates
26182                    .get(snippet_list_cutoff_index)
26183                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
26184                        *snippet_word_count >= word_count
26185                    })
26186                {
26187                    snippet_list_cutoff_index += 1;
26188                }
26189
26190                // Take only the candidates with at least `word_count` many words
26191                let snippet_candidates_at_word_len =
26192                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
26193
26194                let candidates = snippet_candidates_at_word_len
26195                    .iter()
26196                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
26197                    .enumerate() // index in `sorted_snippet_candidates`
26198                    // First char must match
26199                    .filter(|(_ix, prefix)| {
26200                        itertools::equal(
26201                            prefix
26202                                .chars()
26203                                .next()
26204                                .into_iter()
26205                                .flat_map(|c| c.to_lowercase()),
26206                            buffer_window
26207                                .chars()
26208                                .next()
26209                                .into_iter()
26210                                .flat_map(|c| c.to_lowercase()),
26211                        )
26212                    })
26213                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
26214                    .collect::<Vec<StringMatchCandidate>>();
26215
26216                matches.extend(
26217                    fuzzy::match_strings(
26218                        &candidates,
26219                        &buffer_window,
26220                        buffer_window.chars().any(|c| c.is_uppercase()),
26221                        true,
26222                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
26223                        &Default::default(),
26224                        executor.clone(),
26225                    )
26226                    .await
26227                    .into_iter()
26228                    .map(|string_match| (string_match, buffer_window.len())),
26229                );
26230
26231                if matches.len() >= MAX_RESULTS {
26232                    break;
26233                }
26234            }
26235
26236            let to_lsp = |point: &text::Anchor| {
26237                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
26238                point_to_lsp(end)
26239            };
26240            let lsp_end = to_lsp(&buffer_anchor);
26241
26242            if matches.len() >= MAX_RESULTS {
26243                is_incomplete = true;
26244            }
26245
26246            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
26247                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
26248                    sorted_snippet_candidates[string_match.candidate_id];
26249                let snippet = &snippets[snippet_index];
26250                let start = buffer_offset - buffer_window_len;
26251                let start = snapshot.anchor_before(start);
26252                let range = start..buffer_anchor;
26253                let lsp_start = to_lsp(&start);
26254                let lsp_range = lsp::Range {
26255                    start: lsp_start,
26256                    end: lsp_end,
26257                };
26258                Completion {
26259                    replace_range: range,
26260                    new_text: snippet.body.clone(),
26261                    source: CompletionSource::Lsp {
26262                        insert_range: None,
26263                        server_id: LanguageServerId(usize::MAX),
26264                        resolved: true,
26265                        lsp_completion: Box::new(lsp::CompletionItem {
26266                            label: snippet.prefix.first().unwrap().clone(),
26267                            kind: Some(CompletionItemKind::SNIPPET),
26268                            label_details: snippet.description.as_ref().map(|description| {
26269                                lsp::CompletionItemLabelDetails {
26270                                    detail: Some(description.clone()),
26271                                    description: None,
26272                                }
26273                            }),
26274                            insert_text_format: Some(InsertTextFormat::SNIPPET),
26275                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
26276                                lsp::InsertReplaceEdit {
26277                                    new_text: snippet.body.clone(),
26278                                    insert: lsp_range,
26279                                    replace: lsp_range,
26280                                },
26281                            )),
26282                            filter_text: Some(snippet.body.clone()),
26283                            sort_text: Some(char::MAX.to_string()),
26284                            ..lsp::CompletionItem::default()
26285                        }),
26286                        lsp_defaults: None,
26287                    },
26288                    label: CodeLabel {
26289                        text: matching_prefix.clone(),
26290                        runs: Vec::new(),
26291                        filter_range: 0..matching_prefix.len(),
26292                    },
26293                    icon_path: None,
26294                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
26295                        single_line: snippet.name.clone().into(),
26296                        plain_text: snippet
26297                            .description
26298                            .clone()
26299                            .map(|description| description.into()),
26300                    }),
26301                    insert_text_mode: None,
26302                    confirm: None,
26303                    match_start: Some(start),
26304                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
26305                }
26306            }));
26307        }
26308
26309        Ok(CompletionResponse {
26310            completions,
26311            display_options: CompletionDisplayOptions::default(),
26312            is_incomplete,
26313        })
26314    })
26315}
26316
26317impl CompletionProvider for Entity<Project> {
26318    fn completions(
26319        &self,
26320        _excerpt_id: ExcerptId,
26321        buffer: &Entity<Buffer>,
26322        buffer_position: text::Anchor,
26323        options: CompletionContext,
26324        _window: &mut Window,
26325        cx: &mut Context<Editor>,
26326    ) -> Task<Result<Vec<CompletionResponse>>> {
26327        self.update(cx, |project, cx| {
26328            let task = project.completions(buffer, buffer_position, options, cx);
26329            cx.background_spawn(task)
26330        })
26331    }
26332
26333    fn resolve_completions(
26334        &self,
26335        buffer: Entity<Buffer>,
26336        completion_indices: Vec<usize>,
26337        completions: Rc<RefCell<Box<[Completion]>>>,
26338        cx: &mut Context<Editor>,
26339    ) -> Task<Result<bool>> {
26340        self.update(cx, |project, cx| {
26341            project.lsp_store().update(cx, |lsp_store, cx| {
26342                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
26343            })
26344        })
26345    }
26346
26347    fn apply_additional_edits_for_completion(
26348        &self,
26349        buffer: Entity<Buffer>,
26350        completions: Rc<RefCell<Box<[Completion]>>>,
26351        completion_index: usize,
26352        push_to_history: bool,
26353        cx: &mut Context<Editor>,
26354    ) -> Task<Result<Option<language::Transaction>>> {
26355        self.update(cx, |project, cx| {
26356            project.lsp_store().update(cx, |lsp_store, cx| {
26357                lsp_store.apply_additional_edits_for_completion(
26358                    buffer,
26359                    completions,
26360                    completion_index,
26361                    push_to_history,
26362                    cx,
26363                )
26364            })
26365        })
26366    }
26367
26368    fn is_completion_trigger(
26369        &self,
26370        buffer: &Entity<Buffer>,
26371        position: language::Anchor,
26372        text: &str,
26373        trigger_in_words: bool,
26374        cx: &mut Context<Editor>,
26375    ) -> bool {
26376        let mut chars = text.chars();
26377        let char = if let Some(char) = chars.next() {
26378            char
26379        } else {
26380            return false;
26381        };
26382        if chars.next().is_some() {
26383            return false;
26384        }
26385
26386        let buffer = buffer.read(cx);
26387        let snapshot = buffer.snapshot();
26388        let classifier = snapshot
26389            .char_classifier_at(position)
26390            .scope_context(Some(CharScopeContext::Completion));
26391        if trigger_in_words && classifier.is_word(char) {
26392            return true;
26393        }
26394
26395        buffer.completion_triggers().contains(text)
26396    }
26397
26398    fn show_snippets(&self) -> bool {
26399        true
26400    }
26401}
26402
26403impl SemanticsProvider for Entity<Project> {
26404    fn hover(
26405        &self,
26406        buffer: &Entity<Buffer>,
26407        position: text::Anchor,
26408        cx: &mut App,
26409    ) -> Option<Task<Option<Vec<project::Hover>>>> {
26410        Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
26411    }
26412
26413    fn document_highlights(
26414        &self,
26415        buffer: &Entity<Buffer>,
26416        position: text::Anchor,
26417        cx: &mut App,
26418    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
26419        Some(self.update(cx, |project, cx| {
26420            project.document_highlights(buffer, position, cx)
26421        }))
26422    }
26423
26424    fn definitions(
26425        &self,
26426        buffer: &Entity<Buffer>,
26427        position: text::Anchor,
26428        kind: GotoDefinitionKind,
26429        cx: &mut App,
26430    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
26431        Some(self.update(cx, |project, cx| match kind {
26432            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
26433            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
26434            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
26435            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
26436        }))
26437    }
26438
26439    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
26440        self.update(cx, |project, cx| {
26441            if project
26442                .active_debug_session(cx)
26443                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
26444            {
26445                return true;
26446            }
26447
26448            buffer.update(cx, |buffer, cx| {
26449                project.any_language_server_supports_inlay_hints(buffer, cx)
26450            })
26451        })
26452    }
26453
26454    fn inline_values(
26455        &self,
26456        buffer_handle: Entity<Buffer>,
26457        range: Range<text::Anchor>,
26458        cx: &mut App,
26459    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
26460        self.update(cx, |project, cx| {
26461            let (session, active_stack_frame) = project.active_debug_session(cx)?;
26462
26463            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
26464        })
26465    }
26466
26467    fn applicable_inlay_chunks(
26468        &self,
26469        buffer: &Entity<Buffer>,
26470        ranges: &[Range<text::Anchor>],
26471        cx: &mut App,
26472    ) -> Vec<Range<BufferRow>> {
26473        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
26474            lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
26475        })
26476    }
26477
26478    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
26479        self.read(cx).lsp_store().update(cx, |lsp_store, _| {
26480            lsp_store.invalidate_inlay_hints(for_buffers)
26481        });
26482    }
26483
26484    fn inlay_hints(
26485        &self,
26486        invalidate: InvalidationStrategy,
26487        buffer: Entity<Buffer>,
26488        ranges: Vec<Range<text::Anchor>>,
26489        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26490        cx: &mut App,
26491    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
26492        Some(self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
26493            lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
26494        }))
26495    }
26496
26497    fn range_for_rename(
26498        &self,
26499        buffer: &Entity<Buffer>,
26500        position: text::Anchor,
26501        cx: &mut App,
26502    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
26503        Some(self.update(cx, |project, cx| {
26504            let buffer = buffer.clone();
26505            let task = project.prepare_rename(buffer.clone(), position, cx);
26506            cx.spawn(async move |_, cx| {
26507                Ok(match task.await? {
26508                    PrepareRenameResponse::Success(range) => Some(range),
26509                    PrepareRenameResponse::InvalidPosition => None,
26510                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
26511                        // Fallback on using TreeSitter info to determine identifier range
26512                        buffer.read_with(cx, |buffer, _| {
26513                            let snapshot = buffer.snapshot();
26514                            let (range, kind) = snapshot.surrounding_word(position, None);
26515                            if kind != Some(CharKind::Word) {
26516                                return None;
26517                            }
26518                            Some(
26519                                snapshot.anchor_before(range.start)
26520                                    ..snapshot.anchor_after(range.end),
26521                            )
26522                        })
26523                    }
26524                })
26525            })
26526        }))
26527    }
26528
26529    fn perform_rename(
26530        &self,
26531        buffer: &Entity<Buffer>,
26532        position: text::Anchor,
26533        new_name: String,
26534        cx: &mut App,
26535    ) -> Option<Task<Result<ProjectTransaction>>> {
26536        Some(self.update(cx, |project, cx| {
26537            project.perform_rename(buffer.clone(), position, new_name, cx)
26538        }))
26539    }
26540}
26541
26542fn consume_contiguous_rows(
26543    contiguous_row_selections: &mut Vec<Selection<Point>>,
26544    selection: &Selection<Point>,
26545    display_map: &DisplaySnapshot,
26546    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
26547) -> (MultiBufferRow, MultiBufferRow) {
26548    contiguous_row_selections.push(selection.clone());
26549    let start_row = starting_row(selection, display_map);
26550    let mut end_row = ending_row(selection, display_map);
26551
26552    while let Some(next_selection) = selections.peek() {
26553        if next_selection.start.row <= end_row.0 {
26554            end_row = ending_row(next_selection, display_map);
26555            contiguous_row_selections.push(selections.next().unwrap().clone());
26556        } else {
26557            break;
26558        }
26559    }
26560    (start_row, end_row)
26561}
26562
26563fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
26564    if selection.start.column > 0 {
26565        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
26566    } else {
26567        MultiBufferRow(selection.start.row)
26568    }
26569}
26570
26571fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
26572    if next_selection.end.column > 0 || next_selection.is_empty() {
26573        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
26574    } else {
26575        MultiBufferRow(next_selection.end.row)
26576    }
26577}
26578
26579impl EditorSnapshot {
26580    pub fn remote_selections_in_range<'a>(
26581        &'a self,
26582        range: &'a Range<Anchor>,
26583        collaboration_hub: &dyn CollaborationHub,
26584        cx: &'a App,
26585    ) -> impl 'a + Iterator<Item = RemoteSelection> {
26586        let participant_names = collaboration_hub.user_names(cx);
26587        let participant_indices = collaboration_hub.user_participant_indices(cx);
26588        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
26589        let collaborators_by_replica_id = collaborators_by_peer_id
26590            .values()
26591            .map(|collaborator| (collaborator.replica_id, collaborator))
26592            .collect::<HashMap<_, _>>();
26593        self.buffer_snapshot()
26594            .selections_in_range(range, false)
26595            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
26596                if replica_id == ReplicaId::AGENT {
26597                    Some(RemoteSelection {
26598                        replica_id,
26599                        selection,
26600                        cursor_shape,
26601                        line_mode,
26602                        collaborator_id: CollaboratorId::Agent,
26603                        user_name: Some("Agent".into()),
26604                        color: cx.theme().players().agent(),
26605                    })
26606                } else {
26607                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
26608                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
26609                    let user_name = participant_names.get(&collaborator.user_id).cloned();
26610                    Some(RemoteSelection {
26611                        replica_id,
26612                        selection,
26613                        cursor_shape,
26614                        line_mode,
26615                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
26616                        user_name,
26617                        color: if let Some(index) = participant_index {
26618                            cx.theme().players().color_for_participant(index.0)
26619                        } else {
26620                            cx.theme().players().absent()
26621                        },
26622                    })
26623                }
26624            })
26625    }
26626
26627    pub fn hunks_for_ranges(
26628        &self,
26629        ranges: impl IntoIterator<Item = Range<Point>>,
26630    ) -> Vec<MultiBufferDiffHunk> {
26631        let mut hunks = Vec::new();
26632        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
26633            HashMap::default();
26634        for query_range in ranges {
26635            let query_rows =
26636                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
26637            for hunk in self.buffer_snapshot().diff_hunks_in_range(
26638                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
26639            ) {
26640                // Include deleted hunks that are adjacent to the query range, because
26641                // otherwise they would be missed.
26642                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
26643                if hunk.status().is_deleted() {
26644                    intersects_range |= hunk.row_range.start == query_rows.end;
26645                    intersects_range |= hunk.row_range.end == query_rows.start;
26646                }
26647                if intersects_range {
26648                    if !processed_buffer_rows
26649                        .entry(hunk.buffer_id)
26650                        .or_default()
26651                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
26652                    {
26653                        continue;
26654                    }
26655                    hunks.push(hunk);
26656                }
26657            }
26658        }
26659
26660        hunks
26661    }
26662
26663    fn display_diff_hunks_for_rows<'a>(
26664        &'a self,
26665        display_rows: Range<DisplayRow>,
26666        folded_buffers: &'a HashSet<BufferId>,
26667    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
26668        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
26669        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
26670
26671        self.buffer_snapshot()
26672            .diff_hunks_in_range(buffer_start..buffer_end)
26673            .filter_map(|hunk| {
26674                if folded_buffers.contains(&hunk.buffer_id) {
26675                    return None;
26676                }
26677
26678                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
26679                let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
26680
26681                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
26682                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
26683
26684                let display_hunk = if hunk_display_start.column() != 0 {
26685                    DisplayDiffHunk::Folded {
26686                        display_row: hunk_display_start.row(),
26687                    }
26688                } else {
26689                    let mut end_row = hunk_display_end.row();
26690                    if hunk_display_end.column() > 0 {
26691                        end_row.0 += 1;
26692                    }
26693                    let is_created_file = hunk.is_created_file();
26694
26695                    DisplayDiffHunk::Unfolded {
26696                        status: hunk.status(),
26697                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
26698                            ..hunk.diff_base_byte_range.end.0,
26699                        word_diffs: hunk.word_diffs,
26700                        display_row_range: hunk_display_start.row()..end_row,
26701                        multi_buffer_range: Anchor::range_in_buffer(
26702                            hunk.excerpt_id,
26703                            hunk.buffer_range,
26704                        ),
26705                        is_created_file,
26706                    }
26707                };
26708
26709                Some(display_hunk)
26710            })
26711    }
26712
26713    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
26714        self.display_snapshot
26715            .buffer_snapshot()
26716            .language_at(position)
26717    }
26718
26719    pub fn is_focused(&self) -> bool {
26720        self.is_focused
26721    }
26722
26723    pub fn placeholder_text(&self) -> Option<String> {
26724        self.placeholder_display_snapshot
26725            .as_ref()
26726            .map(|display_map| display_map.text())
26727    }
26728
26729    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
26730        self.scroll_anchor.scroll_position(&self.display_snapshot)
26731    }
26732
26733    pub fn gutter_dimensions(
26734        &self,
26735        font_id: FontId,
26736        font_size: Pixels,
26737        style: &EditorStyle,
26738        window: &mut Window,
26739        cx: &App,
26740    ) -> GutterDimensions {
26741        if self.show_gutter
26742            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
26743            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
26744        {
26745            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
26746                matches!(
26747                    ProjectSettings::get_global(cx).git.git_gutter,
26748                    GitGutterSetting::TrackedFiles
26749                )
26750            });
26751            let gutter_settings = EditorSettings::get_global(cx).gutter;
26752            let show_line_numbers = self
26753                .show_line_numbers
26754                .unwrap_or(gutter_settings.line_numbers);
26755            let line_gutter_width = if show_line_numbers {
26756                // Avoid flicker-like gutter resizes when the line number gains another digit by
26757                // only resizing the gutter on files with > 10**min_line_number_digits lines.
26758                let min_width_for_number_on_gutter =
26759                    ch_advance * gutter_settings.min_line_number_digits as f32;
26760                self.max_line_number_width(style, window)
26761                    .max(min_width_for_number_on_gutter)
26762            } else {
26763                0.0.into()
26764            };
26765
26766            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
26767            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
26768
26769            let git_blame_entries_width =
26770                self.git_blame_gutter_max_author_length
26771                    .map(|max_author_length| {
26772                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
26773                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
26774
26775                        /// The number of characters to dedicate to gaps and margins.
26776                        const SPACING_WIDTH: usize = 4;
26777
26778                        let max_char_count = max_author_length.min(renderer.max_author_length())
26779                            + ::git::SHORT_SHA_LENGTH
26780                            + MAX_RELATIVE_TIMESTAMP.len()
26781                            + SPACING_WIDTH;
26782
26783                        ch_advance * max_char_count
26784                    });
26785
26786            let is_singleton = self.buffer_snapshot().is_singleton();
26787
26788            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
26789            left_padding += if !is_singleton {
26790                ch_width * 4.0
26791            } else if show_runnables || show_breakpoints {
26792                ch_width * 3.0
26793            } else if show_git_gutter && show_line_numbers {
26794                ch_width * 2.0
26795            } else if show_git_gutter || show_line_numbers {
26796                ch_width
26797            } else {
26798                px(0.)
26799            };
26800
26801            let shows_folds = is_singleton && gutter_settings.folds;
26802
26803            let right_padding = if shows_folds && show_line_numbers {
26804                ch_width * 4.0
26805            } else if shows_folds || (!is_singleton && show_line_numbers) {
26806                ch_width * 3.0
26807            } else if show_line_numbers {
26808                ch_width
26809            } else {
26810                px(0.)
26811            };
26812
26813            GutterDimensions {
26814                left_padding,
26815                right_padding,
26816                width: line_gutter_width + left_padding + right_padding,
26817                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
26818                git_blame_entries_width,
26819            }
26820        } else if self.offset_content {
26821            GutterDimensions::default_with_margin(font_id, font_size, cx)
26822        } else {
26823            GutterDimensions::default()
26824        }
26825    }
26826
26827    pub fn render_crease_toggle(
26828        &self,
26829        buffer_row: MultiBufferRow,
26830        row_contains_cursor: bool,
26831        editor: Entity<Editor>,
26832        window: &mut Window,
26833        cx: &mut App,
26834    ) -> Option<AnyElement> {
26835        let folded = self.is_line_folded(buffer_row);
26836        let mut is_foldable = false;
26837
26838        if let Some(crease) = self
26839            .crease_snapshot
26840            .query_row(buffer_row, self.buffer_snapshot())
26841        {
26842            is_foldable = true;
26843            match crease {
26844                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
26845                    if let Some(render_toggle) = render_toggle {
26846                        let toggle_callback =
26847                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
26848                                if folded {
26849                                    editor.update(cx, |editor, cx| {
26850                                        editor.fold_at(buffer_row, window, cx)
26851                                    });
26852                                } else {
26853                                    editor.update(cx, |editor, cx| {
26854                                        editor.unfold_at(buffer_row, window, cx)
26855                                    });
26856                                }
26857                            });
26858                        return Some((render_toggle)(
26859                            buffer_row,
26860                            folded,
26861                            toggle_callback,
26862                            window,
26863                            cx,
26864                        ));
26865                    }
26866                }
26867            }
26868        }
26869
26870        is_foldable |= self.starts_indent(buffer_row);
26871
26872        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
26873            Some(
26874                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
26875                    .toggle_state(folded)
26876                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
26877                        if folded {
26878                            this.unfold_at(buffer_row, window, cx);
26879                        } else {
26880                            this.fold_at(buffer_row, window, cx);
26881                        }
26882                    }))
26883                    .into_any_element(),
26884            )
26885        } else {
26886            None
26887        }
26888    }
26889
26890    pub fn render_crease_trailer(
26891        &self,
26892        buffer_row: MultiBufferRow,
26893        window: &mut Window,
26894        cx: &mut App,
26895    ) -> Option<AnyElement> {
26896        let folded = self.is_line_folded(buffer_row);
26897        if let Crease::Inline { render_trailer, .. } = self
26898            .crease_snapshot
26899            .query_row(buffer_row, self.buffer_snapshot())?
26900        {
26901            let render_trailer = render_trailer.as_ref()?;
26902            Some(render_trailer(buffer_row, folded, window, cx))
26903        } else {
26904            None
26905        }
26906    }
26907
26908    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
26909        let digit_count = self.widest_line_number().ilog10() + 1;
26910        column_pixels(style, digit_count as usize, window)
26911    }
26912
26913    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
26914    ///
26915    /// This is positive if `base` is before `line`.
26916    fn relative_line_delta(
26917        &self,
26918        current_selection_head: DisplayRow,
26919        first_visible_row: DisplayRow,
26920        consider_wrapped_lines: bool,
26921    ) -> i64 {
26922        let current_selection_head = current_selection_head.as_display_point().to_point(self);
26923        let first_visible_row = first_visible_row.as_display_point().to_point(self);
26924
26925        if consider_wrapped_lines {
26926            let wrap_snapshot = self.wrap_snapshot();
26927            let base_wrap_row = wrap_snapshot
26928                .make_wrap_point(current_selection_head, Bias::Left)
26929                .row();
26930            let wrap_row = wrap_snapshot
26931                .make_wrap_point(first_visible_row, Bias::Left)
26932                .row();
26933            wrap_row.0 as i64 - base_wrap_row.0 as i64
26934        } else {
26935            let folds = if current_selection_head < first_visible_row {
26936                self.folds_in_range(current_selection_head..first_visible_row)
26937            } else {
26938                self.folds_in_range(first_visible_row..current_selection_head)
26939            };
26940
26941            let folded_lines = folds
26942                .map(|fold| {
26943                    let range = fold.range.0.to_point(self);
26944                    range.end.row.saturating_sub(range.start.row)
26945                })
26946                .sum::<u32>() as i64;
26947
26948            first_visible_row.row as i64 - current_selection_head.row as i64 + folded_lines
26949        }
26950    }
26951
26952    /// Returns the unsigned relative line number to display for each row in `rows`.
26953    ///
26954    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
26955    pub fn calculate_relative_line_numbers(
26956        &self,
26957        rows: &Range<DisplayRow>,
26958        current_selection_head: DisplayRow,
26959        count_wrapped_lines: bool,
26960    ) -> HashMap<DisplayRow, u32> {
26961        let initial_offset =
26962            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
26963        let current_selection_point = current_selection_head.as_display_point().to_point(self);
26964
26965        self.row_infos(rows.start)
26966            .take(rows.len())
26967            .enumerate()
26968            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
26969            .filter(|(_row, row_info)| {
26970                row_info.buffer_row.is_some()
26971                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
26972            })
26973            .enumerate()
26974            .filter(|(_, (row, row_info))| {
26975                // We want to check here that
26976                // - the row is not the current selection head to ensure the current
26977                // line has absolute numbering
26978                // - similarly, should the selection head live in a soft-wrapped line
26979                // and we are not counting those, that the parent line keeps its
26980                // absolute number
26981                // - lastly, if we are in a deleted line, it is fine to number this
26982                // relative with 0, as otherwise it would have no line number at all
26983                (*row != current_selection_head
26984                    && (count_wrapped_lines
26985                        || row_info.buffer_row != Some(current_selection_point.row)))
26986                    || row_info
26987                        .diff_status
26988                        .is_some_and(|status| status.is_deleted())
26989            })
26990            .map(|(i, (row, _))| (row, (initial_offset + i as i64).unsigned_abs() as u32))
26991            .collect()
26992    }
26993}
26994
26995pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
26996    let font_size = style.text.font_size.to_pixels(window.rem_size());
26997    let layout = window.text_system().shape_line(
26998        SharedString::from(" ".repeat(column)),
26999        font_size,
27000        &[TextRun {
27001            len: column,
27002            font: style.text.font(),
27003            color: Hsla::default(),
27004            ..Default::default()
27005        }],
27006        None,
27007    );
27008
27009    layout.width
27010}
27011
27012impl Deref for EditorSnapshot {
27013    type Target = DisplaySnapshot;
27014
27015    fn deref(&self) -> &Self::Target {
27016        &self.display_snapshot
27017    }
27018}
27019
27020#[derive(Clone, Debug, PartialEq, Eq)]
27021pub enum EditorEvent {
27022    /// Emitted when the stored review comments change (added, removed, or updated).
27023    ReviewCommentsChanged {
27024        /// The new total count of review comments.
27025        total_count: usize,
27026    },
27027    InputIgnored {
27028        text: Arc<str>,
27029    },
27030    InputHandled {
27031        utf16_range_to_replace: Option<Range<isize>>,
27032        text: Arc<str>,
27033    },
27034    ExcerptsAdded {
27035        buffer: Entity<Buffer>,
27036        predecessor: ExcerptId,
27037        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27038    },
27039    ExcerptsRemoved {
27040        ids: Vec<ExcerptId>,
27041        removed_buffer_ids: Vec<BufferId>,
27042    },
27043    BufferFoldToggled {
27044        ids: Vec<ExcerptId>,
27045        folded: bool,
27046    },
27047    ExcerptsEdited {
27048        ids: Vec<ExcerptId>,
27049    },
27050    ExcerptsExpanded {
27051        ids: Vec<ExcerptId>,
27052    },
27053    ExpandExcerptsRequested {
27054        excerpt_ids: Vec<ExcerptId>,
27055        lines: u32,
27056        direction: ExpandExcerptDirection,
27057    },
27058    BufferEdited,
27059    Edited {
27060        transaction_id: clock::Lamport,
27061    },
27062    Reparsed(BufferId),
27063    Focused,
27064    FocusedIn,
27065    Blurred,
27066    DirtyChanged,
27067    Saved,
27068    TitleChanged,
27069    SelectionsChanged {
27070        local: bool,
27071    },
27072    ScrollPositionChanged {
27073        local: bool,
27074        autoscroll: bool,
27075    },
27076    TransactionUndone {
27077        transaction_id: clock::Lamport,
27078    },
27079    TransactionBegun {
27080        transaction_id: clock::Lamport,
27081    },
27082    CursorShapeChanged,
27083    BreadcrumbsChanged,
27084    PushedToNavHistory {
27085        anchor: Anchor,
27086        is_deactivate: bool,
27087    },
27088}
27089
27090impl EventEmitter<EditorEvent> for Editor {}
27091
27092impl Focusable for Editor {
27093    fn focus_handle(&self, _cx: &App) -> FocusHandle {
27094        self.focus_handle.clone()
27095    }
27096}
27097
27098impl Render for Editor {
27099    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27100        EditorElement::new(&cx.entity(), self.create_style(cx))
27101    }
27102}
27103
27104impl EntityInputHandler for Editor {
27105    fn text_for_range(
27106        &mut self,
27107        range_utf16: Range<usize>,
27108        adjusted_range: &mut Option<Range<usize>>,
27109        _: &mut Window,
27110        cx: &mut Context<Self>,
27111    ) -> Option<String> {
27112        let snapshot = self.buffer.read(cx).read(cx);
27113        let start = snapshot.clip_offset_utf16(
27114            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
27115            Bias::Left,
27116        );
27117        let end = snapshot.clip_offset_utf16(
27118            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
27119            Bias::Right,
27120        );
27121        if (start.0.0..end.0.0) != range_utf16 {
27122            adjusted_range.replace(start.0.0..end.0.0);
27123        }
27124        Some(snapshot.text_for_range(start..end).collect())
27125    }
27126
27127    fn selected_text_range(
27128        &mut self,
27129        ignore_disabled_input: bool,
27130        _: &mut Window,
27131        cx: &mut Context<Self>,
27132    ) -> Option<UTF16Selection> {
27133        // Prevent the IME menu from appearing when holding down an alphabetic key
27134        // while input is disabled.
27135        if !ignore_disabled_input && !self.input_enabled {
27136            return None;
27137        }
27138
27139        let selection = self
27140            .selections
27141            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
27142        let range = selection.range();
27143
27144        Some(UTF16Selection {
27145            range: range.start.0.0..range.end.0.0,
27146            reversed: selection.reversed,
27147        })
27148    }
27149
27150    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
27151        let snapshot = self.buffer.read(cx).read(cx);
27152        let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
27153        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
27154    }
27155
27156    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
27157        self.clear_highlights::<InputComposition>(cx);
27158        self.ime_transaction.take();
27159    }
27160
27161    fn replace_text_in_range(
27162        &mut self,
27163        range_utf16: Option<Range<usize>>,
27164        text: &str,
27165        window: &mut Window,
27166        cx: &mut Context<Self>,
27167    ) {
27168        if !self.input_enabled {
27169            cx.emit(EditorEvent::InputIgnored { text: text.into() });
27170            return;
27171        }
27172
27173        self.transact(window, cx, |this, window, cx| {
27174            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
27175                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27176                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27177                Some(this.selection_replacement_ranges(range_utf16, cx))
27178            } else {
27179                this.marked_text_ranges(cx)
27180            };
27181
27182            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
27183                let newest_selection_id = this.selections.newest_anchor().id;
27184                this.selections
27185                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27186                    .iter()
27187                    .zip(ranges_to_replace.iter())
27188                    .find_map(|(selection, range)| {
27189                        if selection.id == newest_selection_id {
27190                            Some(
27191                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27192                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27193                            )
27194                        } else {
27195                            None
27196                        }
27197                    })
27198            });
27199
27200            cx.emit(EditorEvent::InputHandled {
27201                utf16_range_to_replace: range_to_replace,
27202                text: text.into(),
27203            });
27204
27205            if let Some(new_selected_ranges) = new_selected_ranges {
27206                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27207                    selections.select_ranges(new_selected_ranges)
27208                });
27209                this.backspace(&Default::default(), window, cx);
27210            }
27211
27212            this.handle_input(text, window, cx);
27213        });
27214
27215        if let Some(transaction) = self.ime_transaction {
27216            self.buffer.update(cx, |buffer, cx| {
27217                buffer.group_until_transaction(transaction, cx);
27218            });
27219        }
27220
27221        self.unmark_text(window, cx);
27222    }
27223
27224    fn replace_and_mark_text_in_range(
27225        &mut self,
27226        range_utf16: Option<Range<usize>>,
27227        text: &str,
27228        new_selected_range_utf16: Option<Range<usize>>,
27229        window: &mut Window,
27230        cx: &mut Context<Self>,
27231    ) {
27232        if !self.input_enabled {
27233            return;
27234        }
27235
27236        let transaction = self.transact(window, cx, |this, window, cx| {
27237            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
27238                let snapshot = this.buffer.read(cx).read(cx);
27239                if let Some(relative_range_utf16) = range_utf16.as_ref() {
27240                    for marked_range in &mut marked_ranges {
27241                        marked_range.end = marked_range.start + relative_range_utf16.end;
27242                        marked_range.start += relative_range_utf16.start;
27243                        marked_range.start =
27244                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
27245                        marked_range.end =
27246                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
27247                    }
27248                }
27249                Some(marked_ranges)
27250            } else if let Some(range_utf16) = range_utf16 {
27251                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27252                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27253                Some(this.selection_replacement_ranges(range_utf16, cx))
27254            } else {
27255                None
27256            };
27257
27258            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
27259                let newest_selection_id = this.selections.newest_anchor().id;
27260                this.selections
27261                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27262                    .iter()
27263                    .zip(ranges_to_replace.iter())
27264                    .find_map(|(selection, range)| {
27265                        if selection.id == newest_selection_id {
27266                            Some(
27267                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27268                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27269                            )
27270                        } else {
27271                            None
27272                        }
27273                    })
27274            });
27275
27276            cx.emit(EditorEvent::InputHandled {
27277                utf16_range_to_replace: range_to_replace,
27278                text: text.into(),
27279            });
27280
27281            if let Some(ranges) = ranges_to_replace {
27282                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
27283                    s.select_ranges(ranges)
27284                });
27285            }
27286
27287            let marked_ranges = {
27288                let snapshot = this.buffer.read(cx).read(cx);
27289                this.selections
27290                    .disjoint_anchors_arc()
27291                    .iter()
27292                    .map(|selection| {
27293                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
27294                    })
27295                    .collect::<Vec<_>>()
27296            };
27297
27298            if text.is_empty() {
27299                this.unmark_text(window, cx);
27300            } else {
27301                this.highlight_text::<InputComposition>(
27302                    marked_ranges.clone(),
27303                    HighlightStyle {
27304                        underline: Some(UnderlineStyle {
27305                            thickness: px(1.),
27306                            color: None,
27307                            wavy: false,
27308                        }),
27309                        ..Default::default()
27310                    },
27311                    cx,
27312                );
27313            }
27314
27315            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
27316            let use_autoclose = this.use_autoclose;
27317            let use_auto_surround = this.use_auto_surround;
27318            this.set_use_autoclose(false);
27319            this.set_use_auto_surround(false);
27320            this.handle_input(text, window, cx);
27321            this.set_use_autoclose(use_autoclose);
27322            this.set_use_auto_surround(use_auto_surround);
27323
27324            if let Some(new_selected_range) = new_selected_range_utf16 {
27325                let snapshot = this.buffer.read(cx).read(cx);
27326                let new_selected_ranges = marked_ranges
27327                    .into_iter()
27328                    .map(|marked_range| {
27329                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
27330                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
27331                            insertion_start.0 + new_selected_range.start,
27332                        ));
27333                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
27334                            insertion_start.0 + new_selected_range.end,
27335                        ));
27336                        snapshot.clip_offset_utf16(new_start, Bias::Left)
27337                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
27338                    })
27339                    .collect::<Vec<_>>();
27340
27341                drop(snapshot);
27342                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27343                    selections.select_ranges(new_selected_ranges)
27344                });
27345            }
27346        });
27347
27348        self.ime_transaction = self.ime_transaction.or(transaction);
27349        if let Some(transaction) = self.ime_transaction {
27350            self.buffer.update(cx, |buffer, cx| {
27351                buffer.group_until_transaction(transaction, cx);
27352            });
27353        }
27354
27355        if self.text_highlights::<InputComposition>(cx).is_none() {
27356            self.ime_transaction.take();
27357        }
27358    }
27359
27360    fn bounds_for_range(
27361        &mut self,
27362        range_utf16: Range<usize>,
27363        element_bounds: gpui::Bounds<Pixels>,
27364        window: &mut Window,
27365        cx: &mut Context<Self>,
27366    ) -> Option<gpui::Bounds<Pixels>> {
27367        let text_layout_details = self.text_layout_details(window);
27368        let CharacterDimensions {
27369            em_width,
27370            em_advance,
27371            line_height,
27372        } = self.character_dimensions(window);
27373
27374        let snapshot = self.snapshot(window, cx);
27375        let scroll_position = snapshot.scroll_position();
27376        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
27377
27378        let start =
27379            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
27380        let x = Pixels::from(
27381            ScrollOffset::from(
27382                snapshot.x_for_display_point(start, &text_layout_details)
27383                    + self.gutter_dimensions.full_width(),
27384            ) - scroll_left,
27385        );
27386        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
27387
27388        Some(Bounds {
27389            origin: element_bounds.origin + point(x, y),
27390            size: size(em_width, line_height),
27391        })
27392    }
27393
27394    fn character_index_for_point(
27395        &mut self,
27396        point: gpui::Point<Pixels>,
27397        _window: &mut Window,
27398        _cx: &mut Context<Self>,
27399    ) -> Option<usize> {
27400        let position_map = self.last_position_map.as_ref()?;
27401        if !position_map.text_hitbox.contains(&point) {
27402            return None;
27403        }
27404        let display_point = position_map.point_for_position(point).previous_valid;
27405        let anchor = position_map
27406            .snapshot
27407            .display_point_to_anchor(display_point, Bias::Left);
27408        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
27409        Some(utf16_offset.0.0)
27410    }
27411
27412    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
27413        self.input_enabled
27414    }
27415}
27416
27417trait SelectionExt {
27418    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
27419    fn spanned_rows(
27420        &self,
27421        include_end_if_at_line_start: bool,
27422        map: &DisplaySnapshot,
27423    ) -> Range<MultiBufferRow>;
27424}
27425
27426impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
27427    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
27428        let start = self
27429            .start
27430            .to_point(map.buffer_snapshot())
27431            .to_display_point(map);
27432        let end = self
27433            .end
27434            .to_point(map.buffer_snapshot())
27435            .to_display_point(map);
27436        if self.reversed {
27437            end..start
27438        } else {
27439            start..end
27440        }
27441    }
27442
27443    fn spanned_rows(
27444        &self,
27445        include_end_if_at_line_start: bool,
27446        map: &DisplaySnapshot,
27447    ) -> Range<MultiBufferRow> {
27448        let start = self.start.to_point(map.buffer_snapshot());
27449        let mut end = self.end.to_point(map.buffer_snapshot());
27450        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
27451            end.row -= 1;
27452        }
27453
27454        let buffer_start = map.prev_line_boundary(start).0;
27455        let buffer_end = map.next_line_boundary(end).0;
27456        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
27457    }
27458}
27459
27460impl<T: InvalidationRegion> InvalidationStack<T> {
27461    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
27462    where
27463        S: Clone + ToOffset,
27464    {
27465        while let Some(region) = self.last() {
27466            let all_selections_inside_invalidation_ranges =
27467                if selections.len() == region.ranges().len() {
27468                    selections
27469                        .iter()
27470                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
27471                        .all(|(selection, invalidation_range)| {
27472                            let head = selection.head().to_offset(buffer);
27473                            invalidation_range.start <= head && invalidation_range.end >= head
27474                        })
27475                } else {
27476                    false
27477                };
27478
27479            if all_selections_inside_invalidation_ranges {
27480                break;
27481            } else {
27482                self.pop();
27483            }
27484        }
27485    }
27486}
27487
27488impl<T> Default for InvalidationStack<T> {
27489    fn default() -> Self {
27490        Self(Default::default())
27491    }
27492}
27493
27494impl<T> Deref for InvalidationStack<T> {
27495    type Target = Vec<T>;
27496
27497    fn deref(&self) -> &Self::Target {
27498        &self.0
27499    }
27500}
27501
27502impl<T> DerefMut for InvalidationStack<T> {
27503    fn deref_mut(&mut self) -> &mut Self::Target {
27504        &mut self.0
27505    }
27506}
27507
27508impl InvalidationRegion for SnippetState {
27509    fn ranges(&self) -> &[Range<Anchor>] {
27510        &self.ranges[self.active_index]
27511    }
27512}
27513
27514fn edit_prediction_edit_text(
27515    current_snapshot: &BufferSnapshot,
27516    edits: &[(Range<Anchor>, impl AsRef<str>)],
27517    edit_preview: &EditPreview,
27518    include_deletions: bool,
27519    cx: &App,
27520) -> HighlightedText {
27521    let edits = edits
27522        .iter()
27523        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
27524        .collect::<Vec<_>>();
27525
27526    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
27527}
27528
27529fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
27530    // Fallback for providers that don't provide edit_preview (like Copilot/Supermaven)
27531    // Just show the raw edit text with basic styling
27532    let mut text = String::new();
27533    let mut highlights = Vec::new();
27534
27535    let insertion_highlight_style = HighlightStyle {
27536        color: Some(cx.theme().colors().text),
27537        ..Default::default()
27538    };
27539
27540    for (_, edit_text) in edits {
27541        let start_offset = text.len();
27542        text.push_str(edit_text);
27543        let end_offset = text.len();
27544
27545        if start_offset < end_offset {
27546            highlights.push((start_offset..end_offset, insertion_highlight_style));
27547        }
27548    }
27549
27550    HighlightedText {
27551        text: text.into(),
27552        highlights,
27553    }
27554}
27555
27556pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
27557    match severity {
27558        lsp::DiagnosticSeverity::ERROR => colors.error,
27559        lsp::DiagnosticSeverity::WARNING => colors.warning,
27560        lsp::DiagnosticSeverity::INFORMATION => colors.info,
27561        lsp::DiagnosticSeverity::HINT => colors.info,
27562        _ => colors.ignored,
27563    }
27564}
27565
27566pub fn styled_runs_for_code_label<'a>(
27567    label: &'a CodeLabel,
27568    syntax_theme: &'a theme::SyntaxTheme,
27569    local_player: &'a theme::PlayerColor,
27570) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
27571    let fade_out = HighlightStyle {
27572        fade_out: Some(0.35),
27573        ..Default::default()
27574    };
27575
27576    let mut prev_end = label.filter_range.end;
27577    label
27578        .runs
27579        .iter()
27580        .enumerate()
27581        .flat_map(move |(ix, (range, highlight_id))| {
27582            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
27583                HighlightStyle {
27584                    color: Some(local_player.cursor),
27585                    ..Default::default()
27586                }
27587            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
27588                HighlightStyle {
27589                    background_color: Some(local_player.selection),
27590                    ..Default::default()
27591                }
27592            } else if let Some(style) = highlight_id.style(syntax_theme) {
27593                style
27594            } else {
27595                return Default::default();
27596            };
27597            let muted_style = style.highlight(fade_out);
27598
27599            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
27600            if range.start >= label.filter_range.end {
27601                if range.start > prev_end {
27602                    runs.push((prev_end..range.start, fade_out));
27603                }
27604                runs.push((range.clone(), muted_style));
27605            } else if range.end <= label.filter_range.end {
27606                runs.push((range.clone(), style));
27607            } else {
27608                runs.push((range.start..label.filter_range.end, style));
27609                runs.push((label.filter_range.end..range.end, muted_style));
27610            }
27611            prev_end = cmp::max(prev_end, range.end);
27612
27613            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
27614                runs.push((prev_end..label.text.len(), fade_out));
27615            }
27616
27617            runs
27618        })
27619}
27620
27621pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
27622    let mut prev_index = 0;
27623    let mut prev_codepoint: Option<char> = None;
27624    text.char_indices()
27625        .chain([(text.len(), '\0')])
27626        .filter_map(move |(index, codepoint)| {
27627            let prev_codepoint = prev_codepoint.replace(codepoint)?;
27628            let is_boundary = index == text.len()
27629                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
27630                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
27631            if is_boundary {
27632                let chunk = &text[prev_index..index];
27633                prev_index = index;
27634                Some(chunk)
27635            } else {
27636                None
27637            }
27638        })
27639}
27640
27641/// Given a string of text immediately before the cursor, iterates over possible
27642/// strings a snippet could match to. More precisely: returns an iterator over
27643/// suffixes of `text` created by splitting at word boundaries (before & after
27644/// every non-word character).
27645///
27646/// Shorter suffixes are returned first.
27647pub(crate) fn snippet_candidate_suffixes(
27648    text: &str,
27649    is_word_char: impl Fn(char) -> bool,
27650) -> impl std::iter::Iterator<Item = &str> {
27651    let mut prev_index = text.len();
27652    let mut prev_codepoint = None;
27653    text.char_indices()
27654        .rev()
27655        .chain([(0, '\0')])
27656        .filter_map(move |(index, codepoint)| {
27657            let prev_index = std::mem::replace(&mut prev_index, index);
27658            let prev_codepoint = prev_codepoint.replace(codepoint)?;
27659            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
27660                None
27661            } else {
27662                let chunk = &text[prev_index..]; // go to end of string
27663                Some(chunk)
27664            }
27665        })
27666}
27667
27668pub trait RangeToAnchorExt: Sized {
27669    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
27670
27671    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
27672        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
27673        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
27674    }
27675}
27676
27677impl<T: ToOffset> RangeToAnchorExt for Range<T> {
27678    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
27679        let start_offset = self.start.to_offset(snapshot);
27680        let end_offset = self.end.to_offset(snapshot);
27681        if start_offset == end_offset {
27682            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
27683        } else {
27684            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
27685        }
27686    }
27687}
27688
27689pub trait RowExt {
27690    fn as_f64(&self) -> f64;
27691
27692    fn next_row(&self) -> Self;
27693
27694    fn previous_row(&self) -> Self;
27695
27696    fn minus(&self, other: Self) -> u32;
27697}
27698
27699impl RowExt for DisplayRow {
27700    fn as_f64(&self) -> f64 {
27701        self.0 as _
27702    }
27703
27704    fn next_row(&self) -> Self {
27705        Self(self.0 + 1)
27706    }
27707
27708    fn previous_row(&self) -> Self {
27709        Self(self.0.saturating_sub(1))
27710    }
27711
27712    fn minus(&self, other: Self) -> u32 {
27713        self.0 - other.0
27714    }
27715}
27716
27717impl RowExt for MultiBufferRow {
27718    fn as_f64(&self) -> f64 {
27719        self.0 as _
27720    }
27721
27722    fn next_row(&self) -> Self {
27723        Self(self.0 + 1)
27724    }
27725
27726    fn previous_row(&self) -> Self {
27727        Self(self.0.saturating_sub(1))
27728    }
27729
27730    fn minus(&self, other: Self) -> u32 {
27731        self.0 - other.0
27732    }
27733}
27734
27735trait RowRangeExt {
27736    type Row;
27737
27738    fn len(&self) -> usize;
27739
27740    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
27741}
27742
27743impl RowRangeExt for Range<MultiBufferRow> {
27744    type Row = MultiBufferRow;
27745
27746    fn len(&self) -> usize {
27747        (self.end.0 - self.start.0) as usize
27748    }
27749
27750    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
27751        (self.start.0..self.end.0).map(MultiBufferRow)
27752    }
27753}
27754
27755impl RowRangeExt for Range<DisplayRow> {
27756    type Row = DisplayRow;
27757
27758    fn len(&self) -> usize {
27759        (self.end.0 - self.start.0) as usize
27760    }
27761
27762    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
27763        (self.start.0..self.end.0).map(DisplayRow)
27764    }
27765}
27766
27767/// If select range has more than one line, we
27768/// just point the cursor to range.start.
27769fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
27770    if range.start.row == range.end.row {
27771        range
27772    } else {
27773        range.start..range.start
27774    }
27775}
27776pub struct KillRing(ClipboardItem);
27777impl Global for KillRing {}
27778
27779const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
27780
27781enum BreakpointPromptEditAction {
27782    Log,
27783    Condition,
27784    HitCondition,
27785}
27786
27787struct BreakpointPromptEditor {
27788    pub(crate) prompt: Entity<Editor>,
27789    editor: WeakEntity<Editor>,
27790    breakpoint_anchor: Anchor,
27791    breakpoint: Breakpoint,
27792    edit_action: BreakpointPromptEditAction,
27793    block_ids: HashSet<CustomBlockId>,
27794    editor_margins: Arc<Mutex<EditorMargins>>,
27795    _subscriptions: Vec<Subscription>,
27796}
27797
27798impl BreakpointPromptEditor {
27799    const MAX_LINES: u8 = 4;
27800
27801    fn new(
27802        editor: WeakEntity<Editor>,
27803        breakpoint_anchor: Anchor,
27804        breakpoint: Breakpoint,
27805        edit_action: BreakpointPromptEditAction,
27806        window: &mut Window,
27807        cx: &mut Context<Self>,
27808    ) -> Self {
27809        let base_text = match edit_action {
27810            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
27811            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
27812            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
27813        }
27814        .map(|msg| msg.to_string())
27815        .unwrap_or_default();
27816
27817        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
27818        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
27819
27820        let prompt = cx.new(|cx| {
27821            let mut prompt = Editor::new(
27822                EditorMode::AutoHeight {
27823                    min_lines: 1,
27824                    max_lines: Some(Self::MAX_LINES as usize),
27825                },
27826                buffer,
27827                None,
27828                window,
27829                cx,
27830            );
27831            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
27832            prompt.set_show_cursor_when_unfocused(false, cx);
27833            prompt.set_placeholder_text(
27834                match edit_action {
27835                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
27836                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
27837                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
27838                },
27839                window,
27840                cx,
27841            );
27842
27843            prompt
27844        });
27845
27846        Self {
27847            prompt,
27848            editor,
27849            breakpoint_anchor,
27850            breakpoint,
27851            edit_action,
27852            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
27853            block_ids: Default::default(),
27854            _subscriptions: vec![],
27855        }
27856    }
27857
27858    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
27859        self.block_ids.extend(block_ids)
27860    }
27861
27862    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
27863        if let Some(editor) = self.editor.upgrade() {
27864            let message = self
27865                .prompt
27866                .read(cx)
27867                .buffer
27868                .read(cx)
27869                .as_singleton()
27870                .expect("A multi buffer in breakpoint prompt isn't possible")
27871                .read(cx)
27872                .as_rope()
27873                .to_string();
27874
27875            editor.update(cx, |editor, cx| {
27876                editor.edit_breakpoint_at_anchor(
27877                    self.breakpoint_anchor,
27878                    self.breakpoint.clone(),
27879                    match self.edit_action {
27880                        BreakpointPromptEditAction::Log => {
27881                            BreakpointEditAction::EditLogMessage(message.into())
27882                        }
27883                        BreakpointPromptEditAction::Condition => {
27884                            BreakpointEditAction::EditCondition(message.into())
27885                        }
27886                        BreakpointPromptEditAction::HitCondition => {
27887                            BreakpointEditAction::EditHitCondition(message.into())
27888                        }
27889                    },
27890                    cx,
27891                );
27892
27893                editor.remove_blocks(self.block_ids.clone(), None, cx);
27894                cx.focus_self(window);
27895            });
27896        }
27897    }
27898
27899    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
27900        self.editor
27901            .update(cx, |editor, cx| {
27902                editor.remove_blocks(self.block_ids.clone(), None, cx);
27903                window.focus(&editor.focus_handle, cx);
27904            })
27905            .log_err();
27906    }
27907
27908    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
27909        let settings = ThemeSettings::get_global(cx);
27910        let text_style = TextStyle {
27911            color: if self.prompt.read(cx).read_only(cx) {
27912                cx.theme().colors().text_disabled
27913            } else {
27914                cx.theme().colors().text
27915            },
27916            font_family: settings.buffer_font.family.clone(),
27917            font_fallbacks: settings.buffer_font.fallbacks.clone(),
27918            font_size: settings.buffer_font_size(cx).into(),
27919            font_weight: settings.buffer_font.weight,
27920            line_height: relative(settings.buffer_line_height.value()),
27921            ..Default::default()
27922        };
27923        EditorElement::new(
27924            &self.prompt,
27925            EditorStyle {
27926                background: cx.theme().colors().editor_background,
27927                local_player: cx.theme().players().local(),
27928                text: text_style,
27929                ..Default::default()
27930            },
27931        )
27932    }
27933}
27934
27935impl Render for BreakpointPromptEditor {
27936    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27937        let editor_margins = *self.editor_margins.lock();
27938        let gutter_dimensions = editor_margins.gutter;
27939        h_flex()
27940            .key_context("Editor")
27941            .bg(cx.theme().colors().editor_background)
27942            .border_y_1()
27943            .border_color(cx.theme().status().info_border)
27944            .size_full()
27945            .py(window.line_height() / 2.5)
27946            .on_action(cx.listener(Self::confirm))
27947            .on_action(cx.listener(Self::cancel))
27948            .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
27949            .child(div().flex_1().child(self.render_prompt_editor(cx)))
27950    }
27951}
27952
27953impl Focusable for BreakpointPromptEditor {
27954    fn focus_handle(&self, cx: &App) -> FocusHandle {
27955        self.prompt.focus_handle(cx)
27956    }
27957}
27958
27959fn all_edits_insertions_or_deletions(
27960    edits: &Vec<(Range<Anchor>, Arc<str>)>,
27961    snapshot: &MultiBufferSnapshot,
27962) -> bool {
27963    let mut all_insertions = true;
27964    let mut all_deletions = true;
27965
27966    for (range, new_text) in edits.iter() {
27967        let range_is_empty = range.to_offset(snapshot).is_empty();
27968        let text_is_empty = new_text.is_empty();
27969
27970        if range_is_empty != text_is_empty {
27971            if range_is_empty {
27972                all_deletions = false;
27973            } else {
27974                all_insertions = false;
27975            }
27976        } else {
27977            return false;
27978        }
27979
27980        if !all_insertions && !all_deletions {
27981            return false;
27982        }
27983    }
27984    all_insertions || all_deletions
27985}
27986
27987struct MissingEditPredictionKeybindingTooltip;
27988
27989impl Render for MissingEditPredictionKeybindingTooltip {
27990    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27991        ui::tooltip_container(cx, |container, cx| {
27992            container
27993                .flex_shrink_0()
27994                .max_w_80()
27995                .min_h(rems_from_px(124.))
27996                .justify_between()
27997                .child(
27998                    v_flex()
27999                        .flex_1()
28000                        .text_ui_sm(cx)
28001                        .child(Label::new("Conflict with Accept Keybinding"))
28002                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
28003                )
28004                .child(
28005                    h_flex()
28006                        .pb_1()
28007                        .gap_1()
28008                        .items_end()
28009                        .w_full()
28010                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
28011                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
28012                        }))
28013                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
28014                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
28015                        })),
28016                )
28017        })
28018    }
28019}
28020
28021#[derive(Debug, Clone, Copy, PartialEq)]
28022pub struct LineHighlight {
28023    pub background: Background,
28024    pub border: Option<gpui::Hsla>,
28025    pub include_gutter: bool,
28026    pub type_id: Option<TypeId>,
28027}
28028
28029struct LineManipulationResult {
28030    pub new_text: String,
28031    pub line_count_before: usize,
28032    pub line_count_after: usize,
28033}
28034
28035fn render_diff_hunk_controls(
28036    row: u32,
28037    status: &DiffHunkStatus,
28038    hunk_range: Range<Anchor>,
28039    is_created_file: bool,
28040    line_height: Pixels,
28041    editor: &Entity<Editor>,
28042    _window: &mut Window,
28043    cx: &mut App,
28044) -> AnyElement {
28045    h_flex()
28046        .h(line_height)
28047        .mr_1()
28048        .gap_1()
28049        .px_0p5()
28050        .pb_1()
28051        .border_x_1()
28052        .border_b_1()
28053        .border_color(cx.theme().colors().border_variant)
28054        .rounded_b_lg()
28055        .bg(cx.theme().colors().editor_background)
28056        .gap_1()
28057        .block_mouse_except_scroll()
28058        .shadow_md()
28059        .child(if status.has_secondary_hunk() {
28060            Button::new(("stage", row as u64), "Stage")
28061                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28062                .tooltip({
28063                    let focus_handle = editor.focus_handle(cx);
28064                    move |_window, cx| {
28065                        Tooltip::for_action_in(
28066                            "Stage Hunk",
28067                            &::git::ToggleStaged,
28068                            &focus_handle,
28069                            cx,
28070                        )
28071                    }
28072                })
28073                .on_click({
28074                    let editor = editor.clone();
28075                    move |_event, _window, cx| {
28076                        editor.update(cx, |editor, cx| {
28077                            editor.stage_or_unstage_diff_hunks(
28078                                true,
28079                                vec![hunk_range.start..hunk_range.start],
28080                                cx,
28081                            );
28082                        });
28083                    }
28084                })
28085        } else {
28086            Button::new(("unstage", row as u64), "Unstage")
28087                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28088                .tooltip({
28089                    let focus_handle = editor.focus_handle(cx);
28090                    move |_window, cx| {
28091                        Tooltip::for_action_in(
28092                            "Unstage Hunk",
28093                            &::git::ToggleStaged,
28094                            &focus_handle,
28095                            cx,
28096                        )
28097                    }
28098                })
28099                .on_click({
28100                    let editor = editor.clone();
28101                    move |_event, _window, cx| {
28102                        editor.update(cx, |editor, cx| {
28103                            editor.stage_or_unstage_diff_hunks(
28104                                false,
28105                                vec![hunk_range.start..hunk_range.start],
28106                                cx,
28107                            );
28108                        });
28109                    }
28110                })
28111        })
28112        .child(
28113            Button::new(("restore", row as u64), "Restore")
28114                .tooltip({
28115                    let focus_handle = editor.focus_handle(cx);
28116                    move |_window, cx| {
28117                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
28118                    }
28119                })
28120                .on_click({
28121                    let editor = editor.clone();
28122                    move |_event, window, cx| {
28123                        editor.update(cx, |editor, cx| {
28124                            let snapshot = editor.snapshot(window, cx);
28125                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
28126                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
28127                        });
28128                    }
28129                })
28130                .disabled(is_created_file),
28131        )
28132        .when(
28133            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
28134            |el| {
28135                el.child(
28136                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
28137                        .shape(IconButtonShape::Square)
28138                        .icon_size(IconSize::Small)
28139                        // .disabled(!has_multiple_hunks)
28140                        .tooltip({
28141                            let focus_handle = editor.focus_handle(cx);
28142                            move |_window, cx| {
28143                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
28144                            }
28145                        })
28146                        .on_click({
28147                            let editor = editor.clone();
28148                            move |_event, window, cx| {
28149                                editor.update(cx, |editor, cx| {
28150                                    let snapshot = editor.snapshot(window, cx);
28151                                    let position =
28152                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
28153                                    editor.go_to_hunk_before_or_after_position(
28154                                        &snapshot,
28155                                        position,
28156                                        Direction::Next,
28157                                        window,
28158                                        cx,
28159                                    );
28160                                    editor.expand_selected_diff_hunks(cx);
28161                                });
28162                            }
28163                        }),
28164                )
28165                .child(
28166                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
28167                        .shape(IconButtonShape::Square)
28168                        .icon_size(IconSize::Small)
28169                        // .disabled(!has_multiple_hunks)
28170                        .tooltip({
28171                            let focus_handle = editor.focus_handle(cx);
28172                            move |_window, cx| {
28173                                Tooltip::for_action_in(
28174                                    "Previous Hunk",
28175                                    &GoToPreviousHunk,
28176                                    &focus_handle,
28177                                    cx,
28178                                )
28179                            }
28180                        })
28181                        .on_click({
28182                            let editor = editor.clone();
28183                            move |_event, window, cx| {
28184                                editor.update(cx, |editor, cx| {
28185                                    let snapshot = editor.snapshot(window, cx);
28186                                    let point =
28187                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
28188                                    editor.go_to_hunk_before_or_after_position(
28189                                        &snapshot,
28190                                        point,
28191                                        Direction::Prev,
28192                                        window,
28193                                        cx,
28194                                    );
28195                                    editor.expand_selected_diff_hunks(cx);
28196                                });
28197                            }
28198                        }),
28199                )
28200            },
28201        )
28202        .into_any_element()
28203}
28204
28205pub fn multibuffer_context_lines(cx: &App) -> u32 {
28206    EditorSettings::try_get(cx)
28207        .map(|settings| settings.excerpt_context_lines)
28208        .unwrap_or(2)
28209        .min(32)
28210}