editor.rs

    1#![allow(rustdoc::private_intra_doc_links)]
    2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
    3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
    4//! It comes in different flavors: single line, multiline and a fixed height one.
    5//!
    6//! Editor contains of multiple large submodules:
    7//! * [`element`] — the place where all rendering happens
    8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
    9//!   Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
   10//!
   11//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
   12//!
   13//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
   14pub mod actions;
   15pub mod blink_manager;
   16mod bracket_colorization;
   17mod clangd_ext;
   18pub mod code_context_menus;
   19pub mod display_map;
   20mod editor_settings;
   21mod element;
   22mod git;
   23mod highlight_matching_bracket;
   24mod hover_links;
   25pub mod hover_popover;
   26mod indent_guides;
   27mod inlays;
   28pub mod items;
   29mod jsx_tag_auto_close;
   30mod linked_editing_ranges;
   31mod lsp_colors;
   32mod lsp_ext;
   33mod mouse_context_menu;
   34pub mod movement;
   35mod persistence;
   36mod rust_analyzer_ext;
   37pub mod scroll;
   38mod selections_collection;
   39mod split;
   40pub mod split_editor_view;
   41pub mod tasks;
   42
   43#[cfg(test)]
   44mod code_completion_tests;
   45#[cfg(test)]
   46mod edit_prediction_tests;
   47#[cfg(test)]
   48mod editor_tests;
   49mod signature_help;
   50#[cfg(any(test, feature = "test-support"))]
   51pub mod test;
   52
   53pub(crate) use actions::*;
   54pub use display_map::{ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder};
   55pub use edit_prediction_types::Direction;
   56pub use editor_settings::{
   57    CompletionDetailAlignment, CurrentLineHighlight, DocumentColorsRenderMode, EditorSettings,
   58    HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowMinimap,
   59};
   60pub use element::{
   61    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   62    render_breadcrumb_text,
   63};
   64pub use git::blame::BlameRenderer;
   65pub use hover_popover::hover_markdown_style;
   66pub use inlays::Inlay;
   67pub use items::MAX_TAB_TITLE_LEN;
   68pub use lsp::CompletionContext;
   69pub use lsp_ext::lsp_tasks;
   70pub use multi_buffer::{
   71    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   72    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   73    ToPoint,
   74};
   75pub use split::{SplitDiffFeatureFlag, SplittableEditor, ToggleLockedCursors, ToggleSplitDiff};
   76pub use split_editor_view::SplitEditorView;
   77pub use text::Bias;
   78
   79use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   80use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   81use anyhow::{Context as _, Result, anyhow, bail};
   82use blink_manager::BlinkManager;
   83use buffer_diff::DiffHunkStatus;
   84use client::{Collaborator, ParticipantIndex, parse_zed_link};
   85use clock::ReplicaId;
   86use code_context_menus::{
   87    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   88    CompletionsMenu, ContextMenuOrigin,
   89};
   90use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   91use convert_case::{Case, Casing};
   92use dap::TelemetrySpawnLocation;
   93use display_map::*;
   94use edit_prediction_types::{
   95    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionGranularity,
   96    SuggestionDisplayType,
   97};
   98use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
   99use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
  100use futures::{
  101    FutureExt,
  102    future::{self, Shared, join},
  103};
  104use fuzzy::{StringMatch, StringMatchCandidate};
  105use git::blame::{GitBlame, GlobalBlameRenderer};
  106use gpui::{
  107    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  108    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  109    DispatchPhase, Edges, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
  110    Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla, KeyContext, Modifiers,
  111    MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement, Pixels, PressureStage,
  112    Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled, Subscription, Task,
  113    TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  114    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  115    pulsating_between, px, relative, size,
  116};
  117use hover_links::{HoverLink, HoveredLinkState, find_file};
  118use hover_popover::{HoverState, hide_hover};
  119use indent_guides::ActiveIndentGuidesState;
  120use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  121use itertools::{Either, Itertools};
  122use language::{
  123    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  124    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  125    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  126    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, OffsetRangeExt,
  127    OutlineItem, Point, Runnable, Selection, SelectionGoal, TextObject, TransactionId,
  128    TreeSitterOptions, WordsQuery,
  129    language_settings::{
  130        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  131        all_language_settings, language_settings,
  132    },
  133    point_from_lsp, point_to_lsp, text_diff_with_options,
  134};
  135use linked_editing_ranges::refresh_linked_ranges;
  136use lsp::{
  137    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  138    LanguageServerId,
  139};
  140use lsp_colors::LspColorData;
  141use markdown::Markdown;
  142use mouse_context_menu::MouseContextMenu;
  143use movement::TextLayoutDetails;
  144use multi_buffer::{
  145    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  146};
  147use parking_lot::Mutex;
  148use persistence::DB;
  149use project::{
  150    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  151    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  152    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  153    ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
  154    debugger::{
  155        breakpoint_store::{
  156            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  157            BreakpointStore, BreakpointStoreEvent,
  158        },
  159        session::{Session, SessionEvent},
  160    },
  161    git_store::GitStoreEvent,
  162    lsp_store::{
  163        CacheInlayHints, CompletionDocumentation, FormatTrigger, LspFormatTarget,
  164        OpenLspBufferHandle,
  165    },
  166    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  167};
  168use rand::seq::SliceRandom;
  169use regex::Regex;
  170use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  171use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  172use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  173use serde::{Deserialize, Serialize};
  174use settings::{
  175    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  176    update_settings_file,
  177};
  178use smallvec::{SmallVec, smallvec};
  179use snippet::Snippet;
  180use std::{
  181    any::{Any, TypeId},
  182    borrow::Cow,
  183    cell::{OnceCell, RefCell},
  184    cmp::{self, Ordering, Reverse},
  185    collections::hash_map,
  186    iter::{self, Peekable},
  187    mem,
  188    num::NonZeroU32,
  189    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  190    path::{Path, PathBuf},
  191    rc::Rc,
  192    sync::Arc,
  193    time::{Duration, Instant},
  194};
  195use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
  196use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _};
  197use theme::{
  198    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  199    ThemeSettings, observe_buffer_font_size_adjustment,
  200};
  201use ui::{
  202    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  203    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  204};
  205use ui_input::ErasedEditor;
  206use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  207use workspace::{
  208    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  209    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  210    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  211    item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  212    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  213    searchable::{CollapseDirection, SearchEvent},
  214};
  215use zed_actions::editor::{MoveDown, MoveUp};
  216
  217use crate::{
  218    code_context_menus::CompletionsMenuSource,
  219    editor_settings::MultiCursorModifier,
  220    hover_links::{find_url, find_url_from_range},
  221    inlays::{
  222        InlineValueCache,
  223        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  224    },
  225    scroll::{ScrollOffset, ScrollPixelOffset},
  226    selections_collection::resolve_selections_wrapping_blocks,
  227    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  228};
  229
  230pub const FILE_HEADER_HEIGHT: u32 = 2;
  231pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  232const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  233const MAX_LINE_LEN: usize = 1024;
  234const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  235const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  236pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  237#[doc(hidden)]
  238pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  239pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  240
  241pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  242pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  243pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  244pub const FETCH_COLORS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(150);
  245
  246pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  247pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  248pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  249
  250pub type RenderDiffHunkControlsFn = Arc<
  251    dyn Fn(
  252        u32,
  253        &DiffHunkStatus,
  254        Range<Anchor>,
  255        bool,
  256        Pixels,
  257        &Entity<Editor>,
  258        &mut Window,
  259        &mut App,
  260    ) -> AnyElement,
  261>;
  262
  263enum ReportEditorEvent {
  264    Saved { auto_saved: bool },
  265    EditorOpened,
  266    Closed,
  267}
  268
  269impl ReportEditorEvent {
  270    pub fn event_type(&self) -> &'static str {
  271        match self {
  272            Self::Saved { .. } => "Editor Saved",
  273            Self::EditorOpened => "Editor Opened",
  274            Self::Closed => "Editor Closed",
  275        }
  276    }
  277}
  278
  279pub enum ActiveDebugLine {}
  280pub enum DebugStackFrameLine {}
  281enum DocumentHighlightRead {}
  282enum DocumentHighlightWrite {}
  283enum InputComposition {}
  284pub enum PendingInput {}
  285enum SelectedTextHighlight {}
  286
  287pub enum ConflictsOuter {}
  288pub enum ConflictsOurs {}
  289pub enum ConflictsTheirs {}
  290pub enum ConflictsOursMarker {}
  291pub enum ConflictsTheirsMarker {}
  292
  293pub struct HunkAddedColor;
  294pub struct HunkRemovedColor;
  295
  296#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  297pub enum Navigated {
  298    Yes,
  299    No,
  300}
  301
  302impl Navigated {
  303    pub fn from_bool(yes: bool) -> Navigated {
  304        if yes { Navigated::Yes } else { Navigated::No }
  305    }
  306}
  307
  308#[derive(Debug, Clone, PartialEq, Eq)]
  309enum DisplayDiffHunk {
  310    Folded {
  311        display_row: DisplayRow,
  312    },
  313    Unfolded {
  314        is_created_file: bool,
  315        diff_base_byte_range: Range<usize>,
  316        display_row_range: Range<DisplayRow>,
  317        multi_buffer_range: Range<Anchor>,
  318        status: DiffHunkStatus,
  319        word_diffs: Vec<Range<MultiBufferOffset>>,
  320    },
  321}
  322
  323pub enum HideMouseCursorOrigin {
  324    TypingAction,
  325    MovementAction,
  326}
  327
  328pub fn init(cx: &mut App) {
  329    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  330
  331    workspace::register_project_item::<Editor>(cx);
  332    workspace::FollowableViewRegistry::register::<Editor>(cx);
  333    workspace::register_serializable_item::<Editor>(cx);
  334
  335    cx.observe_new(
  336        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  337            workspace.register_action(Editor::new_file);
  338            workspace.register_action(Editor::new_file_split);
  339            workspace.register_action(Editor::new_file_vertical);
  340            workspace.register_action(Editor::new_file_horizontal);
  341            workspace.register_action(Editor::cancel_language_server_work);
  342            workspace.register_action(Editor::toggle_focus);
  343        },
  344    )
  345    .detach();
  346
  347    cx.on_action(move |_: &workspace::NewFile, cx| {
  348        let app_state = workspace::AppState::global(cx);
  349        if let Some(app_state) = app_state.upgrade() {
  350            workspace::open_new(
  351                Default::default(),
  352                app_state,
  353                cx,
  354                |workspace, window, cx| {
  355                    Editor::new_file(workspace, &Default::default(), window, cx)
  356                },
  357            )
  358            .detach();
  359        }
  360    })
  361    .on_action(move |_: &workspace::NewWindow, cx| {
  362        let app_state = workspace::AppState::global(cx);
  363        if let Some(app_state) = app_state.upgrade() {
  364            workspace::open_new(
  365                Default::default(),
  366                app_state,
  367                cx,
  368                |workspace, window, cx| {
  369                    cx.activate(true);
  370                    Editor::new_file(workspace, &Default::default(), window, cx)
  371                },
  372            )
  373            .detach();
  374        }
  375    });
  376    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  377        Arc::new(ErasedEditorImpl(
  378            cx.new(|cx| Editor::single_line(window, cx)),
  379        )) as Arc<dyn ErasedEditor>
  380    });
  381    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  382}
  383
  384pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  385    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  386}
  387
  388pub trait DiagnosticRenderer {
  389    fn render_group(
  390        &self,
  391        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  392        buffer_id: BufferId,
  393        snapshot: EditorSnapshot,
  394        editor: WeakEntity<Editor>,
  395        language_registry: Option<Arc<LanguageRegistry>>,
  396        cx: &mut App,
  397    ) -> Vec<BlockProperties<Anchor>>;
  398
  399    fn render_hover(
  400        &self,
  401        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  402        range: Range<Point>,
  403        buffer_id: BufferId,
  404        language_registry: Option<Arc<LanguageRegistry>>,
  405        cx: &mut App,
  406    ) -> Option<Entity<markdown::Markdown>>;
  407
  408    fn open_link(
  409        &self,
  410        editor: &mut Editor,
  411        link: SharedString,
  412        window: &mut Window,
  413        cx: &mut Context<Editor>,
  414    );
  415}
  416
  417pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  418
  419impl GlobalDiagnosticRenderer {
  420    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  421        cx.try_global::<Self>().map(|g| g.0.clone())
  422    }
  423}
  424
  425impl gpui::Global for GlobalDiagnosticRenderer {}
  426pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  427    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  428}
  429
  430pub struct SearchWithinRange;
  431
  432trait InvalidationRegion {
  433    fn ranges(&self) -> &[Range<Anchor>];
  434}
  435
  436#[derive(Clone, Debug, PartialEq)]
  437pub enum SelectPhase {
  438    Begin {
  439        position: DisplayPoint,
  440        add: bool,
  441        click_count: usize,
  442    },
  443    BeginColumnar {
  444        position: DisplayPoint,
  445        reset: bool,
  446        mode: ColumnarMode,
  447        goal_column: u32,
  448    },
  449    Extend {
  450        position: DisplayPoint,
  451        click_count: usize,
  452    },
  453    Update {
  454        position: DisplayPoint,
  455        goal_column: u32,
  456        scroll_delta: gpui::Point<f32>,
  457    },
  458    End,
  459}
  460
  461#[derive(Clone, Debug, PartialEq)]
  462pub enum ColumnarMode {
  463    FromMouse,
  464    FromSelection,
  465}
  466
  467#[derive(Clone, Debug)]
  468pub enum SelectMode {
  469    Character,
  470    Word(Range<Anchor>),
  471    Line(Range<Anchor>),
  472    All,
  473}
  474
  475#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  476pub enum SizingBehavior {
  477    /// The editor will layout itself using `size_full` and will include the vertical
  478    /// scroll margin as requested by user settings.
  479    #[default]
  480    Default,
  481    /// The editor will layout itself using `size_full`, but will not have any
  482    /// vertical overscroll.
  483    ExcludeOverscrollMargin,
  484    /// The editor will request a vertical size according to its content and will be
  485    /// layouted without a vertical scroll margin.
  486    SizeByContent,
  487}
  488
  489#[derive(Clone, PartialEq, Eq, Debug)]
  490pub enum EditorMode {
  491    SingleLine,
  492    AutoHeight {
  493        min_lines: usize,
  494        max_lines: Option<usize>,
  495    },
  496    Full {
  497        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  498        scale_ui_elements_with_buffer_font_size: bool,
  499        /// When set to `true`, the editor will render a background for the active line.
  500        show_active_line_background: bool,
  501        /// Determines the sizing behavior for this editor
  502        sizing_behavior: SizingBehavior,
  503    },
  504    Minimap {
  505        parent: WeakEntity<Editor>,
  506    },
  507}
  508
  509impl EditorMode {
  510    pub fn full() -> Self {
  511        Self::Full {
  512            scale_ui_elements_with_buffer_font_size: true,
  513            show_active_line_background: true,
  514            sizing_behavior: SizingBehavior::Default,
  515        }
  516    }
  517
  518    #[inline]
  519    pub fn is_full(&self) -> bool {
  520        matches!(self, Self::Full { .. })
  521    }
  522
  523    #[inline]
  524    pub fn is_single_line(&self) -> bool {
  525        matches!(self, Self::SingleLine { .. })
  526    }
  527
  528    #[inline]
  529    fn is_minimap(&self) -> bool {
  530        matches!(self, Self::Minimap { .. })
  531    }
  532}
  533
  534#[derive(Copy, Clone, Debug)]
  535pub enum SoftWrap {
  536    /// Prefer not to wrap at all.
  537    ///
  538    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  539    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  540    GitDiff,
  541    /// Prefer a single line generally, unless an overly long line is encountered.
  542    None,
  543    /// Soft wrap lines that exceed the editor width.
  544    EditorWidth,
  545    /// Soft wrap lines at the preferred line length.
  546    Column(u32),
  547    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  548    Bounded(u32),
  549}
  550
  551#[derive(Clone)]
  552pub struct EditorStyle {
  553    pub background: Hsla,
  554    pub border: Hsla,
  555    pub local_player: PlayerColor,
  556    pub text: TextStyle,
  557    pub scrollbar_width: Pixels,
  558    pub syntax: Arc<SyntaxTheme>,
  559    pub status: StatusColors,
  560    pub inlay_hints_style: HighlightStyle,
  561    pub edit_prediction_styles: EditPredictionStyles,
  562    pub unnecessary_code_fade: f32,
  563    pub show_underlines: bool,
  564}
  565
  566impl Default for EditorStyle {
  567    fn default() -> Self {
  568        Self {
  569            background: Hsla::default(),
  570            border: Hsla::default(),
  571            local_player: PlayerColor::default(),
  572            text: TextStyle::default(),
  573            scrollbar_width: Pixels::default(),
  574            syntax: Default::default(),
  575            // HACK: Status colors don't have a real default.
  576            // We should look into removing the status colors from the editor
  577            // style and retrieve them directly from the theme.
  578            status: StatusColors::dark(),
  579            inlay_hints_style: HighlightStyle::default(),
  580            edit_prediction_styles: EditPredictionStyles {
  581                insertion: HighlightStyle::default(),
  582                whitespace: HighlightStyle::default(),
  583            },
  584            unnecessary_code_fade: Default::default(),
  585            show_underlines: true,
  586        }
  587    }
  588}
  589
  590pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  591    let show_background = language_settings::language_settings(None, None, cx)
  592        .inlay_hints
  593        .show_background;
  594
  595    let mut style = cx.theme().syntax().get("hint");
  596
  597    if style.color.is_none() {
  598        style.color = Some(cx.theme().status().hint);
  599    }
  600
  601    if !show_background {
  602        style.background_color = None;
  603        return style;
  604    }
  605
  606    if style.background_color.is_none() {
  607        style.background_color = Some(cx.theme().status().hint_background);
  608    }
  609
  610    style
  611}
  612
  613pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  614    EditPredictionStyles {
  615        insertion: HighlightStyle {
  616            color: Some(cx.theme().status().predictive),
  617            ..HighlightStyle::default()
  618        },
  619        whitespace: HighlightStyle {
  620            background_color: Some(cx.theme().status().created_background),
  621            ..HighlightStyle::default()
  622        },
  623    }
  624}
  625
  626type CompletionId = usize;
  627
  628pub(crate) enum EditDisplayMode {
  629    TabAccept,
  630    DiffPopover,
  631    Inline,
  632}
  633
  634enum EditPrediction {
  635    Edit {
  636        edits: Vec<(Range<Anchor>, Arc<str>)>,
  637        /// Predicted cursor position as (anchor, offset_from_anchor).
  638        /// The anchor is in multibuffer coordinates; after applying edits,
  639        /// resolve the anchor and add the offset to get the final cursor position.
  640        cursor_position: Option<(Anchor, usize)>,
  641        edit_preview: Option<EditPreview>,
  642        display_mode: EditDisplayMode,
  643        snapshot: BufferSnapshot,
  644    },
  645    /// Move to a specific location in the active editor
  646    MoveWithin {
  647        target: Anchor,
  648        snapshot: BufferSnapshot,
  649    },
  650    /// Move to a specific location in a different editor (not the active one)
  651    MoveOutside {
  652        target: language::Anchor,
  653        snapshot: BufferSnapshot,
  654    },
  655}
  656
  657struct EditPredictionState {
  658    inlay_ids: Vec<InlayId>,
  659    completion: EditPrediction,
  660    completion_id: Option<SharedString>,
  661    invalidation_range: Option<Range<Anchor>>,
  662}
  663
  664enum EditPredictionSettings {
  665    Disabled,
  666    Enabled {
  667        show_in_menu: bool,
  668        preview_requires_modifier: bool,
  669    },
  670}
  671
  672enum EditPredictionHighlight {}
  673
  674#[derive(Debug, Clone)]
  675struct InlineDiagnostic {
  676    message: SharedString,
  677    group_id: usize,
  678    is_primary: bool,
  679    start: Point,
  680    severity: lsp::DiagnosticSeverity,
  681}
  682
  683pub enum MenuEditPredictionsPolicy {
  684    Never,
  685    ByProvider,
  686}
  687
  688pub enum EditPredictionPreview {
  689    /// Modifier is not pressed
  690    Inactive { released_too_fast: bool },
  691    /// Modifier pressed
  692    Active {
  693        since: Instant,
  694        previous_scroll_position: Option<SharedScrollAnchor>,
  695    },
  696}
  697
  698impl EditPredictionPreview {
  699    pub fn released_too_fast(&self) -> bool {
  700        match self {
  701            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  702            EditPredictionPreview::Active { .. } => false,
  703        }
  704    }
  705
  706    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  707        if let EditPredictionPreview::Active {
  708            previous_scroll_position,
  709            ..
  710        } = self
  711        {
  712            *previous_scroll_position = scroll_position;
  713        }
  714    }
  715}
  716
  717pub struct ContextMenuOptions {
  718    pub min_entries_visible: usize,
  719    pub max_entries_visible: usize,
  720    pub placement: Option<ContextMenuPlacement>,
  721}
  722
  723#[derive(Debug, Clone, PartialEq, Eq)]
  724pub enum ContextMenuPlacement {
  725    Above,
  726    Below,
  727}
  728
  729#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  730struct EditorActionId(usize);
  731
  732impl EditorActionId {
  733    pub fn post_inc(&mut self) -> Self {
  734        let answer = self.0;
  735
  736        *self = Self(answer + 1);
  737
  738        Self(answer)
  739    }
  740}
  741
  742// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  743// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  744
  745type BackgroundHighlight = (
  746    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  747    Arc<[Range<Anchor>]>,
  748);
  749type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  750
  751#[derive(Default)]
  752struct ScrollbarMarkerState {
  753    scrollbar_size: Size<Pixels>,
  754    dirty: bool,
  755    markers: Arc<[PaintQuad]>,
  756    pending_refresh: Option<Task<Result<()>>>,
  757}
  758
  759impl ScrollbarMarkerState {
  760    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  761        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  762    }
  763}
  764
  765#[derive(Clone, Copy, PartialEq, Eq)]
  766pub enum MinimapVisibility {
  767    Disabled,
  768    Enabled {
  769        /// The configuration currently present in the users settings.
  770        setting_configuration: bool,
  771        /// Whether to override the currently set visibility from the users setting.
  772        toggle_override: bool,
  773    },
  774}
  775
  776impl MinimapVisibility {
  777    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  778        if mode.is_full() {
  779            Self::Enabled {
  780                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  781                toggle_override: false,
  782            }
  783        } else {
  784            Self::Disabled
  785        }
  786    }
  787
  788    fn hidden(&self) -> Self {
  789        match *self {
  790            Self::Enabled {
  791                setting_configuration,
  792                ..
  793            } => Self::Enabled {
  794                setting_configuration,
  795                toggle_override: setting_configuration,
  796            },
  797            Self::Disabled => Self::Disabled,
  798        }
  799    }
  800
  801    fn disabled(&self) -> bool {
  802        matches!(*self, Self::Disabled)
  803    }
  804
  805    fn settings_visibility(&self) -> bool {
  806        match *self {
  807            Self::Enabled {
  808                setting_configuration,
  809                ..
  810            } => setting_configuration,
  811            _ => false,
  812        }
  813    }
  814
  815    fn visible(&self) -> bool {
  816        match *self {
  817            Self::Enabled {
  818                setting_configuration,
  819                toggle_override,
  820            } => setting_configuration ^ toggle_override,
  821            _ => false,
  822        }
  823    }
  824
  825    fn toggle_visibility(&self) -> Self {
  826        match *self {
  827            Self::Enabled {
  828                toggle_override,
  829                setting_configuration,
  830            } => Self::Enabled {
  831                setting_configuration,
  832                toggle_override: !toggle_override,
  833            },
  834            Self::Disabled => Self::Disabled,
  835        }
  836    }
  837}
  838
  839#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  840pub enum BufferSerialization {
  841    All,
  842    NonDirtyBuffers,
  843}
  844
  845impl BufferSerialization {
  846    fn new(restore_unsaved_buffers: bool) -> Self {
  847        if restore_unsaved_buffers {
  848            Self::All
  849        } else {
  850            Self::NonDirtyBuffers
  851        }
  852    }
  853}
  854
  855#[derive(Clone, Debug)]
  856struct RunnableTasks {
  857    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  858    offset: multi_buffer::Anchor,
  859    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  860    column: u32,
  861    // Values of all named captures, including those starting with '_'
  862    extra_variables: HashMap<String, String>,
  863    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  864    context_range: Range<BufferOffset>,
  865}
  866
  867impl RunnableTasks {
  868    fn resolve<'a>(
  869        &'a self,
  870        cx: &'a task::TaskContext,
  871    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  872        self.templates.iter().filter_map(|(kind, template)| {
  873            template
  874                .resolve_task(&kind.to_id_base(), cx)
  875                .map(|task| (kind.clone(), task))
  876        })
  877    }
  878}
  879
  880#[derive(Clone)]
  881pub struct ResolvedTasks {
  882    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  883    position: Anchor,
  884}
  885
  886/// Addons allow storing per-editor state in other crates (e.g. Vim)
  887pub trait Addon: 'static {
  888    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  889
  890    fn render_buffer_header_controls(
  891        &self,
  892        _: &ExcerptInfo,
  893        _: &Window,
  894        _: &App,
  895    ) -> Option<AnyElement> {
  896        None
  897    }
  898
  899    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  900        None
  901    }
  902
  903    fn to_any(&self) -> &dyn std::any::Any;
  904
  905    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  906        None
  907    }
  908}
  909
  910struct ChangeLocation {
  911    current: Option<Vec<Anchor>>,
  912    original: Vec<Anchor>,
  913}
  914impl ChangeLocation {
  915    fn locations(&self) -> &[Anchor] {
  916        self.current.as_ref().unwrap_or(&self.original)
  917    }
  918}
  919
  920/// A set of caret positions, registered when the editor was edited.
  921pub struct ChangeList {
  922    changes: Vec<ChangeLocation>,
  923    /// Currently "selected" change.
  924    position: Option<usize>,
  925}
  926
  927impl ChangeList {
  928    pub fn new() -> Self {
  929        Self {
  930            changes: Vec::new(),
  931            position: None,
  932        }
  933    }
  934
  935    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  936    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  937    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  938        if self.changes.is_empty() {
  939            return None;
  940        }
  941
  942        let prev = self.position.unwrap_or(self.changes.len());
  943        let next = if direction == Direction::Prev {
  944            prev.saturating_sub(count)
  945        } else {
  946            (prev + count).min(self.changes.len() - 1)
  947        };
  948        self.position = Some(next);
  949        self.changes.get(next).map(|change| change.locations())
  950    }
  951
  952    /// Adds a new change to the list, resetting the change list position.
  953    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  954        self.position.take();
  955        if let Some(last) = self.changes.last_mut()
  956            && group
  957        {
  958            last.current = Some(new_positions)
  959        } else {
  960            self.changes.push(ChangeLocation {
  961                original: new_positions,
  962                current: None,
  963            });
  964        }
  965    }
  966
  967    pub fn last(&self) -> Option<&[Anchor]> {
  968        self.changes.last().map(|change| change.locations())
  969    }
  970
  971    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  972        self.changes.last().map(|change| change.original.as_slice())
  973    }
  974
  975    pub fn invert_last_group(&mut self) {
  976        if let Some(last) = self.changes.last_mut()
  977            && let Some(current) = last.current.as_mut()
  978        {
  979            mem::swap(&mut last.original, current);
  980        }
  981    }
  982}
  983
  984#[derive(Clone)]
  985struct InlineBlamePopoverState {
  986    scroll_handle: ScrollHandle,
  987    commit_message: Option<ParsedCommitMessage>,
  988    markdown: Entity<Markdown>,
  989}
  990
  991struct InlineBlamePopover {
  992    position: gpui::Point<Pixels>,
  993    hide_task: Option<Task<()>>,
  994    popover_bounds: Option<Bounds<Pixels>>,
  995    popover_state: InlineBlamePopoverState,
  996    keyboard_grace: bool,
  997}
  998
  999enum SelectionDragState {
 1000    /// State when no drag related activity is detected.
 1001    None,
 1002    /// State when the mouse is down on a selection that is about to be dragged.
 1003    ReadyToDrag {
 1004        selection: Selection<Anchor>,
 1005        click_position: gpui::Point<Pixels>,
 1006        mouse_down_time: Instant,
 1007    },
 1008    /// State when the mouse is dragging the selection in the editor.
 1009    Dragging {
 1010        selection: Selection<Anchor>,
 1011        drop_cursor: Selection<Anchor>,
 1012        hide_drop_cursor: bool,
 1013    },
 1014}
 1015
 1016enum ColumnarSelectionState {
 1017    FromMouse {
 1018        selection_tail: Anchor,
 1019        display_point: Option<DisplayPoint>,
 1020    },
 1021    FromSelection {
 1022        selection_tail: Anchor,
 1023    },
 1024}
 1025
 1026/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1027/// a breakpoint on them.
 1028#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1029struct PhantomBreakpointIndicator {
 1030    display_row: DisplayRow,
 1031    /// There's a small debounce between hovering over the line and showing the indicator.
 1032    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1033    is_active: bool,
 1034    collides_with_existing_breakpoint: bool,
 1035}
 1036
 1037/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1038/// in diff view mode.
 1039#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1040pub(crate) struct PhantomDiffReviewIndicator {
 1041    /// The starting anchor of the selection (or the only row if not dragging).
 1042    pub start: Anchor,
 1043    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1044    pub end: Anchor,
 1045    /// There's a small debounce between hovering over the line and showing the indicator.
 1046    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1047    pub is_active: bool,
 1048}
 1049
 1050#[derive(Clone, Debug)]
 1051pub(crate) struct DiffReviewDragState {
 1052    pub start_anchor: Anchor,
 1053    pub current_anchor: Anchor,
 1054}
 1055
 1056impl DiffReviewDragState {
 1057    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1058        let start = self.start_anchor.to_display_point(snapshot).row();
 1059        let current = self.current_anchor.to_display_point(snapshot).row();
 1060
 1061        (start..=current).sorted()
 1062    }
 1063}
 1064
 1065/// Identifies a specific hunk in the diff buffer.
 1066/// Used as a key to group comments by their location.
 1067#[derive(Clone, Debug)]
 1068pub struct DiffHunkKey {
 1069    /// The file path (relative to worktree) this hunk belongs to.
 1070    pub file_path: Arc<util::rel_path::RelPath>,
 1071    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1072    pub hunk_start_anchor: Anchor,
 1073}
 1074
 1075/// A review comment stored locally before being sent to the Agent panel.
 1076#[derive(Clone)]
 1077pub struct StoredReviewComment {
 1078    /// Unique identifier for this comment (for edit/delete operations).
 1079    pub id: usize,
 1080    /// The comment text entered by the user.
 1081    pub comment: String,
 1082    /// Anchors for the code range being reviewed.
 1083    pub range: Range<Anchor>,
 1084    /// Timestamp when the comment was created (for chronological ordering).
 1085    pub created_at: Instant,
 1086    /// Whether this comment is currently being edited inline.
 1087    pub is_editing: bool,
 1088}
 1089
 1090impl StoredReviewComment {
 1091    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1092        Self {
 1093            id,
 1094            comment,
 1095            range: anchor_range,
 1096            created_at: Instant::now(),
 1097            is_editing: false,
 1098        }
 1099    }
 1100}
 1101
 1102/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1103pub(crate) struct DiffReviewOverlay {
 1104    pub anchor_range: Range<Anchor>,
 1105    /// The block ID for the overlay.
 1106    pub block_id: CustomBlockId,
 1107    /// The editor entity for the review input.
 1108    pub prompt_editor: Entity<Editor>,
 1109    /// The hunk key this overlay belongs to.
 1110    pub hunk_key: DiffHunkKey,
 1111    /// Whether the comments section is expanded.
 1112    pub comments_expanded: bool,
 1113    /// Editors for comments currently being edited inline.
 1114    /// Key: comment ID, Value: Editor entity for inline editing.
 1115    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1116    /// Subscriptions for inline edit editors' action handlers.
 1117    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1118    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1119    /// The current user's avatar URI for display in comment rows.
 1120    pub user_avatar_uri: Option<SharedUri>,
 1121    /// Subscription to keep the action handler alive.
 1122    _subscription: Subscription,
 1123}
 1124
 1125/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1126///
 1127/// See the [module level documentation](self) for more information.
 1128pub struct Editor {
 1129    focus_handle: FocusHandle,
 1130    last_focused_descendant: Option<WeakFocusHandle>,
 1131    /// The text buffer being edited
 1132    buffer: Entity<MultiBuffer>,
 1133    /// Map of how text in the buffer should be displayed.
 1134    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1135    pub display_map: Entity<DisplayMap>,
 1136    placeholder_display_map: Option<Entity<DisplayMap>>,
 1137    pub selections: SelectionsCollection,
 1138    pub scroll_manager: ScrollManager,
 1139    /// When inline assist editors are linked, they all render cursors because
 1140    /// typing enters text into each of them, even the ones that aren't focused.
 1141    pub(crate) show_cursor_when_unfocused: bool,
 1142    columnar_selection_state: Option<ColumnarSelectionState>,
 1143    add_selections_state: Option<AddSelectionsState>,
 1144    select_next_state: Option<SelectNextState>,
 1145    select_prev_state: Option<SelectNextState>,
 1146    selection_history: SelectionHistory,
 1147    defer_selection_effects: bool,
 1148    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1149    autoclose_regions: Vec<AutocloseRegion>,
 1150    snippet_stack: InvalidationStack<SnippetState>,
 1151    select_syntax_node_history: SelectSyntaxNodeHistory,
 1152    ime_transaction: Option<TransactionId>,
 1153    pub diagnostics_max_severity: DiagnosticSeverity,
 1154    active_diagnostics: ActiveDiagnostic,
 1155    show_inline_diagnostics: bool,
 1156    inline_diagnostics_update: Task<()>,
 1157    inline_diagnostics_enabled: bool,
 1158    diagnostics_enabled: bool,
 1159    word_completions_enabled: bool,
 1160    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1161    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1162    hard_wrap: Option<usize>,
 1163    project: Option<Entity<Project>>,
 1164    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1165    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1166    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1167    blink_manager: Entity<BlinkManager>,
 1168    show_cursor_names: bool,
 1169    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1170    pub show_local_selections: bool,
 1171    mode: EditorMode,
 1172    show_breadcrumbs: bool,
 1173    show_gutter: bool,
 1174    show_scrollbars: ScrollbarAxes,
 1175    minimap_visibility: MinimapVisibility,
 1176    offset_content: bool,
 1177    disable_expand_excerpt_buttons: bool,
 1178    delegate_expand_excerpts: bool,
 1179    delegate_stage_and_restore: bool,
 1180    show_line_numbers: Option<bool>,
 1181    use_relative_line_numbers: Option<bool>,
 1182    show_git_diff_gutter: Option<bool>,
 1183    show_code_actions: Option<bool>,
 1184    show_runnables: Option<bool>,
 1185    show_breakpoints: Option<bool>,
 1186    show_diff_review_button: bool,
 1187    show_wrap_guides: Option<bool>,
 1188    show_indent_guides: Option<bool>,
 1189    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1190    highlight_order: usize,
 1191    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1192    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1193    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1194    scrollbar_marker_state: ScrollbarMarkerState,
 1195    active_indent_guides_state: ActiveIndentGuidesState,
 1196    nav_history: Option<ItemNavHistory>,
 1197    context_menu: RefCell<Option<CodeContextMenu>>,
 1198    context_menu_options: Option<ContextMenuOptions>,
 1199    mouse_context_menu: Option<MouseContextMenu>,
 1200    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1201    inline_blame_popover: Option<InlineBlamePopover>,
 1202    inline_blame_popover_show_task: Option<Task<()>>,
 1203    signature_help_state: SignatureHelpState,
 1204    auto_signature_help: Option<bool>,
 1205    find_all_references_task_sources: Vec<Anchor>,
 1206    next_completion_id: CompletionId,
 1207    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1208    code_actions_task: Option<Task<Result<()>>>,
 1209    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1210    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1211    debounced_selection_highlight_complete: bool,
 1212    document_highlights_task: Option<Task<()>>,
 1213    linked_editing_range_task: Option<Task<Option<()>>>,
 1214    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1215    pending_rename: Option<RenameState>,
 1216    searchable: bool,
 1217    cursor_shape: CursorShape,
 1218    /// Whether the cursor is offset one character to the left when something is
 1219    /// selected (needed for vim visual mode)
 1220    cursor_offset_on_selection: bool,
 1221    current_line_highlight: Option<CurrentLineHighlight>,
 1222    /// Whether to collapse search match ranges to just their start position.
 1223    /// When true, navigating to a match positions the cursor at the match
 1224    /// without selecting the matched text.
 1225    collapse_matches: bool,
 1226    autoindent_mode: Option<AutoindentMode>,
 1227    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1228    input_enabled: bool,
 1229    use_modal_editing: bool,
 1230    read_only: bool,
 1231    leader_id: Option<CollaboratorId>,
 1232    remote_id: Option<ViewId>,
 1233    pub hover_state: HoverState,
 1234    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1235    prev_pressure_stage: Option<PressureStage>,
 1236    gutter_hovered: bool,
 1237    hovered_link_state: Option<HoveredLinkState>,
 1238    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1239    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1240    active_edit_prediction: Option<EditPredictionState>,
 1241    /// Used to prevent flickering as the user types while the menu is open
 1242    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1243    edit_prediction_settings: EditPredictionSettings,
 1244    edit_predictions_hidden_for_vim_mode: bool,
 1245    show_edit_predictions_override: Option<bool>,
 1246    show_completions_on_input_override: Option<bool>,
 1247    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1248    edit_prediction_preview: EditPredictionPreview,
 1249    edit_prediction_indent_conflict: bool,
 1250    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1251    next_inlay_id: usize,
 1252    next_color_inlay_id: usize,
 1253    _subscriptions: Vec<Subscription>,
 1254    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1255    gutter_dimensions: GutterDimensions,
 1256    style: Option<EditorStyle>,
 1257    text_style_refinement: Option<TextStyleRefinement>,
 1258    next_editor_action_id: EditorActionId,
 1259    editor_actions: Rc<
 1260        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1261    >,
 1262    use_autoclose: bool,
 1263    use_auto_surround: bool,
 1264    auto_replace_emoji_shortcode: bool,
 1265    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1266    show_git_blame_gutter: bool,
 1267    show_git_blame_inline: bool,
 1268    show_git_blame_inline_delay_task: Option<Task<()>>,
 1269    git_blame_inline_enabled: bool,
 1270    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1271    buffer_serialization: Option<BufferSerialization>,
 1272    show_selection_menu: Option<bool>,
 1273    blame: Option<Entity<GitBlame>>,
 1274    blame_subscription: Option<Subscription>,
 1275    custom_context_menu: Option<
 1276        Box<
 1277            dyn 'static
 1278                + Fn(
 1279                    &mut Self,
 1280                    DisplayPoint,
 1281                    &mut Window,
 1282                    &mut Context<Self>,
 1283                ) -> Option<Entity<ui::ContextMenu>>,
 1284        >,
 1285    >,
 1286    last_bounds: Option<Bounds<Pixels>>,
 1287    last_position_map: Option<Rc<PositionMap>>,
 1288    expect_bounds_change: Option<Bounds<Pixels>>,
 1289    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1290    tasks_update_task: Option<Task<()>>,
 1291    breakpoint_store: Option<Entity<BreakpointStore>>,
 1292    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1293    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1294    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1295    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1296    /// when hunks have comments stored.
 1297    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1298    /// Stored review comments grouped by hunk.
 1299    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1300    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1301    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1302    /// Counter for generating unique comment IDs.
 1303    next_review_comment_id: usize,
 1304    hovered_diff_hunk_row: Option<DisplayRow>,
 1305    pull_diagnostics_task: Task<()>,
 1306    in_project_search: bool,
 1307    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1308    breadcrumb_header: Option<String>,
 1309    focused_block: Option<FocusedBlock>,
 1310    next_scroll_position: NextScrollCursorCenterTopBottom,
 1311    addons: HashMap<TypeId, Box<dyn Addon>>,
 1312    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1313    load_diff_task: Option<Shared<Task<()>>>,
 1314    /// Whether we are temporarily displaying a diff other than git's
 1315    temporary_diff_override: bool,
 1316    selection_mark_mode: bool,
 1317    toggle_fold_multiple_buffers: Task<()>,
 1318    _scroll_cursor_center_top_bottom_task: Task<()>,
 1319    serialize_selections: Task<()>,
 1320    serialize_folds: Task<()>,
 1321    mouse_cursor_hidden: bool,
 1322    minimap: Option<Entity<Self>>,
 1323    hide_mouse_mode: HideMouseMode,
 1324    pub change_list: ChangeList,
 1325    inline_value_cache: InlineValueCache,
 1326    number_deleted_lines: bool,
 1327
 1328    selection_drag_state: SelectionDragState,
 1329    colors: Option<LspColorData>,
 1330    post_scroll_update: Task<()>,
 1331    refresh_colors_task: Task<()>,
 1332    inlay_hints: Option<LspInlayHintData>,
 1333    folding_newlines: Task<()>,
 1334    select_next_is_case_sensitive: Option<bool>,
 1335    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1336    on_local_selections_changed:
 1337        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1338    suppress_selection_callback: bool,
 1339    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1340    accent_data: Option<AccentData>,
 1341    fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1342}
 1343
 1344#[derive(Debug, PartialEq)]
 1345struct AccentData {
 1346    colors: AccentColors,
 1347    overrides: Vec<SharedString>,
 1348}
 1349
 1350fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1351    if debounce_ms > 0 {
 1352        Some(Duration::from_millis(debounce_ms))
 1353    } else {
 1354        None
 1355    }
 1356}
 1357
 1358#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1359enum NextScrollCursorCenterTopBottom {
 1360    #[default]
 1361    Center,
 1362    Top,
 1363    Bottom,
 1364}
 1365
 1366impl NextScrollCursorCenterTopBottom {
 1367    fn next(&self) -> Self {
 1368        match self {
 1369            Self::Center => Self::Top,
 1370            Self::Top => Self::Bottom,
 1371            Self::Bottom => Self::Center,
 1372        }
 1373    }
 1374}
 1375
 1376#[derive(Clone)]
 1377pub struct EditorSnapshot {
 1378    pub mode: EditorMode,
 1379    show_gutter: bool,
 1380    offset_content: bool,
 1381    show_line_numbers: Option<bool>,
 1382    number_deleted_lines: bool,
 1383    show_git_diff_gutter: Option<bool>,
 1384    show_code_actions: Option<bool>,
 1385    show_runnables: Option<bool>,
 1386    show_breakpoints: Option<bool>,
 1387    git_blame_gutter_max_author_length: Option<usize>,
 1388    pub display_snapshot: DisplaySnapshot,
 1389    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1390    is_focused: bool,
 1391    scroll_anchor: SharedScrollAnchor,
 1392    ongoing_scroll: OngoingScroll,
 1393    current_line_highlight: CurrentLineHighlight,
 1394    gutter_hovered: bool,
 1395}
 1396
 1397#[derive(Default, Debug, Clone, Copy)]
 1398pub struct GutterDimensions {
 1399    pub left_padding: Pixels,
 1400    pub right_padding: Pixels,
 1401    pub width: Pixels,
 1402    pub margin: Pixels,
 1403    pub git_blame_entries_width: Option<Pixels>,
 1404}
 1405
 1406impl GutterDimensions {
 1407    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1408        Self {
 1409            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1410            ..Default::default()
 1411        }
 1412    }
 1413
 1414    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1415        -cx.text_system().descent(font_id, font_size)
 1416    }
 1417    /// The full width of the space taken up by the gutter.
 1418    pub fn full_width(&self) -> Pixels {
 1419        self.margin + self.width
 1420    }
 1421
 1422    /// The width of the space reserved for the fold indicators,
 1423    /// use alongside 'justify_end' and `gutter_width` to
 1424    /// right align content with the line numbers
 1425    pub fn fold_area_width(&self) -> Pixels {
 1426        self.margin + self.right_padding
 1427    }
 1428}
 1429
 1430struct CharacterDimensions {
 1431    em_width: Pixels,
 1432    em_advance: Pixels,
 1433    line_height: Pixels,
 1434}
 1435
 1436#[derive(Debug)]
 1437pub struct RemoteSelection {
 1438    pub replica_id: ReplicaId,
 1439    pub selection: Selection<Anchor>,
 1440    pub cursor_shape: CursorShape,
 1441    pub collaborator_id: CollaboratorId,
 1442    pub line_mode: bool,
 1443    pub user_name: Option<SharedString>,
 1444    pub color: PlayerColor,
 1445}
 1446
 1447#[derive(Clone, Debug)]
 1448struct SelectionHistoryEntry {
 1449    selections: Arc<[Selection<Anchor>]>,
 1450    select_next_state: Option<SelectNextState>,
 1451    select_prev_state: Option<SelectNextState>,
 1452    add_selections_state: Option<AddSelectionsState>,
 1453}
 1454
 1455#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1456enum SelectionHistoryMode {
 1457    #[default]
 1458    Normal,
 1459    Undoing,
 1460    Redoing,
 1461    Skipping,
 1462}
 1463
 1464#[derive(Clone, PartialEq, Eq, Hash)]
 1465struct HoveredCursor {
 1466    replica_id: ReplicaId,
 1467    selection_id: usize,
 1468}
 1469
 1470#[derive(Debug)]
 1471/// SelectionEffects controls the side-effects of updating the selection.
 1472///
 1473/// The default behaviour does "what you mostly want":
 1474/// - it pushes to the nav history if the cursor moved by >10 lines
 1475/// - it re-triggers completion requests
 1476/// - it scrolls to fit
 1477///
 1478/// You might want to modify these behaviours. For example when doing a "jump"
 1479/// like go to definition, we always want to add to nav history; but when scrolling
 1480/// in vim mode we never do.
 1481///
 1482/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1483/// move.
 1484#[derive(Clone)]
 1485pub struct SelectionEffects {
 1486    nav_history: Option<bool>,
 1487    completions: bool,
 1488    scroll: Option<Autoscroll>,
 1489}
 1490
 1491impl Default for SelectionEffects {
 1492    fn default() -> Self {
 1493        Self {
 1494            nav_history: None,
 1495            completions: true,
 1496            scroll: Some(Autoscroll::fit()),
 1497        }
 1498    }
 1499}
 1500impl SelectionEffects {
 1501    pub fn scroll(scroll: Autoscroll) -> Self {
 1502        Self {
 1503            scroll: Some(scroll),
 1504            ..Default::default()
 1505        }
 1506    }
 1507
 1508    pub fn no_scroll() -> Self {
 1509        Self {
 1510            scroll: None,
 1511            ..Default::default()
 1512        }
 1513    }
 1514
 1515    pub fn completions(self, completions: bool) -> Self {
 1516        Self {
 1517            completions,
 1518            ..self
 1519        }
 1520    }
 1521
 1522    pub fn nav_history(self, nav_history: bool) -> Self {
 1523        Self {
 1524            nav_history: Some(nav_history),
 1525            ..self
 1526        }
 1527    }
 1528}
 1529
 1530struct DeferredSelectionEffectsState {
 1531    changed: bool,
 1532    effects: SelectionEffects,
 1533    old_cursor_position: Anchor,
 1534    history_entry: SelectionHistoryEntry,
 1535}
 1536
 1537#[derive(Default)]
 1538struct SelectionHistory {
 1539    #[allow(clippy::type_complexity)]
 1540    selections_by_transaction:
 1541        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1542    mode: SelectionHistoryMode,
 1543    undo_stack: VecDeque<SelectionHistoryEntry>,
 1544    redo_stack: VecDeque<SelectionHistoryEntry>,
 1545}
 1546
 1547impl SelectionHistory {
 1548    #[track_caller]
 1549    fn insert_transaction(
 1550        &mut self,
 1551        transaction_id: TransactionId,
 1552        selections: Arc<[Selection<Anchor>]>,
 1553    ) {
 1554        if selections.is_empty() {
 1555            log::error!(
 1556                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1557                std::panic::Location::caller()
 1558            );
 1559            return;
 1560        }
 1561        self.selections_by_transaction
 1562            .insert(transaction_id, (selections, None));
 1563    }
 1564
 1565    #[allow(clippy::type_complexity)]
 1566    fn transaction(
 1567        &self,
 1568        transaction_id: TransactionId,
 1569    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1570        self.selections_by_transaction.get(&transaction_id)
 1571    }
 1572
 1573    #[allow(clippy::type_complexity)]
 1574    fn transaction_mut(
 1575        &mut self,
 1576        transaction_id: TransactionId,
 1577    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1578        self.selections_by_transaction.get_mut(&transaction_id)
 1579    }
 1580
 1581    fn push(&mut self, entry: SelectionHistoryEntry) {
 1582        if !entry.selections.is_empty() {
 1583            match self.mode {
 1584                SelectionHistoryMode::Normal => {
 1585                    self.push_undo(entry);
 1586                    self.redo_stack.clear();
 1587                }
 1588                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1589                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1590                SelectionHistoryMode::Skipping => {}
 1591            }
 1592        }
 1593    }
 1594
 1595    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1596        if self
 1597            .undo_stack
 1598            .back()
 1599            .is_none_or(|e| e.selections != entry.selections)
 1600        {
 1601            self.undo_stack.push_back(entry);
 1602            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1603                self.undo_stack.pop_front();
 1604            }
 1605        }
 1606    }
 1607
 1608    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1609        if self
 1610            .redo_stack
 1611            .back()
 1612            .is_none_or(|e| e.selections != entry.selections)
 1613        {
 1614            self.redo_stack.push_back(entry);
 1615            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1616                self.redo_stack.pop_front();
 1617            }
 1618        }
 1619    }
 1620}
 1621
 1622#[derive(Clone, Copy)]
 1623pub struct RowHighlightOptions {
 1624    pub autoscroll: bool,
 1625    pub include_gutter: bool,
 1626}
 1627
 1628impl Default for RowHighlightOptions {
 1629    fn default() -> Self {
 1630        Self {
 1631            autoscroll: Default::default(),
 1632            include_gutter: true,
 1633        }
 1634    }
 1635}
 1636
 1637struct RowHighlight {
 1638    index: usize,
 1639    range: Range<Anchor>,
 1640    color: Hsla,
 1641    options: RowHighlightOptions,
 1642    type_id: TypeId,
 1643}
 1644
 1645#[derive(Clone, Debug)]
 1646struct AddSelectionsState {
 1647    groups: Vec<AddSelectionsGroup>,
 1648}
 1649
 1650#[derive(Clone, Debug)]
 1651struct AddSelectionsGroup {
 1652    above: bool,
 1653    stack: Vec<usize>,
 1654}
 1655
 1656#[derive(Clone)]
 1657struct SelectNextState {
 1658    query: AhoCorasick,
 1659    wordwise: bool,
 1660    done: bool,
 1661}
 1662
 1663impl std::fmt::Debug for SelectNextState {
 1664    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1665        f.debug_struct(std::any::type_name::<Self>())
 1666            .field("wordwise", &self.wordwise)
 1667            .field("done", &self.done)
 1668            .finish()
 1669    }
 1670}
 1671
 1672#[derive(Debug)]
 1673struct AutocloseRegion {
 1674    selection_id: usize,
 1675    range: Range<Anchor>,
 1676    pair: BracketPair,
 1677}
 1678
 1679#[derive(Debug)]
 1680struct SnippetState {
 1681    ranges: Vec<Vec<Range<Anchor>>>,
 1682    active_index: usize,
 1683    choices: Vec<Option<Vec<String>>>,
 1684}
 1685
 1686#[doc(hidden)]
 1687pub struct RenameState {
 1688    pub range: Range<Anchor>,
 1689    pub old_name: Arc<str>,
 1690    pub editor: Entity<Editor>,
 1691    block_id: CustomBlockId,
 1692}
 1693
 1694struct InvalidationStack<T>(Vec<T>);
 1695
 1696struct RegisteredEditPredictionDelegate {
 1697    provider: Arc<dyn EditPredictionDelegateHandle>,
 1698    _subscription: Subscription,
 1699}
 1700
 1701#[derive(Debug, PartialEq, Eq)]
 1702pub struct ActiveDiagnosticGroup {
 1703    pub active_range: Range<Anchor>,
 1704    pub active_message: String,
 1705    pub group_id: usize,
 1706    pub blocks: HashSet<CustomBlockId>,
 1707}
 1708
 1709#[derive(Debug, PartialEq, Eq)]
 1710
 1711pub(crate) enum ActiveDiagnostic {
 1712    None,
 1713    All,
 1714    Group(ActiveDiagnosticGroup),
 1715}
 1716
 1717#[derive(Serialize, Deserialize, Clone, Debug)]
 1718pub struct ClipboardSelection {
 1719    /// The number of bytes in this selection.
 1720    pub len: usize,
 1721    /// Whether this was a full-line selection.
 1722    pub is_entire_line: bool,
 1723    /// The indentation of the first line when this content was originally copied.
 1724    pub first_line_indent: u32,
 1725    #[serde(default)]
 1726    pub file_path: Option<PathBuf>,
 1727    #[serde(default)]
 1728    pub line_range: Option<RangeInclusive<u32>>,
 1729}
 1730
 1731impl ClipboardSelection {
 1732    pub fn for_buffer(
 1733        len: usize,
 1734        is_entire_line: bool,
 1735        range: Range<Point>,
 1736        buffer: &MultiBufferSnapshot,
 1737        project: Option<&Entity<Project>>,
 1738        cx: &App,
 1739    ) -> Self {
 1740        let first_line_indent = buffer
 1741            .indent_size_for_line(MultiBufferRow(range.start.row))
 1742            .len;
 1743
 1744        let file_path = util::maybe!({
 1745            let project = project?.read(cx);
 1746            let file = buffer.file_at(range.start)?;
 1747            let project_path = ProjectPath {
 1748                worktree_id: file.worktree_id(cx),
 1749                path: file.path().clone(),
 1750            };
 1751            project.absolute_path(&project_path, cx)
 1752        });
 1753
 1754        let line_range = file_path.as_ref().and_then(|_| {
 1755            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1756            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1757            if start_excerpt_id == end_excerpt_id {
 1758                Some(start_point.row..=end_point.row)
 1759            } else {
 1760                None
 1761            }
 1762        });
 1763
 1764        Self {
 1765            len,
 1766            is_entire_line,
 1767            first_line_indent,
 1768            file_path,
 1769            line_range,
 1770        }
 1771    }
 1772}
 1773
 1774// selections, scroll behavior, was newest selection reversed
 1775type SelectSyntaxNodeHistoryState = (
 1776    Box<[Selection<MultiBufferOffset>]>,
 1777    SelectSyntaxNodeScrollBehavior,
 1778    bool,
 1779);
 1780
 1781#[derive(Default)]
 1782struct SelectSyntaxNodeHistory {
 1783    stack: Vec<SelectSyntaxNodeHistoryState>,
 1784    // disable temporarily to allow changing selections without losing the stack
 1785    pub disable_clearing: bool,
 1786}
 1787
 1788impl SelectSyntaxNodeHistory {
 1789    pub fn try_clear(&mut self) {
 1790        if !self.disable_clearing {
 1791            self.stack.clear();
 1792        }
 1793    }
 1794
 1795    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1796        self.stack.push(selection);
 1797    }
 1798
 1799    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1800        self.stack.pop()
 1801    }
 1802}
 1803
 1804enum SelectSyntaxNodeScrollBehavior {
 1805    CursorTop,
 1806    FitSelection,
 1807    CursorBottom,
 1808}
 1809
 1810#[derive(Debug, Clone, Copy)]
 1811pub(crate) struct NavigationData {
 1812    cursor_anchor: Anchor,
 1813    cursor_position: Point,
 1814    scroll_anchor: ScrollAnchor,
 1815    scroll_top_row: u32,
 1816}
 1817
 1818#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1819pub enum GotoDefinitionKind {
 1820    Symbol,
 1821    Declaration,
 1822    Type,
 1823    Implementation,
 1824}
 1825
 1826pub enum FormatTarget {
 1827    Buffers(HashSet<Entity<Buffer>>),
 1828    Ranges(Vec<Range<MultiBufferPoint>>),
 1829}
 1830
 1831pub(crate) struct FocusedBlock {
 1832    id: BlockId,
 1833    focus_handle: WeakFocusHandle,
 1834}
 1835
 1836#[derive(Clone, Debug)]
 1837pub enum JumpData {
 1838    MultiBufferRow {
 1839        row: MultiBufferRow,
 1840        line_offset_from_top: u32,
 1841    },
 1842    MultiBufferPoint {
 1843        excerpt_id: ExcerptId,
 1844        position: Point,
 1845        anchor: text::Anchor,
 1846        line_offset_from_top: u32,
 1847    },
 1848}
 1849
 1850pub enum MultibufferSelectionMode {
 1851    First,
 1852    All,
 1853}
 1854
 1855#[derive(Clone, Copy, Debug, Default)]
 1856pub struct RewrapOptions {
 1857    pub override_language_settings: bool,
 1858    pub preserve_existing_whitespace: bool,
 1859}
 1860
 1861impl Editor {
 1862    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1863        let buffer = cx.new(|cx| Buffer::local("", cx));
 1864        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1865        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1866    }
 1867
 1868    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1869        let buffer = cx.new(|cx| Buffer::local("", cx));
 1870        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1871        Self::new(EditorMode::full(), buffer, None, window, cx)
 1872    }
 1873
 1874    pub fn auto_height(
 1875        min_lines: usize,
 1876        max_lines: usize,
 1877        window: &mut Window,
 1878        cx: &mut Context<Self>,
 1879    ) -> Self {
 1880        let buffer = cx.new(|cx| Buffer::local("", cx));
 1881        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1882        Self::new(
 1883            EditorMode::AutoHeight {
 1884                min_lines,
 1885                max_lines: Some(max_lines),
 1886            },
 1887            buffer,
 1888            None,
 1889            window,
 1890            cx,
 1891        )
 1892    }
 1893
 1894    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1895    /// The editor grows as tall as needed to fit its content.
 1896    pub fn auto_height_unbounded(
 1897        min_lines: usize,
 1898        window: &mut Window,
 1899        cx: &mut Context<Self>,
 1900    ) -> Self {
 1901        let buffer = cx.new(|cx| Buffer::local("", cx));
 1902        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1903        Self::new(
 1904            EditorMode::AutoHeight {
 1905                min_lines,
 1906                max_lines: None,
 1907            },
 1908            buffer,
 1909            None,
 1910            window,
 1911            cx,
 1912        )
 1913    }
 1914
 1915    pub fn for_buffer(
 1916        buffer: Entity<Buffer>,
 1917        project: Option<Entity<Project>>,
 1918        window: &mut Window,
 1919        cx: &mut Context<Self>,
 1920    ) -> Self {
 1921        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1922        Self::new(EditorMode::full(), buffer, project, window, cx)
 1923    }
 1924
 1925    pub fn for_multibuffer(
 1926        buffer: Entity<MultiBuffer>,
 1927        project: Option<Entity<Project>>,
 1928        window: &mut Window,
 1929        cx: &mut Context<Self>,
 1930    ) -> Self {
 1931        Self::new(EditorMode::full(), buffer, project, window, cx)
 1932    }
 1933
 1934    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1935        let mut clone = Self::new(
 1936            self.mode.clone(),
 1937            self.buffer.clone(),
 1938            self.project.clone(),
 1939            window,
 1940            cx,
 1941        );
 1942        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1943            let snapshot = display_map.snapshot(cx);
 1944            clone.display_map.update(cx, |display_map, cx| {
 1945                display_map.set_state(&snapshot, cx);
 1946            });
 1947            snapshot
 1948        });
 1949        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1950        clone.folds_did_change(cx);
 1951        clone.selections.clone_state(&self.selections);
 1952        clone
 1953            .scroll_manager
 1954            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1955        clone.searchable = self.searchable;
 1956        clone.read_only = self.read_only;
 1957        clone
 1958    }
 1959
 1960    pub fn new(
 1961        mode: EditorMode,
 1962        buffer: Entity<MultiBuffer>,
 1963        project: Option<Entity<Project>>,
 1964        window: &mut Window,
 1965        cx: &mut Context<Self>,
 1966    ) -> Self {
 1967        Editor::new_internal(mode, buffer, project, None, window, cx)
 1968    }
 1969
 1970    pub fn sticky_headers(
 1971        &self,
 1972        display_snapshot: &DisplaySnapshot,
 1973        style: &EditorStyle,
 1974        cx: &App,
 1975    ) -> Option<Vec<OutlineItem<Anchor>>> {
 1976        let multi_buffer = self.buffer().read(cx);
 1977        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 1978        let multi_buffer_visible_start = self
 1979            .scroll_manager
 1980            .native_anchor(display_snapshot, cx)
 1981            .anchor
 1982            .to_point(&multi_buffer_snapshot);
 1983        let max_row = multi_buffer_snapshot.max_point().row;
 1984
 1985        let start_row = (multi_buffer_visible_start.row).min(max_row);
 1986        let end_row = (multi_buffer_visible_start.row + 10).min(max_row);
 1987
 1988        if let Some((excerpt_id, _, buffer)) = multi_buffer.read(cx).as_singleton() {
 1989            let outline_items = buffer
 1990                .outline_items_containing(
 1991                    Point::new(start_row, 0)..Point::new(end_row, 0),
 1992                    true,
 1993                    Some(style.syntax.as_ref()),
 1994                )
 1995                .into_iter()
 1996                .map(|outline_item| OutlineItem {
 1997                    depth: outline_item.depth,
 1998                    range: Anchor::range_in_buffer(*excerpt_id, outline_item.range),
 1999                    source_range_for_text: Anchor::range_in_buffer(
 2000                        *excerpt_id,
 2001                        outline_item.source_range_for_text,
 2002                    ),
 2003                    text: outline_item.text,
 2004                    highlight_ranges: outline_item.highlight_ranges,
 2005                    name_ranges: outline_item.name_ranges,
 2006                    body_range: outline_item
 2007                        .body_range
 2008                        .map(|range| Anchor::range_in_buffer(*excerpt_id, range)),
 2009                    annotation_range: outline_item
 2010                        .annotation_range
 2011                        .map(|range| Anchor::range_in_buffer(*excerpt_id, range)),
 2012                });
 2013            return Some(outline_items.collect());
 2014        }
 2015
 2016        None
 2017    }
 2018
 2019    fn new_internal(
 2020        mode: EditorMode,
 2021        multi_buffer: Entity<MultiBuffer>,
 2022        project: Option<Entity<Project>>,
 2023        display_map: Option<Entity<DisplayMap>>,
 2024        window: &mut Window,
 2025        cx: &mut Context<Self>,
 2026    ) -> Self {
 2027        debug_assert!(
 2028            display_map.is_none() || mode.is_minimap(),
 2029            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2030        );
 2031
 2032        let full_mode = mode.is_full();
 2033        let is_minimap = mode.is_minimap();
 2034        let diagnostics_max_severity = if full_mode {
 2035            EditorSettings::get_global(cx)
 2036                .diagnostics_max_severity
 2037                .unwrap_or(DiagnosticSeverity::Hint)
 2038        } else {
 2039            DiagnosticSeverity::Off
 2040        };
 2041        let style = window.text_style();
 2042        let font_size = style.font_size.to_pixels(window.rem_size());
 2043        let editor = cx.entity().downgrade();
 2044        let fold_placeholder = FoldPlaceholder {
 2045            constrain_width: false,
 2046            render: Arc::new(move |fold_id, fold_range, cx| {
 2047                let editor = editor.clone();
 2048                div()
 2049                    .id(fold_id)
 2050                    .bg(cx.theme().colors().ghost_element_background)
 2051                    .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
 2052                    .active(|style| style.bg(cx.theme().colors().ghost_element_active))
 2053                    .rounded_xs()
 2054                    .size_full()
 2055                    .cursor_pointer()
 2056                    .child("")
 2057                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2058                    .on_click(move |_, _window, cx| {
 2059                        editor
 2060                            .update(cx, |editor, cx| {
 2061                                editor.unfold_ranges(
 2062                                    &[fold_range.start..fold_range.end],
 2063                                    true,
 2064                                    false,
 2065                                    cx,
 2066                                );
 2067                                cx.stop_propagation();
 2068                            })
 2069                            .ok();
 2070                    })
 2071                    .into_any()
 2072            }),
 2073            merge_adjacent: true,
 2074            ..FoldPlaceholder::default()
 2075        };
 2076        let display_map = display_map.unwrap_or_else(|| {
 2077            cx.new(|cx| {
 2078                DisplayMap::new(
 2079                    multi_buffer.clone(),
 2080                    style.font(),
 2081                    font_size,
 2082                    None,
 2083                    FILE_HEADER_HEIGHT,
 2084                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2085                    fold_placeholder,
 2086                    diagnostics_max_severity,
 2087                    cx,
 2088                )
 2089            })
 2090        });
 2091
 2092        let selections = SelectionsCollection::new();
 2093
 2094        let blink_manager = cx.new(|cx| {
 2095            let mut blink_manager = BlinkManager::new(
 2096                CURSOR_BLINK_INTERVAL,
 2097                |cx| EditorSettings::get_global(cx).cursor_blink,
 2098                cx,
 2099            );
 2100            if is_minimap {
 2101                blink_manager.disable(cx);
 2102            }
 2103            blink_manager
 2104        });
 2105
 2106        let soft_wrap_mode_override =
 2107            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2108
 2109        let mut project_subscriptions = Vec::new();
 2110        if full_mode && let Some(project) = project.as_ref() {
 2111            project_subscriptions.push(cx.subscribe_in(
 2112                project,
 2113                window,
 2114                |editor, _, event, window, cx| match event {
 2115                    project::Event::RefreshCodeLens => {
 2116                        // we always query lens with actions, without storing them, always refreshing them
 2117                    }
 2118                    project::Event::RefreshInlayHints {
 2119                        server_id,
 2120                        request_id,
 2121                    } => {
 2122                        editor.refresh_inlay_hints(
 2123                            InlayHintRefreshReason::RefreshRequested {
 2124                                server_id: *server_id,
 2125                                request_id: *request_id,
 2126                            },
 2127                            cx,
 2128                        );
 2129                    }
 2130                    project::Event::LanguageServerRemoved(..) => {
 2131                        if editor.tasks_update_task.is_none() {
 2132                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2133                        }
 2134                        editor.registered_buffers.clear();
 2135                        editor.register_visible_buffers(cx);
 2136                    }
 2137                    project::Event::LanguageServerAdded(..) => {
 2138                        if editor.tasks_update_task.is_none() {
 2139                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2140                        }
 2141                    }
 2142                    project::Event::SnippetEdit(id, snippet_edits) => {
 2143                        // todo(lw): Non singletons
 2144                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2145                            let snapshot = buffer.read(cx).snapshot();
 2146                            let focus_handle = editor.focus_handle(cx);
 2147                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2148                                for (range, snippet) in snippet_edits {
 2149                                    let buffer_range =
 2150                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2151                                    editor
 2152                                        .insert_snippet(
 2153                                            &[MultiBufferOffset(buffer_range.start)
 2154                                                ..MultiBufferOffset(buffer_range.end)],
 2155                                            snippet.clone(),
 2156                                            window,
 2157                                            cx,
 2158                                        )
 2159                                        .ok();
 2160                                }
 2161                            }
 2162                        }
 2163                    }
 2164                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2165                        let buffer_id = *buffer_id;
 2166                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2167                            editor.register_buffer(buffer_id, cx);
 2168                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2169                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2170                            refresh_linked_ranges(editor, window, cx);
 2171                            editor.refresh_code_actions(window, cx);
 2172                            editor.refresh_document_highlights(cx);
 2173                        }
 2174                    }
 2175
 2176                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2177                        let Some(workspace) = editor.workspace() else {
 2178                            return;
 2179                        };
 2180                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2181                        else {
 2182                            return;
 2183                        };
 2184
 2185                        if active_editor.entity_id() == cx.entity_id() {
 2186                            let entity_id = cx.entity_id();
 2187                            workspace.update(cx, |this, cx| {
 2188                                this.panes_mut()
 2189                                    .iter_mut()
 2190                                    .filter(|pane| pane.entity_id() != entity_id)
 2191                                    .for_each(|p| {
 2192                                        p.update(cx, |pane, _| {
 2193                                            pane.nav_history_mut().rename_item(
 2194                                                entity_id,
 2195                                                project_path.clone(),
 2196                                                abs_path.clone().into(),
 2197                                            );
 2198                                        })
 2199                                    });
 2200                            });
 2201
 2202                            Self::open_transaction_for_hidden_buffers(
 2203                                workspace,
 2204                                transaction.clone(),
 2205                                "Rename".to_string(),
 2206                                window,
 2207                                cx,
 2208                            );
 2209                        }
 2210                    }
 2211
 2212                    project::Event::WorkspaceEditApplied(transaction) => {
 2213                        let Some(workspace) = editor.workspace() else {
 2214                            return;
 2215                        };
 2216                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2217                        else {
 2218                            return;
 2219                        };
 2220
 2221                        if active_editor.entity_id() == cx.entity_id() {
 2222                            Self::open_transaction_for_hidden_buffers(
 2223                                workspace,
 2224                                transaction.clone(),
 2225                                "LSP Edit".to_string(),
 2226                                window,
 2227                                cx,
 2228                            );
 2229                        }
 2230                    }
 2231
 2232                    _ => {}
 2233                },
 2234            ));
 2235            if let Some(task_inventory) = project
 2236                .read(cx)
 2237                .task_store()
 2238                .read(cx)
 2239                .task_inventory()
 2240                .cloned()
 2241            {
 2242                project_subscriptions.push(cx.observe_in(
 2243                    &task_inventory,
 2244                    window,
 2245                    |editor, _, window, cx| {
 2246                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2247                    },
 2248                ));
 2249            };
 2250
 2251            project_subscriptions.push(cx.subscribe_in(
 2252                &project.read(cx).breakpoint_store(),
 2253                window,
 2254                |editor, _, event, window, cx| match event {
 2255                    BreakpointStoreEvent::ClearDebugLines => {
 2256                        editor.clear_row_highlights::<ActiveDebugLine>();
 2257                        editor.refresh_inline_values(cx);
 2258                    }
 2259                    BreakpointStoreEvent::SetDebugLine => {
 2260                        if editor.go_to_active_debug_line(window, cx) {
 2261                            cx.stop_propagation();
 2262                        }
 2263
 2264                        editor.refresh_inline_values(cx);
 2265                    }
 2266                    _ => {}
 2267                },
 2268            ));
 2269            let git_store = project.read(cx).git_store().clone();
 2270            let project = project.clone();
 2271            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2272                if let GitStoreEvent::RepositoryAdded = event {
 2273                    this.load_diff_task = Some(
 2274                        update_uncommitted_diff_for_buffer(
 2275                            cx.entity(),
 2276                            &project,
 2277                            this.buffer.read(cx).all_buffers(),
 2278                            this.buffer.clone(),
 2279                            cx,
 2280                        )
 2281                        .shared(),
 2282                    );
 2283                }
 2284            }));
 2285        }
 2286
 2287        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2288
 2289        let inlay_hint_settings =
 2290            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2291        let focus_handle = cx.focus_handle();
 2292        if !is_minimap {
 2293            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2294                .detach();
 2295            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2296                .detach();
 2297            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2298                .detach();
 2299            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2300                .detach();
 2301            cx.observe_pending_input(window, Self::observe_pending_input)
 2302                .detach();
 2303        }
 2304
 2305        let show_indent_guides =
 2306            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2307                Some(false)
 2308            } else {
 2309                None
 2310            };
 2311
 2312        let breakpoint_store = match (&mode, project.as_ref()) {
 2313            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2314            _ => None,
 2315        };
 2316
 2317        let mut code_action_providers = Vec::new();
 2318        let mut load_uncommitted_diff = None;
 2319        if let Some(project) = project.clone() {
 2320            load_uncommitted_diff = Some(
 2321                update_uncommitted_diff_for_buffer(
 2322                    cx.entity(),
 2323                    &project,
 2324                    multi_buffer.read(cx).all_buffers(),
 2325                    multi_buffer.clone(),
 2326                    cx,
 2327                )
 2328                .shared(),
 2329            );
 2330            code_action_providers.push(Rc::new(project) as Rc<_>);
 2331        }
 2332
 2333        let mut editor = Self {
 2334            focus_handle,
 2335            show_cursor_when_unfocused: false,
 2336            last_focused_descendant: None,
 2337            buffer: multi_buffer.clone(),
 2338            display_map: display_map.clone(),
 2339            placeholder_display_map: None,
 2340            selections,
 2341            scroll_manager: ScrollManager::new(cx),
 2342            columnar_selection_state: None,
 2343            add_selections_state: None,
 2344            select_next_state: None,
 2345            select_prev_state: None,
 2346            selection_history: SelectionHistory::default(),
 2347            defer_selection_effects: false,
 2348            deferred_selection_effects_state: None,
 2349            autoclose_regions: Vec::new(),
 2350            snippet_stack: InvalidationStack::default(),
 2351            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2352            ime_transaction: None,
 2353            active_diagnostics: ActiveDiagnostic::None,
 2354            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2355            inline_diagnostics_update: Task::ready(()),
 2356            inline_diagnostics: Vec::new(),
 2357            soft_wrap_mode_override,
 2358            diagnostics_max_severity,
 2359            hard_wrap: None,
 2360            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2361            semantics_provider: project.clone().map(|project| Rc::new(project) as _),
 2362            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2363            project,
 2364            blink_manager: blink_manager.clone(),
 2365            show_local_selections: true,
 2366            show_scrollbars: ScrollbarAxes {
 2367                horizontal: full_mode,
 2368                vertical: full_mode,
 2369            },
 2370            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2371            offset_content: !matches!(mode, EditorMode::SingleLine),
 2372            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2373            show_gutter: full_mode,
 2374            show_line_numbers: (!full_mode).then_some(false),
 2375            use_relative_line_numbers: None,
 2376            disable_expand_excerpt_buttons: !full_mode,
 2377            delegate_expand_excerpts: false,
 2378            delegate_stage_and_restore: false,
 2379            show_git_diff_gutter: None,
 2380            show_code_actions: None,
 2381            show_runnables: None,
 2382            show_breakpoints: None,
 2383            show_diff_review_button: false,
 2384            show_wrap_guides: None,
 2385            show_indent_guides,
 2386            buffers_with_disabled_indent_guides: HashSet::default(),
 2387            highlight_order: 0,
 2388            highlighted_rows: HashMap::default(),
 2389            background_highlights: HashMap::default(),
 2390            gutter_highlights: HashMap::default(),
 2391            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2392            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2393            nav_history: None,
 2394            context_menu: RefCell::new(None),
 2395            context_menu_options: None,
 2396            mouse_context_menu: None,
 2397            completion_tasks: Vec::new(),
 2398            inline_blame_popover: None,
 2399            inline_blame_popover_show_task: None,
 2400            signature_help_state: SignatureHelpState::default(),
 2401            auto_signature_help: None,
 2402            find_all_references_task_sources: Vec::new(),
 2403            next_completion_id: 0,
 2404            next_inlay_id: 0,
 2405            code_action_providers,
 2406            available_code_actions: None,
 2407            code_actions_task: None,
 2408            quick_selection_highlight_task: None,
 2409            debounced_selection_highlight_task: None,
 2410            debounced_selection_highlight_complete: false,
 2411            document_highlights_task: None,
 2412            linked_editing_range_task: None,
 2413            pending_rename: None,
 2414            searchable: !is_minimap,
 2415            cursor_shape: EditorSettings::get_global(cx)
 2416                .cursor_shape
 2417                .unwrap_or_default(),
 2418            cursor_offset_on_selection: false,
 2419            current_line_highlight: None,
 2420            autoindent_mode: Some(AutoindentMode::EachLine),
 2421            collapse_matches: false,
 2422            workspace: None,
 2423            input_enabled: !is_minimap,
 2424            use_modal_editing: full_mode,
 2425            read_only: is_minimap,
 2426            use_autoclose: true,
 2427            use_auto_surround: true,
 2428            auto_replace_emoji_shortcode: false,
 2429            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2430            leader_id: None,
 2431            remote_id: None,
 2432            hover_state: HoverState::default(),
 2433            pending_mouse_down: None,
 2434            prev_pressure_stage: None,
 2435            hovered_link_state: None,
 2436            edit_prediction_provider: None,
 2437            active_edit_prediction: None,
 2438            stale_edit_prediction_in_menu: None,
 2439            edit_prediction_preview: EditPredictionPreview::Inactive {
 2440                released_too_fast: false,
 2441            },
 2442            inline_diagnostics_enabled: full_mode,
 2443            diagnostics_enabled: full_mode,
 2444            word_completions_enabled: full_mode,
 2445            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2446            gutter_hovered: false,
 2447            pixel_position_of_newest_cursor: None,
 2448            last_bounds: None,
 2449            last_position_map: None,
 2450            expect_bounds_change: None,
 2451            gutter_dimensions: GutterDimensions::default(),
 2452            style: None,
 2453            show_cursor_names: false,
 2454            hovered_cursors: HashMap::default(),
 2455            next_editor_action_id: EditorActionId::default(),
 2456            editor_actions: Rc::default(),
 2457            edit_predictions_hidden_for_vim_mode: false,
 2458            show_edit_predictions_override: None,
 2459            show_completions_on_input_override: None,
 2460            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2461            edit_prediction_settings: EditPredictionSettings::Disabled,
 2462            edit_prediction_indent_conflict: false,
 2463            edit_prediction_requires_modifier_in_indent_conflict: true,
 2464            custom_context_menu: None,
 2465            show_git_blame_gutter: false,
 2466            show_git_blame_inline: false,
 2467            show_selection_menu: None,
 2468            show_git_blame_inline_delay_task: None,
 2469            git_blame_inline_enabled: full_mode
 2470                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2471            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2472            buffer_serialization: is_minimap.not().then(|| {
 2473                BufferSerialization::new(
 2474                    ProjectSettings::get_global(cx)
 2475                        .session
 2476                        .restore_unsaved_buffers,
 2477                )
 2478            }),
 2479            blame: None,
 2480            blame_subscription: None,
 2481            tasks: BTreeMap::default(),
 2482
 2483            breakpoint_store,
 2484            gutter_breakpoint_indicator: (None, None),
 2485            gutter_diff_review_indicator: (None, None),
 2486            diff_review_drag_state: None,
 2487            diff_review_overlays: Vec::new(),
 2488            stored_review_comments: Vec::new(),
 2489            next_review_comment_id: 0,
 2490            hovered_diff_hunk_row: None,
 2491            _subscriptions: (!is_minimap)
 2492                .then(|| {
 2493                    vec![
 2494                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2495                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2496                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2497                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2498                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2499                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2500                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2501                        cx.observe_window_activation(window, |editor, window, cx| {
 2502                            let active = window.is_window_active();
 2503                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2504                                if active {
 2505                                    blink_manager.enable(cx);
 2506                                } else {
 2507                                    blink_manager.disable(cx);
 2508                                }
 2509                            });
 2510                            if active {
 2511                                editor.show_mouse_cursor(cx);
 2512                            }
 2513                        }),
 2514                    ]
 2515                })
 2516                .unwrap_or_default(),
 2517            tasks_update_task: None,
 2518            pull_diagnostics_task: Task::ready(()),
 2519            colors: None,
 2520            refresh_colors_task: Task::ready(()),
 2521            inlay_hints: None,
 2522            next_color_inlay_id: 0,
 2523            post_scroll_update: Task::ready(()),
 2524            linked_edit_ranges: Default::default(),
 2525            in_project_search: false,
 2526            previous_search_ranges: None,
 2527            breadcrumb_header: None,
 2528            focused_block: None,
 2529            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2530            addons: HashMap::default(),
 2531            registered_buffers: HashMap::default(),
 2532            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2533            selection_mark_mode: false,
 2534            toggle_fold_multiple_buffers: Task::ready(()),
 2535            serialize_selections: Task::ready(()),
 2536            serialize_folds: Task::ready(()),
 2537            text_style_refinement: None,
 2538            load_diff_task: load_uncommitted_diff,
 2539            temporary_diff_override: false,
 2540            mouse_cursor_hidden: false,
 2541            minimap: None,
 2542            hide_mouse_mode: EditorSettings::get_global(cx)
 2543                .hide_mouse
 2544                .unwrap_or_default(),
 2545            change_list: ChangeList::new(),
 2546            mode,
 2547            selection_drag_state: SelectionDragState::None,
 2548            folding_newlines: Task::ready(()),
 2549            lookup_key: None,
 2550            select_next_is_case_sensitive: None,
 2551            on_local_selections_changed: None,
 2552            suppress_selection_callback: false,
 2553            applicable_language_settings: HashMap::default(),
 2554            accent_data: None,
 2555            fetched_tree_sitter_chunks: HashMap::default(),
 2556            number_deleted_lines: false,
 2557        };
 2558
 2559        if is_minimap {
 2560            return editor;
 2561        }
 2562
 2563        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2564        editor.accent_data = editor.fetch_accent_data(cx);
 2565
 2566        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2567            editor
 2568                ._subscriptions
 2569                .push(cx.observe(breakpoints, |_, _, cx| {
 2570                    cx.notify();
 2571                }));
 2572        }
 2573        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2574        editor._subscriptions.extend(project_subscriptions);
 2575
 2576        editor._subscriptions.push(cx.subscribe_in(
 2577            &cx.entity(),
 2578            window,
 2579            |editor, _, e: &EditorEvent, window, cx| match e {
 2580                EditorEvent::ScrollPositionChanged { local, .. } => {
 2581                    if *local {
 2582                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2583                        editor.inline_blame_popover.take();
 2584                        let snapshot = editor.snapshot(window, cx);
 2585                        let new_anchor = editor
 2586                            .scroll_manager
 2587                            .native_anchor(&snapshot.display_snapshot, cx);
 2588                        editor.update_restoration_data(cx, move |data| {
 2589                            data.scroll_position = (
 2590                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2591                                new_anchor.offset,
 2592                            );
 2593                        });
 2594
 2595                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2596                            cx.background_executor()
 2597                                .timer(Duration::from_millis(50))
 2598                                .await;
 2599                            editor
 2600                                .update_in(cx, |editor, window, cx| {
 2601                                    editor.register_visible_buffers(cx);
 2602                                    editor.refresh_colors_for_visible_range(None, window, cx);
 2603                                    editor.refresh_inlay_hints(
 2604                                        InlayHintRefreshReason::NewLinesShown,
 2605                                        cx,
 2606                                    );
 2607                                    editor.colorize_brackets(false, cx);
 2608                                })
 2609                                .ok();
 2610                        });
 2611                    }
 2612                }
 2613                EditorEvent::Edited { .. } => {
 2614                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2615                        .map(|vim_mode| vim_mode.0)
 2616                        .unwrap_or(false);
 2617                    if !vim_mode {
 2618                        let display_map = editor.display_snapshot(cx);
 2619                        let selections = editor.selections.all_adjusted_display(&display_map);
 2620                        let pop_state = editor
 2621                            .change_list
 2622                            .last()
 2623                            .map(|previous| {
 2624                                previous.len() == selections.len()
 2625                                    && previous.iter().enumerate().all(|(ix, p)| {
 2626                                        p.to_display_point(&display_map).row()
 2627                                            == selections[ix].head().row()
 2628                                    })
 2629                            })
 2630                            .unwrap_or(false);
 2631                        let new_positions = selections
 2632                            .into_iter()
 2633                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2634                            .collect();
 2635                        editor
 2636                            .change_list
 2637                            .push_to_change_list(pop_state, new_positions);
 2638                    }
 2639                }
 2640                _ => (),
 2641            },
 2642        ));
 2643
 2644        if let Some(dap_store) = editor
 2645            .project
 2646            .as_ref()
 2647            .map(|project| project.read(cx).dap_store())
 2648        {
 2649            let weak_editor = cx.weak_entity();
 2650
 2651            editor
 2652                ._subscriptions
 2653                .push(
 2654                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2655                        let session_entity = cx.entity();
 2656                        weak_editor
 2657                            .update(cx, |editor, cx| {
 2658                                editor._subscriptions.push(
 2659                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2660                                );
 2661                            })
 2662                            .ok();
 2663                    }),
 2664                );
 2665
 2666            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2667                editor
 2668                    ._subscriptions
 2669                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2670            }
 2671        }
 2672
 2673        // skip adding the initial selection to selection history
 2674        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2675        editor.end_selection(window, cx);
 2676        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2677
 2678        editor.scroll_manager.show_scrollbars(window, cx);
 2679        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2680
 2681        if full_mode {
 2682            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2683            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2684
 2685            if editor.git_blame_inline_enabled {
 2686                editor.start_git_blame_inline(false, window, cx);
 2687            }
 2688
 2689            editor.go_to_active_debug_line(window, cx);
 2690
 2691            editor.minimap =
 2692                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2693            editor.colors = Some(LspColorData::new(cx));
 2694            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2695
 2696            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2697                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2698            }
 2699            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2700        }
 2701
 2702        editor
 2703    }
 2704
 2705    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2706        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2707    }
 2708
 2709    pub fn deploy_mouse_context_menu(
 2710        &mut self,
 2711        position: gpui::Point<Pixels>,
 2712        context_menu: Entity<ContextMenu>,
 2713        window: &mut Window,
 2714        cx: &mut Context<Self>,
 2715    ) {
 2716        self.mouse_context_menu = Some(MouseContextMenu::new(
 2717            self,
 2718            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2719            context_menu,
 2720            window,
 2721            cx,
 2722        ));
 2723    }
 2724
 2725    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2726        self.mouse_context_menu
 2727            .as_ref()
 2728            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2729    }
 2730
 2731    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2732        if self
 2733            .selections
 2734            .pending_anchor()
 2735            .is_some_and(|pending_selection| {
 2736                let snapshot = self.buffer().read(cx).snapshot(cx);
 2737                pending_selection.range().includes(range, &snapshot)
 2738            })
 2739        {
 2740            return true;
 2741        }
 2742
 2743        self.selections
 2744            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2745            .into_iter()
 2746            .any(|selection| {
 2747                // This is needed to cover a corner case, if we just check for an existing
 2748                // selection in the fold range, having a cursor at the start of the fold
 2749                // marks it as selected. Non-empty selections don't cause this.
 2750                let length = selection.end - selection.start;
 2751                length > 0
 2752            })
 2753    }
 2754
 2755    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2756        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2757    }
 2758
 2759    fn key_context_internal(
 2760        &self,
 2761        has_active_edit_prediction: bool,
 2762        window: &mut Window,
 2763        cx: &mut App,
 2764    ) -> KeyContext {
 2765        let mut key_context = KeyContext::new_with_defaults();
 2766        key_context.add("Editor");
 2767        let mode = match self.mode {
 2768            EditorMode::SingleLine => "single_line",
 2769            EditorMode::AutoHeight { .. } => "auto_height",
 2770            EditorMode::Minimap { .. } => "minimap",
 2771            EditorMode::Full { .. } => "full",
 2772        };
 2773
 2774        if EditorSettings::jupyter_enabled(cx) {
 2775            key_context.add("jupyter");
 2776        }
 2777
 2778        key_context.set("mode", mode);
 2779        if self.pending_rename.is_some() {
 2780            key_context.add("renaming");
 2781        }
 2782
 2783        if let Some(snippet_stack) = self.snippet_stack.last() {
 2784            key_context.add("in_snippet");
 2785
 2786            if snippet_stack.active_index > 0 {
 2787                key_context.add("has_previous_tabstop");
 2788            }
 2789
 2790            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2791                key_context.add("has_next_tabstop");
 2792            }
 2793        }
 2794
 2795        match self.context_menu.borrow().as_ref() {
 2796            Some(CodeContextMenu::Completions(menu)) => {
 2797                if menu.visible() {
 2798                    key_context.add("menu");
 2799                    key_context.add("showing_completions");
 2800                }
 2801            }
 2802            Some(CodeContextMenu::CodeActions(menu)) => {
 2803                if menu.visible() {
 2804                    key_context.add("menu");
 2805                    key_context.add("showing_code_actions")
 2806                }
 2807            }
 2808            None => {}
 2809        }
 2810
 2811        if self.signature_help_state.has_multiple_signatures() {
 2812            key_context.add("showing_signature_help");
 2813        }
 2814
 2815        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2816        if !self.focus_handle(cx).contains_focused(window, cx)
 2817            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2818        {
 2819            for addon in self.addons.values() {
 2820                addon.extend_key_context(&mut key_context, cx)
 2821            }
 2822        }
 2823
 2824        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2825            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2826                Some(
 2827                    file.full_path(cx)
 2828                        .extension()?
 2829                        .to_string_lossy()
 2830                        .to_lowercase(),
 2831                )
 2832            }) {
 2833                key_context.set("extension", extension);
 2834            }
 2835        } else {
 2836            key_context.add("multibuffer");
 2837        }
 2838
 2839        if has_active_edit_prediction {
 2840            if self.edit_prediction_in_conflict() {
 2841                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2842            } else {
 2843                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2844                key_context.add("copilot_suggestion");
 2845            }
 2846        }
 2847
 2848        if self.selection_mark_mode {
 2849            key_context.add("selection_mode");
 2850        }
 2851
 2852        let disjoint = self.selections.disjoint_anchors();
 2853        let snapshot = self.snapshot(window, cx);
 2854        let snapshot = snapshot.buffer_snapshot();
 2855        if self.mode == EditorMode::SingleLine
 2856            && let [selection] = disjoint
 2857            && selection.start == selection.end
 2858            && selection.end.to_offset(snapshot) == snapshot.len()
 2859        {
 2860            key_context.add("end_of_input");
 2861        }
 2862
 2863        if self.has_any_expanded_diff_hunks(cx) {
 2864            key_context.add("diffs_expanded");
 2865        }
 2866
 2867        key_context
 2868    }
 2869
 2870    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2871        self.last_bounds.as_ref()
 2872    }
 2873
 2874    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2875        if self.mouse_cursor_hidden {
 2876            self.mouse_cursor_hidden = false;
 2877            cx.notify();
 2878        }
 2879    }
 2880
 2881    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2882        let hide_mouse_cursor = match origin {
 2883            HideMouseCursorOrigin::TypingAction => {
 2884                matches!(
 2885                    self.hide_mouse_mode,
 2886                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2887                )
 2888            }
 2889            HideMouseCursorOrigin::MovementAction => {
 2890                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2891            }
 2892        };
 2893        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2894            self.mouse_cursor_hidden = hide_mouse_cursor;
 2895            cx.notify();
 2896        }
 2897    }
 2898
 2899    pub fn edit_prediction_in_conflict(&self) -> bool {
 2900        if !self.show_edit_predictions_in_menu() {
 2901            return false;
 2902        }
 2903
 2904        let showing_completions = self
 2905            .context_menu
 2906            .borrow()
 2907            .as_ref()
 2908            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2909
 2910        showing_completions
 2911            || self.edit_prediction_requires_modifier()
 2912            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2913            // bindings to insert tab characters.
 2914            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2915    }
 2916
 2917    pub fn accept_edit_prediction_keybind(
 2918        &self,
 2919        granularity: EditPredictionGranularity,
 2920        window: &mut Window,
 2921        cx: &mut App,
 2922    ) -> AcceptEditPredictionBinding {
 2923        let key_context = self.key_context_internal(true, window, cx);
 2924        let in_conflict = self.edit_prediction_in_conflict();
 2925
 2926        let bindings =
 2927            match granularity {
 2928                EditPredictionGranularity::Word => window
 2929                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2930                EditPredictionGranularity::Line => window
 2931                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2932                EditPredictionGranularity::Full => {
 2933                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2934                }
 2935            };
 2936
 2937        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2938            !in_conflict
 2939                || binding
 2940                    .keystrokes()
 2941                    .first()
 2942                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 2943        }))
 2944    }
 2945
 2946    pub fn new_file(
 2947        workspace: &mut Workspace,
 2948        _: &workspace::NewFile,
 2949        window: &mut Window,
 2950        cx: &mut Context<Workspace>,
 2951    ) {
 2952        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 2953            "Failed to create buffer",
 2954            window,
 2955            cx,
 2956            |e, _, _| match e.error_code() {
 2957                ErrorCode::RemoteUpgradeRequired => Some(format!(
 2958                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 2959                e.error_tag("required").unwrap_or("the latest version")
 2960            )),
 2961                _ => None,
 2962            },
 2963        );
 2964    }
 2965
 2966    pub fn new_in_workspace(
 2967        workspace: &mut Workspace,
 2968        window: &mut Window,
 2969        cx: &mut Context<Workspace>,
 2970    ) -> Task<Result<Entity<Editor>>> {
 2971        let project = workspace.project().clone();
 2972        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 2973
 2974        cx.spawn_in(window, async move |workspace, cx| {
 2975            let buffer = create.await?;
 2976            workspace.update_in(cx, |workspace, window, cx| {
 2977                let editor =
 2978                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 2979                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 2980                editor
 2981            })
 2982        })
 2983    }
 2984
 2985    fn new_file_vertical(
 2986        workspace: &mut Workspace,
 2987        _: &workspace::NewFileSplitVertical,
 2988        window: &mut Window,
 2989        cx: &mut Context<Workspace>,
 2990    ) {
 2991        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 2992    }
 2993
 2994    fn new_file_horizontal(
 2995        workspace: &mut Workspace,
 2996        _: &workspace::NewFileSplitHorizontal,
 2997        window: &mut Window,
 2998        cx: &mut Context<Workspace>,
 2999    ) {
 3000        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3001    }
 3002
 3003    fn new_file_split(
 3004        workspace: &mut Workspace,
 3005        action: &workspace::NewFileSplit,
 3006        window: &mut Window,
 3007        cx: &mut Context<Workspace>,
 3008    ) {
 3009        Self::new_file_in_direction(workspace, action.0, window, cx)
 3010    }
 3011
 3012    fn new_file_in_direction(
 3013        workspace: &mut Workspace,
 3014        direction: SplitDirection,
 3015        window: &mut Window,
 3016        cx: &mut Context<Workspace>,
 3017    ) {
 3018        let project = workspace.project().clone();
 3019        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3020
 3021        cx.spawn_in(window, async move |workspace, cx| {
 3022            let buffer = create.await?;
 3023            workspace.update_in(cx, move |workspace, window, cx| {
 3024                workspace.split_item(
 3025                    direction,
 3026                    Box::new(
 3027                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3028                    ),
 3029                    window,
 3030                    cx,
 3031                )
 3032            })?;
 3033            anyhow::Ok(())
 3034        })
 3035        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3036            match e.error_code() {
 3037                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3038                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3039                e.error_tag("required").unwrap_or("the latest version")
 3040            )),
 3041                _ => None,
 3042            }
 3043        });
 3044    }
 3045
 3046    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3047        self.leader_id
 3048    }
 3049
 3050    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3051        &self.buffer
 3052    }
 3053
 3054    pub fn project(&self) -> Option<&Entity<Project>> {
 3055        self.project.as_ref()
 3056    }
 3057
 3058    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3059        self.workspace.as_ref()?.0.upgrade()
 3060    }
 3061
 3062    /// Returns the workspace serialization ID if this editor should be serialized.
 3063    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3064        self.workspace
 3065            .as_ref()
 3066            .filter(|_| self.should_serialize_buffer())
 3067            .and_then(|workspace| workspace.1)
 3068    }
 3069
 3070    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3071        self.buffer().read(cx).title(cx)
 3072    }
 3073
 3074    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3075        let git_blame_gutter_max_author_length = self
 3076            .render_git_blame_gutter(cx)
 3077            .then(|| {
 3078                if let Some(blame) = self.blame.as_ref() {
 3079                    let max_author_length =
 3080                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3081                    Some(max_author_length)
 3082                } else {
 3083                    None
 3084                }
 3085            })
 3086            .flatten();
 3087
 3088        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3089
 3090        EditorSnapshot {
 3091            mode: self.mode.clone(),
 3092            show_gutter: self.show_gutter,
 3093            offset_content: self.offset_content,
 3094            show_line_numbers: self.show_line_numbers,
 3095            number_deleted_lines: self.number_deleted_lines,
 3096            show_git_diff_gutter: self.show_git_diff_gutter,
 3097            show_code_actions: self.show_code_actions,
 3098            show_runnables: self.show_runnables,
 3099            show_breakpoints: self.show_breakpoints,
 3100            git_blame_gutter_max_author_length,
 3101            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3102            display_snapshot,
 3103            placeholder_display_snapshot: self
 3104                .placeholder_display_map
 3105                .as_ref()
 3106                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3107            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3108            is_focused: self.focus_handle.is_focused(window),
 3109            current_line_highlight: self
 3110                .current_line_highlight
 3111                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3112            gutter_hovered: self.gutter_hovered,
 3113        }
 3114    }
 3115
 3116    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3117        self.buffer.read(cx).language_at(point, cx)
 3118    }
 3119
 3120    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3121        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3122    }
 3123
 3124    pub fn active_excerpt(
 3125        &self,
 3126        cx: &App,
 3127    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3128        self.buffer
 3129            .read(cx)
 3130            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3131    }
 3132
 3133    pub fn mode(&self) -> &EditorMode {
 3134        &self.mode
 3135    }
 3136
 3137    pub fn set_mode(&mut self, mode: EditorMode) {
 3138        self.mode = mode;
 3139    }
 3140
 3141    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3142        self.collaboration_hub.as_deref()
 3143    }
 3144
 3145    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3146        self.collaboration_hub = Some(hub);
 3147    }
 3148
 3149    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3150        self.in_project_search = in_project_search;
 3151    }
 3152
 3153    pub fn set_custom_context_menu(
 3154        &mut self,
 3155        f: impl 'static
 3156        + Fn(
 3157            &mut Self,
 3158            DisplayPoint,
 3159            &mut Window,
 3160            &mut Context<Self>,
 3161        ) -> Option<Entity<ui::ContextMenu>>,
 3162    ) {
 3163        self.custom_context_menu = Some(Box::new(f))
 3164    }
 3165
 3166    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3167        self.completion_provider = provider;
 3168    }
 3169
 3170    #[cfg(any(test, feature = "test-support"))]
 3171    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3172        self.completion_provider.clone()
 3173    }
 3174
 3175    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3176        self.semantics_provider.clone()
 3177    }
 3178
 3179    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3180        self.semantics_provider = provider;
 3181    }
 3182
 3183    pub fn set_edit_prediction_provider<T>(
 3184        &mut self,
 3185        provider: Option<Entity<T>>,
 3186        window: &mut Window,
 3187        cx: &mut Context<Self>,
 3188    ) where
 3189        T: EditPredictionDelegate,
 3190    {
 3191        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3192            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3193                if this.focus_handle.is_focused(window) {
 3194                    this.update_visible_edit_prediction(window, cx);
 3195                }
 3196            }),
 3197            provider: Arc::new(provider),
 3198        });
 3199        self.update_edit_prediction_settings(cx);
 3200        self.refresh_edit_prediction(false, false, window, cx);
 3201    }
 3202
 3203    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3204        self.placeholder_display_map
 3205            .as_ref()
 3206            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3207    }
 3208
 3209    pub fn set_placeholder_text(
 3210        &mut self,
 3211        placeholder_text: &str,
 3212        window: &mut Window,
 3213        cx: &mut Context<Self>,
 3214    ) {
 3215        let multibuffer = cx
 3216            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3217
 3218        let style = window.text_style();
 3219
 3220        self.placeholder_display_map = Some(cx.new(|cx| {
 3221            DisplayMap::new(
 3222                multibuffer,
 3223                style.font(),
 3224                style.font_size.to_pixels(window.rem_size()),
 3225                None,
 3226                FILE_HEADER_HEIGHT,
 3227                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3228                Default::default(),
 3229                DiagnosticSeverity::Off,
 3230                cx,
 3231            )
 3232        }));
 3233        cx.notify();
 3234    }
 3235
 3236    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3237        self.cursor_shape = cursor_shape;
 3238
 3239        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3240        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3241
 3242        cx.notify();
 3243    }
 3244
 3245    pub fn cursor_shape(&self) -> CursorShape {
 3246        self.cursor_shape
 3247    }
 3248
 3249    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3250        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3251    }
 3252
 3253    pub fn set_current_line_highlight(
 3254        &mut self,
 3255        current_line_highlight: Option<CurrentLineHighlight>,
 3256    ) {
 3257        self.current_line_highlight = current_line_highlight;
 3258    }
 3259
 3260    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3261        self.collapse_matches = collapse_matches;
 3262    }
 3263
 3264    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3265        if self.collapse_matches {
 3266            return range.start..range.start;
 3267        }
 3268        range.clone()
 3269    }
 3270
 3271    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3272        self.display_map.read(cx).clip_at_line_ends
 3273    }
 3274
 3275    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3276        if self.display_map.read(cx).clip_at_line_ends != clip {
 3277            self.display_map
 3278                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3279        }
 3280    }
 3281
 3282    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3283        self.input_enabled = input_enabled;
 3284    }
 3285
 3286    pub fn set_edit_predictions_hidden_for_vim_mode(
 3287        &mut self,
 3288        hidden: bool,
 3289        window: &mut Window,
 3290        cx: &mut Context<Self>,
 3291    ) {
 3292        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3293            self.edit_predictions_hidden_for_vim_mode = hidden;
 3294            if hidden {
 3295                self.update_visible_edit_prediction(window, cx);
 3296            } else {
 3297                self.refresh_edit_prediction(true, false, window, cx);
 3298            }
 3299        }
 3300    }
 3301
 3302    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3303        self.menu_edit_predictions_policy = value;
 3304    }
 3305
 3306    pub fn set_autoindent(&mut self, autoindent: bool) {
 3307        if autoindent {
 3308            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3309        } else {
 3310            self.autoindent_mode = None;
 3311        }
 3312    }
 3313
 3314    pub fn capability(&self, cx: &App) -> Capability {
 3315        if self.read_only {
 3316            Capability::ReadOnly
 3317        } else {
 3318            self.buffer.read(cx).capability()
 3319        }
 3320    }
 3321
 3322    pub fn read_only(&self, cx: &App) -> bool {
 3323        self.read_only || self.buffer.read(cx).read_only()
 3324    }
 3325
 3326    pub fn set_read_only(&mut self, read_only: bool) {
 3327        self.read_only = read_only;
 3328    }
 3329
 3330    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3331        self.use_autoclose = autoclose;
 3332    }
 3333
 3334    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3335        self.use_auto_surround = auto_surround;
 3336    }
 3337
 3338    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3339        self.auto_replace_emoji_shortcode = auto_replace;
 3340    }
 3341
 3342    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3343        self.buffer_serialization = should_serialize.then(|| {
 3344            BufferSerialization::new(
 3345                ProjectSettings::get_global(cx)
 3346                    .session
 3347                    .restore_unsaved_buffers,
 3348            )
 3349        })
 3350    }
 3351
 3352    fn should_serialize_buffer(&self) -> bool {
 3353        self.buffer_serialization.is_some()
 3354    }
 3355
 3356    pub fn toggle_edit_predictions(
 3357        &mut self,
 3358        _: &ToggleEditPrediction,
 3359        window: &mut Window,
 3360        cx: &mut Context<Self>,
 3361    ) {
 3362        if self.show_edit_predictions_override.is_some() {
 3363            self.set_show_edit_predictions(None, window, cx);
 3364        } else {
 3365            let show_edit_predictions = !self.edit_predictions_enabled();
 3366            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3367        }
 3368    }
 3369
 3370    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3371        self.show_completions_on_input_override = show_completions_on_input;
 3372    }
 3373
 3374    pub fn set_show_edit_predictions(
 3375        &mut self,
 3376        show_edit_predictions: Option<bool>,
 3377        window: &mut Window,
 3378        cx: &mut Context<Self>,
 3379    ) {
 3380        self.show_edit_predictions_override = show_edit_predictions;
 3381        self.update_edit_prediction_settings(cx);
 3382
 3383        if let Some(false) = show_edit_predictions {
 3384            self.discard_edit_prediction(false, cx);
 3385        } else {
 3386            self.refresh_edit_prediction(false, true, window, cx);
 3387        }
 3388    }
 3389
 3390    fn edit_predictions_disabled_in_scope(
 3391        &self,
 3392        buffer: &Entity<Buffer>,
 3393        buffer_position: language::Anchor,
 3394        cx: &App,
 3395    ) -> bool {
 3396        let snapshot = buffer.read(cx).snapshot();
 3397        let settings = snapshot.settings_at(buffer_position, cx);
 3398
 3399        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3400            return false;
 3401        };
 3402
 3403        scope.override_name().is_some_and(|scope_name| {
 3404            settings
 3405                .edit_predictions_disabled_in
 3406                .iter()
 3407                .any(|s| s == scope_name)
 3408        })
 3409    }
 3410
 3411    pub fn set_use_modal_editing(&mut self, to: bool) {
 3412        self.use_modal_editing = to;
 3413    }
 3414
 3415    pub fn use_modal_editing(&self) -> bool {
 3416        self.use_modal_editing
 3417    }
 3418
 3419    fn selections_did_change(
 3420        &mut self,
 3421        local: bool,
 3422        old_cursor_position: &Anchor,
 3423        effects: SelectionEffects,
 3424        window: &mut Window,
 3425        cx: &mut Context<Self>,
 3426    ) {
 3427        window.invalidate_character_coordinates();
 3428
 3429        // Copy selections to primary selection buffer
 3430        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3431        if local {
 3432            let selections = self
 3433                .selections
 3434                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3435            let buffer_handle = self.buffer.read(cx).read(cx);
 3436
 3437            let mut text = String::new();
 3438            for (index, selection) in selections.iter().enumerate() {
 3439                let text_for_selection = buffer_handle
 3440                    .text_for_range(selection.start..selection.end)
 3441                    .collect::<String>();
 3442
 3443                text.push_str(&text_for_selection);
 3444                if index != selections.len() - 1 {
 3445                    text.push('\n');
 3446                }
 3447            }
 3448
 3449            if !text.is_empty() {
 3450                cx.write_to_primary(ClipboardItem::new_string(text));
 3451            }
 3452        }
 3453
 3454        let selection_anchors = self.selections.disjoint_anchors_arc();
 3455
 3456        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3457            self.buffer.update(cx, |buffer, cx| {
 3458                buffer.set_active_selections(
 3459                    &selection_anchors,
 3460                    self.selections.line_mode(),
 3461                    self.cursor_shape,
 3462                    cx,
 3463                )
 3464            });
 3465        }
 3466        let display_map = self
 3467            .display_map
 3468            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3469        let buffer = display_map.buffer_snapshot();
 3470        if self.selections.count() == 1 {
 3471            self.add_selections_state = None;
 3472        }
 3473        self.select_next_state = None;
 3474        self.select_prev_state = None;
 3475        self.select_syntax_node_history.try_clear();
 3476        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3477        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3478        self.take_rename(false, window, cx);
 3479
 3480        let newest_selection = self.selections.newest_anchor();
 3481        let new_cursor_position = newest_selection.head();
 3482        let selection_start = newest_selection.start;
 3483
 3484        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3485            self.push_to_nav_history(
 3486                *old_cursor_position,
 3487                Some(new_cursor_position.to_point(buffer)),
 3488                false,
 3489                effects.nav_history == Some(true),
 3490                cx,
 3491            );
 3492        }
 3493
 3494        if local {
 3495            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3496                self.register_buffer(buffer_id, cx);
 3497            }
 3498
 3499            let mut context_menu = self.context_menu.borrow_mut();
 3500            let completion_menu = match context_menu.as_ref() {
 3501                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3502                Some(CodeContextMenu::CodeActions(_)) => {
 3503                    *context_menu = None;
 3504                    None
 3505                }
 3506                None => None,
 3507            };
 3508            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3509            drop(context_menu);
 3510
 3511            if effects.completions
 3512                && let Some(completion_position) = completion_position
 3513            {
 3514                let start_offset = selection_start.to_offset(buffer);
 3515                let position_matches = start_offset == completion_position.to_offset(buffer);
 3516                let continue_showing = if let Some((snap, ..)) =
 3517                    buffer.point_to_buffer_offset(completion_position)
 3518                    && !snap.capability.editable()
 3519                {
 3520                    false
 3521                } else if position_matches {
 3522                    if self.snippet_stack.is_empty() {
 3523                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3524                            == Some(CharKind::Word)
 3525                    } else {
 3526                        // Snippet choices can be shown even when the cursor is in whitespace.
 3527                        // Dismissing the menu with actions like backspace is handled by
 3528                        // invalidation regions.
 3529                        true
 3530                    }
 3531                } else {
 3532                    false
 3533                };
 3534
 3535                if continue_showing {
 3536                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3537                } else {
 3538                    self.hide_context_menu(window, cx);
 3539                }
 3540            }
 3541
 3542            hide_hover(self, cx);
 3543
 3544            if old_cursor_position.to_display_point(&display_map).row()
 3545                != new_cursor_position.to_display_point(&display_map).row()
 3546            {
 3547                self.available_code_actions.take();
 3548            }
 3549            self.refresh_code_actions(window, cx);
 3550            self.refresh_document_highlights(cx);
 3551            refresh_linked_ranges(self, window, cx);
 3552
 3553            self.refresh_selected_text_highlights(false, window, cx);
 3554            self.refresh_matching_bracket_highlights(window, cx);
 3555            self.update_visible_edit_prediction(window, cx);
 3556            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3557            self.inline_blame_popover.take();
 3558            if self.git_blame_inline_enabled {
 3559                self.start_inline_blame_timer(window, cx);
 3560            }
 3561        }
 3562
 3563        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3564
 3565        if local && !self.suppress_selection_callback {
 3566            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3567                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3568                callback(cursor_position, window, cx);
 3569            }
 3570        }
 3571
 3572        cx.emit(EditorEvent::SelectionsChanged { local });
 3573
 3574        let selections = &self.selections.disjoint_anchors_arc();
 3575        if selections.len() == 1 {
 3576            cx.emit(SearchEvent::ActiveMatchChanged)
 3577        }
 3578        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3579            let inmemory_selections = selections
 3580                .iter()
 3581                .map(|s| {
 3582                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3583                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3584                })
 3585                .collect();
 3586            self.update_restoration_data(cx, |data| {
 3587                data.selections = inmemory_selections;
 3588            });
 3589
 3590            if WorkspaceSettings::get(None, cx).restore_on_startup
 3591                != RestoreOnStartupBehavior::EmptyTab
 3592                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3593            {
 3594                let snapshot = self.buffer().read(cx).snapshot(cx);
 3595                let selections = selections.clone();
 3596                let background_executor = cx.background_executor().clone();
 3597                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3598                self.serialize_selections = cx.background_spawn(async move {
 3599                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3600                    let db_selections = selections
 3601                        .iter()
 3602                        .map(|selection| {
 3603                            (
 3604                                selection.start.to_offset(&snapshot).0,
 3605                                selection.end.to_offset(&snapshot).0,
 3606                            )
 3607                        })
 3608                        .collect();
 3609
 3610                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3611                        .await
 3612                        .with_context(|| {
 3613                            format!(
 3614                                "persisting editor selections for editor {editor_id}, \
 3615                                workspace {workspace_id:?}"
 3616                            )
 3617                        })
 3618                        .log_err();
 3619                });
 3620            }
 3621        }
 3622
 3623        cx.notify();
 3624    }
 3625
 3626    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3627        use text::ToOffset as _;
 3628        use text::ToPoint as _;
 3629
 3630        if self.mode.is_minimap()
 3631            || WorkspaceSettings::get(None, cx).restore_on_startup
 3632                == RestoreOnStartupBehavior::EmptyTab
 3633        {
 3634            return;
 3635        }
 3636
 3637        if !self.buffer().read(cx).is_singleton() {
 3638            return;
 3639        }
 3640
 3641        let display_snapshot = self
 3642            .display_map
 3643            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3644        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3645            return;
 3646        };
 3647        let inmemory_folds = display_snapshot
 3648            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3649            .map(|fold| {
 3650                fold.range.start.text_anchor.to_point(&snapshot)
 3651                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3652            })
 3653            .collect();
 3654        self.update_restoration_data(cx, |data| {
 3655            data.folds = inmemory_folds;
 3656        });
 3657
 3658        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3659            return;
 3660        };
 3661        let background_executor = cx.background_executor().clone();
 3662        let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3663        const FINGERPRINT_LEN: usize = 32;
 3664        let db_folds = display_snapshot
 3665            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3666            .map(|fold| {
 3667                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3668                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3669
 3670                // Extract fingerprints - content at fold boundaries for validation on restore
 3671                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3672                // content that might change independently.
 3673                // start_fp: first min(32, fold_len) bytes of fold content
 3674                // end_fp: last min(32, fold_len) bytes of fold content
 3675                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3676                let fold_len = end - start;
 3677                let start_fp_end = snapshot
 3678                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3679                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3680                let end_fp_start = snapshot
 3681                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3682                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3683
 3684                (start, end, start_fp, end_fp)
 3685            })
 3686            .collect::<Vec<_>>();
 3687        self.serialize_folds = cx.background_spawn(async move {
 3688            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3689            DB.save_editor_folds(editor_id, workspace_id, db_folds)
 3690                .await
 3691                .with_context(|| {
 3692                    format!(
 3693                        "persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"
 3694                    )
 3695                })
 3696                .log_err();
 3697        });
 3698    }
 3699
 3700    pub fn sync_selections(
 3701        &mut self,
 3702        other: Entity<Editor>,
 3703        cx: &mut Context<Self>,
 3704    ) -> gpui::Subscription {
 3705        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3706        if !other_selections.is_empty() {
 3707            self.selections
 3708                .change_with(&self.display_snapshot(cx), |selections| {
 3709                    selections.select_anchors(other_selections);
 3710                });
 3711        }
 3712
 3713        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3714            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3715                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3716                if other_selections.is_empty() {
 3717                    return;
 3718                }
 3719                let snapshot = this.display_snapshot(cx);
 3720                this.selections.change_with(&snapshot, |selections| {
 3721                    selections.select_anchors(other_selections);
 3722                });
 3723            }
 3724        });
 3725
 3726        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3727            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3728                let these_selections = this.selections.disjoint_anchors().to_vec();
 3729                if these_selections.is_empty() {
 3730                    return;
 3731                }
 3732                other.update(cx, |other_editor, cx| {
 3733                    let snapshot = other_editor.display_snapshot(cx);
 3734                    other_editor
 3735                        .selections
 3736                        .change_with(&snapshot, |selections| {
 3737                            selections.select_anchors(these_selections);
 3738                        })
 3739                });
 3740            }
 3741        });
 3742
 3743        Subscription::join(other_subscription, this_subscription)
 3744    }
 3745
 3746    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3747        if self.buffer().read(cx).is_singleton() {
 3748            return;
 3749        }
 3750        let snapshot = self.buffer.read(cx).snapshot(cx);
 3751        let buffer_ids: HashSet<BufferId> = self
 3752            .selections
 3753            .disjoint_anchor_ranges()
 3754            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3755            .collect();
 3756        for buffer_id in buffer_ids {
 3757            self.unfold_buffer(buffer_id, cx);
 3758        }
 3759    }
 3760
 3761    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3762    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3763    /// effects of selection change occur at the end of the transaction.
 3764    pub fn change_selections<R>(
 3765        &mut self,
 3766        effects: SelectionEffects,
 3767        window: &mut Window,
 3768        cx: &mut Context<Self>,
 3769        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3770    ) -> R {
 3771        let snapshot = self.display_snapshot(cx);
 3772        if let Some(state) = &mut self.deferred_selection_effects_state {
 3773            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3774            state.effects.completions = effects.completions;
 3775            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3776            let (changed, result) = self.selections.change_with(&snapshot, change);
 3777            state.changed |= changed;
 3778            return result;
 3779        }
 3780        let mut state = DeferredSelectionEffectsState {
 3781            changed: false,
 3782            effects,
 3783            old_cursor_position: self.selections.newest_anchor().head(),
 3784            history_entry: SelectionHistoryEntry {
 3785                selections: self.selections.disjoint_anchors_arc(),
 3786                select_next_state: self.select_next_state.clone(),
 3787                select_prev_state: self.select_prev_state.clone(),
 3788                add_selections_state: self.add_selections_state.clone(),
 3789            },
 3790        };
 3791        let (changed, result) = self.selections.change_with(&snapshot, change);
 3792        state.changed = state.changed || changed;
 3793        if self.defer_selection_effects {
 3794            self.deferred_selection_effects_state = Some(state);
 3795        } else {
 3796            self.apply_selection_effects(state, window, cx);
 3797        }
 3798        result
 3799    }
 3800
 3801    /// Defers the effects of selection change, so that the effects of multiple calls to
 3802    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3803    /// to selection history and the state of popovers based on selection position aren't
 3804    /// erroneously updated.
 3805    pub fn with_selection_effects_deferred<R>(
 3806        &mut self,
 3807        window: &mut Window,
 3808        cx: &mut Context<Self>,
 3809        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3810    ) -> R {
 3811        let already_deferred = self.defer_selection_effects;
 3812        self.defer_selection_effects = true;
 3813        let result = update(self, window, cx);
 3814        if !already_deferred {
 3815            self.defer_selection_effects = false;
 3816            if let Some(state) = self.deferred_selection_effects_state.take() {
 3817                self.apply_selection_effects(state, window, cx);
 3818            }
 3819        }
 3820        result
 3821    }
 3822
 3823    fn apply_selection_effects(
 3824        &mut self,
 3825        state: DeferredSelectionEffectsState,
 3826        window: &mut Window,
 3827        cx: &mut Context<Self>,
 3828    ) {
 3829        if state.changed {
 3830            self.selection_history.push(state.history_entry);
 3831
 3832            if let Some(autoscroll) = state.effects.scroll {
 3833                self.request_autoscroll(autoscroll, cx);
 3834            }
 3835
 3836            let old_cursor_position = &state.old_cursor_position;
 3837
 3838            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3839
 3840            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3841                self.show_signature_help_auto(window, cx);
 3842            }
 3843        }
 3844    }
 3845
 3846    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3847    where
 3848        I: IntoIterator<Item = (Range<S>, T)>,
 3849        S: ToOffset,
 3850        T: Into<Arc<str>>,
 3851    {
 3852        if self.read_only(cx) {
 3853            return;
 3854        }
 3855
 3856        self.buffer
 3857            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3858    }
 3859
 3860    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3861    where
 3862        I: IntoIterator<Item = (Range<S>, T)>,
 3863        S: ToOffset,
 3864        T: Into<Arc<str>>,
 3865    {
 3866        if self.read_only(cx) {
 3867            return;
 3868        }
 3869
 3870        self.buffer.update(cx, |buffer, cx| {
 3871            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3872        });
 3873    }
 3874
 3875    pub fn edit_with_block_indent<I, S, T>(
 3876        &mut self,
 3877        edits: I,
 3878        original_indent_columns: Vec<Option<u32>>,
 3879        cx: &mut Context<Self>,
 3880    ) where
 3881        I: IntoIterator<Item = (Range<S>, T)>,
 3882        S: ToOffset,
 3883        T: Into<Arc<str>>,
 3884    {
 3885        if self.read_only(cx) {
 3886            return;
 3887        }
 3888
 3889        self.buffer.update(cx, |buffer, cx| {
 3890            buffer.edit(
 3891                edits,
 3892                Some(AutoindentMode::Block {
 3893                    original_indent_columns,
 3894                }),
 3895                cx,
 3896            )
 3897        });
 3898    }
 3899
 3900    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3901        self.hide_context_menu(window, cx);
 3902
 3903        match phase {
 3904            SelectPhase::Begin {
 3905                position,
 3906                add,
 3907                click_count,
 3908            } => self.begin_selection(position, add, click_count, window, cx),
 3909            SelectPhase::BeginColumnar {
 3910                position,
 3911                goal_column,
 3912                reset,
 3913                mode,
 3914            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 3915            SelectPhase::Extend {
 3916                position,
 3917                click_count,
 3918            } => self.extend_selection(position, click_count, window, cx),
 3919            SelectPhase::Update {
 3920                position,
 3921                goal_column,
 3922                scroll_delta,
 3923            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 3924            SelectPhase::End => self.end_selection(window, cx),
 3925        }
 3926    }
 3927
 3928    fn extend_selection(
 3929        &mut self,
 3930        position: DisplayPoint,
 3931        click_count: usize,
 3932        window: &mut Window,
 3933        cx: &mut Context<Self>,
 3934    ) {
 3935        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3936        let tail = self
 3937            .selections
 3938            .newest::<MultiBufferOffset>(&display_map)
 3939            .tail();
 3940        let click_count = click_count.max(match self.selections.select_mode() {
 3941            SelectMode::Character => 1,
 3942            SelectMode::Word(_) => 2,
 3943            SelectMode::Line(_) => 3,
 3944            SelectMode::All => 4,
 3945        });
 3946        self.begin_selection(position, false, click_count, window, cx);
 3947
 3948        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 3949
 3950        let current_selection = match self.selections.select_mode() {
 3951            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 3952            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 3953        };
 3954
 3955        let mut pending_selection = self
 3956            .selections
 3957            .pending_anchor()
 3958            .cloned()
 3959            .expect("extend_selection not called with pending selection");
 3960
 3961        if pending_selection
 3962            .start
 3963            .cmp(&current_selection.start, display_map.buffer_snapshot())
 3964            == Ordering::Greater
 3965        {
 3966            pending_selection.start = current_selection.start;
 3967        }
 3968        if pending_selection
 3969            .end
 3970            .cmp(&current_selection.end, display_map.buffer_snapshot())
 3971            == Ordering::Less
 3972        {
 3973            pending_selection.end = current_selection.end;
 3974            pending_selection.reversed = true;
 3975        }
 3976
 3977        let mut pending_mode = self.selections.pending_mode().unwrap();
 3978        match &mut pending_mode {
 3979            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 3980            _ => {}
 3981        }
 3982
 3983        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 3984            SelectionEffects::scroll(Autoscroll::fit())
 3985        } else {
 3986            SelectionEffects::no_scroll()
 3987        };
 3988
 3989        self.change_selections(effects, window, cx, |s| {
 3990            s.set_pending(pending_selection.clone(), pending_mode);
 3991            s.set_is_extending(true);
 3992        });
 3993    }
 3994
 3995    fn begin_selection(
 3996        &mut self,
 3997        position: DisplayPoint,
 3998        add: bool,
 3999        click_count: usize,
 4000        window: &mut Window,
 4001        cx: &mut Context<Self>,
 4002    ) {
 4003        if !self.focus_handle.is_focused(window) {
 4004            self.last_focused_descendant = None;
 4005            window.focus(&self.focus_handle, cx);
 4006        }
 4007
 4008        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4009        let buffer = display_map.buffer_snapshot();
 4010        let position = display_map.clip_point(position, Bias::Left);
 4011
 4012        let start;
 4013        let end;
 4014        let mode;
 4015        let mut auto_scroll;
 4016        match click_count {
 4017            1 => {
 4018                start = buffer.anchor_before(position.to_point(&display_map));
 4019                end = start;
 4020                mode = SelectMode::Character;
 4021                auto_scroll = true;
 4022            }
 4023            2 => {
 4024                let position = display_map
 4025                    .clip_point(position, Bias::Left)
 4026                    .to_offset(&display_map, Bias::Left);
 4027                let (range, _) = buffer.surrounding_word(position, None);
 4028                start = buffer.anchor_before(range.start);
 4029                end = buffer.anchor_before(range.end);
 4030                mode = SelectMode::Word(start..end);
 4031                auto_scroll = true;
 4032            }
 4033            3 => {
 4034                let position = display_map
 4035                    .clip_point(position, Bias::Left)
 4036                    .to_point(&display_map);
 4037                let line_start = display_map.prev_line_boundary(position).0;
 4038                let next_line_start = buffer.clip_point(
 4039                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4040                    Bias::Left,
 4041                );
 4042                start = buffer.anchor_before(line_start);
 4043                end = buffer.anchor_before(next_line_start);
 4044                mode = SelectMode::Line(start..end);
 4045                auto_scroll = true;
 4046            }
 4047            _ => {
 4048                start = buffer.anchor_before(MultiBufferOffset(0));
 4049                end = buffer.anchor_before(buffer.len());
 4050                mode = SelectMode::All;
 4051                auto_scroll = false;
 4052            }
 4053        }
 4054        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4055
 4056        let point_to_delete: Option<usize> = {
 4057            let selected_points: Vec<Selection<Point>> =
 4058                self.selections.disjoint_in_range(start..end, &display_map);
 4059
 4060            if !add || click_count > 1 {
 4061                None
 4062            } else if !selected_points.is_empty() {
 4063                Some(selected_points[0].id)
 4064            } else {
 4065                let clicked_point_already_selected =
 4066                    self.selections.disjoint_anchors().iter().find(|selection| {
 4067                        selection.start.to_point(buffer) == start.to_point(buffer)
 4068                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4069                    });
 4070
 4071                clicked_point_already_selected.map(|selection| selection.id)
 4072            }
 4073        };
 4074
 4075        let selections_count = self.selections.count();
 4076        let effects = if auto_scroll {
 4077            SelectionEffects::default()
 4078        } else {
 4079            SelectionEffects::no_scroll()
 4080        };
 4081
 4082        self.change_selections(effects, window, cx, |s| {
 4083            if let Some(point_to_delete) = point_to_delete {
 4084                s.delete(point_to_delete);
 4085
 4086                if selections_count == 1 {
 4087                    s.set_pending_anchor_range(start..end, mode);
 4088                }
 4089            } else {
 4090                if !add {
 4091                    s.clear_disjoint();
 4092                }
 4093
 4094                s.set_pending_anchor_range(start..end, mode);
 4095            }
 4096        });
 4097    }
 4098
 4099    fn begin_columnar_selection(
 4100        &mut self,
 4101        position: DisplayPoint,
 4102        goal_column: u32,
 4103        reset: bool,
 4104        mode: ColumnarMode,
 4105        window: &mut Window,
 4106        cx: &mut Context<Self>,
 4107    ) {
 4108        if !self.focus_handle.is_focused(window) {
 4109            self.last_focused_descendant = None;
 4110            window.focus(&self.focus_handle, cx);
 4111        }
 4112
 4113        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4114
 4115        if reset {
 4116            let pointer_position = display_map
 4117                .buffer_snapshot()
 4118                .anchor_before(position.to_point(&display_map));
 4119
 4120            self.change_selections(
 4121                SelectionEffects::scroll(Autoscroll::newest()),
 4122                window,
 4123                cx,
 4124                |s| {
 4125                    s.clear_disjoint();
 4126                    s.set_pending_anchor_range(
 4127                        pointer_position..pointer_position,
 4128                        SelectMode::Character,
 4129                    );
 4130                },
 4131            );
 4132        };
 4133
 4134        let tail = self.selections.newest::<Point>(&display_map).tail();
 4135        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4136        self.columnar_selection_state = match mode {
 4137            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4138                selection_tail: selection_anchor,
 4139                display_point: if reset {
 4140                    if position.column() != goal_column {
 4141                        Some(DisplayPoint::new(position.row(), goal_column))
 4142                    } else {
 4143                        None
 4144                    }
 4145                } else {
 4146                    None
 4147                },
 4148            }),
 4149            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4150                selection_tail: selection_anchor,
 4151            }),
 4152        };
 4153
 4154        if !reset {
 4155            self.select_columns(position, goal_column, &display_map, window, cx);
 4156        }
 4157    }
 4158
 4159    fn update_selection(
 4160        &mut self,
 4161        position: DisplayPoint,
 4162        goal_column: u32,
 4163        scroll_delta: gpui::Point<f32>,
 4164        window: &mut Window,
 4165        cx: &mut Context<Self>,
 4166    ) {
 4167        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4168
 4169        if self.columnar_selection_state.is_some() {
 4170            self.select_columns(position, goal_column, &display_map, window, cx);
 4171        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4172            let buffer = display_map.buffer_snapshot();
 4173            let head;
 4174            let tail;
 4175            let mode = self.selections.pending_mode().unwrap();
 4176            match &mode {
 4177                SelectMode::Character => {
 4178                    head = position.to_point(&display_map);
 4179                    tail = pending.tail().to_point(buffer);
 4180                }
 4181                SelectMode::Word(original_range) => {
 4182                    let offset = display_map
 4183                        .clip_point(position, Bias::Left)
 4184                        .to_offset(&display_map, Bias::Left);
 4185                    let original_range = original_range.to_offset(buffer);
 4186
 4187                    let head_offset = if buffer.is_inside_word(offset, None)
 4188                        || original_range.contains(&offset)
 4189                    {
 4190                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4191                        if word_range.start < original_range.start {
 4192                            word_range.start
 4193                        } else {
 4194                            word_range.end
 4195                        }
 4196                    } else {
 4197                        offset
 4198                    };
 4199
 4200                    head = head_offset.to_point(buffer);
 4201                    if head_offset <= original_range.start {
 4202                        tail = original_range.end.to_point(buffer);
 4203                    } else {
 4204                        tail = original_range.start.to_point(buffer);
 4205                    }
 4206                }
 4207                SelectMode::Line(original_range) => {
 4208                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4209
 4210                    let position = display_map
 4211                        .clip_point(position, Bias::Left)
 4212                        .to_point(&display_map);
 4213                    let line_start = display_map.prev_line_boundary(position).0;
 4214                    let next_line_start = buffer.clip_point(
 4215                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4216                        Bias::Left,
 4217                    );
 4218
 4219                    if line_start < original_range.start {
 4220                        head = line_start
 4221                    } else {
 4222                        head = next_line_start
 4223                    }
 4224
 4225                    if head <= original_range.start {
 4226                        tail = original_range.end;
 4227                    } else {
 4228                        tail = original_range.start;
 4229                    }
 4230                }
 4231                SelectMode::All => {
 4232                    return;
 4233                }
 4234            };
 4235
 4236            if head < tail {
 4237                pending.start = buffer.anchor_before(head);
 4238                pending.end = buffer.anchor_before(tail);
 4239                pending.reversed = true;
 4240            } else {
 4241                pending.start = buffer.anchor_before(tail);
 4242                pending.end = buffer.anchor_before(head);
 4243                pending.reversed = false;
 4244            }
 4245
 4246            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4247                s.set_pending(pending.clone(), mode);
 4248            });
 4249        } else {
 4250            log::error!("update_selection dispatched with no pending selection");
 4251            return;
 4252        }
 4253
 4254        self.apply_scroll_delta(scroll_delta, window, cx);
 4255        cx.notify();
 4256    }
 4257
 4258    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4259        self.columnar_selection_state.take();
 4260        if let Some(pending_mode) = self.selections.pending_mode() {
 4261            let selections = self
 4262                .selections
 4263                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4264            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4265                s.select(selections);
 4266                s.clear_pending();
 4267                if s.is_extending() {
 4268                    s.set_is_extending(false);
 4269                } else {
 4270                    s.set_select_mode(pending_mode);
 4271                }
 4272            });
 4273        }
 4274    }
 4275
 4276    fn select_columns(
 4277        &mut self,
 4278        head: DisplayPoint,
 4279        goal_column: u32,
 4280        display_map: &DisplaySnapshot,
 4281        window: &mut Window,
 4282        cx: &mut Context<Self>,
 4283    ) {
 4284        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4285            return;
 4286        };
 4287
 4288        let tail = match columnar_state {
 4289            ColumnarSelectionState::FromMouse {
 4290                selection_tail,
 4291                display_point,
 4292            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4293            ColumnarSelectionState::FromSelection { selection_tail } => {
 4294                selection_tail.to_display_point(display_map)
 4295            }
 4296        };
 4297
 4298        let start_row = cmp::min(tail.row(), head.row());
 4299        let end_row = cmp::max(tail.row(), head.row());
 4300        let start_column = cmp::min(tail.column(), goal_column);
 4301        let end_column = cmp::max(tail.column(), goal_column);
 4302        let reversed = start_column < tail.column();
 4303
 4304        let selection_ranges = (start_row.0..=end_row.0)
 4305            .map(DisplayRow)
 4306            .filter_map(|row| {
 4307                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4308                    || start_column <= display_map.line_len(row))
 4309                    && !display_map.is_block_line(row)
 4310                {
 4311                    let start = display_map
 4312                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4313                        .to_point(display_map);
 4314                    let end = display_map
 4315                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4316                        .to_point(display_map);
 4317                    if reversed {
 4318                        Some(end..start)
 4319                    } else {
 4320                        Some(start..end)
 4321                    }
 4322                } else {
 4323                    None
 4324                }
 4325            })
 4326            .collect::<Vec<_>>();
 4327        if selection_ranges.is_empty() {
 4328            return;
 4329        }
 4330
 4331        let ranges = match columnar_state {
 4332            ColumnarSelectionState::FromMouse { .. } => {
 4333                let mut non_empty_ranges = selection_ranges
 4334                    .iter()
 4335                    .filter(|selection_range| selection_range.start != selection_range.end)
 4336                    .peekable();
 4337                if non_empty_ranges.peek().is_some() {
 4338                    non_empty_ranges.cloned().collect()
 4339                } else {
 4340                    selection_ranges
 4341                }
 4342            }
 4343            _ => selection_ranges,
 4344        };
 4345
 4346        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4347            s.select_ranges(ranges);
 4348        });
 4349        cx.notify();
 4350    }
 4351
 4352    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4353        self.selections
 4354            .all_adjusted(snapshot)
 4355            .iter()
 4356            .any(|selection| !selection.is_empty())
 4357    }
 4358
 4359    pub fn has_pending_nonempty_selection(&self) -> bool {
 4360        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4361            Some(Selection { start, end, .. }) => start != end,
 4362            None => false,
 4363        };
 4364
 4365        pending_nonempty_selection
 4366            || (self.columnar_selection_state.is_some()
 4367                && self.selections.disjoint_anchors().len() > 1)
 4368    }
 4369
 4370    pub fn has_pending_selection(&self) -> bool {
 4371        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4372    }
 4373
 4374    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4375        self.selection_mark_mode = false;
 4376        self.selection_drag_state = SelectionDragState::None;
 4377
 4378        if self.dismiss_menus_and_popups(true, window, cx) {
 4379            cx.notify();
 4380            return;
 4381        }
 4382        if self.clear_expanded_diff_hunks(cx) {
 4383            cx.notify();
 4384            return;
 4385        }
 4386        if self.show_git_blame_gutter {
 4387            self.show_git_blame_gutter = false;
 4388            cx.notify();
 4389            return;
 4390        }
 4391
 4392        if self.mode.is_full()
 4393            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4394        {
 4395            cx.notify();
 4396            return;
 4397        }
 4398
 4399        cx.propagate();
 4400    }
 4401
 4402    pub fn dismiss_menus_and_popups(
 4403        &mut self,
 4404        is_user_requested: bool,
 4405        window: &mut Window,
 4406        cx: &mut Context<Self>,
 4407    ) -> bool {
 4408        let mut dismissed = false;
 4409
 4410        dismissed |= self.take_rename(false, window, cx).is_some();
 4411        dismissed |= self.hide_blame_popover(true, cx);
 4412        dismissed |= hide_hover(self, cx);
 4413        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4414        dismissed |= self.hide_context_menu(window, cx).is_some();
 4415        dismissed |= self.mouse_context_menu.take().is_some();
 4416        dismissed |= is_user_requested && self.discard_edit_prediction(true, cx);
 4417        dismissed |= self.snippet_stack.pop().is_some();
 4418        if self.diff_review_drag_state.is_some() {
 4419            self.cancel_diff_review_drag(cx);
 4420            dismissed = true;
 4421        }
 4422        if !self.diff_review_overlays.is_empty() {
 4423            self.dismiss_all_diff_review_overlays(cx);
 4424            dismissed = true;
 4425        }
 4426
 4427        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4428            self.dismiss_diagnostics(cx);
 4429            dismissed = true;
 4430        }
 4431
 4432        dismissed
 4433    }
 4434
 4435    fn linked_editing_ranges_for(
 4436        &self,
 4437        selection: Range<text::Anchor>,
 4438        cx: &App,
 4439    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4440        if self.linked_edit_ranges.is_empty() {
 4441            return None;
 4442        }
 4443        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4444            selection.end.buffer_id.and_then(|end_buffer_id| {
 4445                if selection.start.buffer_id != Some(end_buffer_id) {
 4446                    return None;
 4447                }
 4448                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4449                let snapshot = buffer.read(cx).snapshot();
 4450                self.linked_edit_ranges
 4451                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4452                    .map(|ranges| (ranges, snapshot, buffer))
 4453            })?;
 4454        use text::ToOffset as TO;
 4455        // find offset from the start of current range to current cursor position
 4456        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4457
 4458        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4459        let start_difference = start_offset - start_byte_offset;
 4460        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4461        let end_difference = end_offset - start_byte_offset;
 4462        // Current range has associated linked ranges.
 4463        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4464        for range in linked_ranges.iter() {
 4465            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4466            let end_offset = start_offset + end_difference;
 4467            let start_offset = start_offset + start_difference;
 4468            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4469                continue;
 4470            }
 4471            if self.selections.disjoint_anchor_ranges().any(|s| {
 4472                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4473                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4474                {
 4475                    return false;
 4476                }
 4477                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4478                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4479            }) {
 4480                continue;
 4481            }
 4482            let start = buffer_snapshot.anchor_after(start_offset);
 4483            let end = buffer_snapshot.anchor_after(end_offset);
 4484            linked_edits
 4485                .entry(buffer.clone())
 4486                .or_default()
 4487                .push(start..end);
 4488        }
 4489        Some(linked_edits)
 4490    }
 4491
 4492    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4493        let text: Arc<str> = text.into();
 4494
 4495        if self.read_only(cx) {
 4496            return;
 4497        }
 4498
 4499        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4500
 4501        self.unfold_buffers_with_selections(cx);
 4502
 4503        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4504        let mut bracket_inserted = false;
 4505        let mut edits = Vec::new();
 4506        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4507        let mut new_selections = Vec::with_capacity(selections.len());
 4508        let mut new_autoclose_regions = Vec::new();
 4509        let snapshot = self.buffer.read(cx).read(cx);
 4510        let mut clear_linked_edit_ranges = false;
 4511        let mut all_selections_read_only = true;
 4512        let mut has_adjacent_edits = false;
 4513        let mut in_adjacent_group = false;
 4514
 4515        let mut regions = self
 4516            .selections_with_autoclose_regions(selections, &snapshot)
 4517            .peekable();
 4518
 4519        while let Some((selection, autoclose_region)) = regions.next() {
 4520            if snapshot
 4521                .point_to_buffer_point(selection.head())
 4522                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4523            {
 4524                continue;
 4525            }
 4526            if snapshot
 4527                .point_to_buffer_point(selection.tail())
 4528                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4529            {
 4530                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4531                continue;
 4532            }
 4533            all_selections_read_only = false;
 4534
 4535            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4536                // Determine if the inserted text matches the opening or closing
 4537                // bracket of any of this language's bracket pairs.
 4538                let mut bracket_pair = None;
 4539                let mut is_bracket_pair_start = false;
 4540                let mut is_bracket_pair_end = false;
 4541                if !text.is_empty() {
 4542                    let mut bracket_pair_matching_end = None;
 4543                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4544                    //  and they are removing the character that triggered IME popup.
 4545                    for (pair, enabled) in scope.brackets() {
 4546                        if !pair.close && !pair.surround {
 4547                            continue;
 4548                        }
 4549
 4550                        if enabled && pair.start.ends_with(text.as_ref()) {
 4551                            let prefix_len = pair.start.len() - text.len();
 4552                            let preceding_text_matches_prefix = prefix_len == 0
 4553                                || (selection.start.column >= (prefix_len as u32)
 4554                                    && snapshot.contains_str_at(
 4555                                        Point::new(
 4556                                            selection.start.row,
 4557                                            selection.start.column - (prefix_len as u32),
 4558                                        ),
 4559                                        &pair.start[..prefix_len],
 4560                                    ));
 4561                            if preceding_text_matches_prefix {
 4562                                bracket_pair = Some(pair.clone());
 4563                                is_bracket_pair_start = true;
 4564                                break;
 4565                            }
 4566                        }
 4567                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4568                        {
 4569                            // take first bracket pair matching end, but don't break in case a later bracket
 4570                            // pair matches start
 4571                            bracket_pair_matching_end = Some(pair.clone());
 4572                        }
 4573                    }
 4574                    if let Some(end) = bracket_pair_matching_end
 4575                        && bracket_pair.is_none()
 4576                    {
 4577                        bracket_pair = Some(end);
 4578                        is_bracket_pair_end = true;
 4579                    }
 4580                }
 4581
 4582                if let Some(bracket_pair) = bracket_pair {
 4583                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4584                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4585                    let auto_surround =
 4586                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4587                    if selection.is_empty() {
 4588                        if is_bracket_pair_start {
 4589                            // If the inserted text is a suffix of an opening bracket and the
 4590                            // selection is preceded by the rest of the opening bracket, then
 4591                            // insert the closing bracket.
 4592                            let following_text_allows_autoclose = snapshot
 4593                                .chars_at(selection.start)
 4594                                .next()
 4595                                .is_none_or(|c| scope.should_autoclose_before(c));
 4596
 4597                            let preceding_text_allows_autoclose = selection.start.column == 0
 4598                                || snapshot
 4599                                    .reversed_chars_at(selection.start)
 4600                                    .next()
 4601                                    .is_none_or(|c| {
 4602                                        bracket_pair.start != bracket_pair.end
 4603                                            || !snapshot
 4604                                                .char_classifier_at(selection.start)
 4605                                                .is_word(c)
 4606                                    });
 4607
 4608                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4609                                && bracket_pair.start.len() == 1
 4610                            {
 4611                                let target = bracket_pair.start.chars().next().unwrap();
 4612                                let mut byte_offset = 0u32;
 4613                                let current_line_count = snapshot
 4614                                    .reversed_chars_at(selection.start)
 4615                                    .take_while(|&c| c != '\n')
 4616                                    .filter(|c| {
 4617                                        byte_offset += c.len_utf8() as u32;
 4618                                        if *c != target {
 4619                                            return false;
 4620                                        }
 4621
 4622                                        let point = Point::new(
 4623                                            selection.start.row,
 4624                                            selection.start.column.saturating_sub(byte_offset),
 4625                                        );
 4626
 4627                                        let is_enabled = snapshot
 4628                                            .language_scope_at(point)
 4629                                            .and_then(|scope| {
 4630                                                scope
 4631                                                    .brackets()
 4632                                                    .find(|(pair, _)| {
 4633                                                        pair.start == bracket_pair.start
 4634                                                    })
 4635                                                    .map(|(_, enabled)| enabled)
 4636                                            })
 4637                                            .unwrap_or(true);
 4638
 4639                                        let is_delimiter = snapshot
 4640                                            .language_scope_at(Point::new(
 4641                                                point.row,
 4642                                                point.column + 1,
 4643                                            ))
 4644                                            .and_then(|scope| {
 4645                                                scope
 4646                                                    .brackets()
 4647                                                    .find(|(pair, _)| {
 4648                                                        pair.start == bracket_pair.start
 4649                                                    })
 4650                                                    .map(|(_, enabled)| !enabled)
 4651                                            })
 4652                                            .unwrap_or(false);
 4653
 4654                                        is_enabled && !is_delimiter
 4655                                    })
 4656                                    .count();
 4657                                current_line_count % 2 == 1
 4658                            } else {
 4659                                false
 4660                            };
 4661
 4662                            if autoclose
 4663                                && bracket_pair.close
 4664                                && following_text_allows_autoclose
 4665                                && preceding_text_allows_autoclose
 4666                                && !is_closing_quote
 4667                            {
 4668                                let anchor = snapshot.anchor_before(selection.end);
 4669                                new_selections.push((selection.map(|_| anchor), text.len()));
 4670                                new_autoclose_regions.push((
 4671                                    anchor,
 4672                                    text.len(),
 4673                                    selection.id,
 4674                                    bracket_pair.clone(),
 4675                                ));
 4676                                edits.push((
 4677                                    selection.range(),
 4678                                    format!("{}{}", text, bracket_pair.end).into(),
 4679                                ));
 4680                                bracket_inserted = true;
 4681                                continue;
 4682                            }
 4683                        }
 4684
 4685                        if let Some(region) = autoclose_region {
 4686                            // If the selection is followed by an auto-inserted closing bracket,
 4687                            // then don't insert that closing bracket again; just move the selection
 4688                            // past the closing bracket.
 4689                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4690                                && text.as_ref() == region.pair.end.as_str()
 4691                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4692                            if should_skip {
 4693                                let anchor = snapshot.anchor_after(selection.end);
 4694                                new_selections
 4695                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4696                                continue;
 4697                            }
 4698                        }
 4699
 4700                        let always_treat_brackets_as_autoclosed = snapshot
 4701                            .language_settings_at(selection.start, cx)
 4702                            .always_treat_brackets_as_autoclosed;
 4703                        if always_treat_brackets_as_autoclosed
 4704                            && is_bracket_pair_end
 4705                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4706                        {
 4707                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4708                            // and the inserted text is a closing bracket and the selection is followed
 4709                            // by the closing bracket then move the selection past the closing bracket.
 4710                            let anchor = snapshot.anchor_after(selection.end);
 4711                            new_selections.push((selection.map(|_| anchor), text.len()));
 4712                            continue;
 4713                        }
 4714                    }
 4715                    // If an opening bracket is 1 character long and is typed while
 4716                    // text is selected, then surround that text with the bracket pair.
 4717                    else if auto_surround
 4718                        && bracket_pair.surround
 4719                        && is_bracket_pair_start
 4720                        && bracket_pair.start.chars().count() == 1
 4721                    {
 4722                        edits.push((selection.start..selection.start, text.clone()));
 4723                        edits.push((
 4724                            selection.end..selection.end,
 4725                            bracket_pair.end.as_str().into(),
 4726                        ));
 4727                        bracket_inserted = true;
 4728                        new_selections.push((
 4729                            Selection {
 4730                                id: selection.id,
 4731                                start: snapshot.anchor_after(selection.start),
 4732                                end: snapshot.anchor_before(selection.end),
 4733                                reversed: selection.reversed,
 4734                                goal: selection.goal,
 4735                            },
 4736                            0,
 4737                        ));
 4738                        continue;
 4739                    }
 4740                }
 4741            }
 4742
 4743            if self.auto_replace_emoji_shortcode
 4744                && selection.is_empty()
 4745                && text.as_ref().ends_with(':')
 4746                && let Some(possible_emoji_short_code) =
 4747                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4748                && !possible_emoji_short_code.is_empty()
 4749                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4750            {
 4751                let emoji_shortcode_start = Point::new(
 4752                    selection.start.row,
 4753                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4754                );
 4755
 4756                // Remove shortcode from buffer
 4757                edits.push((
 4758                    emoji_shortcode_start..selection.start,
 4759                    "".to_string().into(),
 4760                ));
 4761                new_selections.push((
 4762                    Selection {
 4763                        id: selection.id,
 4764                        start: snapshot.anchor_after(emoji_shortcode_start),
 4765                        end: snapshot.anchor_before(selection.start),
 4766                        reversed: selection.reversed,
 4767                        goal: selection.goal,
 4768                    },
 4769                    0,
 4770                ));
 4771
 4772                // Insert emoji
 4773                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4774                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4775                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4776
 4777                continue;
 4778            }
 4779
 4780            let next_is_adjacent = regions
 4781                .peek()
 4782                .is_some_and(|(next, _)| selection.end == next.start);
 4783
 4784            // If not handling any auto-close operation, then just replace the selected
 4785            // text with the given input and move the selection to the end of the
 4786            // newly inserted text.
 4787            let anchor = if in_adjacent_group || next_is_adjacent {
 4788                // After edits the right bias would shift those anchor to the next visible fragment
 4789                // but we want to resolve to the previous one
 4790                snapshot.anchor_before(selection.end)
 4791            } else {
 4792                snapshot.anchor_after(selection.end)
 4793            };
 4794
 4795            if !self.linked_edit_ranges.is_empty() {
 4796                let start_anchor = snapshot.anchor_before(selection.start);
 4797
 4798                let is_word_char = text.chars().next().is_none_or(|char| {
 4799                    let classifier = snapshot
 4800                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4801                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4802                    classifier.is_word(char)
 4803                });
 4804
 4805                if is_word_char {
 4806                    if let Some(ranges) = self
 4807                        .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
 4808                    {
 4809                        for (buffer, edits) in ranges {
 4810                            linked_edits
 4811                                .entry(buffer.clone())
 4812                                .or_default()
 4813                                .extend(edits.into_iter().map(|range| (range, text.clone())));
 4814                        }
 4815                    }
 4816                } else {
 4817                    clear_linked_edit_ranges = true;
 4818                }
 4819            }
 4820
 4821            new_selections.push((selection.map(|_| anchor), 0));
 4822            edits.push((selection.start..selection.end, text.clone()));
 4823
 4824            has_adjacent_edits |= next_is_adjacent;
 4825            in_adjacent_group = next_is_adjacent;
 4826        }
 4827
 4828        if all_selections_read_only {
 4829            return;
 4830        }
 4831
 4832        drop(regions);
 4833        drop(snapshot);
 4834
 4835        self.transact(window, cx, |this, window, cx| {
 4836            if clear_linked_edit_ranges {
 4837                this.linked_edit_ranges.clear();
 4838            }
 4839            let initial_buffer_versions =
 4840                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4841
 4842            this.buffer.update(cx, |buffer, cx| {
 4843                if has_adjacent_edits {
 4844                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4845                } else {
 4846                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4847                }
 4848            });
 4849            for (buffer, edits) in linked_edits {
 4850                buffer.update(cx, |buffer, cx| {
 4851                    let snapshot = buffer.snapshot();
 4852                    let edits = edits
 4853                        .into_iter()
 4854                        .map(|(range, text)| {
 4855                            use text::ToPoint as TP;
 4856                            let end_point = TP::to_point(&range.end, &snapshot);
 4857                            let start_point = TP::to_point(&range.start, &snapshot);
 4858                            (start_point..end_point, text)
 4859                        })
 4860                        .sorted_by_key(|(range, _)| range.start);
 4861                    buffer.edit(edits, None, cx);
 4862                })
 4863            }
 4864            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4865            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4866            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4867            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4868                new_anchor_selections,
 4869                &map,
 4870            )
 4871            .zip(new_selection_deltas)
 4872            .map(|(selection, delta)| Selection {
 4873                id: selection.id,
 4874                start: selection.start + delta,
 4875                end: selection.end + delta,
 4876                reversed: selection.reversed,
 4877                goal: SelectionGoal::None,
 4878            })
 4879            .collect::<Vec<_>>();
 4880
 4881            let mut i = 0;
 4882            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4883                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4884                let start = map.buffer_snapshot().anchor_before(position);
 4885                let end = map.buffer_snapshot().anchor_after(position);
 4886                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4887                    match existing_state
 4888                        .range
 4889                        .start
 4890                        .cmp(&start, map.buffer_snapshot())
 4891                    {
 4892                        Ordering::Less => i += 1,
 4893                        Ordering::Greater => break,
 4894                        Ordering::Equal => {
 4895                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4896                                Ordering::Less => i += 1,
 4897                                Ordering::Equal => break,
 4898                                Ordering::Greater => break,
 4899                            }
 4900                        }
 4901                    }
 4902                }
 4903                this.autoclose_regions.insert(
 4904                    i,
 4905                    AutocloseRegion {
 4906                        selection_id,
 4907                        range: start..end,
 4908                        pair,
 4909                    },
 4910                );
 4911            }
 4912
 4913            let had_active_edit_prediction = this.has_active_edit_prediction();
 4914            this.change_selections(
 4915                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4916                window,
 4917                cx,
 4918                |s| s.select(new_selections),
 4919            );
 4920
 4921            if !bracket_inserted
 4922                && let Some(on_type_format_task) =
 4923                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 4924            {
 4925                on_type_format_task.detach_and_log_err(cx);
 4926            }
 4927
 4928            let editor_settings = EditorSettings::get_global(cx);
 4929            if bracket_inserted
 4930                && (editor_settings.auto_signature_help
 4931                    || editor_settings.show_signature_help_after_edits)
 4932            {
 4933                this.show_signature_help(&ShowSignatureHelp, window, cx);
 4934            }
 4935
 4936            let trigger_in_words =
 4937                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 4938            if this.hard_wrap.is_some() {
 4939                let latest: Range<Point> = this.selections.newest(&map).range();
 4940                if latest.is_empty()
 4941                    && this
 4942                        .buffer()
 4943                        .read(cx)
 4944                        .snapshot(cx)
 4945                        .line_len(MultiBufferRow(latest.start.row))
 4946                        == latest.start.column
 4947                {
 4948                    this.rewrap_impl(
 4949                        RewrapOptions {
 4950                            override_language_settings: true,
 4951                            preserve_existing_whitespace: true,
 4952                        },
 4953                        cx,
 4954                    )
 4955                }
 4956            }
 4957            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 4958            refresh_linked_ranges(this, window, cx);
 4959            this.refresh_edit_prediction(true, false, window, cx);
 4960            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 4961        });
 4962    }
 4963
 4964    fn find_possible_emoji_shortcode_at_position(
 4965        snapshot: &MultiBufferSnapshot,
 4966        position: Point,
 4967    ) -> Option<String> {
 4968        let mut chars = Vec::new();
 4969        let mut found_colon = false;
 4970        for char in snapshot.reversed_chars_at(position).take(100) {
 4971            // Found a possible emoji shortcode in the middle of the buffer
 4972            if found_colon {
 4973                if char.is_whitespace() {
 4974                    chars.reverse();
 4975                    return Some(chars.iter().collect());
 4976                }
 4977                // If the previous character is not a whitespace, we are in the middle of a word
 4978                // and we only want to complete the shortcode if the word is made up of other emojis
 4979                let mut containing_word = String::new();
 4980                for ch in snapshot
 4981                    .reversed_chars_at(position)
 4982                    .skip(chars.len() + 1)
 4983                    .take(100)
 4984                {
 4985                    if ch.is_whitespace() {
 4986                        break;
 4987                    }
 4988                    containing_word.push(ch);
 4989                }
 4990                let containing_word = containing_word.chars().rev().collect::<String>();
 4991                if util::word_consists_of_emojis(containing_word.as_str()) {
 4992                    chars.reverse();
 4993                    return Some(chars.iter().collect());
 4994                }
 4995            }
 4996
 4997            if char.is_whitespace() || !char.is_ascii() {
 4998                return None;
 4999            }
 5000            if char == ':' {
 5001                found_colon = true;
 5002            } else {
 5003                chars.push(char);
 5004            }
 5005        }
 5006        // Found a possible emoji shortcode at the beginning of the buffer
 5007        chars.reverse();
 5008        Some(chars.iter().collect())
 5009    }
 5010
 5011    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5012        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5013        self.transact(window, cx, |this, window, cx| {
 5014            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5015                let selections = this
 5016                    .selections
 5017                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5018                let multi_buffer = this.buffer.read(cx);
 5019                let buffer = multi_buffer.snapshot(cx);
 5020                selections
 5021                    .iter()
 5022                    .map(|selection| {
 5023                        let start_point = selection.start.to_point(&buffer);
 5024                        let mut existing_indent =
 5025                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5026                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5027                        let start = selection.start;
 5028                        let end = selection.end;
 5029                        let selection_is_empty = start == end;
 5030                        let language_scope = buffer.language_scope_at(start);
 5031                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5032                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5033                                &buffer,
 5034                                start..end,
 5035                                language,
 5036                            )
 5037                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5038                                    &buffer,
 5039                                    start..end,
 5040                                );
 5041
 5042                            let mut newline_config = NewlineConfig::Newline {
 5043                                additional_indent: IndentSize::spaces(0),
 5044                                extra_line_additional_indent: if needs_extra_newline {
 5045                                    Some(IndentSize::spaces(0))
 5046                                } else {
 5047                                    None
 5048                                },
 5049                                prevent_auto_indent: false,
 5050                            };
 5051
 5052                            let comment_delimiter = maybe!({
 5053                                if !selection_is_empty {
 5054                                    return None;
 5055                                }
 5056
 5057                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5058                                    return None;
 5059                                }
 5060
 5061                                return comment_delimiter_for_newline(
 5062                                    &start_point,
 5063                                    &buffer,
 5064                                    language,
 5065                                );
 5066                            });
 5067
 5068                            let doc_delimiter = maybe!({
 5069                                if !selection_is_empty {
 5070                                    return None;
 5071                                }
 5072
 5073                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5074                                    return None;
 5075                                }
 5076
 5077                                return documentation_delimiter_for_newline(
 5078                                    &start_point,
 5079                                    &buffer,
 5080                                    language,
 5081                                    &mut newline_config,
 5082                                );
 5083                            });
 5084
 5085                            let list_delimiter = maybe!({
 5086                                if !selection_is_empty {
 5087                                    return None;
 5088                                }
 5089
 5090                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5091                                    return None;
 5092                                }
 5093
 5094                                return list_delimiter_for_newline(
 5095                                    &start_point,
 5096                                    &buffer,
 5097                                    language,
 5098                                    &mut newline_config,
 5099                                );
 5100                            });
 5101
 5102                            (
 5103                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5104                                newline_config,
 5105                            )
 5106                        } else {
 5107                            (
 5108                                None,
 5109                                NewlineConfig::Newline {
 5110                                    additional_indent: IndentSize::spaces(0),
 5111                                    extra_line_additional_indent: None,
 5112                                    prevent_auto_indent: false,
 5113                                },
 5114                            )
 5115                        };
 5116
 5117                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5118                            NewlineConfig::ClearCurrentLine => {
 5119                                let row_start =
 5120                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5121                                (row_start, String::new(), false)
 5122                            }
 5123                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5124                                let row_start =
 5125                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5126                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5127                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5128                                let reduced_indent =
 5129                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5130                                let mut new_text = String::new();
 5131                                new_text.extend(reduced_indent.chars());
 5132                                new_text.push_str(continuation);
 5133                                (row_start, new_text, true)
 5134                            }
 5135                            NewlineConfig::Newline {
 5136                                additional_indent,
 5137                                extra_line_additional_indent,
 5138                                prevent_auto_indent,
 5139                            } => {
 5140                                let capacity_for_delimiter =
 5141                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5142                                let extra_line_len = extra_line_additional_indent
 5143                                    .map(|i| 1 + existing_indent.len as usize + i.len as usize)
 5144                                    .unwrap_or(0);
 5145                                let mut new_text = String::with_capacity(
 5146                                    1 + capacity_for_delimiter
 5147                                        + existing_indent.len as usize
 5148                                        + additional_indent.len as usize
 5149                                        + extra_line_len,
 5150                                );
 5151                                new_text.push('\n');
 5152                                new_text.extend(existing_indent.chars());
 5153                                new_text.extend(additional_indent.chars());
 5154                                if let Some(delimiter) = &delimiter {
 5155                                    new_text.push_str(delimiter);
 5156                                }
 5157                                if let Some(extra_indent) = extra_line_additional_indent {
 5158                                    new_text.push('\n');
 5159                                    new_text.extend(existing_indent.chars());
 5160                                    new_text.extend(extra_indent.chars());
 5161                                }
 5162                                (start, new_text, *prevent_auto_indent)
 5163                            }
 5164                        };
 5165
 5166                        let anchor = buffer.anchor_after(end);
 5167                        let new_selection = selection.map(|_| anchor);
 5168                        (
 5169                            ((edit_start..end, new_text), prevent_auto_indent),
 5170                            (newline_config.has_extra_line(), new_selection),
 5171                        )
 5172                    })
 5173                    .unzip()
 5174            };
 5175
 5176            let mut auto_indent_edits = Vec::new();
 5177            let mut edits = Vec::new();
 5178            for (edit, prevent_auto_indent) in edits_with_flags {
 5179                if prevent_auto_indent {
 5180                    edits.push(edit);
 5181                } else {
 5182                    auto_indent_edits.push(edit);
 5183                }
 5184            }
 5185            if !edits.is_empty() {
 5186                this.edit(edits, cx);
 5187            }
 5188            if !auto_indent_edits.is_empty() {
 5189                this.edit_with_autoindent(auto_indent_edits, cx);
 5190            }
 5191
 5192            let buffer = this.buffer.read(cx).snapshot(cx);
 5193            let new_selections = selection_info
 5194                .into_iter()
 5195                .map(|(extra_newline_inserted, new_selection)| {
 5196                    let mut cursor = new_selection.end.to_point(&buffer);
 5197                    if extra_newline_inserted {
 5198                        cursor.row -= 1;
 5199                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5200                    }
 5201                    new_selection.map(|_| cursor)
 5202                })
 5203                .collect();
 5204
 5205            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5206            this.refresh_edit_prediction(true, false, window, cx);
 5207            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5208                task.detach_and_log_err(cx);
 5209            }
 5210        });
 5211    }
 5212
 5213    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5214        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5215
 5216        let buffer = self.buffer.read(cx);
 5217        let snapshot = buffer.snapshot(cx);
 5218
 5219        let mut edits = Vec::new();
 5220        let mut rows = Vec::new();
 5221
 5222        for (rows_inserted, selection) in self
 5223            .selections
 5224            .all_adjusted(&self.display_snapshot(cx))
 5225            .into_iter()
 5226            .enumerate()
 5227        {
 5228            let cursor = selection.head();
 5229            let row = cursor.row;
 5230
 5231            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5232
 5233            let newline = "\n".to_string();
 5234            edits.push((start_of_line..start_of_line, newline));
 5235
 5236            rows.push(row + rows_inserted as u32);
 5237        }
 5238
 5239        self.transact(window, cx, |editor, window, cx| {
 5240            editor.edit(edits, cx);
 5241
 5242            editor.change_selections(Default::default(), window, cx, |s| {
 5243                let mut index = 0;
 5244                s.move_cursors_with(|map, _, _| {
 5245                    let row = rows[index];
 5246                    index += 1;
 5247
 5248                    let point = Point::new(row, 0);
 5249                    let boundary = map.next_line_boundary(point).1;
 5250                    let clipped = map.clip_point(boundary, Bias::Left);
 5251
 5252                    (clipped, SelectionGoal::None)
 5253                });
 5254            });
 5255
 5256            let mut indent_edits = Vec::new();
 5257            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5258            for row in rows {
 5259                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5260                for (row, indent) in indents {
 5261                    if indent.len == 0 {
 5262                        continue;
 5263                    }
 5264
 5265                    let text = match indent.kind {
 5266                        IndentKind::Space => " ".repeat(indent.len as usize),
 5267                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5268                    };
 5269                    let point = Point::new(row.0, 0);
 5270                    indent_edits.push((point..point, text));
 5271                }
 5272            }
 5273            editor.edit(indent_edits, cx);
 5274            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5275                format.detach_and_log_err(cx);
 5276            }
 5277        });
 5278    }
 5279
 5280    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5281        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5282
 5283        let buffer = self.buffer.read(cx);
 5284        let snapshot = buffer.snapshot(cx);
 5285
 5286        let mut edits = Vec::new();
 5287        let mut rows = Vec::new();
 5288        let mut rows_inserted = 0;
 5289
 5290        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5291            let cursor = selection.head();
 5292            let row = cursor.row;
 5293
 5294            let point = Point::new(row + 1, 0);
 5295            let start_of_line = snapshot.clip_point(point, Bias::Left);
 5296
 5297            let newline = "\n".to_string();
 5298            edits.push((start_of_line..start_of_line, newline));
 5299
 5300            rows_inserted += 1;
 5301            rows.push(row + rows_inserted);
 5302        }
 5303
 5304        self.transact(window, cx, |editor, window, cx| {
 5305            editor.edit(edits, cx);
 5306
 5307            editor.change_selections(Default::default(), window, cx, |s| {
 5308                let mut index = 0;
 5309                s.move_cursors_with(|map, _, _| {
 5310                    let row = rows[index];
 5311                    index += 1;
 5312
 5313                    let point = Point::new(row, 0);
 5314                    let boundary = map.next_line_boundary(point).1;
 5315                    let clipped = map.clip_point(boundary, Bias::Left);
 5316
 5317                    (clipped, SelectionGoal::None)
 5318                });
 5319            });
 5320
 5321            let mut indent_edits = Vec::new();
 5322            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5323            for row in rows {
 5324                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5325                for (row, indent) in indents {
 5326                    if indent.len == 0 {
 5327                        continue;
 5328                    }
 5329
 5330                    let text = match indent.kind {
 5331                        IndentKind::Space => " ".repeat(indent.len as usize),
 5332                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5333                    };
 5334                    let point = Point::new(row.0, 0);
 5335                    indent_edits.push((point..point, text));
 5336                }
 5337            }
 5338            editor.edit(indent_edits, cx);
 5339            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5340                format.detach_and_log_err(cx);
 5341            }
 5342        });
 5343    }
 5344
 5345    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5346        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5347            original_indent_columns: Vec::new(),
 5348        });
 5349        self.insert_with_autoindent_mode(text, autoindent, window, cx);
 5350    }
 5351
 5352    fn insert_with_autoindent_mode(
 5353        &mut self,
 5354        text: &str,
 5355        autoindent_mode: Option<AutoindentMode>,
 5356        window: &mut Window,
 5357        cx: &mut Context<Self>,
 5358    ) {
 5359        if self.read_only(cx) {
 5360            return;
 5361        }
 5362
 5363        let text: Arc<str> = text.into();
 5364        self.transact(window, cx, |this, window, cx| {
 5365            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5366            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5367                let anchors = {
 5368                    let snapshot = buffer.read(cx);
 5369                    old_selections
 5370                        .iter()
 5371                        .map(|s| {
 5372                            let anchor = snapshot.anchor_after(s.head());
 5373                            s.map(|_| anchor)
 5374                        })
 5375                        .collect::<Vec<_>>()
 5376                };
 5377                buffer.edit(
 5378                    old_selections
 5379                        .iter()
 5380                        .map(|s| (s.start..s.end, text.clone())),
 5381                    autoindent_mode,
 5382                    cx,
 5383                );
 5384                anchors
 5385            });
 5386
 5387            this.change_selections(Default::default(), window, cx, |s| {
 5388                s.select_anchors(selection_anchors);
 5389            });
 5390
 5391            cx.notify();
 5392        });
 5393    }
 5394
 5395    fn trigger_completion_on_input(
 5396        &mut self,
 5397        text: &str,
 5398        trigger_in_words: bool,
 5399        window: &mut Window,
 5400        cx: &mut Context<Self>,
 5401    ) {
 5402        let completions_source = self
 5403            .context_menu
 5404            .borrow()
 5405            .as_ref()
 5406            .and_then(|menu| match menu {
 5407                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5408                CodeContextMenu::CodeActions(_) => None,
 5409            });
 5410
 5411        match completions_source {
 5412            Some(CompletionsMenuSource::Words { .. }) => {
 5413                self.open_or_update_completions_menu(
 5414                    Some(CompletionsMenuSource::Words {
 5415                        ignore_threshold: false,
 5416                    }),
 5417                    None,
 5418                    trigger_in_words,
 5419                    window,
 5420                    cx,
 5421                );
 5422            }
 5423            _ => self.open_or_update_completions_menu(
 5424                None,
 5425                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5426                true,
 5427                window,
 5428                cx,
 5429            ),
 5430        }
 5431    }
 5432
 5433    /// If any empty selections is touching the start of its innermost containing autoclose
 5434    /// region, expand it to select the brackets.
 5435    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5436        let selections = self
 5437            .selections
 5438            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5439        let buffer = self.buffer.read(cx).read(cx);
 5440        let new_selections = self
 5441            .selections_with_autoclose_regions(selections, &buffer)
 5442            .map(|(mut selection, region)| {
 5443                if !selection.is_empty() {
 5444                    return selection;
 5445                }
 5446
 5447                if let Some(region) = region {
 5448                    let mut range = region.range.to_offset(&buffer);
 5449                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5450                        range.start -= region.pair.start.len();
 5451                        if buffer.contains_str_at(range.start, &region.pair.start)
 5452                            && buffer.contains_str_at(range.end, &region.pair.end)
 5453                        {
 5454                            range.end += region.pair.end.len();
 5455                            selection.start = range.start;
 5456                            selection.end = range.end;
 5457
 5458                            return selection;
 5459                        }
 5460                    }
 5461                }
 5462
 5463                let always_treat_brackets_as_autoclosed = buffer
 5464                    .language_settings_at(selection.start, cx)
 5465                    .always_treat_brackets_as_autoclosed;
 5466
 5467                if !always_treat_brackets_as_autoclosed {
 5468                    return selection;
 5469                }
 5470
 5471                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5472                    for (pair, enabled) in scope.brackets() {
 5473                        if !enabled || !pair.close {
 5474                            continue;
 5475                        }
 5476
 5477                        if buffer.contains_str_at(selection.start, &pair.end) {
 5478                            let pair_start_len = pair.start.len();
 5479                            if buffer.contains_str_at(
 5480                                selection.start.saturating_sub_usize(pair_start_len),
 5481                                &pair.start,
 5482                            ) {
 5483                                selection.start -= pair_start_len;
 5484                                selection.end += pair.end.len();
 5485
 5486                                return selection;
 5487                            }
 5488                        }
 5489                    }
 5490                }
 5491
 5492                selection
 5493            })
 5494            .collect();
 5495
 5496        drop(buffer);
 5497        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5498            selections.select(new_selections)
 5499        });
 5500    }
 5501
 5502    /// Iterate the given selections, and for each one, find the smallest surrounding
 5503    /// autoclose region. This uses the ordering of the selections and the autoclose
 5504    /// regions to avoid repeated comparisons.
 5505    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5506        &'a self,
 5507        selections: impl IntoIterator<Item = Selection<D>>,
 5508        buffer: &'a MultiBufferSnapshot,
 5509    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5510        let mut i = 0;
 5511        let mut regions = self.autoclose_regions.as_slice();
 5512        selections.into_iter().map(move |selection| {
 5513            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5514
 5515            let mut enclosing = None;
 5516            while let Some(pair_state) = regions.get(i) {
 5517                if pair_state.range.end.to_offset(buffer) < range.start {
 5518                    regions = &regions[i + 1..];
 5519                    i = 0;
 5520                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5521                    break;
 5522                } else {
 5523                    if pair_state.selection_id == selection.id {
 5524                        enclosing = Some(pair_state);
 5525                    }
 5526                    i += 1;
 5527                }
 5528            }
 5529
 5530            (selection, enclosing)
 5531        })
 5532    }
 5533
 5534    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5535    fn invalidate_autoclose_regions(
 5536        &mut self,
 5537        mut selections: &[Selection<Anchor>],
 5538        buffer: &MultiBufferSnapshot,
 5539    ) {
 5540        self.autoclose_regions.retain(|state| {
 5541            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5542                return false;
 5543            }
 5544
 5545            let mut i = 0;
 5546            while let Some(selection) = selections.get(i) {
 5547                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5548                    selections = &selections[1..];
 5549                    continue;
 5550                }
 5551                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5552                    break;
 5553                }
 5554                if selection.id == state.selection_id {
 5555                    return true;
 5556                } else {
 5557                    i += 1;
 5558                }
 5559            }
 5560            false
 5561        });
 5562    }
 5563
 5564    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5565        let offset = position.to_offset(buffer);
 5566        let (word_range, kind) =
 5567            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5568        if offset > word_range.start && kind == Some(CharKind::Word) {
 5569            Some(
 5570                buffer
 5571                    .text_for_range(word_range.start..offset)
 5572                    .collect::<String>(),
 5573            )
 5574        } else {
 5575            None
 5576        }
 5577    }
 5578
 5579    pub fn visible_excerpts(
 5580        &self,
 5581        lsp_related_only: bool,
 5582        cx: &mut Context<Editor>,
 5583    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5584        let project = self.project().cloned();
 5585        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5586        let multi_buffer = self.buffer().read(cx);
 5587        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5588        let multi_buffer_visible_start = self
 5589            .scroll_manager
 5590            .native_anchor(&display_snapshot, cx)
 5591            .anchor
 5592            .to_point(&multi_buffer_snapshot);
 5593        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5594            multi_buffer_visible_start
 5595                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5596            Bias::Left,
 5597        );
 5598        multi_buffer_snapshot
 5599            .range_to_buffer_ranges(multi_buffer_visible_start..=multi_buffer_visible_end)
 5600            .into_iter()
 5601            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5602            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5603                if !lsp_related_only {
 5604                    return Some((
 5605                        excerpt_id,
 5606                        (
 5607                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5608                            buffer.version().clone(),
 5609                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5610                        ),
 5611                    ));
 5612                }
 5613
 5614                let project = project.as_ref()?.read(cx);
 5615                let buffer_file = project::File::from_dyn(buffer.file())?;
 5616                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5617                let worktree_entry = buffer_worktree
 5618                    .read(cx)
 5619                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5620                if worktree_entry.is_ignored {
 5621                    None
 5622                } else {
 5623                    Some((
 5624                        excerpt_id,
 5625                        (
 5626                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5627                            buffer.version().clone(),
 5628                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5629                        ),
 5630                    ))
 5631                }
 5632            })
 5633            .collect()
 5634    }
 5635
 5636    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5637        TextLayoutDetails {
 5638            text_system: window.text_system().clone(),
 5639            editor_style: self.style.clone().unwrap(),
 5640            rem_size: window.rem_size(),
 5641            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5642            visible_rows: self.visible_line_count(),
 5643            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5644        }
 5645    }
 5646
 5647    fn trigger_on_type_formatting(
 5648        &self,
 5649        input: String,
 5650        window: &mut Window,
 5651        cx: &mut Context<Self>,
 5652    ) -> Option<Task<Result<()>>> {
 5653        if input.chars().count() != 1 {
 5654            return None;
 5655        }
 5656
 5657        let project = self.project()?;
 5658        let position = self.selections.newest_anchor().head();
 5659        let (buffer, buffer_position) = self
 5660            .buffer
 5661            .read(cx)
 5662            .text_anchor_for_position(position, cx)?;
 5663
 5664        let settings = language_settings::language_settings(
 5665            buffer
 5666                .read(cx)
 5667                .language_at(buffer_position)
 5668                .map(|l| l.name()),
 5669            buffer.read(cx).file(),
 5670            cx,
 5671        );
 5672        if !settings.use_on_type_format {
 5673            return None;
 5674        }
 5675
 5676        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5677        // hence we do LSP request & edit on host side only — add formats to host's history.
 5678        let push_to_lsp_host_history = true;
 5679        // If this is not the host, append its history with new edits.
 5680        let push_to_client_history = project.read(cx).is_via_collab();
 5681
 5682        let on_type_formatting = project.update(cx, |project, cx| {
 5683            project.on_type_format(
 5684                buffer.clone(),
 5685                buffer_position,
 5686                input,
 5687                push_to_lsp_host_history,
 5688                cx,
 5689            )
 5690        });
 5691        Some(cx.spawn_in(window, async move |editor, cx| {
 5692            if let Some(transaction) = on_type_formatting.await? {
 5693                if push_to_client_history {
 5694                    buffer.update(cx, |buffer, _| {
 5695                        buffer.push_transaction(transaction, Instant::now());
 5696                        buffer.finalize_last_transaction();
 5697                    });
 5698                }
 5699                editor.update(cx, |editor, cx| {
 5700                    editor.refresh_document_highlights(cx);
 5701                })?;
 5702            }
 5703            Ok(())
 5704        }))
 5705    }
 5706
 5707    pub fn show_word_completions(
 5708        &mut self,
 5709        _: &ShowWordCompletions,
 5710        window: &mut Window,
 5711        cx: &mut Context<Self>,
 5712    ) {
 5713        self.open_or_update_completions_menu(
 5714            Some(CompletionsMenuSource::Words {
 5715                ignore_threshold: true,
 5716            }),
 5717            None,
 5718            false,
 5719            window,
 5720            cx,
 5721        );
 5722    }
 5723
 5724    pub fn show_completions(
 5725        &mut self,
 5726        _: &ShowCompletions,
 5727        window: &mut Window,
 5728        cx: &mut Context<Self>,
 5729    ) {
 5730        self.open_or_update_completions_menu(None, None, false, window, cx);
 5731    }
 5732
 5733    fn open_or_update_completions_menu(
 5734        &mut self,
 5735        requested_source: Option<CompletionsMenuSource>,
 5736        trigger: Option<String>,
 5737        trigger_in_words: bool,
 5738        window: &mut Window,
 5739        cx: &mut Context<Self>,
 5740    ) {
 5741        if self.pending_rename.is_some() {
 5742            return;
 5743        }
 5744
 5745        let completions_source = self
 5746            .context_menu
 5747            .borrow()
 5748            .as_ref()
 5749            .and_then(|menu| match menu {
 5750                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5751                CodeContextMenu::CodeActions(_) => None,
 5752            });
 5753
 5754        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5755
 5756        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5757        // inserted and selected. To handle that case, the start of the selection is used so that
 5758        // the menu starts with all choices.
 5759        let position = self
 5760            .selections
 5761            .newest_anchor()
 5762            .start
 5763            .bias_right(&multibuffer_snapshot);
 5764        if position.diff_base_anchor.is_some() {
 5765            return;
 5766        }
 5767        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5768        let Some(buffer) = buffer_position
 5769            .text_anchor
 5770            .buffer_id
 5771            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5772        else {
 5773            return;
 5774        };
 5775        let buffer_snapshot = buffer.read(cx).snapshot();
 5776
 5777        let menu_is_open = matches!(
 5778            self.context_menu.borrow().as_ref(),
 5779            Some(CodeContextMenu::Completions(_))
 5780        );
 5781
 5782        let language = buffer_snapshot
 5783            .language_at(buffer_position.text_anchor)
 5784            .map(|language| language.name());
 5785
 5786        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5787        let completion_settings = language_settings.completions.clone();
 5788
 5789        let show_completions_on_input = self
 5790            .show_completions_on_input_override
 5791            .unwrap_or(language_settings.show_completions_on_input);
 5792        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5793            return;
 5794        }
 5795
 5796        let query: Option<Arc<String>> =
 5797            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5798                .map(|query| query.into());
 5799
 5800        drop(multibuffer_snapshot);
 5801
 5802        // Hide the current completions menu when query is empty. Without this, cached
 5803        // completions from before the trigger char may be reused (#32774).
 5804        if query.is_none() && menu_is_open {
 5805            self.hide_context_menu(window, cx);
 5806        }
 5807
 5808        let mut ignore_word_threshold = false;
 5809        let provider = match requested_source {
 5810            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5811            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5812                ignore_word_threshold = ignore_threshold;
 5813                None
 5814            }
 5815            Some(CompletionsMenuSource::SnippetChoices)
 5816            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5817                log::error!("bug: SnippetChoices requested_source is not handled");
 5818                None
 5819            }
 5820        };
 5821
 5822        let sort_completions = provider
 5823            .as_ref()
 5824            .is_some_and(|provider| provider.sort_completions());
 5825
 5826        let filter_completions = provider
 5827            .as_ref()
 5828            .is_none_or(|provider| provider.filter_completions());
 5829
 5830        let was_snippets_only = matches!(
 5831            completions_source,
 5832            Some(CompletionsMenuSource::SnippetsOnly)
 5833        );
 5834
 5835        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5836            if filter_completions {
 5837                menu.filter(
 5838                    query.clone().unwrap_or_default(),
 5839                    buffer_position.text_anchor,
 5840                    &buffer,
 5841                    provider.clone(),
 5842                    window,
 5843                    cx,
 5844                );
 5845            }
 5846            // When `is_incomplete` is false, no need to re-query completions when the current query
 5847            // is a suffix of the initial query.
 5848            let was_complete = !menu.is_incomplete;
 5849            if was_complete && !was_snippets_only {
 5850                // If the new query is a suffix of the old query (typing more characters) and
 5851                // the previous result was complete, the existing completions can be filtered.
 5852                //
 5853                // Note that snippet completions are always complete.
 5854                let query_matches = match (&menu.initial_query, &query) {
 5855                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 5856                    (None, _) => true,
 5857                    _ => false,
 5858                };
 5859                if query_matches {
 5860                    let position_matches = if menu.initial_position == position {
 5861                        true
 5862                    } else {
 5863                        let snapshot = self.buffer.read(cx).read(cx);
 5864                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 5865                    };
 5866                    if position_matches {
 5867                        return;
 5868                    }
 5869                }
 5870            }
 5871        };
 5872
 5873        let Anchor {
 5874            excerpt_id: buffer_excerpt_id,
 5875            text_anchor: buffer_position,
 5876            ..
 5877        } = buffer_position;
 5878
 5879        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 5880            buffer_snapshot.surrounding_word(buffer_position, None)
 5881        {
 5882            let word_to_exclude = buffer_snapshot
 5883                .text_for_range(word_range.clone())
 5884                .collect::<String>();
 5885            (
 5886                buffer_snapshot.anchor_before(word_range.start)
 5887                    ..buffer_snapshot.anchor_after(buffer_position),
 5888                Some(word_to_exclude),
 5889            )
 5890        } else {
 5891            (buffer_position..buffer_position, None)
 5892        };
 5893
 5894        let show_completion_documentation = buffer_snapshot
 5895            .settings_at(buffer_position, cx)
 5896            .show_completion_documentation;
 5897
 5898        // The document can be large, so stay in reasonable bounds when searching for words,
 5899        // otherwise completion pop-up might be slow to appear.
 5900        const WORD_LOOKUP_ROWS: u32 = 5_000;
 5901        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 5902        let min_word_search = buffer_snapshot.clip_point(
 5903            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 5904            Bias::Left,
 5905        );
 5906        let max_word_search = buffer_snapshot.clip_point(
 5907            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 5908            Bias::Right,
 5909        );
 5910        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 5911            ..buffer_snapshot.point_to_offset(max_word_search);
 5912
 5913        let skip_digits = query
 5914            .as_ref()
 5915            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 5916
 5917        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 5918            trigger.as_ref().is_none_or(|trigger| {
 5919                provider.is_completion_trigger(
 5920                    &buffer,
 5921                    position.text_anchor,
 5922                    trigger,
 5923                    trigger_in_words,
 5924                    cx,
 5925                )
 5926            })
 5927        });
 5928
 5929        let provider_responses = if let Some(provider) = &provider
 5930            && load_provider_completions
 5931        {
 5932            let trigger_character =
 5933                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 5934            let completion_context = CompletionContext {
 5935                trigger_kind: match &trigger_character {
 5936                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 5937                    None => CompletionTriggerKind::INVOKED,
 5938                },
 5939                trigger_character,
 5940            };
 5941
 5942            provider.completions(
 5943                buffer_excerpt_id,
 5944                &buffer,
 5945                buffer_position,
 5946                completion_context,
 5947                window,
 5948                cx,
 5949            )
 5950        } else {
 5951            Task::ready(Ok(Vec::new()))
 5952        };
 5953
 5954        let load_word_completions = if !self.word_completions_enabled {
 5955            false
 5956        } else if requested_source
 5957            == Some(CompletionsMenuSource::Words {
 5958                ignore_threshold: true,
 5959            })
 5960        {
 5961            true
 5962        } else {
 5963            load_provider_completions
 5964                && completion_settings.words != WordsCompletionMode::Disabled
 5965                && (ignore_word_threshold || {
 5966                    let words_min_length = completion_settings.words_min_length;
 5967                    // check whether word has at least `words_min_length` characters
 5968                    let query_chars = query.iter().flat_map(|q| q.chars());
 5969                    query_chars.take(words_min_length).count() == words_min_length
 5970                })
 5971        };
 5972
 5973        let mut words = if load_word_completions {
 5974            cx.background_spawn({
 5975                let buffer_snapshot = buffer_snapshot.clone();
 5976                async move {
 5977                    buffer_snapshot.words_in_range(WordsQuery {
 5978                        fuzzy_contents: None,
 5979                        range: word_search_range,
 5980                        skip_digits,
 5981                    })
 5982                }
 5983            })
 5984        } else {
 5985            Task::ready(BTreeMap::default())
 5986        };
 5987
 5988        let snippets = if let Some(provider) = &provider
 5989            && provider.show_snippets()
 5990            && let Some(project) = self.project()
 5991        {
 5992            let char_classifier = buffer_snapshot
 5993                .char_classifier_at(buffer_position)
 5994                .scope_context(Some(CharScopeContext::Completion));
 5995            project.update(cx, |project, cx| {
 5996                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 5997            })
 5998        } else {
 5999            Task::ready(Ok(CompletionResponse {
 6000                completions: Vec::new(),
 6001                display_options: Default::default(),
 6002                is_incomplete: false,
 6003            }))
 6004        };
 6005
 6006        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6007
 6008        let id = post_inc(&mut self.next_completion_id);
 6009        let task = cx.spawn_in(window, async move |editor, cx| {
 6010            let Ok(()) = editor.update(cx, |this, _| {
 6011                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6012            }) else {
 6013                return;
 6014            };
 6015
 6016            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6017            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6018            let mut completions = Vec::new();
 6019            let mut is_incomplete = false;
 6020            let mut display_options: Option<CompletionDisplayOptions> = None;
 6021            if let Some(provider_responses) = provider_responses.await.log_err()
 6022                && !provider_responses.is_empty()
 6023            {
 6024                for response in provider_responses {
 6025                    completions.extend(response.completions);
 6026                    is_incomplete = is_incomplete || response.is_incomplete;
 6027                    match display_options.as_mut() {
 6028                        None => {
 6029                            display_options = Some(response.display_options);
 6030                        }
 6031                        Some(options) => options.merge(&response.display_options),
 6032                    }
 6033                }
 6034                if completion_settings.words == WordsCompletionMode::Fallback {
 6035                    words = Task::ready(BTreeMap::default());
 6036                }
 6037            }
 6038            let display_options = display_options.unwrap_or_default();
 6039
 6040            let mut words = words.await;
 6041            if let Some(word_to_exclude) = &word_to_exclude {
 6042                words.remove(word_to_exclude);
 6043            }
 6044            for lsp_completion in &completions {
 6045                words.remove(&lsp_completion.new_text);
 6046            }
 6047            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6048                replace_range: word_replace_range.clone(),
 6049                new_text: word.clone(),
 6050                label: CodeLabel::plain(word, None),
 6051                match_start: None,
 6052                snippet_deduplication_key: None,
 6053                icon_path: None,
 6054                documentation: None,
 6055                source: CompletionSource::BufferWord {
 6056                    word_range,
 6057                    resolved: false,
 6058                },
 6059                insert_text_mode: Some(InsertTextMode::AS_IS),
 6060                confirm: None,
 6061            }));
 6062
 6063            completions.extend(
 6064                snippets
 6065                    .await
 6066                    .into_iter()
 6067                    .flat_map(|response| response.completions),
 6068            );
 6069
 6070            let menu = if completions.is_empty() {
 6071                None
 6072            } else {
 6073                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6074                    let languages = editor
 6075                        .workspace
 6076                        .as_ref()
 6077                        .and_then(|(workspace, _)| workspace.upgrade())
 6078                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6079                    let menu = CompletionsMenu::new(
 6080                        id,
 6081                        requested_source.unwrap_or(if load_provider_completions {
 6082                            CompletionsMenuSource::Normal
 6083                        } else {
 6084                            CompletionsMenuSource::SnippetsOnly
 6085                        }),
 6086                        sort_completions,
 6087                        show_completion_documentation,
 6088                        position,
 6089                        query.clone(),
 6090                        is_incomplete,
 6091                        buffer.clone(),
 6092                        completions.into(),
 6093                        editor
 6094                            .context_menu()
 6095                            .borrow_mut()
 6096                            .as_ref()
 6097                            .map(|menu| menu.primary_scroll_handle()),
 6098                        display_options,
 6099                        snippet_sort_order,
 6100                        languages,
 6101                        language,
 6102                        cx,
 6103                    );
 6104
 6105                    let query = if filter_completions { query } else { None };
 6106                    let matches_task = menu.do_async_filtering(
 6107                        query.unwrap_or_default(),
 6108                        buffer_position,
 6109                        &buffer,
 6110                        cx,
 6111                    );
 6112                    (menu, matches_task)
 6113                }) else {
 6114                    return;
 6115                };
 6116
 6117                let matches = matches_task.await;
 6118
 6119                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6120                    // Newer menu already set, so exit.
 6121                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6122                        editor.context_menu.borrow().as_ref()
 6123                        && prev_menu.id > id
 6124                    {
 6125                        return;
 6126                    };
 6127
 6128                    // Only valid to take prev_menu because either the new menu is immediately set
 6129                    // below, or the menu is hidden.
 6130                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6131                        editor.context_menu.borrow_mut().take()
 6132                    {
 6133                        let position_matches =
 6134                            if prev_menu.initial_position == menu.initial_position {
 6135                                true
 6136                            } else {
 6137                                let snapshot = editor.buffer.read(cx).read(cx);
 6138                                prev_menu.initial_position.to_offset(&snapshot)
 6139                                    == menu.initial_position.to_offset(&snapshot)
 6140                            };
 6141                        if position_matches {
 6142                            // Preserve markdown cache before `set_filter_results` because it will
 6143                            // try to populate the documentation cache.
 6144                            menu.preserve_markdown_cache(prev_menu);
 6145                        }
 6146                    };
 6147
 6148                    menu.set_filter_results(matches, provider, window, cx);
 6149                }) else {
 6150                    return;
 6151                };
 6152
 6153                menu.visible().then_some(menu)
 6154            };
 6155
 6156            editor
 6157                .update_in(cx, |editor, window, cx| {
 6158                    if editor.focus_handle.is_focused(window)
 6159                        && let Some(menu) = menu
 6160                    {
 6161                        *editor.context_menu.borrow_mut() =
 6162                            Some(CodeContextMenu::Completions(menu));
 6163
 6164                        crate::hover_popover::hide_hover(editor, cx);
 6165                        if editor.show_edit_predictions_in_menu() {
 6166                            editor.update_visible_edit_prediction(window, cx);
 6167                        } else {
 6168                            editor.discard_edit_prediction(false, cx);
 6169                        }
 6170
 6171                        cx.notify();
 6172                        return;
 6173                    }
 6174
 6175                    if editor.completion_tasks.len() <= 1 {
 6176                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6177                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6178                        // If it was already hidden and we don't show edit predictions in the menu,
 6179                        // we should also show the edit prediction when available.
 6180                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6181                            editor.update_visible_edit_prediction(window, cx);
 6182                        }
 6183                    }
 6184                })
 6185                .ok();
 6186        });
 6187
 6188        self.completion_tasks.push((id, task));
 6189    }
 6190
 6191    #[cfg(feature = "test-support")]
 6192    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6193        let menu = self.context_menu.borrow();
 6194        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6195            let completions = menu.completions.borrow();
 6196            Some(completions.to_vec())
 6197        } else {
 6198            None
 6199        }
 6200    }
 6201
 6202    pub fn with_completions_menu_matching_id<R>(
 6203        &self,
 6204        id: CompletionId,
 6205        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6206    ) -> R {
 6207        let mut context_menu = self.context_menu.borrow_mut();
 6208        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6209            return f(None);
 6210        };
 6211        if completions_menu.id != id {
 6212            return f(None);
 6213        }
 6214        f(Some(completions_menu))
 6215    }
 6216
 6217    pub fn confirm_completion(
 6218        &mut self,
 6219        action: &ConfirmCompletion,
 6220        window: &mut Window,
 6221        cx: &mut Context<Self>,
 6222    ) -> Option<Task<Result<()>>> {
 6223        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6224        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6225    }
 6226
 6227    pub fn confirm_completion_insert(
 6228        &mut self,
 6229        _: &ConfirmCompletionInsert,
 6230        window: &mut Window,
 6231        cx: &mut Context<Self>,
 6232    ) -> Option<Task<Result<()>>> {
 6233        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6234        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6235    }
 6236
 6237    pub fn confirm_completion_replace(
 6238        &mut self,
 6239        _: &ConfirmCompletionReplace,
 6240        window: &mut Window,
 6241        cx: &mut Context<Self>,
 6242    ) -> Option<Task<Result<()>>> {
 6243        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6244        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6245    }
 6246
 6247    pub fn compose_completion(
 6248        &mut self,
 6249        action: &ComposeCompletion,
 6250        window: &mut Window,
 6251        cx: &mut Context<Self>,
 6252    ) -> Option<Task<Result<()>>> {
 6253        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6254        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6255    }
 6256
 6257    fn do_completion(
 6258        &mut self,
 6259        item_ix: Option<usize>,
 6260        intent: CompletionIntent,
 6261        window: &mut Window,
 6262        cx: &mut Context<Editor>,
 6263    ) -> Option<Task<Result<()>>> {
 6264        use language::ToOffset as _;
 6265
 6266        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6267        else {
 6268            return None;
 6269        };
 6270
 6271        let candidate_id = {
 6272            let entries = completions_menu.entries.borrow();
 6273            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6274            if self.show_edit_predictions_in_menu() {
 6275                self.discard_edit_prediction(true, cx);
 6276            }
 6277            mat.candidate_id
 6278        };
 6279
 6280        let completion = completions_menu
 6281            .completions
 6282            .borrow()
 6283            .get(candidate_id)?
 6284            .clone();
 6285        cx.stop_propagation();
 6286
 6287        let buffer_handle = completions_menu.buffer.clone();
 6288
 6289        let CompletionEdit {
 6290            new_text,
 6291            snippet,
 6292            replace_range,
 6293        } = process_completion_for_edit(
 6294            &completion,
 6295            intent,
 6296            &buffer_handle,
 6297            &completions_menu.initial_position.text_anchor,
 6298            cx,
 6299        );
 6300
 6301        let buffer = buffer_handle.read(cx);
 6302        let snapshot = self.buffer.read(cx).snapshot(cx);
 6303        let newest_anchor = self.selections.newest_anchor();
 6304        let replace_range_multibuffer = {
 6305            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6306            excerpt.map_range_from_buffer(replace_range.clone())
 6307        };
 6308        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6309            return None;
 6310        }
 6311
 6312        let old_text = buffer
 6313            .text_for_range(replace_range.clone())
 6314            .collect::<String>();
 6315        let lookbehind = newest_anchor
 6316            .start
 6317            .text_anchor
 6318            .to_offset(buffer)
 6319            .saturating_sub(replace_range.start.0);
 6320        let lookahead = replace_range
 6321            .end
 6322            .0
 6323            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6324        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6325        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6326
 6327        let selections = self
 6328            .selections
 6329            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6330        let mut ranges = Vec::new();
 6331        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 6332
 6333        for selection in &selections {
 6334            let range = if selection.id == newest_anchor.id {
 6335                replace_range_multibuffer.clone()
 6336            } else {
 6337                let mut range = selection.range();
 6338
 6339                // if prefix is present, don't duplicate it
 6340                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6341                    range.start = range.start.saturating_sub_usize(lookbehind);
 6342
 6343                    // if suffix is also present, mimic the newest cursor and replace it
 6344                    if selection.id != newest_anchor.id
 6345                        && snapshot.contains_str_at(range.end, suffix)
 6346                    {
 6347                        range.end += lookahead;
 6348                    }
 6349                }
 6350                range
 6351            };
 6352
 6353            ranges.push(range.clone());
 6354
 6355            if !self.linked_edit_ranges.is_empty() {
 6356                let start_anchor = snapshot.anchor_before(range.start);
 6357                let end_anchor = snapshot.anchor_after(range.end);
 6358                if let Some(ranges) = self
 6359                    .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
 6360                {
 6361                    for (buffer, edits) in ranges {
 6362                        linked_edits
 6363                            .entry(buffer.clone())
 6364                            .or_default()
 6365                            .extend(edits.into_iter().map(|range| (range, new_text.to_owned())));
 6366                    }
 6367                }
 6368            }
 6369        }
 6370
 6371        let common_prefix_len = old_text
 6372            .chars()
 6373            .zip(new_text.chars())
 6374            .take_while(|(a, b)| a == b)
 6375            .map(|(a, _)| a.len_utf8())
 6376            .sum::<usize>();
 6377
 6378        cx.emit(EditorEvent::InputHandled {
 6379            utf16_range_to_replace: None,
 6380            text: new_text[common_prefix_len..].into(),
 6381        });
 6382
 6383        self.transact(window, cx, |editor, window, cx| {
 6384            if let Some(mut snippet) = snippet {
 6385                snippet.text = new_text.to_string();
 6386                editor
 6387                    .insert_snippet(&ranges, snippet, window, cx)
 6388                    .log_err();
 6389            } else {
 6390                editor.buffer.update(cx, |multi_buffer, cx| {
 6391                    let auto_indent = match completion.insert_text_mode {
 6392                        Some(InsertTextMode::AS_IS) => None,
 6393                        _ => editor.autoindent_mode.clone(),
 6394                    };
 6395                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6396                    multi_buffer.edit(edits, auto_indent, cx);
 6397                });
 6398            }
 6399            for (buffer, edits) in linked_edits {
 6400                buffer.update(cx, |buffer, cx| {
 6401                    let snapshot = buffer.snapshot();
 6402                    let edits = edits
 6403                        .into_iter()
 6404                        .map(|(range, text)| {
 6405                            use text::ToPoint as TP;
 6406                            let end_point = TP::to_point(&range.end, &snapshot);
 6407                            let start_point = TP::to_point(&range.start, &snapshot);
 6408                            (start_point..end_point, text)
 6409                        })
 6410                        .sorted_by_key(|(range, _)| range.start);
 6411                    buffer.edit(edits, None, cx);
 6412                })
 6413            }
 6414
 6415            editor.refresh_edit_prediction(true, false, window, cx);
 6416        });
 6417        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6418
 6419        let show_new_completions_on_confirm = completion
 6420            .confirm
 6421            .as_ref()
 6422            .is_some_and(|confirm| confirm(intent, window, cx));
 6423        if show_new_completions_on_confirm {
 6424            self.open_or_update_completions_menu(None, None, false, window, cx);
 6425        }
 6426
 6427        let provider = self.completion_provider.as_ref()?;
 6428
 6429        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6430        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6431            let CompletionSource::Lsp {
 6432                lsp_completion,
 6433                server_id,
 6434                ..
 6435            } = &completion.source
 6436            else {
 6437                return None;
 6438            };
 6439            let lsp_command = lsp_completion.command.as_ref()?;
 6440            let available_commands = lsp_store
 6441                .read(cx)
 6442                .lsp_server_capabilities
 6443                .get(server_id)
 6444                .and_then(|server_capabilities| {
 6445                    server_capabilities
 6446                        .execute_command_provider
 6447                        .as_ref()
 6448                        .map(|options| options.commands.as_slice())
 6449                })?;
 6450            if available_commands.contains(&lsp_command.command) {
 6451                Some(CodeAction {
 6452                    server_id: *server_id,
 6453                    range: language::Anchor::MIN..language::Anchor::MIN,
 6454                    lsp_action: LspAction::Command(lsp_command.clone()),
 6455                    resolved: false,
 6456                })
 6457            } else {
 6458                None
 6459            }
 6460        });
 6461
 6462        drop(completion);
 6463        let apply_edits = provider.apply_additional_edits_for_completion(
 6464            buffer_handle.clone(),
 6465            completions_menu.completions.clone(),
 6466            candidate_id,
 6467            true,
 6468            cx,
 6469        );
 6470
 6471        let editor_settings = EditorSettings::get_global(cx);
 6472        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6473            // After the code completion is finished, users often want to know what signatures are needed.
 6474            // so we should automatically call signature_help
 6475            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6476        }
 6477
 6478        Some(cx.spawn_in(window, async move |editor, cx| {
 6479            apply_edits.await?;
 6480
 6481            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6482                let title = command.lsp_action.title().to_owned();
 6483                let project_transaction = lsp_store
 6484                    .update(cx, |lsp_store, cx| {
 6485                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6486                    })
 6487                    .await
 6488                    .context("applying post-completion command")?;
 6489                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6490                    Self::open_project_transaction(
 6491                        &editor,
 6492                        workspace.downgrade(),
 6493                        project_transaction,
 6494                        title,
 6495                        cx,
 6496                    )
 6497                    .await?;
 6498                }
 6499            }
 6500
 6501            Ok(())
 6502        }))
 6503    }
 6504
 6505    pub fn toggle_code_actions(
 6506        &mut self,
 6507        action: &ToggleCodeActions,
 6508        window: &mut Window,
 6509        cx: &mut Context<Self>,
 6510    ) {
 6511        let quick_launch = action.quick_launch;
 6512        let mut context_menu = self.context_menu.borrow_mut();
 6513        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6514            if code_actions.deployed_from == action.deployed_from {
 6515                // Toggle if we're selecting the same one
 6516                *context_menu = None;
 6517                cx.notify();
 6518                return;
 6519            } else {
 6520                // Otherwise, clear it and start a new one
 6521                *context_menu = None;
 6522                cx.notify();
 6523            }
 6524        }
 6525        drop(context_menu);
 6526        let snapshot = self.snapshot(window, cx);
 6527        let deployed_from = action.deployed_from.clone();
 6528        let action = action.clone();
 6529        self.completion_tasks.clear();
 6530        self.discard_edit_prediction(false, cx);
 6531
 6532        let multibuffer_point = match &action.deployed_from {
 6533            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6534                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6535            }
 6536            _ => self
 6537                .selections
 6538                .newest::<Point>(&snapshot.display_snapshot)
 6539                .head(),
 6540        };
 6541        let Some((buffer, buffer_row)) = snapshot
 6542            .buffer_snapshot()
 6543            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6544            .and_then(|(buffer_snapshot, range)| {
 6545                self.buffer()
 6546                    .read(cx)
 6547                    .buffer(buffer_snapshot.remote_id())
 6548                    .map(|buffer| (buffer, range.start.row))
 6549            })
 6550        else {
 6551            return;
 6552        };
 6553        let buffer_id = buffer.read(cx).remote_id();
 6554        let tasks = self
 6555            .tasks
 6556            .get(&(buffer_id, buffer_row))
 6557            .map(|t| Arc::new(t.to_owned()));
 6558
 6559        if !self.focus_handle.is_focused(window) {
 6560            return;
 6561        }
 6562        let project = self.project.clone();
 6563
 6564        let code_actions_task = match deployed_from {
 6565            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6566            _ => self.code_actions(buffer_row, window, cx),
 6567        };
 6568
 6569        let runnable_task = match deployed_from {
 6570            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6571            _ => {
 6572                let mut task_context_task = Task::ready(None);
 6573                if let Some(tasks) = &tasks
 6574                    && let Some(project) = project
 6575                {
 6576                    task_context_task =
 6577                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6578                }
 6579
 6580                cx.spawn_in(window, {
 6581                    let buffer = buffer.clone();
 6582                    async move |editor, cx| {
 6583                        let task_context = task_context_task.await;
 6584
 6585                        let resolved_tasks =
 6586                            tasks
 6587                                .zip(task_context.clone())
 6588                                .map(|(tasks, task_context)| ResolvedTasks {
 6589                                    templates: tasks.resolve(&task_context).collect(),
 6590                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6591                                        multibuffer_point.row,
 6592                                        tasks.column,
 6593                                    )),
 6594                                });
 6595                        let debug_scenarios = editor
 6596                            .update(cx, |editor, cx| {
 6597                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6598                            })?
 6599                            .await;
 6600                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6601                    }
 6602                })
 6603            }
 6604        };
 6605
 6606        cx.spawn_in(window, async move |editor, cx| {
 6607            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6608            let code_actions = code_actions_task.await;
 6609            let spawn_straight_away = quick_launch
 6610                && resolved_tasks
 6611                    .as_ref()
 6612                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6613                && code_actions
 6614                    .as_ref()
 6615                    .is_none_or(|actions| actions.is_empty())
 6616                && debug_scenarios.is_empty();
 6617
 6618            editor.update_in(cx, |editor, window, cx| {
 6619                crate::hover_popover::hide_hover(editor, cx);
 6620                let actions = CodeActionContents::new(
 6621                    resolved_tasks,
 6622                    code_actions,
 6623                    debug_scenarios,
 6624                    task_context.unwrap_or_default(),
 6625                );
 6626
 6627                // Don't show the menu if there are no actions available
 6628                if actions.is_empty() {
 6629                    cx.notify();
 6630                    return Task::ready(Ok(()));
 6631                }
 6632
 6633                *editor.context_menu.borrow_mut() =
 6634                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6635                        buffer,
 6636                        actions,
 6637                        selected_item: Default::default(),
 6638                        scroll_handle: UniformListScrollHandle::default(),
 6639                        deployed_from,
 6640                    }));
 6641                cx.notify();
 6642                if spawn_straight_away
 6643                    && let Some(task) = editor.confirm_code_action(
 6644                        &ConfirmCodeAction { item_ix: Some(0) },
 6645                        window,
 6646                        cx,
 6647                    )
 6648                {
 6649                    return task;
 6650                }
 6651
 6652                Task::ready(Ok(()))
 6653            })
 6654        })
 6655        .detach_and_log_err(cx);
 6656    }
 6657
 6658    fn debug_scenarios(
 6659        &mut self,
 6660        resolved_tasks: &Option<ResolvedTasks>,
 6661        buffer: &Entity<Buffer>,
 6662        cx: &mut App,
 6663    ) -> Task<Vec<task::DebugScenario>> {
 6664        maybe!({
 6665            let project = self.project()?;
 6666            let dap_store = project.read(cx).dap_store();
 6667            let mut scenarios = vec![];
 6668            let resolved_tasks = resolved_tasks.as_ref()?;
 6669            let buffer = buffer.read(cx);
 6670            let language = buffer.language()?;
 6671            let file = buffer.file();
 6672            let debug_adapter = language_settings(language.name().into(), file, cx)
 6673                .debuggers
 6674                .first()
 6675                .map(SharedString::from)
 6676                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6677
 6678            dap_store.update(cx, |dap_store, cx| {
 6679                for (_, task) in &resolved_tasks.templates {
 6680                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6681                        task.original_task().clone(),
 6682                        debug_adapter.clone().into(),
 6683                        task.display_label().to_owned().into(),
 6684                        cx,
 6685                    );
 6686                    scenarios.push(maybe_scenario);
 6687                }
 6688            });
 6689            Some(cx.background_spawn(async move {
 6690                futures::future::join_all(scenarios)
 6691                    .await
 6692                    .into_iter()
 6693                    .flatten()
 6694                    .collect::<Vec<_>>()
 6695            }))
 6696        })
 6697        .unwrap_or_else(|| Task::ready(vec![]))
 6698    }
 6699
 6700    fn code_actions(
 6701        &mut self,
 6702        buffer_row: u32,
 6703        window: &mut Window,
 6704        cx: &mut Context<Self>,
 6705    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6706        let mut task = self.code_actions_task.take();
 6707        cx.spawn_in(window, async move |editor, cx| {
 6708            while let Some(prev_task) = task {
 6709                prev_task.await.log_err();
 6710                task = editor
 6711                    .update(cx, |this, _| this.code_actions_task.take())
 6712                    .ok()?;
 6713            }
 6714
 6715            editor
 6716                .update(cx, |editor, cx| {
 6717                    editor
 6718                        .available_code_actions
 6719                        .clone()
 6720                        .and_then(|(location, code_actions)| {
 6721                            let snapshot = location.buffer.read(cx).snapshot();
 6722                            let point_range = location.range.to_point(&snapshot);
 6723                            let point_range = point_range.start.row..=point_range.end.row;
 6724                            if point_range.contains(&buffer_row) {
 6725                                Some(code_actions)
 6726                            } else {
 6727                                None
 6728                            }
 6729                        })
 6730                })
 6731                .ok()
 6732                .flatten()
 6733        })
 6734    }
 6735
 6736    pub fn confirm_code_action(
 6737        &mut self,
 6738        action: &ConfirmCodeAction,
 6739        window: &mut Window,
 6740        cx: &mut Context<Self>,
 6741    ) -> Option<Task<Result<()>>> {
 6742        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6743
 6744        let actions_menu =
 6745            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6746                menu
 6747            } else {
 6748                return None;
 6749            };
 6750
 6751        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6752        let action = actions_menu.actions.get(action_ix)?;
 6753        let title = action.label();
 6754        let buffer = actions_menu.buffer;
 6755        let workspace = self.workspace()?;
 6756
 6757        match action {
 6758            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6759                workspace.update(cx, |workspace, cx| {
 6760                    workspace.schedule_resolved_task(
 6761                        task_source_kind,
 6762                        resolved_task,
 6763                        false,
 6764                        window,
 6765                        cx,
 6766                    );
 6767
 6768                    Some(Task::ready(Ok(())))
 6769                })
 6770            }
 6771            CodeActionsItem::CodeAction {
 6772                excerpt_id,
 6773                action,
 6774                provider,
 6775            } => {
 6776                let apply_code_action =
 6777                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6778                let workspace = workspace.downgrade();
 6779                Some(cx.spawn_in(window, async move |editor, cx| {
 6780                    let project_transaction = apply_code_action.await?;
 6781                    Self::open_project_transaction(
 6782                        &editor,
 6783                        workspace,
 6784                        project_transaction,
 6785                        title,
 6786                        cx,
 6787                    )
 6788                    .await
 6789                }))
 6790            }
 6791            CodeActionsItem::DebugScenario(scenario) => {
 6792                let context = actions_menu.actions.context;
 6793
 6794                workspace.update(cx, |workspace, cx| {
 6795                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6796                    workspace.start_debug_session(
 6797                        scenario,
 6798                        context,
 6799                        Some(buffer),
 6800                        None,
 6801                        window,
 6802                        cx,
 6803                    );
 6804                });
 6805                Some(Task::ready(Ok(())))
 6806            }
 6807        }
 6808    }
 6809
 6810    fn open_transaction_for_hidden_buffers(
 6811        workspace: Entity<Workspace>,
 6812        transaction: ProjectTransaction,
 6813        title: String,
 6814        window: &mut Window,
 6815        cx: &mut Context<Self>,
 6816    ) {
 6817        if transaction.0.is_empty() {
 6818            return;
 6819        }
 6820
 6821        let edited_buffers_already_open = {
 6822            let other_editors: Vec<Entity<Editor>> = workspace
 6823                .read(cx)
 6824                .panes()
 6825                .iter()
 6826                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6827                .filter(|editor| editor.entity_id() != cx.entity_id())
 6828                .collect();
 6829
 6830            transaction.0.keys().all(|buffer| {
 6831                other_editors.iter().any(|editor| {
 6832                    let multi_buffer = editor.read(cx).buffer();
 6833                    multi_buffer.read(cx).is_singleton()
 6834                        && multi_buffer
 6835                            .read(cx)
 6836                            .as_singleton()
 6837                            .map_or(false, |singleton| {
 6838                                singleton.entity_id() == buffer.entity_id()
 6839                            })
 6840                })
 6841            })
 6842        };
 6843        if !edited_buffers_already_open {
 6844            let workspace = workspace.downgrade();
 6845            cx.defer_in(window, move |_, window, cx| {
 6846                cx.spawn_in(window, async move |editor, cx| {
 6847                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6848                        .await
 6849                        .ok()
 6850                })
 6851                .detach();
 6852            });
 6853        }
 6854    }
 6855
 6856    pub async fn open_project_transaction(
 6857        editor: &WeakEntity<Editor>,
 6858        workspace: WeakEntity<Workspace>,
 6859        transaction: ProjectTransaction,
 6860        title: String,
 6861        cx: &mut AsyncWindowContext,
 6862    ) -> Result<()> {
 6863        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 6864        cx.update(|_, cx| {
 6865            entries.sort_unstable_by_key(|(buffer, _)| {
 6866                buffer.read(cx).file().map(|f| f.path().clone())
 6867            });
 6868        })?;
 6869        if entries.is_empty() {
 6870            return Ok(());
 6871        }
 6872
 6873        // If the project transaction's edits are all contained within this editor, then
 6874        // avoid opening a new editor to display them.
 6875
 6876        if let [(buffer, transaction)] = &*entries {
 6877            let excerpt = editor.update(cx, |editor, cx| {
 6878                editor
 6879                    .buffer()
 6880                    .read(cx)
 6881                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 6882            })?;
 6883            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 6884                && excerpted_buffer == *buffer
 6885            {
 6886                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 6887                    let excerpt_range = excerpt_range.to_offset(buffer);
 6888                    buffer
 6889                        .edited_ranges_for_transaction::<usize>(transaction)
 6890                        .all(|range| {
 6891                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 6892                        })
 6893                });
 6894
 6895                if all_edits_within_excerpt {
 6896                    return Ok(());
 6897                }
 6898            }
 6899        }
 6900
 6901        let mut ranges_to_highlight = Vec::new();
 6902        let excerpt_buffer = cx.new(|cx| {
 6903            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 6904            for (buffer_handle, transaction) in &entries {
 6905                let edited_ranges = buffer_handle
 6906                    .read(cx)
 6907                    .edited_ranges_for_transaction::<Point>(transaction)
 6908                    .collect::<Vec<_>>();
 6909                let (ranges, _) = multibuffer.set_excerpts_for_path(
 6910                    PathKey::for_buffer(buffer_handle, cx),
 6911                    buffer_handle.clone(),
 6912                    edited_ranges,
 6913                    multibuffer_context_lines(cx),
 6914                    cx,
 6915                );
 6916
 6917                ranges_to_highlight.extend(ranges);
 6918            }
 6919            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 6920            multibuffer
 6921        });
 6922
 6923        workspace.update_in(cx, |workspace, window, cx| {
 6924            let project = workspace.project().clone();
 6925            let editor =
 6926                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 6927            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 6928            editor.update(cx, |editor, cx| {
 6929                editor.highlight_background::<Self>(
 6930                    &ranges_to_highlight,
 6931                    |_, theme| theme.colors().editor_highlighted_line_background,
 6932                    cx,
 6933                );
 6934            });
 6935        })?;
 6936
 6937        Ok(())
 6938    }
 6939
 6940    pub fn clear_code_action_providers(&mut self) {
 6941        self.code_action_providers.clear();
 6942        self.available_code_actions.take();
 6943    }
 6944
 6945    pub fn add_code_action_provider(
 6946        &mut self,
 6947        provider: Rc<dyn CodeActionProvider>,
 6948        window: &mut Window,
 6949        cx: &mut Context<Self>,
 6950    ) {
 6951        if self
 6952            .code_action_providers
 6953            .iter()
 6954            .any(|existing_provider| existing_provider.id() == provider.id())
 6955        {
 6956            return;
 6957        }
 6958
 6959        self.code_action_providers.push(provider);
 6960        self.refresh_code_actions(window, cx);
 6961    }
 6962
 6963    pub fn remove_code_action_provider(
 6964        &mut self,
 6965        id: Arc<str>,
 6966        window: &mut Window,
 6967        cx: &mut Context<Self>,
 6968    ) {
 6969        self.code_action_providers
 6970            .retain(|provider| provider.id() != id);
 6971        self.refresh_code_actions(window, cx);
 6972    }
 6973
 6974    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 6975        !self.code_action_providers.is_empty()
 6976            && EditorSettings::get_global(cx).toolbar.code_actions
 6977    }
 6978
 6979    pub fn has_available_code_actions(&self) -> bool {
 6980        self.available_code_actions
 6981            .as_ref()
 6982            .is_some_and(|(_, actions)| !actions.is_empty())
 6983    }
 6984
 6985    fn render_inline_code_actions(
 6986        &self,
 6987        icon_size: ui::IconSize,
 6988        display_row: DisplayRow,
 6989        is_active: bool,
 6990        cx: &mut Context<Self>,
 6991    ) -> AnyElement {
 6992        let show_tooltip = !self.context_menu_visible();
 6993        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 6994            .icon_size(icon_size)
 6995            .shape(ui::IconButtonShape::Square)
 6996            .icon_color(ui::Color::Hidden)
 6997            .toggle_state(is_active)
 6998            .when(show_tooltip, |this| {
 6999                this.tooltip({
 7000                    let focus_handle = self.focus_handle.clone();
 7001                    move |_window, cx| {
 7002                        Tooltip::for_action_in(
 7003                            "Toggle Code Actions",
 7004                            &ToggleCodeActions {
 7005                                deployed_from: None,
 7006                                quick_launch: false,
 7007                            },
 7008                            &focus_handle,
 7009                            cx,
 7010                        )
 7011                    }
 7012                })
 7013            })
 7014            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7015                window.focus(&editor.focus_handle(cx), cx);
 7016                editor.toggle_code_actions(
 7017                    &crate::actions::ToggleCodeActions {
 7018                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7019                            display_row,
 7020                        )),
 7021                        quick_launch: false,
 7022                    },
 7023                    window,
 7024                    cx,
 7025                );
 7026            }))
 7027            .into_any_element()
 7028    }
 7029
 7030    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7031        &self.context_menu
 7032    }
 7033
 7034    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7035        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7036            cx.background_executor()
 7037                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7038                .await;
 7039
 7040            let (start_buffer, start, _, end, newest_selection) = this
 7041                .update(cx, |this, cx| {
 7042                    let newest_selection = this.selections.newest_anchor().clone();
 7043                    if newest_selection.head().diff_base_anchor.is_some() {
 7044                        return None;
 7045                    }
 7046                    let display_snapshot = this.display_snapshot(cx);
 7047                    let newest_selection_adjusted =
 7048                        this.selections.newest_adjusted(&display_snapshot);
 7049                    let buffer = this.buffer.read(cx);
 7050
 7051                    let (start_buffer, start) =
 7052                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7053                    let (end_buffer, end) =
 7054                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7055
 7056                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7057                })?
 7058                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7059                .context(
 7060                    "Expected selection to lie in a single buffer when refreshing code actions",
 7061                )?;
 7062            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7063                let providers = this.code_action_providers.clone();
 7064                let tasks = this
 7065                    .code_action_providers
 7066                    .iter()
 7067                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7068                    .collect::<Vec<_>>();
 7069                (providers, tasks)
 7070            })?;
 7071
 7072            let mut actions = Vec::new();
 7073            for (provider, provider_actions) in
 7074                providers.into_iter().zip(future::join_all(tasks).await)
 7075            {
 7076                if let Some(provider_actions) = provider_actions.log_err() {
 7077                    actions.extend(provider_actions.into_iter().map(|action| {
 7078                        AvailableCodeAction {
 7079                            excerpt_id: newest_selection.start.excerpt_id,
 7080                            action,
 7081                            provider: provider.clone(),
 7082                        }
 7083                    }));
 7084                }
 7085            }
 7086
 7087            this.update(cx, |this, cx| {
 7088                this.available_code_actions = if actions.is_empty() {
 7089                    None
 7090                } else {
 7091                    Some((
 7092                        Location {
 7093                            buffer: start_buffer,
 7094                            range: start..end,
 7095                        },
 7096                        actions.into(),
 7097                    ))
 7098                };
 7099                cx.notify();
 7100            })
 7101        }));
 7102    }
 7103
 7104    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7105        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7106            self.show_git_blame_inline = false;
 7107
 7108            self.show_git_blame_inline_delay_task =
 7109                Some(cx.spawn_in(window, async move |this, cx| {
 7110                    cx.background_executor().timer(delay).await;
 7111
 7112                    this.update(cx, |this, cx| {
 7113                        this.show_git_blame_inline = true;
 7114                        cx.notify();
 7115                    })
 7116                    .log_err();
 7117                }));
 7118        }
 7119    }
 7120
 7121    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7122        let snapshot = self.snapshot(window, cx);
 7123        let cursor = self
 7124            .selections
 7125            .newest::<Point>(&snapshot.display_snapshot)
 7126            .head();
 7127        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7128        else {
 7129            return;
 7130        };
 7131
 7132        if self.blame.is_none() {
 7133            self.start_git_blame(true, window, cx);
 7134        }
 7135        let Some(blame) = self.blame.as_ref() else {
 7136            return;
 7137        };
 7138
 7139        let row_info = RowInfo {
 7140            buffer_id: Some(buffer.remote_id()),
 7141            buffer_row: Some(point.row),
 7142            ..Default::default()
 7143        };
 7144        let Some((buffer, blame_entry)) = blame
 7145            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7146            .flatten()
 7147        else {
 7148            return;
 7149        };
 7150
 7151        let anchor = self.selections.newest_anchor().head();
 7152        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7153        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7154            self.show_blame_popover(
 7155                buffer,
 7156                &blame_entry,
 7157                position + last_bounds.origin,
 7158                true,
 7159                cx,
 7160            );
 7161        };
 7162    }
 7163
 7164    fn show_blame_popover(
 7165        &mut self,
 7166        buffer: BufferId,
 7167        blame_entry: &BlameEntry,
 7168        position: gpui::Point<Pixels>,
 7169        ignore_timeout: bool,
 7170        cx: &mut Context<Self>,
 7171    ) {
 7172        if let Some(state) = &mut self.inline_blame_popover {
 7173            state.hide_task.take();
 7174        } else {
 7175            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7176            let blame_entry = blame_entry.clone();
 7177            let show_task = cx.spawn(async move |editor, cx| {
 7178                if !ignore_timeout {
 7179                    cx.background_executor()
 7180                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7181                        .await;
 7182                }
 7183                editor
 7184                    .update(cx, |editor, cx| {
 7185                        editor.inline_blame_popover_show_task.take();
 7186                        let Some(blame) = editor.blame.as_ref() else {
 7187                            return;
 7188                        };
 7189                        let blame = blame.read(cx);
 7190                        let details = blame.details_for_entry(buffer, &blame_entry);
 7191                        let markdown = cx.new(|cx| {
 7192                            Markdown::new(
 7193                                details
 7194                                    .as_ref()
 7195                                    .map(|message| message.message.clone())
 7196                                    .unwrap_or_default(),
 7197                                None,
 7198                                None,
 7199                                cx,
 7200                            )
 7201                        });
 7202                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7203                            position,
 7204                            hide_task: None,
 7205                            popover_bounds: None,
 7206                            popover_state: InlineBlamePopoverState {
 7207                                scroll_handle: ScrollHandle::new(),
 7208                                commit_message: details,
 7209                                markdown,
 7210                            },
 7211                            keyboard_grace: ignore_timeout,
 7212                        });
 7213                        cx.notify();
 7214                    })
 7215                    .ok();
 7216            });
 7217            self.inline_blame_popover_show_task = Some(show_task);
 7218        }
 7219    }
 7220
 7221    pub fn has_mouse_context_menu(&self) -> bool {
 7222        self.mouse_context_menu.is_some()
 7223    }
 7224
 7225    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7226        self.inline_blame_popover_show_task.take();
 7227        if let Some(state) = &mut self.inline_blame_popover {
 7228            let hide_task = cx.spawn(async move |editor, cx| {
 7229                if !ignore_timeout {
 7230                    cx.background_executor()
 7231                        .timer(std::time::Duration::from_millis(100))
 7232                        .await;
 7233                }
 7234                editor
 7235                    .update(cx, |editor, cx| {
 7236                        editor.inline_blame_popover.take();
 7237                        cx.notify();
 7238                    })
 7239                    .ok();
 7240            });
 7241            state.hide_task = Some(hide_task);
 7242            true
 7243        } else {
 7244            false
 7245        }
 7246    }
 7247
 7248    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7249        if self.pending_rename.is_some() {
 7250            return None;
 7251        }
 7252
 7253        let provider = self.semantics_provider.clone()?;
 7254        let buffer = self.buffer.read(cx);
 7255        let newest_selection = self.selections.newest_anchor().clone();
 7256        let cursor_position = newest_selection.head();
 7257        let (cursor_buffer, cursor_buffer_position) =
 7258            buffer.text_anchor_for_position(cursor_position, cx)?;
 7259        let (tail_buffer, tail_buffer_position) =
 7260            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7261        if cursor_buffer != tail_buffer {
 7262            return None;
 7263        }
 7264
 7265        let snapshot = cursor_buffer.read(cx).snapshot();
 7266        let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7267        let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7268        if start_word_range != end_word_range {
 7269            self.document_highlights_task.take();
 7270            self.clear_background_highlights::<DocumentHighlightRead>(cx);
 7271            self.clear_background_highlights::<DocumentHighlightWrite>(cx);
 7272            return None;
 7273        }
 7274
 7275        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7276        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7277            cx.background_executor()
 7278                .timer(Duration::from_millis(debounce))
 7279                .await;
 7280
 7281            let highlights = if let Some(highlights) = cx.update(|cx| {
 7282                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7283            }) {
 7284                highlights.await.log_err()
 7285            } else {
 7286                None
 7287            };
 7288
 7289            if let Some(highlights) = highlights {
 7290                this.update(cx, |this, cx| {
 7291                    if this.pending_rename.is_some() {
 7292                        return;
 7293                    }
 7294
 7295                    let buffer = this.buffer.read(cx);
 7296                    if buffer
 7297                        .text_anchor_for_position(cursor_position, cx)
 7298                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7299                    {
 7300                        return;
 7301                    }
 7302
 7303                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7304                    let mut write_ranges = Vec::new();
 7305                    let mut read_ranges = Vec::new();
 7306                    for highlight in highlights {
 7307                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7308                        for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
 7309                        {
 7310                            let start = highlight
 7311                                .range
 7312                                .start
 7313                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7314                            let end = highlight
 7315                                .range
 7316                                .end
 7317                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7318                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7319                                continue;
 7320                            }
 7321
 7322                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7323                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7324                                write_ranges.push(range);
 7325                            } else {
 7326                                read_ranges.push(range);
 7327                            }
 7328                        }
 7329                    }
 7330
 7331                    this.highlight_background::<DocumentHighlightRead>(
 7332                        &read_ranges,
 7333                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7334                        cx,
 7335                    );
 7336                    this.highlight_background::<DocumentHighlightWrite>(
 7337                        &write_ranges,
 7338                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7339                        cx,
 7340                    );
 7341                    cx.notify();
 7342                })
 7343                .log_err();
 7344            }
 7345        }));
 7346        None
 7347    }
 7348
 7349    fn prepare_highlight_query_from_selection(
 7350        &mut self,
 7351        window: &Window,
 7352        cx: &mut Context<Editor>,
 7353    ) -> Option<(String, Range<Anchor>)> {
 7354        if matches!(self.mode, EditorMode::SingleLine) {
 7355            return None;
 7356        }
 7357        if !EditorSettings::get_global(cx).selection_highlight {
 7358            return None;
 7359        }
 7360        if self.selections.count() != 1 || self.selections.line_mode() {
 7361            return None;
 7362        }
 7363        let snapshot = self.snapshot(window, cx);
 7364        let selection = self.selections.newest::<Point>(&snapshot);
 7365        // If the selection spans multiple rows OR it is empty
 7366        if selection.start.row != selection.end.row
 7367            || selection.start.column == selection.end.column
 7368        {
 7369            return None;
 7370        }
 7371        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7372        let query = snapshot
 7373            .buffer_snapshot()
 7374            .text_for_range(selection_anchor_range.clone())
 7375            .collect::<String>();
 7376        if query.trim().is_empty() {
 7377            return None;
 7378        }
 7379        Some((query, selection_anchor_range))
 7380    }
 7381
 7382    #[ztracing::instrument(skip_all)]
 7383    fn update_selection_occurrence_highlights(
 7384        &mut self,
 7385        query_text: String,
 7386        query_range: Range<Anchor>,
 7387        multi_buffer_range_to_query: Range<Point>,
 7388        use_debounce: bool,
 7389        window: &mut Window,
 7390        cx: &mut Context<Editor>,
 7391    ) -> Task<()> {
 7392        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7393        cx.spawn_in(window, async move |editor, cx| {
 7394            if use_debounce {
 7395                cx.background_executor()
 7396                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7397                    .await;
 7398            }
 7399            let match_task = cx.background_spawn(async move {
 7400                let buffer_ranges = multi_buffer_snapshot
 7401                    .range_to_buffer_ranges(
 7402                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7403                    )
 7404                    .into_iter()
 7405                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7406                let mut match_ranges = Vec::new();
 7407                let Ok(regex) = project::search::SearchQuery::text(
 7408                    query_text.clone(),
 7409                    false,
 7410                    false,
 7411                    false,
 7412                    Default::default(),
 7413                    Default::default(),
 7414                    false,
 7415                    None,
 7416                ) else {
 7417                    return Vec::default();
 7418                };
 7419                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7420                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7421                    match_ranges.extend(
 7422                        regex
 7423                            .search(
 7424                                buffer_snapshot,
 7425                                Some(search_range.start.0..search_range.end.0),
 7426                            )
 7427                            .await
 7428                            .into_iter()
 7429                            .filter_map(|match_range| {
 7430                                let match_start = buffer_snapshot
 7431                                    .anchor_after(search_range.start + match_range.start);
 7432                                let match_end = buffer_snapshot
 7433                                    .anchor_before(search_range.start + match_range.end);
 7434                                let match_anchor_range =
 7435                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7436                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7437                            }),
 7438                    );
 7439                }
 7440                match_ranges
 7441            });
 7442            let match_ranges = match_task.await;
 7443            editor
 7444                .update_in(cx, |editor, _, cx| {
 7445                    if use_debounce {
 7446                        editor.clear_background_highlights::<SelectedTextHighlight>(cx);
 7447                        editor.debounced_selection_highlight_complete = true;
 7448                    } else if editor.debounced_selection_highlight_complete {
 7449                        return;
 7450                    }
 7451                    if !match_ranges.is_empty() {
 7452                        editor.highlight_background::<SelectedTextHighlight>(
 7453                            &match_ranges,
 7454                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7455                            cx,
 7456                        )
 7457                    }
 7458                })
 7459                .log_err();
 7460        })
 7461    }
 7462
 7463    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7464        struct NewlineFold;
 7465        let type_id = std::any::TypeId::of::<NewlineFold>();
 7466        if !self.mode.is_single_line() {
 7467            return;
 7468        }
 7469        let snapshot = self.snapshot(window, cx);
 7470        if snapshot.buffer_snapshot().max_point().row == 0 {
 7471            return;
 7472        }
 7473        let task = cx.background_spawn(async move {
 7474            let new_newlines = snapshot
 7475                .buffer_chars_at(MultiBufferOffset(0))
 7476                .filter_map(|(c, i)| {
 7477                    if c == '\n' {
 7478                        Some(
 7479                            snapshot.buffer_snapshot().anchor_after(i)
 7480                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7481                        )
 7482                    } else {
 7483                        None
 7484                    }
 7485                })
 7486                .collect::<Vec<_>>();
 7487            let existing_newlines = snapshot
 7488                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7489                .filter_map(|fold| {
 7490                    if fold.placeholder.type_tag == Some(type_id) {
 7491                        Some(fold.range.start..fold.range.end)
 7492                    } else {
 7493                        None
 7494                    }
 7495                })
 7496                .collect::<Vec<_>>();
 7497
 7498            (new_newlines, existing_newlines)
 7499        });
 7500        self.folding_newlines = cx.spawn(async move |this, cx| {
 7501            let (new_newlines, existing_newlines) = task.await;
 7502            if new_newlines == existing_newlines {
 7503                return;
 7504            }
 7505            let placeholder = FoldPlaceholder {
 7506                render: Arc::new(move |_, _, cx| {
 7507                    div()
 7508                        .bg(cx.theme().status().hint_background)
 7509                        .border_b_1()
 7510                        .size_full()
 7511                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7512                        .border_color(cx.theme().status().hint)
 7513                        .child("\\n")
 7514                        .into_any()
 7515                }),
 7516                constrain_width: false,
 7517                merge_adjacent: false,
 7518                type_tag: Some(type_id),
 7519            };
 7520            let creases = new_newlines
 7521                .into_iter()
 7522                .map(|range| Crease::simple(range, placeholder.clone()))
 7523                .collect();
 7524            this.update(cx, |this, cx| {
 7525                this.display_map.update(cx, |display_map, cx| {
 7526                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7527                    display_map.fold(creases, cx);
 7528                });
 7529            })
 7530            .ok();
 7531        });
 7532    }
 7533
 7534    #[ztracing::instrument(skip_all)]
 7535    fn refresh_selected_text_highlights(
 7536        &mut self,
 7537        on_buffer_edit: bool,
 7538        window: &mut Window,
 7539        cx: &mut Context<Editor>,
 7540    ) {
 7541        let Some((query_text, query_range)) =
 7542            self.prepare_highlight_query_from_selection(window, cx)
 7543        else {
 7544            self.clear_background_highlights::<SelectedTextHighlight>(cx);
 7545            self.quick_selection_highlight_task.take();
 7546            self.debounced_selection_highlight_task.take();
 7547            self.debounced_selection_highlight_complete = false;
 7548            return;
 7549        };
 7550        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7551        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7552        let query_changed = self
 7553            .quick_selection_highlight_task
 7554            .as_ref()
 7555            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7556        if query_changed {
 7557            self.debounced_selection_highlight_complete = false;
 7558        }
 7559        if on_buffer_edit || query_changed {
 7560            let multi_buffer_visible_start = self
 7561                .scroll_manager
 7562                .native_anchor(&display_snapshot, cx)
 7563                .anchor
 7564                .to_point(&multi_buffer_snapshot);
 7565            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7566                multi_buffer_visible_start
 7567                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7568                Bias::Left,
 7569            );
 7570            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7571            self.quick_selection_highlight_task = Some((
 7572                query_range.clone(),
 7573                self.update_selection_occurrence_highlights(
 7574                    query_text.clone(),
 7575                    query_range.clone(),
 7576                    multi_buffer_visible_range,
 7577                    false,
 7578                    window,
 7579                    cx,
 7580                ),
 7581            ));
 7582        }
 7583        if on_buffer_edit
 7584            || self
 7585                .debounced_selection_highlight_task
 7586                .as_ref()
 7587                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7588        {
 7589            let multi_buffer_start = multi_buffer_snapshot
 7590                .anchor_before(MultiBufferOffset(0))
 7591                .to_point(&multi_buffer_snapshot);
 7592            let multi_buffer_end = multi_buffer_snapshot
 7593                .anchor_after(multi_buffer_snapshot.len())
 7594                .to_point(&multi_buffer_snapshot);
 7595            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7596            self.debounced_selection_highlight_task = Some((
 7597                query_range.clone(),
 7598                self.update_selection_occurrence_highlights(
 7599                    query_text,
 7600                    query_range,
 7601                    multi_buffer_full_range,
 7602                    true,
 7603                    window,
 7604                    cx,
 7605                ),
 7606            ));
 7607        }
 7608    }
 7609
 7610    pub fn refresh_edit_prediction(
 7611        &mut self,
 7612        debounce: bool,
 7613        user_requested: bool,
 7614        window: &mut Window,
 7615        cx: &mut Context<Self>,
 7616    ) -> Option<()> {
 7617        if DisableAiSettings::get_global(cx).disable_ai {
 7618            return None;
 7619        }
 7620
 7621        let provider = self.edit_prediction_provider()?;
 7622        let cursor = self.selections.newest_anchor().head();
 7623        let (buffer, cursor_buffer_position) =
 7624            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7625
 7626        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7627            self.discard_edit_prediction(false, cx);
 7628            return None;
 7629        }
 7630
 7631        self.update_visible_edit_prediction(window, cx);
 7632
 7633        if !user_requested
 7634            && (!self.should_show_edit_predictions()
 7635                || !self.is_focused(window)
 7636                || buffer.read(cx).is_empty())
 7637        {
 7638            self.discard_edit_prediction(false, cx);
 7639            return None;
 7640        }
 7641
 7642        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7643        Some(())
 7644    }
 7645
 7646    fn show_edit_predictions_in_menu(&self) -> bool {
 7647        match self.edit_prediction_settings {
 7648            EditPredictionSettings::Disabled => false,
 7649            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7650        }
 7651    }
 7652
 7653    pub fn edit_predictions_enabled(&self) -> bool {
 7654        match self.edit_prediction_settings {
 7655            EditPredictionSettings::Disabled => false,
 7656            EditPredictionSettings::Enabled { .. } => true,
 7657        }
 7658    }
 7659
 7660    fn edit_prediction_requires_modifier(&self) -> bool {
 7661        match self.edit_prediction_settings {
 7662            EditPredictionSettings::Disabled => false,
 7663            EditPredictionSettings::Enabled {
 7664                preview_requires_modifier,
 7665                ..
 7666            } => preview_requires_modifier,
 7667        }
 7668    }
 7669
 7670    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7671        if self.edit_prediction_provider.is_none() || DisableAiSettings::get_global(cx).disable_ai {
 7672            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7673            self.discard_edit_prediction(false, cx);
 7674        } else {
 7675            let selection = self.selections.newest_anchor();
 7676            let cursor = selection.head();
 7677
 7678            if let Some((buffer, cursor_buffer_position)) =
 7679                self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7680            {
 7681                self.edit_prediction_settings =
 7682                    self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7683            }
 7684        }
 7685    }
 7686
 7687    fn edit_prediction_settings_at_position(
 7688        &self,
 7689        buffer: &Entity<Buffer>,
 7690        buffer_position: language::Anchor,
 7691        cx: &App,
 7692    ) -> EditPredictionSettings {
 7693        if !self.mode.is_full()
 7694            || !self.show_edit_predictions_override.unwrap_or(true)
 7695            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7696        {
 7697            return EditPredictionSettings::Disabled;
 7698        }
 7699
 7700        let buffer = buffer.read(cx);
 7701
 7702        let file = buffer.file();
 7703
 7704        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7705            return EditPredictionSettings::Disabled;
 7706        };
 7707
 7708        let by_provider = matches!(
 7709            self.menu_edit_predictions_policy,
 7710            MenuEditPredictionsPolicy::ByProvider
 7711        );
 7712
 7713        let show_in_menu = by_provider
 7714            && self
 7715                .edit_prediction_provider
 7716                .as_ref()
 7717                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7718
 7719        let preview_requires_modifier =
 7720            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7721
 7722        EditPredictionSettings::Enabled {
 7723            show_in_menu,
 7724            preview_requires_modifier,
 7725        }
 7726    }
 7727
 7728    fn should_show_edit_predictions(&self) -> bool {
 7729        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7730    }
 7731
 7732    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7733        matches!(
 7734            self.edit_prediction_preview,
 7735            EditPredictionPreview::Active { .. }
 7736        )
 7737    }
 7738
 7739    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7740        let cursor = self.selections.newest_anchor().head();
 7741        if let Some((buffer, cursor_position)) =
 7742            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7743        {
 7744            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7745        } else {
 7746            false
 7747        }
 7748    }
 7749
 7750    pub fn supports_minimap(&self, cx: &App) -> bool {
 7751        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7752    }
 7753
 7754    fn edit_predictions_enabled_in_buffer(
 7755        &self,
 7756        buffer: &Entity<Buffer>,
 7757        buffer_position: language::Anchor,
 7758        cx: &App,
 7759    ) -> bool {
 7760        maybe!({
 7761            if self.read_only(cx) {
 7762                return Some(false);
 7763            }
 7764            let provider = self.edit_prediction_provider()?;
 7765            if !provider.is_enabled(buffer, buffer_position, cx) {
 7766                return Some(false);
 7767            }
 7768            let buffer = buffer.read(cx);
 7769            let Some(file) = buffer.file() else {
 7770                return Some(true);
 7771            };
 7772            let settings = all_language_settings(Some(file), cx);
 7773            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7774        })
 7775        .unwrap_or(false)
 7776    }
 7777
 7778    pub fn show_edit_prediction(
 7779        &mut self,
 7780        _: &ShowEditPrediction,
 7781        window: &mut Window,
 7782        cx: &mut Context<Self>,
 7783    ) {
 7784        if !self.has_active_edit_prediction() {
 7785            self.refresh_edit_prediction(false, true, window, cx);
 7786            return;
 7787        }
 7788
 7789        self.update_visible_edit_prediction(window, cx);
 7790    }
 7791
 7792    pub fn display_cursor_names(
 7793        &mut self,
 7794        _: &DisplayCursorNames,
 7795        window: &mut Window,
 7796        cx: &mut Context<Self>,
 7797    ) {
 7798        self.show_cursor_names(window, cx);
 7799    }
 7800
 7801    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7802        self.show_cursor_names = true;
 7803        cx.notify();
 7804        cx.spawn_in(window, async move |this, cx| {
 7805            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 7806            this.update(cx, |this, cx| {
 7807                this.show_cursor_names = false;
 7808                cx.notify()
 7809            })
 7810            .ok()
 7811        })
 7812        .detach();
 7813    }
 7814
 7815    pub fn accept_partial_edit_prediction(
 7816        &mut self,
 7817        granularity: EditPredictionGranularity,
 7818        window: &mut Window,
 7819        cx: &mut Context<Self>,
 7820    ) {
 7821        if self.show_edit_predictions_in_menu() {
 7822            self.hide_context_menu(window, cx);
 7823        }
 7824
 7825        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 7826            return;
 7827        };
 7828
 7829        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 7830            return;
 7831        }
 7832
 7833        match &active_edit_prediction.completion {
 7834            EditPrediction::MoveWithin { target, .. } => {
 7835                let target = *target;
 7836
 7837                if matches!(granularity, EditPredictionGranularity::Full) {
 7838                    if let Some(position_map) = &self.last_position_map {
 7839                        let target_row = target.to_display_point(&position_map.snapshot).row();
 7840                        let is_visible = position_map.visible_row_range.contains(&target_row);
 7841
 7842                        if is_visible || !self.edit_prediction_requires_modifier() {
 7843                            self.unfold_ranges(&[target..target], true, false, cx);
 7844                            self.change_selections(
 7845                                SelectionEffects::scroll(Autoscroll::newest()),
 7846                                window,
 7847                                cx,
 7848                                |selections| {
 7849                                    selections.select_anchor_ranges([target..target]);
 7850                                },
 7851                            );
 7852                            self.clear_row_highlights::<EditPredictionPreview>();
 7853                            self.edit_prediction_preview
 7854                                .set_previous_scroll_position(None);
 7855                        } else {
 7856                            // Highlight and request scroll
 7857                            self.edit_prediction_preview
 7858                                .set_previous_scroll_position(Some(
 7859                                    position_map.snapshot.scroll_anchor,
 7860                                ));
 7861                            self.highlight_rows::<EditPredictionPreview>(
 7862                                target..target,
 7863                                cx.theme().colors().editor_highlighted_line_background,
 7864                                RowHighlightOptions {
 7865                                    autoscroll: true,
 7866                                    ..Default::default()
 7867                                },
 7868                                cx,
 7869                            );
 7870                            self.request_autoscroll(Autoscroll::fit(), cx);
 7871                        }
 7872                    }
 7873                } else {
 7874                    self.change_selections(
 7875                        SelectionEffects::scroll(Autoscroll::newest()),
 7876                        window,
 7877                        cx,
 7878                        |selections| {
 7879                            selections.select_anchor_ranges([target..target]);
 7880                        },
 7881                    );
 7882                }
 7883            }
 7884            EditPrediction::MoveOutside { snapshot, target } => {
 7885                if let Some(workspace) = self.workspace() {
 7886                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 7887                        .detach_and_log_err(cx);
 7888                }
 7889            }
 7890            EditPrediction::Edit {
 7891                edits,
 7892                cursor_position,
 7893                ..
 7894            } => {
 7895                self.report_edit_prediction_event(
 7896                    active_edit_prediction.completion_id.clone(),
 7897                    true,
 7898                    cx,
 7899                );
 7900
 7901                match granularity {
 7902                    EditPredictionGranularity::Full => {
 7903                        if let Some(provider) = self.edit_prediction_provider() {
 7904                            provider.accept(cx);
 7905                        }
 7906
 7907                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 7908
 7909                        // Compute fallback cursor position BEFORE applying the edit,
 7910                        // so the anchor tracks through the edit correctly
 7911                        let fallback_cursor_target = {
 7912                            let snapshot = self.buffer.read(cx).snapshot(cx);
 7913                            edits.last().unwrap().0.end.bias_right(&snapshot)
 7914                        };
 7915
 7916                        self.buffer.update(cx, |buffer, cx| {
 7917                            buffer.edit(edits.iter().cloned(), None, cx)
 7918                        });
 7919
 7920                        // Resolve cursor position after the edit is applied
 7921                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 7922                            // The anchor tracks through the edit, then we add the offset
 7923                            let snapshot = self.buffer.read(cx).snapshot(cx);
 7924                            let base_offset = anchor.to_offset(&snapshot).0;
 7925                            let target_offset =
 7926                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 7927                            snapshot.anchor_after(target_offset)
 7928                        } else {
 7929                            fallback_cursor_target
 7930                        };
 7931
 7932                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 7933                            s.select_anchor_ranges([cursor_target..cursor_target]);
 7934                        });
 7935
 7936                        let selections = self.selections.disjoint_anchors_arc();
 7937                        if let Some(transaction_id_now) =
 7938                            self.buffer.read(cx).last_transaction_id(cx)
 7939                        {
 7940                            if transaction_id_prev != Some(transaction_id_now) {
 7941                                self.selection_history
 7942                                    .insert_transaction(transaction_id_now, selections);
 7943                            }
 7944                        }
 7945
 7946                        self.update_visible_edit_prediction(window, cx);
 7947                        if self.active_edit_prediction.is_none() {
 7948                            self.refresh_edit_prediction(true, true, window, cx);
 7949                        }
 7950                        cx.notify();
 7951                    }
 7952                    _ => {
 7953                        let snapshot = self.buffer.read(cx).snapshot(cx);
 7954                        let cursor_offset = self
 7955                            .selections
 7956                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 7957                            .head();
 7958
 7959                        let insertion = edits.iter().find_map(|(range, text)| {
 7960                            let range = range.to_offset(&snapshot);
 7961                            if range.is_empty() && range.start == cursor_offset {
 7962                                Some(text)
 7963                            } else {
 7964                                None
 7965                            }
 7966                        });
 7967
 7968                        if let Some(text) = insertion {
 7969                            let text_to_insert = match granularity {
 7970                                EditPredictionGranularity::Word => {
 7971                                    let mut partial = text
 7972                                        .chars()
 7973                                        .by_ref()
 7974                                        .take_while(|c| c.is_alphabetic())
 7975                                        .collect::<String>();
 7976                                    if partial.is_empty() {
 7977                                        partial = text
 7978                                            .chars()
 7979                                            .by_ref()
 7980                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 7981                                            .collect::<String>();
 7982                                    }
 7983                                    partial
 7984                                }
 7985                                EditPredictionGranularity::Line => {
 7986                                    if let Some(line) = text.split_inclusive('\n').next() {
 7987                                        line.to_string()
 7988                                    } else {
 7989                                        text.to_string()
 7990                                    }
 7991                                }
 7992                                EditPredictionGranularity::Full => unreachable!(),
 7993                            };
 7994
 7995                            cx.emit(EditorEvent::InputHandled {
 7996                                utf16_range_to_replace: None,
 7997                                text: text_to_insert.clone().into(),
 7998                            });
 7999
 8000                            self.insert_with_autoindent_mode(&text_to_insert, None, window, cx);
 8001                            self.refresh_edit_prediction(true, true, window, cx);
 8002                            cx.notify();
 8003                        } else {
 8004                            self.accept_partial_edit_prediction(
 8005                                EditPredictionGranularity::Full,
 8006                                window,
 8007                                cx,
 8008                            );
 8009                        }
 8010                    }
 8011                }
 8012            }
 8013        }
 8014
 8015        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8016    }
 8017
 8018    pub fn accept_next_word_edit_prediction(
 8019        &mut self,
 8020        _: &AcceptNextWordEditPrediction,
 8021        window: &mut Window,
 8022        cx: &mut Context<Self>,
 8023    ) {
 8024        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8025    }
 8026
 8027    pub fn accept_next_line_edit_prediction(
 8028        &mut self,
 8029        _: &AcceptNextLineEditPrediction,
 8030        window: &mut Window,
 8031        cx: &mut Context<Self>,
 8032    ) {
 8033        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8034    }
 8035
 8036    pub fn accept_edit_prediction(
 8037        &mut self,
 8038        _: &AcceptEditPrediction,
 8039        window: &mut Window,
 8040        cx: &mut Context<Self>,
 8041    ) {
 8042        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8043    }
 8044
 8045    fn discard_edit_prediction(
 8046        &mut self,
 8047        should_report_edit_prediction_event: bool,
 8048        cx: &mut Context<Self>,
 8049    ) -> bool {
 8050        if should_report_edit_prediction_event {
 8051            let completion_id = self
 8052                .active_edit_prediction
 8053                .as_ref()
 8054                .and_then(|active_completion| active_completion.completion_id.clone());
 8055
 8056            self.report_edit_prediction_event(completion_id, false, cx);
 8057        }
 8058
 8059        if let Some(provider) = self.edit_prediction_provider() {
 8060            provider.discard(cx);
 8061        }
 8062
 8063        self.take_active_edit_prediction(cx)
 8064    }
 8065
 8066    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8067        let Some(provider) = self.edit_prediction_provider() else {
 8068            return;
 8069        };
 8070
 8071        let Some((_, buffer, _)) = self
 8072            .buffer
 8073            .read(cx)
 8074            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8075        else {
 8076            return;
 8077        };
 8078
 8079        let extension = buffer
 8080            .read(cx)
 8081            .file()
 8082            .and_then(|file| Some(file.path().extension()?.to_string()));
 8083
 8084        let event_type = match accepted {
 8085            true => "Edit Prediction Accepted",
 8086            false => "Edit Prediction Discarded",
 8087        };
 8088        telemetry::event!(
 8089            event_type,
 8090            provider = provider.name(),
 8091            prediction_id = id,
 8092            suggestion_accepted = accepted,
 8093            file_extension = extension,
 8094        );
 8095    }
 8096
 8097    fn open_editor_at_anchor(
 8098        snapshot: &language::BufferSnapshot,
 8099        target: language::Anchor,
 8100        workspace: &Entity<Workspace>,
 8101        window: &mut Window,
 8102        cx: &mut App,
 8103    ) -> Task<Result<()>> {
 8104        workspace.update(cx, |workspace, cx| {
 8105            let path = snapshot.file().map(|file| file.full_path(cx));
 8106            let Some(path) =
 8107                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8108            else {
 8109                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8110            };
 8111            let target = text::ToPoint::to_point(&target, snapshot);
 8112            let item = workspace.open_path(path, None, true, window, cx);
 8113            window.spawn(cx, async move |cx| {
 8114                let Some(editor) = item.await?.downcast::<Editor>() else {
 8115                    return Ok(());
 8116                };
 8117                editor
 8118                    .update_in(cx, |editor, window, cx| {
 8119                        editor.go_to_singleton_buffer_point(target, window, cx);
 8120                    })
 8121                    .ok();
 8122                anyhow::Ok(())
 8123            })
 8124        })
 8125    }
 8126
 8127    pub fn has_active_edit_prediction(&self) -> bool {
 8128        self.active_edit_prediction.is_some()
 8129    }
 8130
 8131    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8132        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8133            return false;
 8134        };
 8135
 8136        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8137        self.clear_highlights::<EditPredictionHighlight>(cx);
 8138        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8139        true
 8140    }
 8141
 8142    /// Returns true when we're displaying the edit prediction popover below the cursor
 8143    /// like we are not previewing and the LSP autocomplete menu is visible
 8144    /// or we are in `when_holding_modifier` mode.
 8145    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8146        if self.edit_prediction_preview_is_active()
 8147            || !self.show_edit_predictions_in_menu()
 8148            || !self.edit_predictions_enabled()
 8149        {
 8150            return false;
 8151        }
 8152
 8153        if self.has_visible_completions_menu() {
 8154            return true;
 8155        }
 8156
 8157        has_completion && self.edit_prediction_requires_modifier()
 8158    }
 8159
 8160    fn handle_modifiers_changed(
 8161        &mut self,
 8162        modifiers: Modifiers,
 8163        position_map: &PositionMap,
 8164        window: &mut Window,
 8165        cx: &mut Context<Self>,
 8166    ) {
 8167        // Ensure that the edit prediction preview is updated, even when not
 8168        // enabled, if there's an active edit prediction preview.
 8169        if self.show_edit_predictions_in_menu()
 8170            || matches!(
 8171                self.edit_prediction_preview,
 8172                EditPredictionPreview::Active { .. }
 8173            )
 8174        {
 8175            self.update_edit_prediction_preview(&modifiers, window, cx);
 8176        }
 8177
 8178        self.update_selection_mode(&modifiers, position_map, window, cx);
 8179
 8180        let mouse_position = window.mouse_position();
 8181        if !position_map.text_hitbox.is_hovered(window) {
 8182            return;
 8183        }
 8184
 8185        self.update_hovered_link(
 8186            position_map.point_for_position(mouse_position),
 8187            &position_map.snapshot,
 8188            modifiers,
 8189            window,
 8190            cx,
 8191        )
 8192    }
 8193
 8194    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8195        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8196            MultiCursorModifier::Alt => modifiers.secondary(),
 8197            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8198        }
 8199    }
 8200
 8201    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8202        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8203            MultiCursorModifier::Alt => modifiers.alt,
 8204            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8205        }
 8206    }
 8207
 8208    fn columnar_selection_mode(
 8209        modifiers: &Modifiers,
 8210        cx: &mut Context<Self>,
 8211    ) -> Option<ColumnarMode> {
 8212        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8213            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8214                Some(ColumnarMode::FromMouse)
 8215            } else if Self::is_alt_pressed(modifiers, cx) {
 8216                Some(ColumnarMode::FromSelection)
 8217            } else {
 8218                None
 8219            }
 8220        } else {
 8221            None
 8222        }
 8223    }
 8224
 8225    fn update_selection_mode(
 8226        &mut self,
 8227        modifiers: &Modifiers,
 8228        position_map: &PositionMap,
 8229        window: &mut Window,
 8230        cx: &mut Context<Self>,
 8231    ) {
 8232        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8233            return;
 8234        };
 8235        if self.selections.pending_anchor().is_none() {
 8236            return;
 8237        }
 8238
 8239        let mouse_position = window.mouse_position();
 8240        let point_for_position = position_map.point_for_position(mouse_position);
 8241        let position = point_for_position.previous_valid;
 8242
 8243        self.select(
 8244            SelectPhase::BeginColumnar {
 8245                position,
 8246                reset: false,
 8247                mode,
 8248                goal_column: point_for_position.exact_unclipped.column(),
 8249            },
 8250            window,
 8251            cx,
 8252        );
 8253    }
 8254
 8255    fn update_edit_prediction_preview(
 8256        &mut self,
 8257        modifiers: &Modifiers,
 8258        window: &mut Window,
 8259        cx: &mut Context<Self>,
 8260    ) {
 8261        let mut modifiers_held = false;
 8262
 8263        // Check bindings for all granularities.
 8264        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8265        let granularities = [
 8266            EditPredictionGranularity::Full,
 8267            EditPredictionGranularity::Line,
 8268            EditPredictionGranularity::Word,
 8269        ];
 8270
 8271        for granularity in granularities {
 8272            if let Some(keystroke) = self
 8273                .accept_edit_prediction_keybind(granularity, window, cx)
 8274                .keystroke()
 8275            {
 8276                modifiers_held = modifiers_held
 8277                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8278            }
 8279        }
 8280
 8281        if modifiers_held {
 8282            if matches!(
 8283                self.edit_prediction_preview,
 8284                EditPredictionPreview::Inactive { .. }
 8285            ) {
 8286                self.edit_prediction_preview = EditPredictionPreview::Active {
 8287                    previous_scroll_position: None,
 8288                    since: Instant::now(),
 8289                };
 8290
 8291                self.update_visible_edit_prediction(window, cx);
 8292                cx.notify();
 8293            }
 8294        } else if let EditPredictionPreview::Active {
 8295            previous_scroll_position,
 8296            since,
 8297        } = self.edit_prediction_preview
 8298        {
 8299            if let (Some(previous_scroll_position), Some(position_map)) =
 8300                (previous_scroll_position, self.last_position_map.as_ref())
 8301            {
 8302                self.set_scroll_position(
 8303                    previous_scroll_position
 8304                        .scroll_position(&position_map.snapshot.display_snapshot),
 8305                    window,
 8306                    cx,
 8307                );
 8308            }
 8309
 8310            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8311                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8312            };
 8313            self.clear_row_highlights::<EditPredictionPreview>();
 8314            self.update_visible_edit_prediction(window, cx);
 8315            cx.notify();
 8316        }
 8317    }
 8318
 8319    fn update_visible_edit_prediction(
 8320        &mut self,
 8321        _window: &mut Window,
 8322        cx: &mut Context<Self>,
 8323    ) -> Option<()> {
 8324        if DisableAiSettings::get_global(cx).disable_ai {
 8325            return None;
 8326        }
 8327
 8328        if self.ime_transaction.is_some() {
 8329            self.discard_edit_prediction(false, cx);
 8330            return None;
 8331        }
 8332
 8333        let selection = self.selections.newest_anchor();
 8334        let cursor = selection.head();
 8335        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8336        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8337        let excerpt_id = cursor.excerpt_id;
 8338
 8339        let show_in_menu = self.show_edit_predictions_in_menu();
 8340        let completions_menu_has_precedence = !show_in_menu
 8341            && (self.context_menu.borrow().is_some()
 8342                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8343
 8344        if completions_menu_has_precedence
 8345            || !offset_selection.is_empty()
 8346            || self
 8347                .active_edit_prediction
 8348                .as_ref()
 8349                .is_some_and(|completion| {
 8350                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8351                        return false;
 8352                    };
 8353                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8354                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8355                    !invalidation_range.contains(&offset_selection.head())
 8356                })
 8357        {
 8358            self.discard_edit_prediction(false, cx);
 8359            return None;
 8360        }
 8361
 8362        self.take_active_edit_prediction(cx);
 8363        let Some(provider) = self.edit_prediction_provider() else {
 8364            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8365            return None;
 8366        };
 8367
 8368        let (buffer, cursor_buffer_position) =
 8369            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8370
 8371        self.edit_prediction_settings =
 8372            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8373
 8374        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8375
 8376        if self.edit_prediction_indent_conflict {
 8377            let cursor_point = cursor.to_point(&multibuffer);
 8378            let mut suggested_indent = None;
 8379            multibuffer.suggested_indents_callback(
 8380                cursor_point.row..cursor_point.row + 1,
 8381                |_, indent| {
 8382                    suggested_indent = Some(indent);
 8383                    ControlFlow::Break(())
 8384                },
 8385                cx,
 8386            );
 8387
 8388            if let Some(indent) = suggested_indent
 8389                && indent.len == cursor_point.column
 8390            {
 8391                self.edit_prediction_indent_conflict = false;
 8392            }
 8393        }
 8394
 8395        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8396
 8397        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8398        {
 8399            edit_prediction_types::EditPrediction::Local {
 8400                id,
 8401                edits,
 8402                cursor_position,
 8403                edit_preview,
 8404            } => (id, edits, cursor_position, edit_preview),
 8405            edit_prediction_types::EditPrediction::Jump {
 8406                id,
 8407                snapshot,
 8408                target,
 8409            } => {
 8410                if let Some(provider) = &self.edit_prediction_provider {
 8411                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8412                }
 8413                self.stale_edit_prediction_in_menu = None;
 8414                self.active_edit_prediction = Some(EditPredictionState {
 8415                    inlay_ids: vec![],
 8416                    completion: EditPrediction::MoveOutside { snapshot, target },
 8417                    completion_id: id,
 8418                    invalidation_range: None,
 8419                });
 8420                cx.notify();
 8421                return Some(());
 8422            }
 8423        };
 8424
 8425        let edits = edits
 8426            .into_iter()
 8427            .flat_map(|(range, new_text)| {
 8428                Some((
 8429                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8430                    new_text,
 8431                ))
 8432            })
 8433            .collect::<Vec<_>>();
 8434        if edits.is_empty() {
 8435            return None;
 8436        }
 8437
 8438        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8439            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8440            Some((anchor, predicted.offset))
 8441        });
 8442
 8443        let first_edit_start = edits.first().unwrap().0.start;
 8444        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8445        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8446
 8447        let last_edit_end = edits.last().unwrap().0.end;
 8448        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8449        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8450
 8451        let cursor_row = cursor.to_point(&multibuffer).row;
 8452
 8453        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8454
 8455        let mut inlay_ids = Vec::new();
 8456        let invalidation_row_range;
 8457        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8458            Some(cursor_row..edit_end_row)
 8459        } else if cursor_row > edit_end_row {
 8460            Some(edit_start_row..cursor_row)
 8461        } else {
 8462            None
 8463        };
 8464        let supports_jump = self
 8465            .edit_prediction_provider
 8466            .as_ref()
 8467            .map(|provider| provider.provider.supports_jump_to_edit())
 8468            .unwrap_or(true);
 8469
 8470        let is_move = supports_jump
 8471            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8472        let completion = if is_move {
 8473            if let Some(provider) = &self.edit_prediction_provider {
 8474                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8475            }
 8476            invalidation_row_range =
 8477                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8478            let target = first_edit_start;
 8479            EditPrediction::MoveWithin { target, snapshot }
 8480        } else {
 8481            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8482                && !self.edit_predictions_hidden_for_vim_mode;
 8483
 8484            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8485                if provider.show_tab_accept_marker() {
 8486                    EditDisplayMode::TabAccept
 8487                } else {
 8488                    EditDisplayMode::Inline
 8489                }
 8490            } else {
 8491                EditDisplayMode::DiffPopover
 8492            };
 8493
 8494            if show_completions_in_buffer {
 8495                if let Some(provider) = &self.edit_prediction_provider {
 8496                    let suggestion_display_type = match display_mode {
 8497                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8498                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8499                            SuggestionDisplayType::GhostText
 8500                        }
 8501                    };
 8502                    provider.provider.did_show(suggestion_display_type, cx);
 8503                }
 8504                if edits
 8505                    .iter()
 8506                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8507                {
 8508                    let mut inlays = Vec::new();
 8509                    for (range, new_text) in &edits {
 8510                        let inlay = Inlay::edit_prediction(
 8511                            post_inc(&mut self.next_inlay_id),
 8512                            range.start,
 8513                            new_text.as_ref(),
 8514                        );
 8515                        inlay_ids.push(inlay.id);
 8516                        inlays.push(inlay);
 8517                    }
 8518
 8519                    self.splice_inlays(&[], inlays, cx);
 8520                } else {
 8521                    let background_color = cx.theme().status().deleted_background;
 8522                    self.highlight_text::<EditPredictionHighlight>(
 8523                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8524                        HighlightStyle {
 8525                            background_color: Some(background_color),
 8526                            ..Default::default()
 8527                        },
 8528                        cx,
 8529                    );
 8530                }
 8531            }
 8532
 8533            invalidation_row_range = edit_start_row..edit_end_row;
 8534
 8535            EditPrediction::Edit {
 8536                edits,
 8537                cursor_position,
 8538                edit_preview,
 8539                display_mode,
 8540                snapshot,
 8541            }
 8542        };
 8543
 8544        let invalidation_range = multibuffer
 8545            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8546            ..multibuffer.anchor_after(Point::new(
 8547                invalidation_row_range.end,
 8548                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8549            ));
 8550
 8551        self.stale_edit_prediction_in_menu = None;
 8552        self.active_edit_prediction = Some(EditPredictionState {
 8553            inlay_ids,
 8554            completion,
 8555            completion_id,
 8556            invalidation_range: Some(invalidation_range),
 8557        });
 8558
 8559        cx.notify();
 8560
 8561        Some(())
 8562    }
 8563
 8564    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8565        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8566    }
 8567
 8568    fn clear_tasks(&mut self) {
 8569        self.tasks.clear()
 8570    }
 8571
 8572    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8573        if self.tasks.insert(key, value).is_some() {
 8574            // This case should hopefully be rare, but just in case...
 8575            log::error!(
 8576                "multiple different run targets found on a single line, only the last target will be rendered"
 8577            )
 8578        }
 8579    }
 8580
 8581    /// Get all display points of breakpoints that will be rendered within editor
 8582    ///
 8583    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8584    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8585    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8586    fn active_breakpoints(
 8587        &self,
 8588        range: Range<DisplayRow>,
 8589        window: &mut Window,
 8590        cx: &mut Context<Self>,
 8591    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8592        let mut breakpoint_display_points = HashMap::default();
 8593
 8594        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8595            return breakpoint_display_points;
 8596        };
 8597
 8598        let snapshot = self.snapshot(window, cx);
 8599
 8600        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8601        let Some(project) = self.project() else {
 8602            return breakpoint_display_points;
 8603        };
 8604
 8605        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8606            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8607
 8608        for (buffer_snapshot, range, excerpt_id) in
 8609            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8610        {
 8611            let Some(buffer) = project
 8612                .read(cx)
 8613                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8614            else {
 8615                continue;
 8616            };
 8617            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8618                &buffer,
 8619                Some(
 8620                    buffer_snapshot.anchor_before(range.start)
 8621                        ..buffer_snapshot.anchor_after(range.end),
 8622                ),
 8623                buffer_snapshot,
 8624                cx,
 8625            );
 8626            for (breakpoint, state) in breakpoints {
 8627                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8628                let position = multi_buffer_anchor
 8629                    .to_point(&multi_buffer_snapshot)
 8630                    .to_display_point(&snapshot);
 8631
 8632                breakpoint_display_points.insert(
 8633                    position.row(),
 8634                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8635                );
 8636            }
 8637        }
 8638
 8639        breakpoint_display_points
 8640    }
 8641
 8642    fn breakpoint_context_menu(
 8643        &self,
 8644        anchor: Anchor,
 8645        window: &mut Window,
 8646        cx: &mut Context<Self>,
 8647    ) -> Entity<ui::ContextMenu> {
 8648        let weak_editor = cx.weak_entity();
 8649        let focus_handle = self.focus_handle(cx);
 8650
 8651        let row = self
 8652            .buffer
 8653            .read(cx)
 8654            .snapshot(cx)
 8655            .summary_for_anchor::<Point>(&anchor)
 8656            .row;
 8657
 8658        let breakpoint = self
 8659            .breakpoint_at_row(row, window, cx)
 8660            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8661
 8662        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8663            "Edit Log Breakpoint"
 8664        } else {
 8665            "Set Log Breakpoint"
 8666        };
 8667
 8668        let condition_breakpoint_msg = if breakpoint
 8669            .as_ref()
 8670            .is_some_and(|bp| bp.1.condition.is_some())
 8671        {
 8672            "Edit Condition Breakpoint"
 8673        } else {
 8674            "Set Condition Breakpoint"
 8675        };
 8676
 8677        let hit_condition_breakpoint_msg = if breakpoint
 8678            .as_ref()
 8679            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8680        {
 8681            "Edit Hit Condition Breakpoint"
 8682        } else {
 8683            "Set Hit Condition Breakpoint"
 8684        };
 8685
 8686        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8687            "Unset Breakpoint"
 8688        } else {
 8689            "Set Breakpoint"
 8690        };
 8691
 8692        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8693
 8694        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8695            BreakpointState::Enabled => Some("Disable"),
 8696            BreakpointState::Disabled => Some("Enable"),
 8697        });
 8698
 8699        let (anchor, breakpoint) =
 8700            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8701
 8702        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8703            menu.on_blur_subscription(Subscription::new(|| {}))
 8704                .context(focus_handle)
 8705                .when(run_to_cursor, |this| {
 8706                    let weak_editor = weak_editor.clone();
 8707                    this.entry("Run to cursor", None, move |window, cx| {
 8708                        weak_editor
 8709                            .update(cx, |editor, cx| {
 8710                                editor.change_selections(
 8711                                    SelectionEffects::no_scroll(),
 8712                                    window,
 8713                                    cx,
 8714                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8715                                );
 8716                            })
 8717                            .ok();
 8718
 8719                        window.dispatch_action(Box::new(RunToCursor), cx);
 8720                    })
 8721                    .separator()
 8722                })
 8723                .when_some(toggle_state_msg, |this, msg| {
 8724                    this.entry(msg, None, {
 8725                        let weak_editor = weak_editor.clone();
 8726                        let breakpoint = breakpoint.clone();
 8727                        move |_window, cx| {
 8728                            weak_editor
 8729                                .update(cx, |this, cx| {
 8730                                    this.edit_breakpoint_at_anchor(
 8731                                        anchor,
 8732                                        breakpoint.as_ref().clone(),
 8733                                        BreakpointEditAction::InvertState,
 8734                                        cx,
 8735                                    );
 8736                                })
 8737                                .log_err();
 8738                        }
 8739                    })
 8740                })
 8741                .entry(set_breakpoint_msg, None, {
 8742                    let weak_editor = weak_editor.clone();
 8743                    let breakpoint = breakpoint.clone();
 8744                    move |_window, cx| {
 8745                        weak_editor
 8746                            .update(cx, |this, cx| {
 8747                                this.edit_breakpoint_at_anchor(
 8748                                    anchor,
 8749                                    breakpoint.as_ref().clone(),
 8750                                    BreakpointEditAction::Toggle,
 8751                                    cx,
 8752                                );
 8753                            })
 8754                            .log_err();
 8755                    }
 8756                })
 8757                .entry(log_breakpoint_msg, None, {
 8758                    let breakpoint = breakpoint.clone();
 8759                    let weak_editor = weak_editor.clone();
 8760                    move |window, cx| {
 8761                        weak_editor
 8762                            .update(cx, |this, cx| {
 8763                                this.add_edit_breakpoint_block(
 8764                                    anchor,
 8765                                    breakpoint.as_ref(),
 8766                                    BreakpointPromptEditAction::Log,
 8767                                    window,
 8768                                    cx,
 8769                                );
 8770                            })
 8771                            .log_err();
 8772                    }
 8773                })
 8774                .entry(condition_breakpoint_msg, None, {
 8775                    let breakpoint = breakpoint.clone();
 8776                    let weak_editor = weak_editor.clone();
 8777                    move |window, cx| {
 8778                        weak_editor
 8779                            .update(cx, |this, cx| {
 8780                                this.add_edit_breakpoint_block(
 8781                                    anchor,
 8782                                    breakpoint.as_ref(),
 8783                                    BreakpointPromptEditAction::Condition,
 8784                                    window,
 8785                                    cx,
 8786                                );
 8787                            })
 8788                            .log_err();
 8789                    }
 8790                })
 8791                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8792                    weak_editor
 8793                        .update(cx, |this, cx| {
 8794                            this.add_edit_breakpoint_block(
 8795                                anchor,
 8796                                breakpoint.as_ref(),
 8797                                BreakpointPromptEditAction::HitCondition,
 8798                                window,
 8799                                cx,
 8800                            );
 8801                        })
 8802                        .log_err();
 8803                })
 8804        })
 8805    }
 8806
 8807    fn render_breakpoint(
 8808        &self,
 8809        position: Anchor,
 8810        row: DisplayRow,
 8811        breakpoint: &Breakpoint,
 8812        state: Option<BreakpointSessionState>,
 8813        cx: &mut Context<Self>,
 8814    ) -> IconButton {
 8815        let is_rejected = state.is_some_and(|s| !s.verified);
 8816        // Is it a breakpoint that shows up when hovering over gutter?
 8817        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 8818            (false, false),
 8819            |PhantomBreakpointIndicator {
 8820                 is_active,
 8821                 display_row,
 8822                 collides_with_existing_breakpoint,
 8823             }| {
 8824                (
 8825                    is_active && display_row == row,
 8826                    collides_with_existing_breakpoint,
 8827                )
 8828            },
 8829        );
 8830
 8831        let (color, icon) = {
 8832            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 8833                (false, false) => ui::IconName::DebugBreakpoint,
 8834                (true, false) => ui::IconName::DebugLogBreakpoint,
 8835                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 8836                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 8837            };
 8838
 8839            let theme_colors = cx.theme().colors();
 8840
 8841            let color = if is_phantom {
 8842                if collides_with_existing {
 8843                    Color::Custom(
 8844                        theme_colors
 8845                            .debugger_accent
 8846                            .blend(theme_colors.text.opacity(0.6)),
 8847                    )
 8848                } else {
 8849                    Color::Hint
 8850                }
 8851            } else if is_rejected {
 8852                Color::Disabled
 8853            } else {
 8854                Color::Debugger
 8855            };
 8856
 8857            (color, icon)
 8858        };
 8859
 8860        let breakpoint = Arc::from(breakpoint.clone());
 8861
 8862        let alt_as_text = gpui::Keystroke {
 8863            modifiers: Modifiers::secondary_key(),
 8864            ..Default::default()
 8865        };
 8866        let primary_action_text = if breakpoint.is_disabled() {
 8867            "Enable breakpoint"
 8868        } else if is_phantom && !collides_with_existing {
 8869            "Set breakpoint"
 8870        } else {
 8871            "Unset breakpoint"
 8872        };
 8873        let focus_handle = self.focus_handle.clone();
 8874
 8875        let meta = if is_rejected {
 8876            SharedString::from("No executable code is associated with this line.")
 8877        } else if collides_with_existing && !breakpoint.is_disabled() {
 8878            SharedString::from(format!(
 8879                "{alt_as_text}-click to disable,\nright-click for more options."
 8880            ))
 8881        } else {
 8882            SharedString::from("Right-click for more options.")
 8883        };
 8884        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 8885            .icon_size(IconSize::XSmall)
 8886            .size(ui::ButtonSize::None)
 8887            .when(is_rejected, |this| {
 8888                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 8889            })
 8890            .icon_color(color)
 8891            .style(ButtonStyle::Transparent)
 8892            .on_click(cx.listener({
 8893                move |editor, event: &ClickEvent, window, cx| {
 8894                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 8895                        BreakpointEditAction::InvertState
 8896                    } else {
 8897                        BreakpointEditAction::Toggle
 8898                    };
 8899
 8900                    window.focus(&editor.focus_handle(cx), cx);
 8901                    editor.edit_breakpoint_at_anchor(
 8902                        position,
 8903                        breakpoint.as_ref().clone(),
 8904                        edit_action,
 8905                        cx,
 8906                    );
 8907                }
 8908            }))
 8909            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 8910                editor.set_breakpoint_context_menu(
 8911                    row,
 8912                    Some(position),
 8913                    event.position(),
 8914                    window,
 8915                    cx,
 8916                );
 8917            }))
 8918            .tooltip(move |_window, cx| {
 8919                Tooltip::with_meta_in(
 8920                    primary_action_text,
 8921                    Some(&ToggleBreakpoint),
 8922                    meta.clone(),
 8923                    &focus_handle,
 8924                    cx,
 8925                )
 8926            })
 8927    }
 8928
 8929    fn build_tasks_context(
 8930        project: &Entity<Project>,
 8931        buffer: &Entity<Buffer>,
 8932        buffer_row: u32,
 8933        tasks: &Arc<RunnableTasks>,
 8934        cx: &mut Context<Self>,
 8935    ) -> Task<Option<task::TaskContext>> {
 8936        let position = Point::new(buffer_row, tasks.column);
 8937        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 8938        let location = Location {
 8939            buffer: buffer.clone(),
 8940            range: range_start..range_start,
 8941        };
 8942        // Fill in the environmental variables from the tree-sitter captures
 8943        let mut captured_task_variables = TaskVariables::default();
 8944        for (capture_name, value) in tasks.extra_variables.clone() {
 8945            captured_task_variables.insert(
 8946                task::VariableName::Custom(capture_name.into()),
 8947                value.clone(),
 8948            );
 8949        }
 8950        project.update(cx, |project, cx| {
 8951            project.task_store().update(cx, |task_store, cx| {
 8952                task_store.task_context_for_location(captured_task_variables, location, cx)
 8953            })
 8954        })
 8955    }
 8956
 8957    pub fn spawn_nearest_task(
 8958        &mut self,
 8959        action: &SpawnNearestTask,
 8960        window: &mut Window,
 8961        cx: &mut Context<Self>,
 8962    ) {
 8963        let Some((workspace, _)) = self.workspace.clone() else {
 8964            return;
 8965        };
 8966        let Some(project) = self.project.clone() else {
 8967            return;
 8968        };
 8969
 8970        // Try to find a closest, enclosing node using tree-sitter that has a task
 8971        let Some((buffer, buffer_row, tasks)) = self
 8972            .find_enclosing_node_task(cx)
 8973            // Or find the task that's closest in row-distance.
 8974            .or_else(|| self.find_closest_task(cx))
 8975        else {
 8976            return;
 8977        };
 8978
 8979        let reveal_strategy = action.reveal;
 8980        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 8981        cx.spawn_in(window, async move |_, cx| {
 8982            let context = task_context.await?;
 8983            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 8984
 8985            let resolved = &mut resolved_task.resolved;
 8986            resolved.reveal = reveal_strategy;
 8987
 8988            workspace
 8989                .update_in(cx, |workspace, window, cx| {
 8990                    workspace.schedule_resolved_task(
 8991                        task_source_kind,
 8992                        resolved_task,
 8993                        false,
 8994                        window,
 8995                        cx,
 8996                    );
 8997                })
 8998                .ok()
 8999        })
 9000        .detach();
 9001    }
 9002
 9003    fn find_closest_task(
 9004        &mut self,
 9005        cx: &mut Context<Self>,
 9006    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9007        let cursor_row = self
 9008            .selections
 9009            .newest_adjusted(&self.display_snapshot(cx))
 9010            .head()
 9011            .row;
 9012
 9013        let ((buffer_id, row), tasks) = self
 9014            .tasks
 9015            .iter()
 9016            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 9017
 9018        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 9019        let tasks = Arc::new(tasks.to_owned());
 9020        Some((buffer, *row, tasks))
 9021    }
 9022
 9023    fn find_enclosing_node_task(
 9024        &mut self,
 9025        cx: &mut Context<Self>,
 9026    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9027        let snapshot = self.buffer.read(cx).snapshot(cx);
 9028        let offset = self
 9029            .selections
 9030            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 9031            .head();
 9032        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 9033        let offset = excerpt.map_offset_to_buffer(offset);
 9034        let buffer_id = excerpt.buffer().remote_id();
 9035
 9036        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 9037        let mut cursor = layer.node().walk();
 9038
 9039        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 9040            if cursor.node().end_byte() == offset.0 {
 9041                cursor.goto_next_sibling();
 9042            }
 9043        }
 9044
 9045        // Ascend to the smallest ancestor that contains the range and has a task.
 9046        loop {
 9047            let node = cursor.node();
 9048            let node_range = node.byte_range();
 9049            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 9050
 9051            // Check if this node contains our offset
 9052            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 9053                // If it contains offset, check for task
 9054                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 9055                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 9056                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 9057                }
 9058            }
 9059
 9060            if !cursor.goto_parent() {
 9061                break;
 9062            }
 9063        }
 9064        None
 9065    }
 9066
 9067    fn render_run_indicator(
 9068        &self,
 9069        _style: &EditorStyle,
 9070        is_active: bool,
 9071        row: DisplayRow,
 9072        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 9073        cx: &mut Context<Self>,
 9074    ) -> IconButton {
 9075        let color = Color::Muted;
 9076        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 9077
 9078        IconButton::new(
 9079            ("run_indicator", row.0 as usize),
 9080            ui::IconName::PlayOutlined,
 9081        )
 9082        .shape(ui::IconButtonShape::Square)
 9083        .icon_size(IconSize::XSmall)
 9084        .icon_color(color)
 9085        .toggle_state(is_active)
 9086        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9087            let quick_launch = match e {
 9088                ClickEvent::Keyboard(_) => true,
 9089                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9090            };
 9091
 9092            window.focus(&editor.focus_handle(cx), cx);
 9093            editor.toggle_code_actions(
 9094                &ToggleCodeActions {
 9095                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9096                    quick_launch,
 9097                },
 9098                window,
 9099                cx,
 9100            );
 9101        }))
 9102        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9103            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9104        }))
 9105    }
 9106
 9107    pub fn context_menu_visible(&self) -> bool {
 9108        !self.edit_prediction_preview_is_active()
 9109            && self
 9110                .context_menu
 9111                .borrow()
 9112                .as_ref()
 9113                .is_some_and(|menu| menu.visible())
 9114    }
 9115
 9116    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9117        self.context_menu
 9118            .borrow()
 9119            .as_ref()
 9120            .map(|menu| menu.origin())
 9121    }
 9122
 9123    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9124        self.context_menu_options = Some(options);
 9125    }
 9126
 9127    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9128    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9129
 9130    fn render_edit_prediction_popover(
 9131        &mut self,
 9132        text_bounds: &Bounds<Pixels>,
 9133        content_origin: gpui::Point<Pixels>,
 9134        right_margin: Pixels,
 9135        editor_snapshot: &EditorSnapshot,
 9136        visible_row_range: Range<DisplayRow>,
 9137        scroll_top: ScrollOffset,
 9138        scroll_bottom: ScrollOffset,
 9139        line_layouts: &[LineWithInvisibles],
 9140        line_height: Pixels,
 9141        scroll_position: gpui::Point<ScrollOffset>,
 9142        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9143        newest_selection_head: Option<DisplayPoint>,
 9144        editor_width: Pixels,
 9145        style: &EditorStyle,
 9146        window: &mut Window,
 9147        cx: &mut App,
 9148    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9149        if self.mode().is_minimap() {
 9150            return None;
 9151        }
 9152        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9153
 9154        if self.edit_prediction_visible_in_cursor_popover(true) {
 9155            return None;
 9156        }
 9157
 9158        match &active_edit_prediction.completion {
 9159            EditPrediction::MoveWithin { target, .. } => {
 9160                let target_display_point = target.to_display_point(editor_snapshot);
 9161
 9162                if self.edit_prediction_requires_modifier() {
 9163                    if !self.edit_prediction_preview_is_active() {
 9164                        return None;
 9165                    }
 9166
 9167                    self.render_edit_prediction_modifier_jump_popover(
 9168                        text_bounds,
 9169                        content_origin,
 9170                        visible_row_range,
 9171                        line_layouts,
 9172                        line_height,
 9173                        scroll_pixel_position,
 9174                        newest_selection_head,
 9175                        target_display_point,
 9176                        window,
 9177                        cx,
 9178                    )
 9179                } else {
 9180                    self.render_edit_prediction_eager_jump_popover(
 9181                        text_bounds,
 9182                        content_origin,
 9183                        editor_snapshot,
 9184                        visible_row_range,
 9185                        scroll_top,
 9186                        scroll_bottom,
 9187                        line_height,
 9188                        scroll_pixel_position,
 9189                        target_display_point,
 9190                        editor_width,
 9191                        window,
 9192                        cx,
 9193                    )
 9194                }
 9195            }
 9196            EditPrediction::Edit {
 9197                display_mode: EditDisplayMode::Inline,
 9198                ..
 9199            } => None,
 9200            EditPrediction::Edit {
 9201                display_mode: EditDisplayMode::TabAccept,
 9202                edits,
 9203                ..
 9204            } => {
 9205                let range = &edits.first()?.0;
 9206                let target_display_point = range.end.to_display_point(editor_snapshot);
 9207
 9208                self.render_edit_prediction_end_of_line_popover(
 9209                    "Accept",
 9210                    editor_snapshot,
 9211                    visible_row_range,
 9212                    target_display_point,
 9213                    line_height,
 9214                    scroll_pixel_position,
 9215                    content_origin,
 9216                    editor_width,
 9217                    window,
 9218                    cx,
 9219                )
 9220            }
 9221            EditPrediction::Edit {
 9222                edits,
 9223                edit_preview,
 9224                display_mode: EditDisplayMode::DiffPopover,
 9225                snapshot,
 9226                ..
 9227            } => self.render_edit_prediction_diff_popover(
 9228                text_bounds,
 9229                content_origin,
 9230                right_margin,
 9231                editor_snapshot,
 9232                visible_row_range,
 9233                line_layouts,
 9234                line_height,
 9235                scroll_position,
 9236                scroll_pixel_position,
 9237                newest_selection_head,
 9238                editor_width,
 9239                style,
 9240                edits,
 9241                edit_preview,
 9242                snapshot,
 9243                window,
 9244                cx,
 9245            ),
 9246            EditPrediction::MoveOutside { snapshot, .. } => {
 9247                let mut element = self
 9248                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9249                    .into_any();
 9250
 9251                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9252                let origin_x = text_bounds.size.width - size.width - px(30.);
 9253                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9254                element.prepaint_at(origin, window, cx);
 9255
 9256                Some((element, origin))
 9257            }
 9258        }
 9259    }
 9260
 9261    fn render_edit_prediction_modifier_jump_popover(
 9262        &mut self,
 9263        text_bounds: &Bounds<Pixels>,
 9264        content_origin: gpui::Point<Pixels>,
 9265        visible_row_range: Range<DisplayRow>,
 9266        line_layouts: &[LineWithInvisibles],
 9267        line_height: Pixels,
 9268        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9269        newest_selection_head: Option<DisplayPoint>,
 9270        target_display_point: DisplayPoint,
 9271        window: &mut Window,
 9272        cx: &mut App,
 9273    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9274        let scrolled_content_origin =
 9275            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9276
 9277        const SCROLL_PADDING_Y: Pixels = px(12.);
 9278
 9279        if target_display_point.row() < visible_row_range.start {
 9280            return self.render_edit_prediction_scroll_popover(
 9281                |_| SCROLL_PADDING_Y,
 9282                IconName::ArrowUp,
 9283                visible_row_range,
 9284                line_layouts,
 9285                newest_selection_head,
 9286                scrolled_content_origin,
 9287                window,
 9288                cx,
 9289            );
 9290        } else if target_display_point.row() >= visible_row_range.end {
 9291            return self.render_edit_prediction_scroll_popover(
 9292                |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9293                IconName::ArrowDown,
 9294                visible_row_range,
 9295                line_layouts,
 9296                newest_selection_head,
 9297                scrolled_content_origin,
 9298                window,
 9299                cx,
 9300            );
 9301        }
 9302
 9303        const POLE_WIDTH: Pixels = px(2.);
 9304
 9305        let line_layout =
 9306            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9307        let target_column = target_display_point.column() as usize;
 9308
 9309        let target_x = line_layout.x_for_index(target_column);
 9310        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9311            - scroll_pixel_position.y;
 9312
 9313        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9314
 9315        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9316        border_color.l += 0.001;
 9317
 9318        let mut element = v_flex()
 9319            .items_end()
 9320            .when(flag_on_right, |el| el.items_start())
 9321            .child(if flag_on_right {
 9322                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9323                    .rounded_bl(px(0.))
 9324                    .rounded_tl(px(0.))
 9325                    .border_l_2()
 9326                    .border_color(border_color)
 9327            } else {
 9328                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9329                    .rounded_br(px(0.))
 9330                    .rounded_tr(px(0.))
 9331                    .border_r_2()
 9332                    .border_color(border_color)
 9333            })
 9334            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9335            .into_any();
 9336
 9337        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9338
 9339        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9340            - point(
 9341                if flag_on_right {
 9342                    POLE_WIDTH
 9343                } else {
 9344                    size.width - POLE_WIDTH
 9345                },
 9346                size.height - line_height,
 9347            );
 9348
 9349        origin.x = origin.x.max(content_origin.x);
 9350
 9351        element.prepaint_at(origin, window, cx);
 9352
 9353        Some((element, origin))
 9354    }
 9355
 9356    fn render_edit_prediction_scroll_popover(
 9357        &mut self,
 9358        to_y: impl Fn(Size<Pixels>) -> Pixels,
 9359        scroll_icon: IconName,
 9360        visible_row_range: Range<DisplayRow>,
 9361        line_layouts: &[LineWithInvisibles],
 9362        newest_selection_head: Option<DisplayPoint>,
 9363        scrolled_content_origin: gpui::Point<Pixels>,
 9364        window: &mut Window,
 9365        cx: &mut App,
 9366    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9367        let mut element = self
 9368            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9369            .into_any();
 9370
 9371        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9372
 9373        let cursor = newest_selection_head?;
 9374        let cursor_row_layout =
 9375            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9376        let cursor_column = cursor.column() as usize;
 9377
 9378        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9379
 9380        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9381
 9382        element.prepaint_at(origin, window, cx);
 9383        Some((element, origin))
 9384    }
 9385
 9386    fn render_edit_prediction_eager_jump_popover(
 9387        &mut self,
 9388        text_bounds: &Bounds<Pixels>,
 9389        content_origin: gpui::Point<Pixels>,
 9390        editor_snapshot: &EditorSnapshot,
 9391        visible_row_range: Range<DisplayRow>,
 9392        scroll_top: ScrollOffset,
 9393        scroll_bottom: ScrollOffset,
 9394        line_height: Pixels,
 9395        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9396        target_display_point: DisplayPoint,
 9397        editor_width: Pixels,
 9398        window: &mut Window,
 9399        cx: &mut App,
 9400    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9401        if target_display_point.row().as_f64() < scroll_top {
 9402            let mut element = self
 9403                .render_edit_prediction_line_popover(
 9404                    "Jump to Edit",
 9405                    Some(IconName::ArrowUp),
 9406                    window,
 9407                    cx,
 9408                )
 9409                .into_any();
 9410
 9411            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9412            let offset = point(
 9413                (text_bounds.size.width - size.width) / 2.,
 9414                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9415            );
 9416
 9417            let origin = text_bounds.origin + offset;
 9418            element.prepaint_at(origin, window, cx);
 9419            Some((element, origin))
 9420        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9421            let mut element = self
 9422                .render_edit_prediction_line_popover(
 9423                    "Jump to Edit",
 9424                    Some(IconName::ArrowDown),
 9425                    window,
 9426                    cx,
 9427                )
 9428                .into_any();
 9429
 9430            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9431            let offset = point(
 9432                (text_bounds.size.width - size.width) / 2.,
 9433                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9434            );
 9435
 9436            let origin = text_bounds.origin + offset;
 9437            element.prepaint_at(origin, window, cx);
 9438            Some((element, origin))
 9439        } else {
 9440            self.render_edit_prediction_end_of_line_popover(
 9441                "Jump to Edit",
 9442                editor_snapshot,
 9443                visible_row_range,
 9444                target_display_point,
 9445                line_height,
 9446                scroll_pixel_position,
 9447                content_origin,
 9448                editor_width,
 9449                window,
 9450                cx,
 9451            )
 9452        }
 9453    }
 9454
 9455    fn render_edit_prediction_end_of_line_popover(
 9456        self: &mut Editor,
 9457        label: &'static str,
 9458        editor_snapshot: &EditorSnapshot,
 9459        visible_row_range: Range<DisplayRow>,
 9460        target_display_point: DisplayPoint,
 9461        line_height: Pixels,
 9462        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9463        content_origin: gpui::Point<Pixels>,
 9464        editor_width: Pixels,
 9465        window: &mut Window,
 9466        cx: &mut App,
 9467    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9468        let target_line_end = DisplayPoint::new(
 9469            target_display_point.row(),
 9470            editor_snapshot.line_len(target_display_point.row()),
 9471        );
 9472
 9473        let mut element = self
 9474            .render_edit_prediction_line_popover(label, None, window, cx)
 9475            .into_any();
 9476
 9477        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9478
 9479        let line_origin =
 9480            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9481
 9482        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9483        let mut origin = start_point
 9484            + line_origin
 9485            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9486        origin.x = origin.x.max(content_origin.x);
 9487
 9488        let max_x = content_origin.x + editor_width - size.width;
 9489
 9490        if origin.x > max_x {
 9491            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9492
 9493            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9494                origin.y += offset;
 9495                IconName::ArrowUp
 9496            } else {
 9497                origin.y -= offset;
 9498                IconName::ArrowDown
 9499            };
 9500
 9501            element = self
 9502                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9503                .into_any();
 9504
 9505            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9506
 9507            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9508        }
 9509
 9510        element.prepaint_at(origin, window, cx);
 9511        Some((element, origin))
 9512    }
 9513
 9514    fn render_edit_prediction_diff_popover(
 9515        self: &Editor,
 9516        text_bounds: &Bounds<Pixels>,
 9517        content_origin: gpui::Point<Pixels>,
 9518        right_margin: Pixels,
 9519        editor_snapshot: &EditorSnapshot,
 9520        visible_row_range: Range<DisplayRow>,
 9521        line_layouts: &[LineWithInvisibles],
 9522        line_height: Pixels,
 9523        scroll_position: gpui::Point<ScrollOffset>,
 9524        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9525        newest_selection_head: Option<DisplayPoint>,
 9526        editor_width: Pixels,
 9527        style: &EditorStyle,
 9528        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9529        edit_preview: &Option<language::EditPreview>,
 9530        snapshot: &language::BufferSnapshot,
 9531        window: &mut Window,
 9532        cx: &mut App,
 9533    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9534        let edit_start = edits
 9535            .first()
 9536            .unwrap()
 9537            .0
 9538            .start
 9539            .to_display_point(editor_snapshot);
 9540        let edit_end = edits
 9541            .last()
 9542            .unwrap()
 9543            .0
 9544            .end
 9545            .to_display_point(editor_snapshot);
 9546
 9547        let is_visible = visible_row_range.contains(&edit_start.row())
 9548            || visible_row_range.contains(&edit_end.row());
 9549        if !is_visible {
 9550            return None;
 9551        }
 9552
 9553        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9554            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9555        } else {
 9556            // Fallback for providers without edit_preview
 9557            crate::edit_prediction_fallback_text(edits, cx)
 9558        };
 9559
 9560        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9561        let line_count = highlighted_edits.text.lines().count();
 9562
 9563        const BORDER_WIDTH: Pixels = px(1.);
 9564
 9565        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9566        let has_keybind = keybind.is_some();
 9567
 9568        let mut element = h_flex()
 9569            .items_start()
 9570            .child(
 9571                h_flex()
 9572                    .bg(cx.theme().colors().editor_background)
 9573                    .border(BORDER_WIDTH)
 9574                    .shadow_xs()
 9575                    .border_color(cx.theme().colors().border)
 9576                    .rounded_l_lg()
 9577                    .when(line_count > 1, |el| el.rounded_br_lg())
 9578                    .pr_1()
 9579                    .child(styled_text),
 9580            )
 9581            .child(
 9582                h_flex()
 9583                    .h(line_height + BORDER_WIDTH * 2.)
 9584                    .px_1p5()
 9585                    .gap_1()
 9586                    // Workaround: For some reason, there's a gap if we don't do this
 9587                    .ml(-BORDER_WIDTH)
 9588                    .shadow(vec![gpui::BoxShadow {
 9589                        color: gpui::black().opacity(0.05),
 9590                        offset: point(px(1.), px(1.)),
 9591                        blur_radius: px(2.),
 9592                        spread_radius: px(0.),
 9593                    }])
 9594                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9595                    .border(BORDER_WIDTH)
 9596                    .border_color(cx.theme().colors().border)
 9597                    .rounded_r_lg()
 9598                    .id("edit_prediction_diff_popover_keybind")
 9599                    .when(!has_keybind, |el| {
 9600                        let status_colors = cx.theme().status();
 9601
 9602                        el.bg(status_colors.error_background)
 9603                            .border_color(status_colors.error.opacity(0.6))
 9604                            .child(Icon::new(IconName::Info).color(Color::Error))
 9605                            .cursor_default()
 9606                            .hoverable_tooltip(move |_window, cx| {
 9607                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9608                            })
 9609                    })
 9610                    .children(keybind),
 9611            )
 9612            .into_any();
 9613
 9614        let longest_row =
 9615            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9616        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9617            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9618        } else {
 9619            layout_line(
 9620                longest_row,
 9621                editor_snapshot,
 9622                style,
 9623                editor_width,
 9624                |_| false,
 9625                window,
 9626                cx,
 9627            )
 9628            .width
 9629        };
 9630
 9631        let viewport_bounds =
 9632            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9633                right: -right_margin,
 9634                ..Default::default()
 9635            });
 9636
 9637        let x_after_longest = Pixels::from(
 9638            ScrollPixelOffset::from(
 9639                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9640            ) - scroll_pixel_position.x,
 9641        );
 9642
 9643        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9644
 9645        // Fully visible if it can be displayed within the window (allow overlapping other
 9646        // panes). However, this is only allowed if the popover starts within text_bounds.
 9647        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9648            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9649
 9650        let mut origin = if can_position_to_the_right {
 9651            point(
 9652                x_after_longest,
 9653                text_bounds.origin.y
 9654                    + Pixels::from(
 9655                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9656                            - scroll_pixel_position.y,
 9657                    ),
 9658            )
 9659        } else {
 9660            let cursor_row = newest_selection_head.map(|head| head.row());
 9661            let above_edit = edit_start
 9662                .row()
 9663                .0
 9664                .checked_sub(line_count as u32)
 9665                .map(DisplayRow);
 9666            let below_edit = Some(edit_end.row() + 1);
 9667            let above_cursor =
 9668                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9669            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9670
 9671            // Place the edit popover adjacent to the edit if there is a location
 9672            // available that is onscreen and does not obscure the cursor. Otherwise,
 9673            // place it adjacent to the cursor.
 9674            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9675                .into_iter()
 9676                .flatten()
 9677                .find(|&start_row| {
 9678                    let end_row = start_row + line_count as u32;
 9679                    visible_row_range.contains(&start_row)
 9680                        && visible_row_range.contains(&end_row)
 9681                        && cursor_row
 9682                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9683                })?;
 9684
 9685            content_origin
 9686                + point(
 9687                    Pixels::from(-scroll_pixel_position.x),
 9688                    Pixels::from(
 9689                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9690                    ),
 9691                )
 9692        };
 9693
 9694        origin.x -= BORDER_WIDTH;
 9695
 9696        window.defer_draw(element, origin, 1);
 9697
 9698        // Do not return an element, since it will already be drawn due to defer_draw.
 9699        None
 9700    }
 9701
 9702    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9703        px(30.)
 9704    }
 9705
 9706    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9707        if self.read_only(cx) {
 9708            cx.theme().players().read_only()
 9709        } else {
 9710            self.style.as_ref().unwrap().local_player
 9711        }
 9712    }
 9713
 9714    fn render_edit_prediction_accept_keybind(
 9715        &self,
 9716        window: &mut Window,
 9717        cx: &mut App,
 9718    ) -> Option<AnyElement> {
 9719        let accept_binding =
 9720            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9721        let accept_keystroke = accept_binding.keystroke()?;
 9722
 9723        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9724
 9725        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9726            Color::Accent
 9727        } else {
 9728            Color::Muted
 9729        };
 9730
 9731        h_flex()
 9732            .px_0p5()
 9733            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9734            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9735            .text_size(TextSize::XSmall.rems(cx))
 9736            .child(h_flex().children(ui::render_modifiers(
 9737                accept_keystroke.modifiers(),
 9738                PlatformStyle::platform(),
 9739                Some(modifiers_color),
 9740                Some(IconSize::XSmall.rems().into()),
 9741                true,
 9742            )))
 9743            .when(is_platform_style_mac, |parent| {
 9744                parent.child(accept_keystroke.key().to_string())
 9745            })
 9746            .when(!is_platform_style_mac, |parent| {
 9747                parent.child(
 9748                    Key::new(
 9749                        util::capitalize(accept_keystroke.key()),
 9750                        Some(Color::Default),
 9751                    )
 9752                    .size(Some(IconSize::XSmall.rems().into())),
 9753                )
 9754            })
 9755            .into_any()
 9756            .into()
 9757    }
 9758
 9759    fn render_edit_prediction_line_popover(
 9760        &self,
 9761        label: impl Into<SharedString>,
 9762        icon: Option<IconName>,
 9763        window: &mut Window,
 9764        cx: &mut App,
 9765    ) -> Stateful<Div> {
 9766        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9767
 9768        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9769        let has_keybind = keybind.is_some();
 9770        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9771
 9772        h_flex()
 9773            .id("ep-line-popover")
 9774            .py_0p5()
 9775            .pl_1()
 9776            .pr(padding_right)
 9777            .gap_1()
 9778            .rounded_md()
 9779            .border_1()
 9780            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9781            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9782            .shadow_xs()
 9783            .when(!has_keybind, |el| {
 9784                let status_colors = cx.theme().status();
 9785
 9786                el.bg(status_colors.error_background)
 9787                    .border_color(status_colors.error.opacity(0.6))
 9788                    .pl_2()
 9789                    .child(Icon::new(icons.error).color(Color::Error))
 9790                    .cursor_default()
 9791                    .hoverable_tooltip(move |_window, cx| {
 9792                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9793                    })
 9794            })
 9795            .children(keybind)
 9796            .child(
 9797                Label::new(label)
 9798                    .size(LabelSize::Small)
 9799                    .when(!has_keybind, |el| {
 9800                        el.color(cx.theme().status().error.into()).strikethrough()
 9801                    }),
 9802            )
 9803            .when(!has_keybind, |el| {
 9804                el.child(
 9805                    h_flex().ml_1().child(
 9806                        Icon::new(IconName::Info)
 9807                            .size(IconSize::Small)
 9808                            .color(cx.theme().status().error.into()),
 9809                    ),
 9810                )
 9811            })
 9812            .when_some(icon, |element, icon| {
 9813                element.child(
 9814                    div()
 9815                        .mt(px(1.5))
 9816                        .child(Icon::new(icon).size(IconSize::Small)),
 9817                )
 9818            })
 9819    }
 9820
 9821    fn render_edit_prediction_jump_outside_popover(
 9822        &self,
 9823        snapshot: &BufferSnapshot,
 9824        window: &mut Window,
 9825        cx: &mut App,
 9826    ) -> Stateful<Div> {
 9827        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9828        let has_keybind = keybind.is_some();
 9829        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9830
 9831        let file_name = snapshot
 9832            .file()
 9833            .map(|file| SharedString::new(file.file_name(cx)))
 9834            .unwrap_or(SharedString::new_static("untitled"));
 9835
 9836        h_flex()
 9837            .id("ep-jump-outside-popover")
 9838            .py_1()
 9839            .px_2()
 9840            .gap_1()
 9841            .rounded_md()
 9842            .border_1()
 9843            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9844            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9845            .shadow_xs()
 9846            .when(!has_keybind, |el| {
 9847                let status_colors = cx.theme().status();
 9848
 9849                el.bg(status_colors.error_background)
 9850                    .border_color(status_colors.error.opacity(0.6))
 9851                    .pl_2()
 9852                    .child(Icon::new(icons.error).color(Color::Error))
 9853                    .cursor_default()
 9854                    .hoverable_tooltip(move |_window, cx| {
 9855                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9856                    })
 9857            })
 9858            .children(keybind)
 9859            .child(
 9860                Label::new(file_name)
 9861                    .size(LabelSize::Small)
 9862                    .buffer_font(cx)
 9863                    .when(!has_keybind, |el| {
 9864                        el.color(cx.theme().status().error.into()).strikethrough()
 9865                    }),
 9866            )
 9867            .when(!has_keybind, |el| {
 9868                el.child(
 9869                    h_flex().ml_1().child(
 9870                        Icon::new(IconName::Info)
 9871                            .size(IconSize::Small)
 9872                            .color(cx.theme().status().error.into()),
 9873                    ),
 9874                )
 9875            })
 9876            .child(
 9877                div()
 9878                    .mt(px(1.5))
 9879                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
 9880            )
 9881    }
 9882
 9883    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
 9884        let accent_color = cx.theme().colors().text_accent;
 9885        let editor_bg_color = cx.theme().colors().editor_background;
 9886        editor_bg_color.blend(accent_color.opacity(0.1))
 9887    }
 9888
 9889    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
 9890        let accent_color = cx.theme().colors().text_accent;
 9891        let editor_bg_color = cx.theme().colors().editor_background;
 9892        editor_bg_color.blend(accent_color.opacity(0.6))
 9893    }
 9894    fn get_prediction_provider_icons(
 9895        provider: &Option<RegisteredEditPredictionDelegate>,
 9896        cx: &App,
 9897    ) -> edit_prediction_types::EditPredictionIconSet {
 9898        match provider {
 9899            Some(provider) => provider.provider.icons(cx),
 9900            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
 9901        }
 9902    }
 9903
 9904    fn render_edit_prediction_cursor_popover(
 9905        &self,
 9906        min_width: Pixels,
 9907        max_width: Pixels,
 9908        cursor_point: Point,
 9909        style: &EditorStyle,
 9910        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
 9911        _window: &Window,
 9912        cx: &mut Context<Editor>,
 9913    ) -> Option<AnyElement> {
 9914        let provider = self.edit_prediction_provider.as_ref()?;
 9915        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9916
 9917        let is_refreshing = provider.provider.is_refreshing(cx);
 9918
 9919        fn pending_completion_container(icon: IconName) -> Div {
 9920            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
 9921        }
 9922
 9923        let completion = match &self.active_edit_prediction {
 9924            Some(prediction) => {
 9925                if !self.has_visible_completions_menu() {
 9926                    const RADIUS: Pixels = px(6.);
 9927                    const BORDER_WIDTH: Pixels = px(1.);
 9928
 9929                    return Some(
 9930                        h_flex()
 9931                            .elevation_2(cx)
 9932                            .border(BORDER_WIDTH)
 9933                            .border_color(cx.theme().colors().border)
 9934                            .when(accept_keystroke.is_none(), |el| {
 9935                                el.border_color(cx.theme().status().error)
 9936                            })
 9937                            .rounded(RADIUS)
 9938                            .rounded_tl(px(0.))
 9939                            .overflow_hidden()
 9940                            .child(div().px_1p5().child(match &prediction.completion {
 9941                                EditPrediction::MoveWithin { target, snapshot } => {
 9942                                    use text::ToPoint as _;
 9943                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
 9944                                    {
 9945                                        Icon::new(icons.down)
 9946                                    } else {
 9947                                        Icon::new(icons.up)
 9948                                    }
 9949                                }
 9950                                EditPrediction::MoveOutside { .. } => {
 9951                                    // TODO [zeta2] custom icon for external jump?
 9952                                    Icon::new(icons.base)
 9953                                }
 9954                                EditPrediction::Edit { .. } => Icon::new(icons.base),
 9955                            }))
 9956                            .child(
 9957                                h_flex()
 9958                                    .gap_1()
 9959                                    .py_1()
 9960                                    .px_2()
 9961                                    .rounded_r(RADIUS - BORDER_WIDTH)
 9962                                    .border_l_1()
 9963                                    .border_color(cx.theme().colors().border)
 9964                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9965                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
 9966                                        el.child(
 9967                                            Label::new("Hold")
 9968                                                .size(LabelSize::Small)
 9969                                                .when(accept_keystroke.is_none(), |el| {
 9970                                                    el.strikethrough()
 9971                                                })
 9972                                                .line_height_style(LineHeightStyle::UiLabel),
 9973                                        )
 9974                                    })
 9975                                    .id("edit_prediction_cursor_popover_keybind")
 9976                                    .when(accept_keystroke.is_none(), |el| {
 9977                                        let status_colors = cx.theme().status();
 9978
 9979                                        el.bg(status_colors.error_background)
 9980                                            .border_color(status_colors.error.opacity(0.6))
 9981                                            .child(Icon::new(IconName::Info).color(Color::Error))
 9982                                            .cursor_default()
 9983                                            .hoverable_tooltip(move |_window, cx| {
 9984                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
 9985                                                    .into()
 9986                                            })
 9987                                    })
 9988                                    .when_some(
 9989                                        accept_keystroke.as_ref(),
 9990                                        |el, accept_keystroke| {
 9991                                            el.child(h_flex().children(ui::render_modifiers(
 9992                                                accept_keystroke.modifiers(),
 9993                                                PlatformStyle::platform(),
 9994                                                Some(Color::Default),
 9995                                                Some(IconSize::XSmall.rems().into()),
 9996                                                false,
 9997                                            )))
 9998                                        },
 9999                                    ),
10000                            )
10001                            .into_any(),
10002                    );
10003                }
10004
10005                self.render_edit_prediction_cursor_popover_preview(
10006                    prediction,
10007                    cursor_point,
10008                    style,
10009                    cx,
10010                )?
10011            }
10012
10013            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10014                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10015                    stale_completion,
10016                    cursor_point,
10017                    style,
10018                    cx,
10019                )?,
10020
10021                None => pending_completion_container(icons.base)
10022                    .child(Label::new("...").size(LabelSize::Small)),
10023            },
10024
10025            None => pending_completion_container(icons.base)
10026                .child(Label::new("...").size(LabelSize::Small)),
10027        };
10028
10029        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10030            completion
10031                .with_animation(
10032                    "loading-completion",
10033                    Animation::new(Duration::from_secs(2))
10034                        .repeat()
10035                        .with_easing(pulsating_between(0.4, 0.8)),
10036                    |label, delta| label.opacity(delta),
10037                )
10038                .into_any_element()
10039        } else {
10040            completion.into_any_element()
10041        };
10042
10043        let has_completion = self.active_edit_prediction.is_some();
10044
10045        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10046        Some(
10047            h_flex()
10048                .min_w(min_width)
10049                .max_w(max_width)
10050                .flex_1()
10051                .elevation_2(cx)
10052                .border_color(cx.theme().colors().border)
10053                .child(
10054                    div()
10055                        .flex_1()
10056                        .py_1()
10057                        .px_2()
10058                        .overflow_hidden()
10059                        .child(completion),
10060                )
10061                .when_some(accept_keystroke, |el, accept_keystroke| {
10062                    if !accept_keystroke.modifiers().modified() {
10063                        return el;
10064                    }
10065
10066                    el.child(
10067                        h_flex()
10068                            .h_full()
10069                            .border_l_1()
10070                            .rounded_r_lg()
10071                            .border_color(cx.theme().colors().border)
10072                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10073                            .gap_1()
10074                            .py_1()
10075                            .px_2()
10076                            .child(
10077                                h_flex()
10078                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10079                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10080                                    .child(h_flex().children(ui::render_modifiers(
10081                                        accept_keystroke.modifiers(),
10082                                        PlatformStyle::platform(),
10083                                        Some(if !has_completion {
10084                                            Color::Muted
10085                                        } else {
10086                                            Color::Default
10087                                        }),
10088                                        None,
10089                                        false,
10090                                    ))),
10091                            )
10092                            .child(Label::new("Preview").into_any_element())
10093                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10094                    )
10095                })
10096                .into_any(),
10097        )
10098    }
10099
10100    fn render_edit_prediction_cursor_popover_preview(
10101        &self,
10102        completion: &EditPredictionState,
10103        cursor_point: Point,
10104        style: &EditorStyle,
10105        cx: &mut Context<Editor>,
10106    ) -> Option<Div> {
10107        use text::ToPoint as _;
10108
10109        fn render_relative_row_jump(
10110            prefix: impl Into<String>,
10111            current_row: u32,
10112            target_row: u32,
10113        ) -> Div {
10114            let (row_diff, arrow) = if target_row < current_row {
10115                (current_row - target_row, IconName::ArrowUp)
10116            } else {
10117                (target_row - current_row, IconName::ArrowDown)
10118            };
10119
10120            h_flex()
10121                .child(
10122                    Label::new(format!("{}{}", prefix.into(), row_diff))
10123                        .color(Color::Muted)
10124                        .size(LabelSize::Small),
10125                )
10126                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10127        }
10128
10129        let supports_jump = self
10130            .edit_prediction_provider
10131            .as_ref()
10132            .map(|provider| provider.provider.supports_jump_to_edit())
10133            .unwrap_or(true);
10134
10135        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10136
10137        match &completion.completion {
10138            EditPrediction::MoveWithin {
10139                target, snapshot, ..
10140            } => {
10141                if !supports_jump {
10142                    return None;
10143                }
10144
10145                Some(
10146                    h_flex()
10147                        .px_2()
10148                        .gap_2()
10149                        .flex_1()
10150                        .child(
10151                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10152                                Icon::new(icons.down)
10153                            } else {
10154                                Icon::new(icons.up)
10155                            },
10156                        )
10157                        .child(Label::new("Jump to Edit")),
10158                )
10159            }
10160            EditPrediction::MoveOutside { snapshot, .. } => {
10161                let file_name = snapshot
10162                    .file()
10163                    .map(|file| file.file_name(cx))
10164                    .unwrap_or("untitled");
10165                Some(
10166                    h_flex()
10167                        .px_2()
10168                        .gap_2()
10169                        .flex_1()
10170                        .child(Icon::new(icons.base))
10171                        .child(Label::new(format!("Jump to {file_name}"))),
10172                )
10173            }
10174            EditPrediction::Edit {
10175                edits,
10176                edit_preview,
10177                snapshot,
10178                ..
10179            } => {
10180                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10181
10182                let (highlighted_edits, has_more_lines) =
10183                    if let Some(edit_preview) = edit_preview.as_ref() {
10184                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10185                            .first_line_preview()
10186                    } else {
10187                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10188                    };
10189
10190                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10191                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10192
10193                let preview = h_flex()
10194                    .gap_1()
10195                    .min_w_16()
10196                    .child(styled_text)
10197                    .when(has_more_lines, |parent| parent.child(""));
10198
10199                let left = if supports_jump && first_edit_row != cursor_point.row {
10200                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10201                        .into_any_element()
10202                } else {
10203                    Icon::new(icons.base).into_any_element()
10204                };
10205
10206                Some(
10207                    h_flex()
10208                        .h_full()
10209                        .flex_1()
10210                        .gap_2()
10211                        .pr_1()
10212                        .overflow_x_hidden()
10213                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10214                        .child(left)
10215                        .child(preview),
10216                )
10217            }
10218        }
10219    }
10220
10221    pub fn render_context_menu(
10222        &mut self,
10223        max_height_in_lines: u32,
10224        window: &mut Window,
10225        cx: &mut Context<Editor>,
10226    ) -> Option<AnyElement> {
10227        let menu = self.context_menu.borrow();
10228        let menu = menu.as_ref()?;
10229        if !menu.visible() {
10230            return None;
10231        };
10232        self.style
10233            .as_ref()
10234            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10235    }
10236
10237    fn render_context_menu_aside(
10238        &mut self,
10239        max_size: Size<Pixels>,
10240        window: &mut Window,
10241        cx: &mut Context<Editor>,
10242    ) -> Option<AnyElement> {
10243        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10244            if menu.visible() {
10245                menu.render_aside(max_size, window, cx)
10246            } else {
10247                None
10248            }
10249        })
10250    }
10251
10252    fn hide_context_menu(
10253        &mut self,
10254        window: &mut Window,
10255        cx: &mut Context<Self>,
10256    ) -> Option<CodeContextMenu> {
10257        cx.notify();
10258        self.completion_tasks.clear();
10259        let context_menu = self.context_menu.borrow_mut().take();
10260        self.stale_edit_prediction_in_menu.take();
10261        self.update_visible_edit_prediction(window, cx);
10262        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10263            && let Some(completion_provider) = &self.completion_provider
10264        {
10265            completion_provider.selection_changed(None, window, cx);
10266        }
10267        context_menu
10268    }
10269
10270    fn show_snippet_choices(
10271        &mut self,
10272        choices: &Vec<String>,
10273        selection: Range<Anchor>,
10274        cx: &mut Context<Self>,
10275    ) {
10276        let Some((_, buffer, _)) = self
10277            .buffer()
10278            .read(cx)
10279            .excerpt_containing(selection.start, cx)
10280        else {
10281            return;
10282        };
10283        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10284        else {
10285            return;
10286        };
10287        if buffer != end_buffer {
10288            log::error!("expected anchor range to have matching buffer IDs");
10289            return;
10290        }
10291
10292        let id = post_inc(&mut self.next_completion_id);
10293        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10294        let mut context_menu = self.context_menu.borrow_mut();
10295        let old_menu = context_menu.take();
10296        *context_menu = Some(CodeContextMenu::Completions(
10297            CompletionsMenu::new_snippet_choices(
10298                id,
10299                true,
10300                choices,
10301                selection,
10302                buffer,
10303                old_menu.map(|menu| menu.primary_scroll_handle()),
10304                snippet_sort_order,
10305            ),
10306        ));
10307    }
10308
10309    pub fn insert_snippet(
10310        &mut self,
10311        insertion_ranges: &[Range<MultiBufferOffset>],
10312        snippet: Snippet,
10313        window: &mut Window,
10314        cx: &mut Context<Self>,
10315    ) -> Result<()> {
10316        struct Tabstop<T> {
10317            is_end_tabstop: bool,
10318            ranges: Vec<Range<T>>,
10319            choices: Option<Vec<String>>,
10320        }
10321
10322        let tabstops = self.buffer.update(cx, |buffer, cx| {
10323            let snippet_text: Arc<str> = snippet.text.clone().into();
10324            let edits = insertion_ranges
10325                .iter()
10326                .cloned()
10327                .map(|range| (range, snippet_text.clone()));
10328            let autoindent_mode = AutoindentMode::Block {
10329                original_indent_columns: Vec::new(),
10330            };
10331            buffer.edit(edits, Some(autoindent_mode), cx);
10332
10333            let snapshot = &*buffer.read(cx);
10334            let snippet = &snippet;
10335            snippet
10336                .tabstops
10337                .iter()
10338                .map(|tabstop| {
10339                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10340                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10341                    });
10342                    let mut tabstop_ranges = tabstop
10343                        .ranges
10344                        .iter()
10345                        .flat_map(|tabstop_range| {
10346                            let mut delta = 0_isize;
10347                            insertion_ranges.iter().map(move |insertion_range| {
10348                                let insertion_start = insertion_range.start + delta;
10349                                delta += snippet.text.len() as isize
10350                                    - (insertion_range.end - insertion_range.start) as isize;
10351
10352                                let start =
10353                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10354                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10355                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10356                            })
10357                        })
10358                        .collect::<Vec<_>>();
10359                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10360
10361                    Tabstop {
10362                        is_end_tabstop,
10363                        ranges: tabstop_ranges,
10364                        choices: tabstop.choices.clone(),
10365                    }
10366                })
10367                .collect::<Vec<_>>()
10368        });
10369        if let Some(tabstop) = tabstops.first() {
10370            self.change_selections(Default::default(), window, cx, |s| {
10371                // Reverse order so that the first range is the newest created selection.
10372                // Completions will use it and autoscroll will prioritize it.
10373                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10374            });
10375
10376            if let Some(choices) = &tabstop.choices
10377                && let Some(selection) = tabstop.ranges.first()
10378            {
10379                self.show_snippet_choices(choices, selection.clone(), cx)
10380            }
10381
10382            // If we're already at the last tabstop and it's at the end of the snippet,
10383            // we're done, we don't need to keep the state around.
10384            if !tabstop.is_end_tabstop {
10385                let choices = tabstops
10386                    .iter()
10387                    .map(|tabstop| tabstop.choices.clone())
10388                    .collect();
10389
10390                let ranges = tabstops
10391                    .into_iter()
10392                    .map(|tabstop| tabstop.ranges)
10393                    .collect::<Vec<_>>();
10394
10395                self.snippet_stack.push(SnippetState {
10396                    active_index: 0,
10397                    ranges,
10398                    choices,
10399                });
10400            }
10401
10402            // Check whether the just-entered snippet ends with an auto-closable bracket.
10403            if self.autoclose_regions.is_empty() {
10404                let snapshot = self.buffer.read(cx).snapshot(cx);
10405                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10406                    let selection_head = selection.head();
10407                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10408                        continue;
10409                    };
10410
10411                    let mut bracket_pair = None;
10412                    let max_lookup_length = scope
10413                        .brackets()
10414                        .map(|(pair, _)| {
10415                            pair.start
10416                                .as_str()
10417                                .chars()
10418                                .count()
10419                                .max(pair.end.as_str().chars().count())
10420                        })
10421                        .max();
10422                    if let Some(max_lookup_length) = max_lookup_length {
10423                        let next_text = snapshot
10424                            .chars_at(selection_head)
10425                            .take(max_lookup_length)
10426                            .collect::<String>();
10427                        let prev_text = snapshot
10428                            .reversed_chars_at(selection_head)
10429                            .take(max_lookup_length)
10430                            .collect::<String>();
10431
10432                        for (pair, enabled) in scope.brackets() {
10433                            if enabled
10434                                && pair.close
10435                                && prev_text.starts_with(pair.start.as_str())
10436                                && next_text.starts_with(pair.end.as_str())
10437                            {
10438                                bracket_pair = Some(pair.clone());
10439                                break;
10440                            }
10441                        }
10442                    }
10443
10444                    if let Some(pair) = bracket_pair {
10445                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10446                        let autoclose_enabled =
10447                            self.use_autoclose && snapshot_settings.use_autoclose;
10448                        if autoclose_enabled {
10449                            let start = snapshot.anchor_after(selection_head);
10450                            let end = snapshot.anchor_after(selection_head);
10451                            self.autoclose_regions.push(AutocloseRegion {
10452                                selection_id: selection.id,
10453                                range: start..end,
10454                                pair,
10455                            });
10456                        }
10457                    }
10458                }
10459            }
10460        }
10461        Ok(())
10462    }
10463
10464    pub fn move_to_next_snippet_tabstop(
10465        &mut self,
10466        window: &mut Window,
10467        cx: &mut Context<Self>,
10468    ) -> bool {
10469        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10470    }
10471
10472    pub fn move_to_prev_snippet_tabstop(
10473        &mut self,
10474        window: &mut Window,
10475        cx: &mut Context<Self>,
10476    ) -> bool {
10477        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10478    }
10479
10480    pub fn move_to_snippet_tabstop(
10481        &mut self,
10482        bias: Bias,
10483        window: &mut Window,
10484        cx: &mut Context<Self>,
10485    ) -> bool {
10486        if let Some(mut snippet) = self.snippet_stack.pop() {
10487            match bias {
10488                Bias::Left => {
10489                    if snippet.active_index > 0 {
10490                        snippet.active_index -= 1;
10491                    } else {
10492                        self.snippet_stack.push(snippet);
10493                        return false;
10494                    }
10495                }
10496                Bias::Right => {
10497                    if snippet.active_index + 1 < snippet.ranges.len() {
10498                        snippet.active_index += 1;
10499                    } else {
10500                        self.snippet_stack.push(snippet);
10501                        return false;
10502                    }
10503                }
10504            }
10505            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10506                self.change_selections(Default::default(), window, cx, |s| {
10507                    // Reverse order so that the first range is the newest created selection.
10508                    // Completions will use it and autoscroll will prioritize it.
10509                    s.select_ranges(current_ranges.iter().rev().cloned())
10510                });
10511
10512                if let Some(choices) = &snippet.choices[snippet.active_index]
10513                    && let Some(selection) = current_ranges.first()
10514                {
10515                    self.show_snippet_choices(choices, selection.clone(), cx);
10516                }
10517
10518                // If snippet state is not at the last tabstop, push it back on the stack
10519                if snippet.active_index + 1 < snippet.ranges.len() {
10520                    self.snippet_stack.push(snippet);
10521                }
10522                return true;
10523            }
10524        }
10525
10526        false
10527    }
10528
10529    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10530        self.transact(window, cx, |this, window, cx| {
10531            this.select_all(&SelectAll, window, cx);
10532            this.insert("", window, cx);
10533        });
10534    }
10535
10536    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10537        if self.read_only(cx) {
10538            return;
10539        }
10540        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10541        self.transact(window, cx, |this, window, cx| {
10542            this.select_autoclose_pair(window, cx);
10543
10544            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10545
10546            let mut linked_ranges = HashMap::<_, Vec<_>>::default();
10547            if !this.linked_edit_ranges.is_empty() {
10548                let selections = this.selections.all::<MultiBufferPoint>(&display_map);
10549                let snapshot = this.buffer.read(cx).snapshot(cx);
10550
10551                for selection in selections.iter() {
10552                    let selection_start = snapshot.anchor_before(selection.start).text_anchor;
10553                    let selection_end = snapshot.anchor_after(selection.end).text_anchor;
10554                    if selection_start.buffer_id != selection_end.buffer_id {
10555                        continue;
10556                    }
10557                    if let Some(ranges) =
10558                        this.linked_editing_ranges_for(selection_start..selection_end, cx)
10559                    {
10560                        for (buffer, entries) in ranges {
10561                            linked_ranges.entry(buffer).or_default().extend(entries);
10562                        }
10563                    }
10564                }
10565            }
10566
10567            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10568            for selection in &mut selections {
10569                if selection.is_empty() {
10570                    let old_head = selection.head();
10571                    let mut new_head =
10572                        movement::left(&display_map, old_head.to_display_point(&display_map))
10573                            .to_point(&display_map);
10574                    if let Some((buffer, line_buffer_range)) = display_map
10575                        .buffer_snapshot()
10576                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10577                    {
10578                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10579                        let indent_len = match indent_size.kind {
10580                            IndentKind::Space => {
10581                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10582                            }
10583                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10584                        };
10585                        if old_head.column <= indent_size.len && old_head.column > 0 {
10586                            let indent_len = indent_len.get();
10587                            new_head = cmp::min(
10588                                new_head,
10589                                MultiBufferPoint::new(
10590                                    old_head.row,
10591                                    ((old_head.column - 1) / indent_len) * indent_len,
10592                                ),
10593                            );
10594                        }
10595                    }
10596
10597                    selection.set_head(new_head, SelectionGoal::None);
10598                }
10599            }
10600
10601            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10602            this.insert("", window, cx);
10603            let empty_str: Arc<str> = Arc::from("");
10604            for (buffer, edits) in linked_ranges {
10605                let snapshot = buffer.read(cx).snapshot();
10606                use text::ToPoint as TP;
10607
10608                let edits = edits
10609                    .into_iter()
10610                    .map(|range| {
10611                        let end_point = TP::to_point(&range.end, &snapshot);
10612                        let mut start_point = TP::to_point(&range.start, &snapshot);
10613
10614                        if end_point == start_point {
10615                            let offset = text::ToOffset::to_offset(&range.start, &snapshot)
10616                                .saturating_sub(1);
10617                            start_point =
10618                                snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
10619                        };
10620
10621                        (start_point..end_point, empty_str.clone())
10622                    })
10623                    .sorted_by_key(|(range, _)| range.start)
10624                    .collect::<Vec<_>>();
10625                buffer.update(cx, |this, cx| {
10626                    this.edit(edits, None, cx);
10627                })
10628            }
10629            this.refresh_edit_prediction(true, false, window, cx);
10630            refresh_linked_ranges(this, window, cx);
10631        });
10632    }
10633
10634    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10635        if self.read_only(cx) {
10636            return;
10637        }
10638        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10639        self.transact(window, cx, |this, window, cx| {
10640            this.change_selections(Default::default(), window, cx, |s| {
10641                s.move_with(|map, selection| {
10642                    if selection.is_empty() {
10643                        let cursor = movement::right(map, selection.head());
10644                        selection.end = cursor;
10645                        selection.reversed = true;
10646                        selection.goal = SelectionGoal::None;
10647                    }
10648                })
10649            });
10650            this.insert("", window, cx);
10651            this.refresh_edit_prediction(true, false, window, cx);
10652        });
10653    }
10654
10655    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10656        if self.mode.is_single_line() {
10657            cx.propagate();
10658            return;
10659        }
10660
10661        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10662        if self.move_to_prev_snippet_tabstop(window, cx) {
10663            return;
10664        }
10665        self.outdent(&Outdent, window, cx);
10666    }
10667
10668    pub fn next_snippet_tabstop(
10669        &mut self,
10670        _: &NextSnippetTabstop,
10671        window: &mut Window,
10672        cx: &mut Context<Self>,
10673    ) {
10674        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10675            cx.propagate();
10676            return;
10677        }
10678
10679        if self.move_to_next_snippet_tabstop(window, cx) {
10680            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10681            return;
10682        }
10683        cx.propagate();
10684    }
10685
10686    pub fn previous_snippet_tabstop(
10687        &mut self,
10688        _: &PreviousSnippetTabstop,
10689        window: &mut Window,
10690        cx: &mut Context<Self>,
10691    ) {
10692        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10693            cx.propagate();
10694            return;
10695        }
10696
10697        if self.move_to_prev_snippet_tabstop(window, cx) {
10698            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10699            return;
10700        }
10701        cx.propagate();
10702    }
10703
10704    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10705        if self.mode.is_single_line() {
10706            cx.propagate();
10707            return;
10708        }
10709
10710        if self.move_to_next_snippet_tabstop(window, cx) {
10711            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10712            return;
10713        }
10714        if self.read_only(cx) {
10715            return;
10716        }
10717        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10718        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10719        let buffer = self.buffer.read(cx);
10720        let snapshot = buffer.snapshot(cx);
10721        let rows_iter = selections.iter().map(|s| s.head().row);
10722        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10723
10724        let has_some_cursor_in_whitespace = selections
10725            .iter()
10726            .filter(|selection| selection.is_empty())
10727            .any(|selection| {
10728                let cursor = selection.head();
10729                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10730                cursor.column < current_indent.len
10731            });
10732
10733        let mut edits = Vec::new();
10734        let mut prev_edited_row = 0;
10735        let mut row_delta = 0;
10736        for selection in &mut selections {
10737            if selection.start.row != prev_edited_row {
10738                row_delta = 0;
10739            }
10740            prev_edited_row = selection.end.row;
10741
10742            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10743            if selection.is_empty() {
10744                let cursor = selection.head();
10745                let settings = buffer.language_settings_at(cursor, cx);
10746                if settings.indent_list_on_tab {
10747                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10748                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10749                            row_delta = Self::indent_selection(
10750                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10751                            );
10752                            continue;
10753                        }
10754                    }
10755                }
10756            }
10757
10758            // If the selection is non-empty, then increase the indentation of the selected lines.
10759            if !selection.is_empty() {
10760                row_delta =
10761                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10762                continue;
10763            }
10764
10765            let cursor = selection.head();
10766            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10767            if let Some(suggested_indent) =
10768                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10769            {
10770                // Don't do anything if already at suggested indent
10771                // and there is any other cursor which is not
10772                if has_some_cursor_in_whitespace
10773                    && cursor.column == current_indent.len
10774                    && current_indent.len == suggested_indent.len
10775                {
10776                    continue;
10777                }
10778
10779                // Adjust line and move cursor to suggested indent
10780                // if cursor is not at suggested indent
10781                if cursor.column < suggested_indent.len
10782                    && cursor.column <= current_indent.len
10783                    && current_indent.len <= suggested_indent.len
10784                {
10785                    selection.start = Point::new(cursor.row, suggested_indent.len);
10786                    selection.end = selection.start;
10787                    if row_delta == 0 {
10788                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10789                            cursor.row,
10790                            current_indent,
10791                            suggested_indent,
10792                        ));
10793                        row_delta = suggested_indent.len - current_indent.len;
10794                    }
10795                    continue;
10796                }
10797
10798                // If current indent is more than suggested indent
10799                // only move cursor to current indent and skip indent
10800                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10801                    selection.start = Point::new(cursor.row, current_indent.len);
10802                    selection.end = selection.start;
10803                    continue;
10804                }
10805            }
10806
10807            // Otherwise, insert a hard or soft tab.
10808            let settings = buffer.language_settings_at(cursor, cx);
10809            let tab_size = if settings.hard_tabs {
10810                IndentSize::tab()
10811            } else {
10812                let tab_size = settings.tab_size.get();
10813                let indent_remainder = snapshot
10814                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10815                    .flat_map(str::chars)
10816                    .fold(row_delta % tab_size, |counter: u32, c| {
10817                        if c == '\t' {
10818                            0
10819                        } else {
10820                            (counter + 1) % tab_size
10821                        }
10822                    });
10823
10824                let chars_to_next_tab_stop = tab_size - indent_remainder;
10825                IndentSize::spaces(chars_to_next_tab_stop)
10826            };
10827            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10828            selection.end = selection.start;
10829            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10830            row_delta += tab_size.len;
10831        }
10832
10833        self.transact(window, cx, |this, window, cx| {
10834            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10835            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10836            this.refresh_edit_prediction(true, false, window, cx);
10837        });
10838    }
10839
10840    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10841        if self.read_only(cx) {
10842            return;
10843        }
10844        if self.mode.is_single_line() {
10845            cx.propagate();
10846            return;
10847        }
10848
10849        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10850        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
10851        let mut prev_edited_row = 0;
10852        let mut row_delta = 0;
10853        let mut edits = Vec::new();
10854        let buffer = self.buffer.read(cx);
10855        let snapshot = buffer.snapshot(cx);
10856        for selection in &mut selections {
10857            if selection.start.row != prev_edited_row {
10858                row_delta = 0;
10859            }
10860            prev_edited_row = selection.end.row;
10861
10862            row_delta =
10863                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10864        }
10865
10866        self.transact(window, cx, |this, window, cx| {
10867            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10868            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10869        });
10870    }
10871
10872    fn indent_selection(
10873        buffer: &MultiBuffer,
10874        snapshot: &MultiBufferSnapshot,
10875        selection: &mut Selection<Point>,
10876        edits: &mut Vec<(Range<Point>, String)>,
10877        delta_for_start_row: u32,
10878        cx: &App,
10879    ) -> u32 {
10880        let settings = buffer.language_settings_at(selection.start, cx);
10881        let tab_size = settings.tab_size.get();
10882        let indent_kind = if settings.hard_tabs {
10883            IndentKind::Tab
10884        } else {
10885            IndentKind::Space
10886        };
10887        let mut start_row = selection.start.row;
10888        let mut end_row = selection.end.row + 1;
10889
10890        // If a selection ends at the beginning of a line, don't indent
10891        // that last line.
10892        if selection.end.column == 0 && selection.end.row > selection.start.row {
10893            end_row -= 1;
10894        }
10895
10896        // Avoid re-indenting a row that has already been indented by a
10897        // previous selection, but still update this selection's column
10898        // to reflect that indentation.
10899        if delta_for_start_row > 0 {
10900            start_row += 1;
10901            selection.start.column += delta_for_start_row;
10902            if selection.end.row == selection.start.row {
10903                selection.end.column += delta_for_start_row;
10904            }
10905        }
10906
10907        let mut delta_for_end_row = 0;
10908        let has_multiple_rows = start_row + 1 != end_row;
10909        for row in start_row..end_row {
10910            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
10911            let indent_delta = match (current_indent.kind, indent_kind) {
10912                (IndentKind::Space, IndentKind::Space) => {
10913                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
10914                    IndentSize::spaces(columns_to_next_tab_stop)
10915                }
10916                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
10917                (_, IndentKind::Tab) => IndentSize::tab(),
10918            };
10919
10920            let start = if has_multiple_rows || current_indent.len < selection.start.column {
10921                0
10922            } else {
10923                selection.start.column
10924            };
10925            let row_start = Point::new(row, start);
10926            edits.push((
10927                row_start..row_start,
10928                indent_delta.chars().collect::<String>(),
10929            ));
10930
10931            // Update this selection's endpoints to reflect the indentation.
10932            if row == selection.start.row {
10933                selection.start.column += indent_delta.len;
10934            }
10935            if row == selection.end.row {
10936                selection.end.column += indent_delta.len;
10937                delta_for_end_row = indent_delta.len;
10938            }
10939        }
10940
10941        if selection.start.row == selection.end.row {
10942            delta_for_start_row + delta_for_end_row
10943        } else {
10944            delta_for_end_row
10945        }
10946    }
10947
10948    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
10949        if self.read_only(cx) {
10950            return;
10951        }
10952        if self.mode.is_single_line() {
10953            cx.propagate();
10954            return;
10955        }
10956
10957        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10958        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10959        let selections = self.selections.all::<Point>(&display_map);
10960        let mut deletion_ranges = Vec::new();
10961        let mut last_outdent = None;
10962        {
10963            let buffer = self.buffer.read(cx);
10964            let snapshot = buffer.snapshot(cx);
10965            for selection in &selections {
10966                let settings = buffer.language_settings_at(selection.start, cx);
10967                let tab_size = settings.tab_size.get();
10968                let mut rows = selection.spanned_rows(false, &display_map);
10969
10970                // Avoid re-outdenting a row that has already been outdented by a
10971                // previous selection.
10972                if let Some(last_row) = last_outdent
10973                    && last_row == rows.start
10974                {
10975                    rows.start = rows.start.next_row();
10976                }
10977                let has_multiple_rows = rows.len() > 1;
10978                for row in rows.iter_rows() {
10979                    let indent_size = snapshot.indent_size_for_line(row);
10980                    if indent_size.len > 0 {
10981                        let deletion_len = match indent_size.kind {
10982                            IndentKind::Space => {
10983                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
10984                                if columns_to_prev_tab_stop == 0 {
10985                                    tab_size
10986                                } else {
10987                                    columns_to_prev_tab_stop
10988                                }
10989                            }
10990                            IndentKind::Tab => 1,
10991                        };
10992                        let start = if has_multiple_rows
10993                            || deletion_len > selection.start.column
10994                            || indent_size.len < selection.start.column
10995                        {
10996                            0
10997                        } else {
10998                            selection.start.column - deletion_len
10999                        };
11000                        deletion_ranges.push(
11001                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11002                        );
11003                        last_outdent = Some(row);
11004                    }
11005                }
11006            }
11007        }
11008
11009        self.transact(window, cx, |this, window, cx| {
11010            this.buffer.update(cx, |buffer, cx| {
11011                let empty_str: Arc<str> = Arc::default();
11012                buffer.edit(
11013                    deletion_ranges
11014                        .into_iter()
11015                        .map(|range| (range, empty_str.clone())),
11016                    None,
11017                    cx,
11018                );
11019            });
11020            let selections = this
11021                .selections
11022                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11023            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11024        });
11025    }
11026
11027    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11028        if self.read_only(cx) {
11029            return;
11030        }
11031        if self.mode.is_single_line() {
11032            cx.propagate();
11033            return;
11034        }
11035
11036        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11037        let selections = self
11038            .selections
11039            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11040            .into_iter()
11041            .map(|s| s.range());
11042
11043        self.transact(window, cx, |this, window, cx| {
11044            this.buffer.update(cx, |buffer, cx| {
11045                buffer.autoindent_ranges(selections, cx);
11046            });
11047            let selections = this
11048                .selections
11049                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11050            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11051        });
11052    }
11053
11054    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11055        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11056        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11057        let selections = self.selections.all::<Point>(&display_map);
11058
11059        let mut new_cursors = Vec::new();
11060        let mut edit_ranges = Vec::new();
11061        let mut selections = selections.iter().peekable();
11062        while let Some(selection) = selections.next() {
11063            let mut rows = selection.spanned_rows(false, &display_map);
11064
11065            // Accumulate contiguous regions of rows that we want to delete.
11066            while let Some(next_selection) = selections.peek() {
11067                let next_rows = next_selection.spanned_rows(false, &display_map);
11068                if next_rows.start <= rows.end {
11069                    rows.end = next_rows.end;
11070                    selections.next().unwrap();
11071                } else {
11072                    break;
11073                }
11074            }
11075
11076            let buffer = display_map.buffer_snapshot();
11077            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11078            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11079                // If there's a line after the range, delete the \n from the end of the row range
11080                (
11081                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11082                    rows.end,
11083                )
11084            } else {
11085                // If there isn't a line after the range, delete the \n from the line before the
11086                // start of the row range
11087                edit_start = edit_start.saturating_sub_usize(1);
11088                (buffer.len(), rows.start.previous_row())
11089            };
11090
11091            let text_layout_details = self.text_layout_details(window, cx);
11092            let x = display_map.x_for_display_point(
11093                selection.head().to_display_point(&display_map),
11094                &text_layout_details,
11095            );
11096            let row = Point::new(target_row.0, 0)
11097                .to_display_point(&display_map)
11098                .row();
11099            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11100
11101            new_cursors.push((
11102                selection.id,
11103                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11104                SelectionGoal::None,
11105            ));
11106            edit_ranges.push(edit_start..edit_end);
11107        }
11108
11109        self.transact(window, cx, |this, window, cx| {
11110            let buffer = this.buffer.update(cx, |buffer, cx| {
11111                let empty_str: Arc<str> = Arc::default();
11112                buffer.edit(
11113                    edit_ranges
11114                        .into_iter()
11115                        .map(|range| (range, empty_str.clone())),
11116                    None,
11117                    cx,
11118                );
11119                buffer.snapshot(cx)
11120            });
11121            let new_selections = new_cursors
11122                .into_iter()
11123                .map(|(id, cursor, goal)| {
11124                    let cursor = cursor.to_point(&buffer);
11125                    Selection {
11126                        id,
11127                        start: cursor,
11128                        end: cursor,
11129                        reversed: false,
11130                        goal,
11131                    }
11132                })
11133                .collect();
11134
11135            this.change_selections(Default::default(), window, cx, |s| {
11136                s.select(new_selections);
11137            });
11138        });
11139    }
11140
11141    pub fn join_lines_impl(
11142        &mut self,
11143        insert_whitespace: bool,
11144        window: &mut Window,
11145        cx: &mut Context<Self>,
11146    ) {
11147        if self.read_only(cx) {
11148            return;
11149        }
11150        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11151        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11152            let start = MultiBufferRow(selection.start.row);
11153            // Treat single line selections as if they include the next line. Otherwise this action
11154            // would do nothing for single line selections individual cursors.
11155            let end = if selection.start.row == selection.end.row {
11156                MultiBufferRow(selection.start.row + 1)
11157            } else {
11158                MultiBufferRow(selection.end.row)
11159            };
11160
11161            if let Some(last_row_range) = row_ranges.last_mut()
11162                && start <= last_row_range.end
11163            {
11164                last_row_range.end = end;
11165                continue;
11166            }
11167            row_ranges.push(start..end);
11168        }
11169
11170        let snapshot = self.buffer.read(cx).snapshot(cx);
11171        let mut cursor_positions = Vec::new();
11172        for row_range in &row_ranges {
11173            let anchor = snapshot.anchor_before(Point::new(
11174                row_range.end.previous_row().0,
11175                snapshot.line_len(row_range.end.previous_row()),
11176            ));
11177            cursor_positions.push(anchor..anchor);
11178        }
11179
11180        self.transact(window, cx, |this, window, cx| {
11181            for row_range in row_ranges.into_iter().rev() {
11182                for row in row_range.iter_rows().rev() {
11183                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11184                    let next_line_row = row.next_row();
11185                    let indent = snapshot.indent_size_for_line(next_line_row);
11186                    let start_of_next_line = Point::new(next_line_row.0, indent.len);
11187
11188                    let replace =
11189                        if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
11190                            " "
11191                        } else {
11192                            ""
11193                        };
11194
11195                    this.buffer.update(cx, |buffer, cx| {
11196                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11197                    });
11198                }
11199            }
11200
11201            this.change_selections(Default::default(), window, cx, |s| {
11202                s.select_anchor_ranges(cursor_positions)
11203            });
11204        });
11205    }
11206
11207    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11208        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11209        self.join_lines_impl(true, window, cx);
11210    }
11211
11212    pub fn sort_lines_case_sensitive(
11213        &mut self,
11214        _: &SortLinesCaseSensitive,
11215        window: &mut Window,
11216        cx: &mut Context<Self>,
11217    ) {
11218        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11219    }
11220
11221    pub fn sort_lines_by_length(
11222        &mut self,
11223        _: &SortLinesByLength,
11224        window: &mut Window,
11225        cx: &mut Context<Self>,
11226    ) {
11227        self.manipulate_immutable_lines(window, cx, |lines| {
11228            lines.sort_by_key(|&line| line.chars().count())
11229        })
11230    }
11231
11232    pub fn sort_lines_case_insensitive(
11233        &mut self,
11234        _: &SortLinesCaseInsensitive,
11235        window: &mut Window,
11236        cx: &mut Context<Self>,
11237    ) {
11238        self.manipulate_immutable_lines(window, cx, |lines| {
11239            lines.sort_by_key(|line| line.to_lowercase())
11240        })
11241    }
11242
11243    pub fn unique_lines_case_insensitive(
11244        &mut self,
11245        _: &UniqueLinesCaseInsensitive,
11246        window: &mut Window,
11247        cx: &mut Context<Self>,
11248    ) {
11249        self.manipulate_immutable_lines(window, cx, |lines| {
11250            let mut seen = HashSet::default();
11251            lines.retain(|line| seen.insert(line.to_lowercase()));
11252        })
11253    }
11254
11255    pub fn unique_lines_case_sensitive(
11256        &mut self,
11257        _: &UniqueLinesCaseSensitive,
11258        window: &mut Window,
11259        cx: &mut Context<Self>,
11260    ) {
11261        self.manipulate_immutable_lines(window, cx, |lines| {
11262            let mut seen = HashSet::default();
11263            lines.retain(|line| seen.insert(*line));
11264        })
11265    }
11266
11267    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11268        let snapshot = self.buffer.read(cx).snapshot(cx);
11269        for selection in self.selections.disjoint_anchors_arc().iter() {
11270            if snapshot
11271                .language_at(selection.start)
11272                .and_then(|lang| lang.config().wrap_characters.as_ref())
11273                .is_some()
11274            {
11275                return true;
11276            }
11277        }
11278        false
11279    }
11280
11281    fn wrap_selections_in_tag(
11282        &mut self,
11283        _: &WrapSelectionsInTag,
11284        window: &mut Window,
11285        cx: &mut Context<Self>,
11286    ) {
11287        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11288
11289        let snapshot = self.buffer.read(cx).snapshot(cx);
11290
11291        let mut edits = Vec::new();
11292        let mut boundaries = Vec::new();
11293
11294        for selection in self
11295            .selections
11296            .all_adjusted(&self.display_snapshot(cx))
11297            .iter()
11298        {
11299            let Some(wrap_config) = snapshot
11300                .language_at(selection.start)
11301                .and_then(|lang| lang.config().wrap_characters.clone())
11302            else {
11303                continue;
11304            };
11305
11306            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11307            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11308
11309            let start_before = snapshot.anchor_before(selection.start);
11310            let end_after = snapshot.anchor_after(selection.end);
11311
11312            edits.push((start_before..start_before, open_tag));
11313            edits.push((end_after..end_after, close_tag));
11314
11315            boundaries.push((
11316                start_before,
11317                end_after,
11318                wrap_config.start_prefix.len(),
11319                wrap_config.end_suffix.len(),
11320            ));
11321        }
11322
11323        if edits.is_empty() {
11324            return;
11325        }
11326
11327        self.transact(window, cx, |this, window, cx| {
11328            let buffer = this.buffer.update(cx, |buffer, cx| {
11329                buffer.edit(edits, None, cx);
11330                buffer.snapshot(cx)
11331            });
11332
11333            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11334            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11335                boundaries.into_iter()
11336            {
11337                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11338                let close_offset = end_after
11339                    .to_offset(&buffer)
11340                    .saturating_sub_usize(end_suffix_len);
11341                new_selections.push(open_offset..open_offset);
11342                new_selections.push(close_offset..close_offset);
11343            }
11344
11345            this.change_selections(Default::default(), window, cx, |s| {
11346                s.select_ranges(new_selections);
11347            });
11348
11349            this.request_autoscroll(Autoscroll::fit(), cx);
11350        });
11351    }
11352
11353    pub fn toggle_read_only(
11354        &mut self,
11355        _: &workspace::ToggleReadOnlyFile,
11356        _: &mut Window,
11357        cx: &mut Context<Self>,
11358    ) {
11359        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11360            buffer.update(cx, |buffer, cx| {
11361                buffer.set_capability(
11362                    match buffer.capability() {
11363                        Capability::ReadWrite => Capability::Read,
11364                        Capability::Read => Capability::ReadWrite,
11365                        Capability::ReadOnly => Capability::ReadOnly,
11366                    },
11367                    cx,
11368                );
11369            })
11370        }
11371    }
11372
11373    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11374        let Some(project) = self.project.clone() else {
11375            return;
11376        };
11377        self.reload(project, window, cx)
11378            .detach_and_notify_err(window, cx);
11379    }
11380
11381    pub fn restore_file(
11382        &mut self,
11383        _: &::git::RestoreFile,
11384        window: &mut Window,
11385        cx: &mut Context<Self>,
11386    ) {
11387        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11388        let mut buffer_ids = HashSet::default();
11389        let snapshot = self.buffer().read(cx).snapshot(cx);
11390        for selection in self
11391            .selections
11392            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11393        {
11394            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11395        }
11396
11397        let buffer = self.buffer().read(cx);
11398        let ranges = buffer_ids
11399            .into_iter()
11400            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11401            .collect::<Vec<_>>();
11402
11403        self.restore_hunks_in_ranges(ranges, window, cx);
11404    }
11405
11406    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11407        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11408        let selections = self
11409            .selections
11410            .all(&self.display_snapshot(cx))
11411            .into_iter()
11412            .map(|s| s.range())
11413            .collect();
11414        self.restore_hunks_in_ranges(selections, window, cx);
11415    }
11416
11417    pub fn restore_hunks_in_ranges(
11418        &mut self,
11419        ranges: Vec<Range<Point>>,
11420        window: &mut Window,
11421        cx: &mut Context<Editor>,
11422    ) {
11423        if self.delegate_stage_and_restore {
11424            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11425            if !hunks.is_empty() {
11426                cx.emit(EditorEvent::RestoreRequested { hunks });
11427            }
11428            return;
11429        }
11430        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11431        self.transact(window, cx, |editor, window, cx| {
11432            editor.restore_diff_hunks(hunks, cx);
11433            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11434                selections.refresh()
11435            });
11436        });
11437    }
11438
11439    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11440        let mut revert_changes = HashMap::default();
11441        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11442        for (buffer_id, hunks) in &chunk_by {
11443            let hunks = hunks.collect::<Vec<_>>();
11444            for hunk in &hunks {
11445                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11446            }
11447            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11448        }
11449        if !revert_changes.is_empty() {
11450            self.buffer().update(cx, |multi_buffer, cx| {
11451                for (buffer_id, changes) in revert_changes {
11452                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11453                        buffer.update(cx, |buffer, cx| {
11454                            buffer.edit(
11455                                changes
11456                                    .into_iter()
11457                                    .map(|(range, text)| (range, text.to_string())),
11458                                None,
11459                                cx,
11460                            );
11461                        });
11462                    }
11463                }
11464            });
11465        }
11466    }
11467
11468    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11469        if let Some(status) = self
11470            .addons
11471            .iter()
11472            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11473        {
11474            return Some(status);
11475        }
11476        self.project
11477            .as_ref()?
11478            .read(cx)
11479            .status_for_buffer_id(buffer_id, cx)
11480    }
11481
11482    pub fn open_active_item_in_terminal(
11483        &mut self,
11484        _: &OpenInTerminal,
11485        window: &mut Window,
11486        cx: &mut Context<Self>,
11487    ) {
11488        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11489            let project_path = buffer.read(cx).project_path(cx)?;
11490            let project = self.project()?.read(cx);
11491            let entry = project.entry_for_path(&project_path, cx)?;
11492            let parent = match &entry.canonical_path {
11493                Some(canonical_path) => canonical_path.to_path_buf(),
11494                None => project.absolute_path(&project_path, cx)?,
11495            }
11496            .parent()?
11497            .to_path_buf();
11498            Some(parent)
11499        }) {
11500            window.dispatch_action(
11501                OpenTerminal {
11502                    working_directory,
11503                    local: false,
11504                }
11505                .boxed_clone(),
11506                cx,
11507            );
11508        }
11509    }
11510
11511    fn set_breakpoint_context_menu(
11512        &mut self,
11513        display_row: DisplayRow,
11514        position: Option<Anchor>,
11515        clicked_point: gpui::Point<Pixels>,
11516        window: &mut Window,
11517        cx: &mut Context<Self>,
11518    ) {
11519        let source = self
11520            .buffer
11521            .read(cx)
11522            .snapshot(cx)
11523            .anchor_before(Point::new(display_row.0, 0u32));
11524
11525        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11526
11527        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11528            self,
11529            source,
11530            clicked_point,
11531            context_menu,
11532            window,
11533            cx,
11534        );
11535    }
11536
11537    fn add_edit_breakpoint_block(
11538        &mut self,
11539        anchor: Anchor,
11540        breakpoint: &Breakpoint,
11541        edit_action: BreakpointPromptEditAction,
11542        window: &mut Window,
11543        cx: &mut Context<Self>,
11544    ) {
11545        let weak_editor = cx.weak_entity();
11546        let bp_prompt = cx.new(|cx| {
11547            BreakpointPromptEditor::new(
11548                weak_editor,
11549                anchor,
11550                breakpoint.clone(),
11551                edit_action,
11552                window,
11553                cx,
11554            )
11555        });
11556
11557        let height = bp_prompt.update(cx, |this, cx| {
11558            this.prompt
11559                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11560        });
11561        let cloned_prompt = bp_prompt.clone();
11562        let blocks = vec![BlockProperties {
11563            style: BlockStyle::Sticky,
11564            placement: BlockPlacement::Above(anchor),
11565            height: Some(height),
11566            render: Arc::new(move |cx| {
11567                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11568                cloned_prompt.clone().into_any_element()
11569            }),
11570            priority: 0,
11571        }];
11572
11573        let focus_handle = bp_prompt.focus_handle(cx);
11574        window.focus(&focus_handle, cx);
11575
11576        let block_ids = self.insert_blocks(blocks, None, cx);
11577        bp_prompt.update(cx, |prompt, _| {
11578            prompt.add_block_ids(block_ids);
11579        });
11580    }
11581
11582    pub(crate) fn breakpoint_at_row(
11583        &self,
11584        row: u32,
11585        window: &mut Window,
11586        cx: &mut Context<Self>,
11587    ) -> Option<(Anchor, Breakpoint)> {
11588        let snapshot = self.snapshot(window, cx);
11589        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11590
11591        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11592    }
11593
11594    pub(crate) fn breakpoint_at_anchor(
11595        &self,
11596        breakpoint_position: Anchor,
11597        snapshot: &EditorSnapshot,
11598        cx: &mut Context<Self>,
11599    ) -> Option<(Anchor, Breakpoint)> {
11600        let buffer = self
11601            .buffer
11602            .read(cx)
11603            .buffer_for_anchor(breakpoint_position, cx)?;
11604
11605        let enclosing_excerpt = breakpoint_position.excerpt_id;
11606        let buffer_snapshot = buffer.read(cx).snapshot();
11607
11608        let row = buffer_snapshot
11609            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11610            .row;
11611
11612        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11613        let anchor_end = snapshot
11614            .buffer_snapshot()
11615            .anchor_after(Point::new(row, line_len));
11616
11617        self.breakpoint_store
11618            .as_ref()?
11619            .read_with(cx, |breakpoint_store, cx| {
11620                breakpoint_store
11621                    .breakpoints(
11622                        &buffer,
11623                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11624                        &buffer_snapshot,
11625                        cx,
11626                    )
11627                    .next()
11628                    .and_then(|(bp, _)| {
11629                        let breakpoint_row = buffer_snapshot
11630                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11631                            .row;
11632
11633                        if breakpoint_row == row {
11634                            snapshot
11635                                .buffer_snapshot()
11636                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11637                                .map(|position| (position, bp.bp.clone()))
11638                        } else {
11639                            None
11640                        }
11641                    })
11642            })
11643    }
11644
11645    pub fn edit_log_breakpoint(
11646        &mut self,
11647        _: &EditLogBreakpoint,
11648        window: &mut Window,
11649        cx: &mut Context<Self>,
11650    ) {
11651        if self.breakpoint_store.is_none() {
11652            return;
11653        }
11654
11655        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11656            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11657                message: None,
11658                state: BreakpointState::Enabled,
11659                condition: None,
11660                hit_condition: None,
11661            });
11662
11663            self.add_edit_breakpoint_block(
11664                anchor,
11665                &breakpoint,
11666                BreakpointPromptEditAction::Log,
11667                window,
11668                cx,
11669            );
11670        }
11671    }
11672
11673    fn breakpoints_at_cursors(
11674        &self,
11675        window: &mut Window,
11676        cx: &mut Context<Self>,
11677    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11678        let snapshot = self.snapshot(window, cx);
11679        let cursors = self
11680            .selections
11681            .disjoint_anchors_arc()
11682            .iter()
11683            .map(|selection| {
11684                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11685
11686                let breakpoint_position = self
11687                    .breakpoint_at_row(cursor_position.row, window, cx)
11688                    .map(|bp| bp.0)
11689                    .unwrap_or_else(|| {
11690                        snapshot
11691                            .display_snapshot
11692                            .buffer_snapshot()
11693                            .anchor_after(Point::new(cursor_position.row, 0))
11694                    });
11695
11696                let breakpoint = self
11697                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11698                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11699
11700                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11701            })
11702            // 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.
11703            .collect::<HashMap<Anchor, _>>();
11704
11705        cursors.into_iter().collect()
11706    }
11707
11708    pub fn enable_breakpoint(
11709        &mut self,
11710        _: &crate::actions::EnableBreakpoint,
11711        window: &mut Window,
11712        cx: &mut Context<Self>,
11713    ) {
11714        if self.breakpoint_store.is_none() {
11715            return;
11716        }
11717
11718        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11719            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11720                continue;
11721            };
11722            self.edit_breakpoint_at_anchor(
11723                anchor,
11724                breakpoint,
11725                BreakpointEditAction::InvertState,
11726                cx,
11727            );
11728        }
11729    }
11730
11731    pub fn disable_breakpoint(
11732        &mut self,
11733        _: &crate::actions::DisableBreakpoint,
11734        window: &mut Window,
11735        cx: &mut Context<Self>,
11736    ) {
11737        if self.breakpoint_store.is_none() {
11738            return;
11739        }
11740
11741        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11742            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11743                continue;
11744            };
11745            self.edit_breakpoint_at_anchor(
11746                anchor,
11747                breakpoint,
11748                BreakpointEditAction::InvertState,
11749                cx,
11750            );
11751        }
11752    }
11753
11754    pub fn toggle_breakpoint(
11755        &mut self,
11756        _: &crate::actions::ToggleBreakpoint,
11757        window: &mut Window,
11758        cx: &mut Context<Self>,
11759    ) {
11760        if self.breakpoint_store.is_none() {
11761            return;
11762        }
11763
11764        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11765            if let Some(breakpoint) = breakpoint {
11766                self.edit_breakpoint_at_anchor(
11767                    anchor,
11768                    breakpoint,
11769                    BreakpointEditAction::Toggle,
11770                    cx,
11771                );
11772            } else {
11773                self.edit_breakpoint_at_anchor(
11774                    anchor,
11775                    Breakpoint::new_standard(),
11776                    BreakpointEditAction::Toggle,
11777                    cx,
11778                );
11779            }
11780        }
11781    }
11782
11783    pub fn edit_breakpoint_at_anchor(
11784        &mut self,
11785        breakpoint_position: Anchor,
11786        breakpoint: Breakpoint,
11787        edit_action: BreakpointEditAction,
11788        cx: &mut Context<Self>,
11789    ) {
11790        let Some(breakpoint_store) = &self.breakpoint_store else {
11791            return;
11792        };
11793
11794        let Some(buffer) = self
11795            .buffer
11796            .read(cx)
11797            .buffer_for_anchor(breakpoint_position, cx)
11798        else {
11799            return;
11800        };
11801
11802        breakpoint_store.update(cx, |breakpoint_store, cx| {
11803            breakpoint_store.toggle_breakpoint(
11804                buffer,
11805                BreakpointWithPosition {
11806                    position: breakpoint_position.text_anchor,
11807                    bp: breakpoint,
11808                },
11809                edit_action,
11810                cx,
11811            );
11812        });
11813
11814        cx.notify();
11815    }
11816
11817    #[cfg(any(test, feature = "test-support"))]
11818    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
11819        self.breakpoint_store.clone()
11820    }
11821
11822    pub fn prepare_restore_change(
11823        &self,
11824        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11825        hunk: &MultiBufferDiffHunk,
11826        cx: &mut App,
11827    ) -> Option<()> {
11828        if hunk.is_created_file() {
11829            return None;
11830        }
11831        let buffer = self.buffer.read(cx);
11832        let diff = buffer.diff_for(hunk.buffer_id)?;
11833        let buffer = buffer.buffer(hunk.buffer_id)?;
11834        let buffer = buffer.read(cx);
11835        let original_text = diff
11836            .read(cx)
11837            .base_text(cx)
11838            .as_rope()
11839            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
11840        let buffer_snapshot = buffer.snapshot();
11841        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
11842        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
11843            probe
11844                .0
11845                .start
11846                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
11847                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
11848        }) {
11849            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
11850            Some(())
11851        } else {
11852            None
11853        }
11854    }
11855
11856    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
11857        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
11858    }
11859
11860    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
11861        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
11862    }
11863
11864    pub fn rotate_selections_forward(
11865        &mut self,
11866        _: &RotateSelectionsForward,
11867        window: &mut Window,
11868        cx: &mut Context<Self>,
11869    ) {
11870        self.rotate_selections(window, cx, false)
11871    }
11872
11873    pub fn rotate_selections_backward(
11874        &mut self,
11875        _: &RotateSelectionsBackward,
11876        window: &mut Window,
11877        cx: &mut Context<Self>,
11878    ) {
11879        self.rotate_selections(window, cx, true)
11880    }
11881
11882    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
11883        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11884        let display_snapshot = self.display_snapshot(cx);
11885        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
11886
11887        if selections.len() < 2 {
11888            return;
11889        }
11890
11891        let (edits, new_selections) = {
11892            let buffer = self.buffer.read(cx).read(cx);
11893            let has_selections = selections.iter().any(|s| !s.is_empty());
11894            if has_selections {
11895                let mut selected_texts: Vec<String> = selections
11896                    .iter()
11897                    .map(|selection| {
11898                        buffer
11899                            .text_for_range(selection.start..selection.end)
11900                            .collect()
11901                    })
11902                    .collect();
11903
11904                if reverse {
11905                    selected_texts.rotate_left(1);
11906                } else {
11907                    selected_texts.rotate_right(1);
11908                }
11909
11910                let mut offset_delta: i64 = 0;
11911                let mut new_selections = Vec::new();
11912                let edits: Vec<_> = selections
11913                    .iter()
11914                    .zip(selected_texts.iter())
11915                    .map(|(selection, new_text)| {
11916                        let old_len = (selection.end.0 - selection.start.0) as i64;
11917                        let new_len = new_text.len() as i64;
11918                        let adjusted_start =
11919                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
11920                        let adjusted_end =
11921                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
11922
11923                        new_selections.push(Selection {
11924                            id: selection.id,
11925                            start: adjusted_start,
11926                            end: adjusted_end,
11927                            reversed: selection.reversed,
11928                            goal: selection.goal,
11929                        });
11930
11931                        offset_delta += new_len - old_len;
11932                        (selection.start..selection.end, new_text.clone())
11933                    })
11934                    .collect();
11935                (edits, new_selections)
11936            } else {
11937                let mut all_rows: Vec<u32> = selections
11938                    .iter()
11939                    .map(|selection| buffer.offset_to_point(selection.start).row)
11940                    .collect();
11941                all_rows.sort_unstable();
11942                all_rows.dedup();
11943
11944                if all_rows.len() < 2 {
11945                    return;
11946                }
11947
11948                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
11949                    .iter()
11950                    .map(|&row| {
11951                        let start = Point::new(row, 0);
11952                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11953                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
11954                    })
11955                    .collect();
11956
11957                let mut line_texts: Vec<String> = line_ranges
11958                    .iter()
11959                    .map(|range| buffer.text_for_range(range.clone()).collect())
11960                    .collect();
11961
11962                if reverse {
11963                    line_texts.rotate_left(1);
11964                } else {
11965                    line_texts.rotate_right(1);
11966                }
11967
11968                let edits = line_ranges
11969                    .iter()
11970                    .zip(line_texts.iter())
11971                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
11972                    .collect();
11973
11974                let num_rows = all_rows.len();
11975                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
11976                    .iter()
11977                    .enumerate()
11978                    .map(|(i, &row)| (row, i))
11979                    .collect();
11980
11981                // Compute new line start offsets after rotation (handles CRLF)
11982                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
11983                let first_line_start = line_ranges[0].start.0;
11984                let mut new_line_starts: Vec<usize> = vec![first_line_start];
11985                for text in line_texts.iter().take(num_rows - 1) {
11986                    let prev_start = *new_line_starts.last().unwrap();
11987                    new_line_starts.push(prev_start + text.len() + newline_len);
11988                }
11989
11990                let new_selections = selections
11991                    .iter()
11992                    .map(|selection| {
11993                        let point = buffer.offset_to_point(selection.start);
11994                        let old_index = row_to_index[&point.row];
11995                        let new_index = if reverse {
11996                            (old_index + num_rows - 1) % num_rows
11997                        } else {
11998                            (old_index + 1) % num_rows
11999                        };
12000                        let new_offset =
12001                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12002                        Selection {
12003                            id: selection.id,
12004                            start: new_offset,
12005                            end: new_offset,
12006                            reversed: selection.reversed,
12007                            goal: selection.goal,
12008                        }
12009                    })
12010                    .collect();
12011
12012                (edits, new_selections)
12013            }
12014        };
12015
12016        self.transact(window, cx, |this, window, cx| {
12017            this.buffer.update(cx, |buffer, cx| {
12018                buffer.edit(edits, None, cx);
12019            });
12020            this.change_selections(Default::default(), window, cx, |s| {
12021                s.select(new_selections);
12022            });
12023        });
12024    }
12025
12026    fn manipulate_lines<M>(
12027        &mut self,
12028        window: &mut Window,
12029        cx: &mut Context<Self>,
12030        mut manipulate: M,
12031    ) where
12032        M: FnMut(&str) -> LineManipulationResult,
12033    {
12034        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12035
12036        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12037        let buffer = self.buffer.read(cx).snapshot(cx);
12038
12039        let mut edits = Vec::new();
12040
12041        let selections = self.selections.all::<Point>(&display_map);
12042        let mut selections = selections.iter().peekable();
12043        let mut contiguous_row_selections = Vec::new();
12044        let mut new_selections = Vec::new();
12045        let mut added_lines = 0;
12046        let mut removed_lines = 0;
12047
12048        while let Some(selection) = selections.next() {
12049            let (start_row, end_row) = consume_contiguous_rows(
12050                &mut contiguous_row_selections,
12051                selection,
12052                &display_map,
12053                &mut selections,
12054            );
12055
12056            let start_point = Point::new(start_row.0, 0);
12057            let end_point = Point::new(
12058                end_row.previous_row().0,
12059                buffer.line_len(end_row.previous_row()),
12060            );
12061            let text = buffer
12062                .text_for_range(start_point..end_point)
12063                .collect::<String>();
12064
12065            let LineManipulationResult {
12066                new_text,
12067                line_count_before,
12068                line_count_after,
12069            } = manipulate(&text);
12070
12071            edits.push((start_point..end_point, new_text));
12072
12073            // Selections must change based on added and removed line count
12074            let start_row =
12075                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12076            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12077            new_selections.push(Selection {
12078                id: selection.id,
12079                start: start_row,
12080                end: end_row,
12081                goal: SelectionGoal::None,
12082                reversed: selection.reversed,
12083            });
12084
12085            if line_count_after > line_count_before {
12086                added_lines += line_count_after - line_count_before;
12087            } else if line_count_before > line_count_after {
12088                removed_lines += line_count_before - line_count_after;
12089            }
12090        }
12091
12092        self.transact(window, cx, |this, window, cx| {
12093            let buffer = this.buffer.update(cx, |buffer, cx| {
12094                buffer.edit(edits, None, cx);
12095                buffer.snapshot(cx)
12096            });
12097
12098            // Recalculate offsets on newly edited buffer
12099            let new_selections = new_selections
12100                .iter()
12101                .map(|s| {
12102                    let start_point = Point::new(s.start.0, 0);
12103                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12104                    Selection {
12105                        id: s.id,
12106                        start: buffer.point_to_offset(start_point),
12107                        end: buffer.point_to_offset(end_point),
12108                        goal: s.goal,
12109                        reversed: s.reversed,
12110                    }
12111                })
12112                .collect();
12113
12114            this.change_selections(Default::default(), window, cx, |s| {
12115                s.select(new_selections);
12116            });
12117
12118            this.request_autoscroll(Autoscroll::fit(), cx);
12119        });
12120    }
12121
12122    fn manipulate_immutable_lines<Fn>(
12123        &mut self,
12124        window: &mut Window,
12125        cx: &mut Context<Self>,
12126        mut callback: Fn,
12127    ) where
12128        Fn: FnMut(&mut Vec<&str>),
12129    {
12130        self.manipulate_lines(window, cx, |text| {
12131            let mut lines: Vec<&str> = text.split('\n').collect();
12132            let line_count_before = lines.len();
12133
12134            callback(&mut lines);
12135
12136            LineManipulationResult {
12137                new_text: lines.join("\n"),
12138                line_count_before,
12139                line_count_after: lines.len(),
12140            }
12141        });
12142    }
12143
12144    fn manipulate_mutable_lines<Fn>(
12145        &mut self,
12146        window: &mut Window,
12147        cx: &mut Context<Self>,
12148        mut callback: Fn,
12149    ) where
12150        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12151    {
12152        self.manipulate_lines(window, cx, |text| {
12153            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12154            let line_count_before = lines.len();
12155
12156            callback(&mut lines);
12157
12158            LineManipulationResult {
12159                new_text: lines.join("\n"),
12160                line_count_before,
12161                line_count_after: lines.len(),
12162            }
12163        });
12164    }
12165
12166    pub fn convert_indentation_to_spaces(
12167        &mut self,
12168        _: &ConvertIndentationToSpaces,
12169        window: &mut Window,
12170        cx: &mut Context<Self>,
12171    ) {
12172        let settings = self.buffer.read(cx).language_settings(cx);
12173        let tab_size = settings.tab_size.get() as usize;
12174
12175        self.manipulate_mutable_lines(window, cx, |lines| {
12176            // Allocates a reasonably sized scratch buffer once for the whole loop
12177            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12178            // Avoids recomputing spaces that could be inserted many times
12179            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12180                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12181                .collect();
12182
12183            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12184                let mut chars = line.as_ref().chars();
12185                let mut col = 0;
12186                let mut changed = false;
12187
12188                for ch in chars.by_ref() {
12189                    match ch {
12190                        ' ' => {
12191                            reindented_line.push(' ');
12192                            col += 1;
12193                        }
12194                        '\t' => {
12195                            // \t are converted to spaces depending on the current column
12196                            let spaces_len = tab_size - (col % tab_size);
12197                            reindented_line.extend(&space_cache[spaces_len - 1]);
12198                            col += spaces_len;
12199                            changed = true;
12200                        }
12201                        _ => {
12202                            // If we dont append before break, the character is consumed
12203                            reindented_line.push(ch);
12204                            break;
12205                        }
12206                    }
12207                }
12208
12209                if !changed {
12210                    reindented_line.clear();
12211                    continue;
12212                }
12213                // Append the rest of the line and replace old reference with new one
12214                reindented_line.extend(chars);
12215                *line = Cow::Owned(reindented_line.clone());
12216                reindented_line.clear();
12217            }
12218        });
12219    }
12220
12221    pub fn convert_indentation_to_tabs(
12222        &mut self,
12223        _: &ConvertIndentationToTabs,
12224        window: &mut Window,
12225        cx: &mut Context<Self>,
12226    ) {
12227        let settings = self.buffer.read(cx).language_settings(cx);
12228        let tab_size = settings.tab_size.get() as usize;
12229
12230        self.manipulate_mutable_lines(window, cx, |lines| {
12231            // Allocates a reasonably sized buffer once for the whole loop
12232            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12233            // Avoids recomputing spaces that could be inserted many times
12234            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12235                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12236                .collect();
12237
12238            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12239                let mut chars = line.chars();
12240                let mut spaces_count = 0;
12241                let mut first_non_indent_char = None;
12242                let mut changed = false;
12243
12244                for ch in chars.by_ref() {
12245                    match ch {
12246                        ' ' => {
12247                            // Keep track of spaces. Append \t when we reach tab_size
12248                            spaces_count += 1;
12249                            changed = true;
12250                            if spaces_count == tab_size {
12251                                reindented_line.push('\t');
12252                                spaces_count = 0;
12253                            }
12254                        }
12255                        '\t' => {
12256                            reindented_line.push('\t');
12257                            spaces_count = 0;
12258                        }
12259                        _ => {
12260                            // Dont append it yet, we might have remaining spaces
12261                            first_non_indent_char = Some(ch);
12262                            break;
12263                        }
12264                    }
12265                }
12266
12267                if !changed {
12268                    reindented_line.clear();
12269                    continue;
12270                }
12271                // Remaining spaces that didn't make a full tab stop
12272                if spaces_count > 0 {
12273                    reindented_line.extend(&space_cache[spaces_count - 1]);
12274                }
12275                // If we consume an extra character that was not indentation, add it back
12276                if let Some(extra_char) = first_non_indent_char {
12277                    reindented_line.push(extra_char);
12278                }
12279                // Append the rest of the line and replace old reference with new one
12280                reindented_line.extend(chars);
12281                *line = Cow::Owned(reindented_line.clone());
12282                reindented_line.clear();
12283            }
12284        });
12285    }
12286
12287    pub fn convert_to_upper_case(
12288        &mut self,
12289        _: &ConvertToUpperCase,
12290        window: &mut Window,
12291        cx: &mut Context<Self>,
12292    ) {
12293        self.manipulate_text(window, cx, |text| text.to_uppercase())
12294    }
12295
12296    pub fn convert_to_lower_case(
12297        &mut self,
12298        _: &ConvertToLowerCase,
12299        window: &mut Window,
12300        cx: &mut Context<Self>,
12301    ) {
12302        self.manipulate_text(window, cx, |text| text.to_lowercase())
12303    }
12304
12305    pub fn convert_to_title_case(
12306        &mut self,
12307        _: &ConvertToTitleCase,
12308        window: &mut Window,
12309        cx: &mut Context<Self>,
12310    ) {
12311        self.manipulate_text(window, cx, |text| {
12312            text.split('\n')
12313                .map(|line| line.to_case(Case::Title))
12314                .join("\n")
12315        })
12316    }
12317
12318    pub fn convert_to_snake_case(
12319        &mut self,
12320        _: &ConvertToSnakeCase,
12321        window: &mut Window,
12322        cx: &mut Context<Self>,
12323    ) {
12324        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12325    }
12326
12327    pub fn convert_to_kebab_case(
12328        &mut self,
12329        _: &ConvertToKebabCase,
12330        window: &mut Window,
12331        cx: &mut Context<Self>,
12332    ) {
12333        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12334    }
12335
12336    pub fn convert_to_upper_camel_case(
12337        &mut self,
12338        _: &ConvertToUpperCamelCase,
12339        window: &mut Window,
12340        cx: &mut Context<Self>,
12341    ) {
12342        self.manipulate_text(window, cx, |text| {
12343            text.split('\n')
12344                .map(|line| line.to_case(Case::UpperCamel))
12345                .join("\n")
12346        })
12347    }
12348
12349    pub fn convert_to_lower_camel_case(
12350        &mut self,
12351        _: &ConvertToLowerCamelCase,
12352        window: &mut Window,
12353        cx: &mut Context<Self>,
12354    ) {
12355        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12356    }
12357
12358    pub fn convert_to_opposite_case(
12359        &mut self,
12360        _: &ConvertToOppositeCase,
12361        window: &mut Window,
12362        cx: &mut Context<Self>,
12363    ) {
12364        self.manipulate_text(window, cx, |text| {
12365            text.chars()
12366                .fold(String::with_capacity(text.len()), |mut t, c| {
12367                    if c.is_uppercase() {
12368                        t.extend(c.to_lowercase());
12369                    } else {
12370                        t.extend(c.to_uppercase());
12371                    }
12372                    t
12373                })
12374        })
12375    }
12376
12377    pub fn convert_to_sentence_case(
12378        &mut self,
12379        _: &ConvertToSentenceCase,
12380        window: &mut Window,
12381        cx: &mut Context<Self>,
12382    ) {
12383        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12384    }
12385
12386    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12387        self.manipulate_text(window, cx, |text| {
12388            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12389            if has_upper_case_characters {
12390                text.to_lowercase()
12391            } else {
12392                text.to_uppercase()
12393            }
12394        })
12395    }
12396
12397    pub fn convert_to_rot13(
12398        &mut self,
12399        _: &ConvertToRot13,
12400        window: &mut Window,
12401        cx: &mut Context<Self>,
12402    ) {
12403        self.manipulate_text(window, cx, |text| {
12404            text.chars()
12405                .map(|c| match c {
12406                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12407                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12408                    _ => c,
12409                })
12410                .collect()
12411        })
12412    }
12413
12414    pub fn convert_to_rot47(
12415        &mut self,
12416        _: &ConvertToRot47,
12417        window: &mut Window,
12418        cx: &mut Context<Self>,
12419    ) {
12420        self.manipulate_text(window, cx, |text| {
12421            text.chars()
12422                .map(|c| {
12423                    let code_point = c as u32;
12424                    if code_point >= 33 && code_point <= 126 {
12425                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12426                    }
12427                    c
12428                })
12429                .collect()
12430        })
12431    }
12432
12433    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12434    where
12435        Fn: FnMut(&str) -> String,
12436    {
12437        let buffer = self.buffer.read(cx).snapshot(cx);
12438
12439        let mut new_selections = Vec::new();
12440        let mut edits = Vec::new();
12441        let mut selection_adjustment = 0isize;
12442
12443        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12444            let selection_is_empty = selection.is_empty();
12445
12446            let (start, end) = if selection_is_empty {
12447                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12448                (word_range.start, word_range.end)
12449            } else {
12450                (
12451                    buffer.point_to_offset(selection.start),
12452                    buffer.point_to_offset(selection.end),
12453                )
12454            };
12455
12456            let text = buffer.text_for_range(start..end).collect::<String>();
12457            let old_length = text.len() as isize;
12458            let text = callback(&text);
12459
12460            new_selections.push(Selection {
12461                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12462                end: MultiBufferOffset(
12463                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12464                ),
12465                goal: SelectionGoal::None,
12466                id: selection.id,
12467                reversed: selection.reversed,
12468            });
12469
12470            selection_adjustment += old_length - text.len() as isize;
12471
12472            edits.push((start..end, text));
12473        }
12474
12475        self.transact(window, cx, |this, window, cx| {
12476            this.buffer.update(cx, |buffer, cx| {
12477                buffer.edit(edits, None, cx);
12478            });
12479
12480            this.change_selections(Default::default(), window, cx, |s| {
12481                s.select(new_selections);
12482            });
12483
12484            this.request_autoscroll(Autoscroll::fit(), cx);
12485        });
12486    }
12487
12488    pub fn move_selection_on_drop(
12489        &mut self,
12490        selection: &Selection<Anchor>,
12491        target: DisplayPoint,
12492        is_cut: bool,
12493        window: &mut Window,
12494        cx: &mut Context<Self>,
12495    ) {
12496        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12497        let buffer = display_map.buffer_snapshot();
12498        let mut edits = Vec::new();
12499        let insert_point = display_map
12500            .clip_point(target, Bias::Left)
12501            .to_point(&display_map);
12502        let text = buffer
12503            .text_for_range(selection.start..selection.end)
12504            .collect::<String>();
12505        if is_cut {
12506            edits.push(((selection.start..selection.end), String::new()));
12507        }
12508        let insert_anchor = buffer.anchor_before(insert_point);
12509        edits.push(((insert_anchor..insert_anchor), text));
12510        let last_edit_start = insert_anchor.bias_left(buffer);
12511        let last_edit_end = insert_anchor.bias_right(buffer);
12512        self.transact(window, cx, |this, window, cx| {
12513            this.buffer.update(cx, |buffer, cx| {
12514                buffer.edit(edits, None, cx);
12515            });
12516            this.change_selections(Default::default(), window, cx, |s| {
12517                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12518            });
12519        });
12520    }
12521
12522    pub fn clear_selection_drag_state(&mut self) {
12523        self.selection_drag_state = SelectionDragState::None;
12524    }
12525
12526    pub fn duplicate(
12527        &mut self,
12528        upwards: bool,
12529        whole_lines: bool,
12530        window: &mut Window,
12531        cx: &mut Context<Self>,
12532    ) {
12533        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12534
12535        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12536        let buffer = display_map.buffer_snapshot();
12537        let selections = self.selections.all::<Point>(&display_map);
12538
12539        let mut edits = Vec::new();
12540        let mut selections_iter = selections.iter().peekable();
12541        while let Some(selection) = selections_iter.next() {
12542            let mut rows = selection.spanned_rows(false, &display_map);
12543            // duplicate line-wise
12544            if whole_lines || selection.start == selection.end {
12545                // Avoid duplicating the same lines twice.
12546                while let Some(next_selection) = selections_iter.peek() {
12547                    let next_rows = next_selection.spanned_rows(false, &display_map);
12548                    if next_rows.start < rows.end {
12549                        rows.end = next_rows.end;
12550                        selections_iter.next().unwrap();
12551                    } else {
12552                        break;
12553                    }
12554                }
12555
12556                // Copy the text from the selected row region and splice it either at the start
12557                // or end of the region.
12558                let start = Point::new(rows.start.0, 0);
12559                let end = Point::new(
12560                    rows.end.previous_row().0,
12561                    buffer.line_len(rows.end.previous_row()),
12562                );
12563
12564                let mut text = buffer.text_for_range(start..end).collect::<String>();
12565
12566                let insert_location = if upwards {
12567                    // When duplicating upward, we need to insert before the current line.
12568                    // If we're on the last line and it doesn't end with a newline,
12569                    // we need to add a newline before the duplicated content.
12570                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12571                        && buffer.max_point().column > 0
12572                        && !text.ends_with('\n');
12573
12574                    if needs_leading_newline {
12575                        text.insert(0, '\n');
12576                        end
12577                    } else {
12578                        text.push('\n');
12579                        Point::new(rows.start.0, 0)
12580                    }
12581                } else {
12582                    text.push('\n');
12583                    start
12584                };
12585                edits.push((insert_location..insert_location, text));
12586            } else {
12587                // duplicate character-wise
12588                let start = selection.start;
12589                let end = selection.end;
12590                let text = buffer.text_for_range(start..end).collect::<String>();
12591                edits.push((selection.end..selection.end, text));
12592            }
12593        }
12594
12595        self.transact(window, cx, |this, window, cx| {
12596            this.buffer.update(cx, |buffer, cx| {
12597                buffer.edit(edits, None, cx);
12598            });
12599
12600            // When duplicating upward with whole lines, move the cursor to the duplicated line
12601            if upwards && whole_lines {
12602                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12603
12604                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12605                    let mut new_ranges = Vec::new();
12606                    let selections = s.all::<Point>(&display_map);
12607                    let mut selections_iter = selections.iter().peekable();
12608
12609                    while let Some(first_selection) = selections_iter.next() {
12610                        // Group contiguous selections together to find the total row span
12611                        let mut group_selections = vec![first_selection];
12612                        let mut rows = first_selection.spanned_rows(false, &display_map);
12613
12614                        while let Some(next_selection) = selections_iter.peek() {
12615                            let next_rows = next_selection.spanned_rows(false, &display_map);
12616                            if next_rows.start < rows.end {
12617                                rows.end = next_rows.end;
12618                                group_selections.push(selections_iter.next().unwrap());
12619                            } else {
12620                                break;
12621                            }
12622                        }
12623
12624                        let row_count = rows.end.0 - rows.start.0;
12625
12626                        // Move all selections in this group up by the total number of duplicated rows
12627                        for selection in group_selections {
12628                            let new_start = Point::new(
12629                                selection.start.row.saturating_sub(row_count),
12630                                selection.start.column,
12631                            );
12632
12633                            let new_end = Point::new(
12634                                selection.end.row.saturating_sub(row_count),
12635                                selection.end.column,
12636                            );
12637
12638                            new_ranges.push(new_start..new_end);
12639                        }
12640                    }
12641
12642                    s.select_ranges(new_ranges);
12643                });
12644            }
12645
12646            this.request_autoscroll(Autoscroll::fit(), cx);
12647        });
12648    }
12649
12650    pub fn duplicate_line_up(
12651        &mut self,
12652        _: &DuplicateLineUp,
12653        window: &mut Window,
12654        cx: &mut Context<Self>,
12655    ) {
12656        self.duplicate(true, true, window, cx);
12657    }
12658
12659    pub fn duplicate_line_down(
12660        &mut self,
12661        _: &DuplicateLineDown,
12662        window: &mut Window,
12663        cx: &mut Context<Self>,
12664    ) {
12665        self.duplicate(false, true, window, cx);
12666    }
12667
12668    pub fn duplicate_selection(
12669        &mut self,
12670        _: &DuplicateSelection,
12671        window: &mut Window,
12672        cx: &mut Context<Self>,
12673    ) {
12674        self.duplicate(false, false, window, cx);
12675    }
12676
12677    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
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 before the line preceding the row range
12706            if start_row.0 > 0 {
12707                let range_to_move = Point::new(
12708                    start_row.previous_row().0,
12709                    buffer.line_len(start_row.previous_row()),
12710                )
12711                    ..Point::new(
12712                        end_row.previous_row().0,
12713                        buffer.line_len(end_row.previous_row()),
12714                    );
12715                let insertion_point = display_map
12716                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12717                    .0;
12718
12719                // Don't move lines across excerpts
12720                if buffer
12721                    .excerpt_containing(insertion_point..range_to_move.end)
12722                    .is_some()
12723                {
12724                    let text = buffer
12725                        .text_for_range(range_to_move.clone())
12726                        .flat_map(|s| s.chars())
12727                        .skip(1)
12728                        .chain(['\n'])
12729                        .collect::<String>();
12730
12731                    edits.push((
12732                        buffer.anchor_after(range_to_move.start)
12733                            ..buffer.anchor_before(range_to_move.end),
12734                        String::new(),
12735                    ));
12736                    let insertion_anchor = buffer.anchor_after(insertion_point);
12737                    edits.push((insertion_anchor..insertion_anchor, text));
12738
12739                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12740
12741                    // Move selections up
12742                    new_selections.extend(contiguous_row_selections.drain(..).map(
12743                        |mut selection| {
12744                            selection.start.row -= row_delta;
12745                            selection.end.row -= row_delta;
12746                            selection
12747                        },
12748                    ));
12749
12750                    // Move folds up
12751                    unfold_ranges.push(range_to_move.clone());
12752                    for fold in display_map.folds_in_range(
12753                        buffer.anchor_before(range_to_move.start)
12754                            ..buffer.anchor_after(range_to_move.end),
12755                    ) {
12756                        let mut start = fold.range.start.to_point(&buffer);
12757                        let mut end = fold.range.end.to_point(&buffer);
12758                        start.row -= row_delta;
12759                        end.row -= row_delta;
12760                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12761                    }
12762                }
12763            }
12764
12765            // If we didn't move line(s), preserve the existing selections
12766            new_selections.append(&mut contiguous_row_selections);
12767        }
12768
12769        self.transact(window, cx, |this, window, cx| {
12770            this.unfold_ranges(&unfold_ranges, true, true, cx);
12771            this.buffer.update(cx, |buffer, cx| {
12772                for (range, text) in edits {
12773                    buffer.edit([(range, text)], None, cx);
12774                }
12775            });
12776            this.fold_creases(refold_creases, true, window, cx);
12777            this.change_selections(Default::default(), window, cx, |s| {
12778                s.select(new_selections);
12779            })
12780        });
12781    }
12782
12783    pub fn move_line_down(
12784        &mut self,
12785        _: &MoveLineDown,
12786        window: &mut Window,
12787        cx: &mut Context<Self>,
12788    ) {
12789        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12790        if self.mode.is_single_line() {
12791            cx.propagate();
12792            return;
12793        }
12794
12795        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12796        let buffer = self.buffer.read(cx).snapshot(cx);
12797
12798        let mut edits = Vec::new();
12799        let mut unfold_ranges = Vec::new();
12800        let mut refold_creases = Vec::new();
12801
12802        let selections = self.selections.all::<Point>(&display_map);
12803        let mut selections = selections.iter().peekable();
12804        let mut contiguous_row_selections = Vec::new();
12805        let mut new_selections = Vec::new();
12806
12807        while let Some(selection) = selections.next() {
12808            // Find all the selections that span a contiguous row range
12809            let (start_row, end_row) = consume_contiguous_rows(
12810                &mut contiguous_row_selections,
12811                selection,
12812                &display_map,
12813                &mut selections,
12814            );
12815
12816            // Move the text spanned by the row range to be after the last line of the row range
12817            if end_row.0 <= buffer.max_point().row {
12818                let range_to_move =
12819                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
12820                let insertion_point = display_map
12821                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
12822                    .0;
12823
12824                // Don't move lines across excerpt boundaries
12825                if buffer
12826                    .excerpt_containing(range_to_move.start..insertion_point)
12827                    .is_some()
12828                {
12829                    let mut text = String::from("\n");
12830                    text.extend(buffer.text_for_range(range_to_move.clone()));
12831                    text.pop(); // Drop trailing newline
12832                    edits.push((
12833                        buffer.anchor_after(range_to_move.start)
12834                            ..buffer.anchor_before(range_to_move.end),
12835                        String::new(),
12836                    ));
12837                    let insertion_anchor = buffer.anchor_after(insertion_point);
12838                    edits.push((insertion_anchor..insertion_anchor, text));
12839
12840                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
12841
12842                    // Move selections down
12843                    new_selections.extend(contiguous_row_selections.drain(..).map(
12844                        |mut selection| {
12845                            selection.start.row += row_delta;
12846                            selection.end.row += row_delta;
12847                            selection
12848                        },
12849                    ));
12850
12851                    // Move folds down
12852                    unfold_ranges.push(range_to_move.clone());
12853                    for fold in display_map.folds_in_range(
12854                        buffer.anchor_before(range_to_move.start)
12855                            ..buffer.anchor_after(range_to_move.end),
12856                    ) {
12857                        let mut start = fold.range.start.to_point(&buffer);
12858                        let mut end = fold.range.end.to_point(&buffer);
12859                        start.row += row_delta;
12860                        end.row += row_delta;
12861                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12862                    }
12863                }
12864            }
12865
12866            // If we didn't move line(s), preserve the existing selections
12867            new_selections.append(&mut contiguous_row_selections);
12868        }
12869
12870        self.transact(window, cx, |this, window, cx| {
12871            this.unfold_ranges(&unfold_ranges, true, true, cx);
12872            this.buffer.update(cx, |buffer, cx| {
12873                for (range, text) in edits {
12874                    buffer.edit([(range, text)], None, cx);
12875                }
12876            });
12877            this.fold_creases(refold_creases, true, window, cx);
12878            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
12879        });
12880    }
12881
12882    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
12883        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12884        let text_layout_details = &self.text_layout_details(window, cx);
12885        self.transact(window, cx, |this, window, cx| {
12886            let edits = this.change_selections(Default::default(), window, cx, |s| {
12887                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
12888                s.move_with(|display_map, selection| {
12889                    if !selection.is_empty() {
12890                        return;
12891                    }
12892
12893                    let mut head = selection.head();
12894                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
12895                    if head.column() == display_map.line_len(head.row()) {
12896                        transpose_offset = display_map
12897                            .buffer_snapshot()
12898                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
12899                    }
12900
12901                    if transpose_offset == MultiBufferOffset(0) {
12902                        return;
12903                    }
12904
12905                    *head.column_mut() += 1;
12906                    head = display_map.clip_point(head, Bias::Right);
12907                    let goal = SelectionGoal::HorizontalPosition(
12908                        display_map
12909                            .x_for_display_point(head, text_layout_details)
12910                            .into(),
12911                    );
12912                    selection.collapse_to(head, goal);
12913
12914                    let transpose_start = display_map
12915                        .buffer_snapshot()
12916                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
12917                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
12918                        let transpose_end = display_map
12919                            .buffer_snapshot()
12920                            .clip_offset(transpose_offset + 1usize, Bias::Right);
12921                        if let Some(ch) = display_map
12922                            .buffer_snapshot()
12923                            .chars_at(transpose_start)
12924                            .next()
12925                        {
12926                            edits.push((transpose_start..transpose_offset, String::new()));
12927                            edits.push((transpose_end..transpose_end, ch.to_string()));
12928                        }
12929                    }
12930                });
12931                edits
12932            });
12933            this.buffer
12934                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
12935            let selections = this
12936                .selections
12937                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
12938            this.change_selections(Default::default(), window, cx, |s| {
12939                s.select(selections);
12940            });
12941        });
12942    }
12943
12944    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
12945        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12946        if self.mode.is_single_line() {
12947            cx.propagate();
12948            return;
12949        }
12950
12951        self.rewrap_impl(RewrapOptions::default(), cx)
12952    }
12953
12954    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
12955        let buffer = self.buffer.read(cx).snapshot(cx);
12956        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
12957
12958        #[derive(Clone, Debug, PartialEq)]
12959        enum CommentFormat {
12960            /// single line comment, with prefix for line
12961            Line(String),
12962            /// single line within a block comment, with prefix for line
12963            BlockLine(String),
12964            /// a single line of a block comment that includes the initial delimiter
12965            BlockCommentWithStart(BlockCommentConfig),
12966            /// a single line of a block comment that includes the ending delimiter
12967            BlockCommentWithEnd(BlockCommentConfig),
12968        }
12969
12970        // Split selections to respect paragraph, indent, and comment prefix boundaries.
12971        let wrap_ranges = selections.into_iter().flat_map(|selection| {
12972            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
12973                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
12974                .peekable();
12975
12976            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
12977                row
12978            } else {
12979                return Vec::new();
12980            };
12981
12982            let language_settings = buffer.language_settings_at(selection.head(), cx);
12983            let language_scope = buffer.language_scope_at(selection.head());
12984
12985            let indent_and_prefix_for_row =
12986                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
12987                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
12988                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
12989                        &language_scope
12990                    {
12991                        let indent_end = Point::new(row, indent.len);
12992                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12993                        let line_text_after_indent = buffer
12994                            .text_for_range(indent_end..line_end)
12995                            .collect::<String>();
12996
12997                        let is_within_comment_override = buffer
12998                            .language_scope_at(indent_end)
12999                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13000                        let comment_delimiters = if is_within_comment_override {
13001                            // we are within a comment syntax node, but we don't
13002                            // yet know what kind of comment: block, doc or line
13003                            match (
13004                                language_scope.documentation_comment(),
13005                                language_scope.block_comment(),
13006                            ) {
13007                                (Some(config), _) | (_, Some(config))
13008                                    if buffer.contains_str_at(indent_end, &config.start) =>
13009                                {
13010                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13011                                }
13012                                (Some(config), _) | (_, Some(config))
13013                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13014                                {
13015                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13016                                }
13017                                (Some(config), _) | (_, Some(config))
13018                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13019                                {
13020                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13021                                }
13022                                (_, _) => language_scope
13023                                    .line_comment_prefixes()
13024                                    .iter()
13025                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13026                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13027                            }
13028                        } else {
13029                            // we not in an overridden comment node, but we may
13030                            // be within a non-overridden line comment node
13031                            language_scope
13032                                .line_comment_prefixes()
13033                                .iter()
13034                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13035                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13036                        };
13037
13038                        let rewrap_prefix = language_scope
13039                            .rewrap_prefixes()
13040                            .iter()
13041                            .find_map(|prefix_regex| {
13042                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13043                                    if mat.start() == 0 {
13044                                        Some(mat.as_str().to_string())
13045                                    } else {
13046                                        None
13047                                    }
13048                                })
13049                            })
13050                            .flatten();
13051                        (comment_delimiters, rewrap_prefix)
13052                    } else {
13053                        (None, None)
13054                    };
13055                    (indent, comment_prefix, rewrap_prefix)
13056                };
13057
13058            let mut ranges = Vec::new();
13059            let from_empty_selection = selection.is_empty();
13060
13061            let mut current_range_start = first_row;
13062            let mut prev_row = first_row;
13063            let (
13064                mut current_range_indent,
13065                mut current_range_comment_delimiters,
13066                mut current_range_rewrap_prefix,
13067            ) = indent_and_prefix_for_row(first_row);
13068
13069            for row in non_blank_rows_iter.skip(1) {
13070                let has_paragraph_break = row > prev_row + 1;
13071
13072                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13073                    indent_and_prefix_for_row(row);
13074
13075                let has_indent_change = row_indent != current_range_indent;
13076                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13077
13078                let has_boundary_change = has_comment_change
13079                    || row_rewrap_prefix.is_some()
13080                    || (has_indent_change && current_range_comment_delimiters.is_some());
13081
13082                if has_paragraph_break || has_boundary_change {
13083                    ranges.push((
13084                        language_settings.clone(),
13085                        Point::new(current_range_start, 0)
13086                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13087                        current_range_indent,
13088                        current_range_comment_delimiters.clone(),
13089                        current_range_rewrap_prefix.clone(),
13090                        from_empty_selection,
13091                    ));
13092                    current_range_start = row;
13093                    current_range_indent = row_indent;
13094                    current_range_comment_delimiters = row_comment_delimiters;
13095                    current_range_rewrap_prefix = row_rewrap_prefix;
13096                }
13097                prev_row = row;
13098            }
13099
13100            ranges.push((
13101                language_settings.clone(),
13102                Point::new(current_range_start, 0)
13103                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13104                current_range_indent,
13105                current_range_comment_delimiters,
13106                current_range_rewrap_prefix,
13107                from_empty_selection,
13108            ));
13109
13110            ranges
13111        });
13112
13113        let mut edits = Vec::new();
13114        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13115
13116        for (
13117            language_settings,
13118            wrap_range,
13119            mut indent_size,
13120            comment_prefix,
13121            rewrap_prefix,
13122            from_empty_selection,
13123        ) in wrap_ranges
13124        {
13125            let mut start_row = wrap_range.start.row;
13126            let mut end_row = wrap_range.end.row;
13127
13128            // Skip selections that overlap with a range that has already been rewrapped.
13129            let selection_range = start_row..end_row;
13130            if rewrapped_row_ranges
13131                .iter()
13132                .any(|range| range.overlaps(&selection_range))
13133            {
13134                continue;
13135            }
13136
13137            let tab_size = language_settings.tab_size;
13138
13139            let (line_prefix, inside_comment) = match &comment_prefix {
13140                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13141                    (Some(prefix.as_str()), true)
13142                }
13143                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13144                    (Some(prefix.as_ref()), true)
13145                }
13146                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13147                    start: _,
13148                    end: _,
13149                    prefix,
13150                    tab_size,
13151                })) => {
13152                    indent_size.len += tab_size;
13153                    (Some(prefix.as_ref()), true)
13154                }
13155                None => (None, false),
13156            };
13157            let indent_prefix = indent_size.chars().collect::<String>();
13158            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13159
13160            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13161                RewrapBehavior::InComments => inside_comment,
13162                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13163                RewrapBehavior::Anywhere => true,
13164            };
13165
13166            let should_rewrap = options.override_language_settings
13167                || allow_rewrap_based_on_language
13168                || self.hard_wrap.is_some();
13169            if !should_rewrap {
13170                continue;
13171            }
13172
13173            if from_empty_selection {
13174                'expand_upwards: while start_row > 0 {
13175                    let prev_row = start_row - 1;
13176                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13177                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13178                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13179                    {
13180                        start_row = prev_row;
13181                    } else {
13182                        break 'expand_upwards;
13183                    }
13184                }
13185
13186                'expand_downwards: while end_row < buffer.max_point().row {
13187                    let next_row = end_row + 1;
13188                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13189                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13190                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13191                    {
13192                        end_row = next_row;
13193                    } else {
13194                        break 'expand_downwards;
13195                    }
13196                }
13197            }
13198
13199            let start = Point::new(start_row, 0);
13200            let start_offset = ToOffset::to_offset(&start, &buffer);
13201            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13202            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13203            let mut first_line_delimiter = None;
13204            let mut last_line_delimiter = None;
13205            let Some(lines_without_prefixes) = selection_text
13206                .lines()
13207                .enumerate()
13208                .map(|(ix, line)| {
13209                    let line_trimmed = line.trim_start();
13210                    if rewrap_prefix.is_some() && ix > 0 {
13211                        Ok(line_trimmed)
13212                    } else if let Some(
13213                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13214                            start,
13215                            prefix,
13216                            end,
13217                            tab_size,
13218                        })
13219                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13220                            start,
13221                            prefix,
13222                            end,
13223                            tab_size,
13224                        }),
13225                    ) = &comment_prefix
13226                    {
13227                        let line_trimmed = line_trimmed
13228                            .strip_prefix(start.as_ref())
13229                            .map(|s| {
13230                                let mut indent_size = indent_size;
13231                                indent_size.len -= tab_size;
13232                                let indent_prefix: String = indent_size.chars().collect();
13233                                first_line_delimiter = Some((indent_prefix, start));
13234                                s.trim_start()
13235                            })
13236                            .unwrap_or(line_trimmed);
13237                        let line_trimmed = line_trimmed
13238                            .strip_suffix(end.as_ref())
13239                            .map(|s| {
13240                                last_line_delimiter = Some(end);
13241                                s.trim_end()
13242                            })
13243                            .unwrap_or(line_trimmed);
13244                        let line_trimmed = line_trimmed
13245                            .strip_prefix(prefix.as_ref())
13246                            .unwrap_or(line_trimmed);
13247                        Ok(line_trimmed)
13248                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13249                        line_trimmed.strip_prefix(prefix).with_context(|| {
13250                            format!("line did not start with prefix {prefix:?}: {line:?}")
13251                        })
13252                    } else {
13253                        line_trimmed
13254                            .strip_prefix(&line_prefix.trim_start())
13255                            .with_context(|| {
13256                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13257                            })
13258                    }
13259                })
13260                .collect::<Result<Vec<_>, _>>()
13261                .log_err()
13262            else {
13263                continue;
13264            };
13265
13266            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13267                buffer
13268                    .language_settings_at(Point::new(start_row, 0), cx)
13269                    .preferred_line_length as usize
13270            });
13271
13272            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13273                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13274            } else {
13275                line_prefix.clone()
13276            };
13277
13278            let wrapped_text = {
13279                let mut wrapped_text = wrap_with_prefix(
13280                    line_prefix,
13281                    subsequent_lines_prefix,
13282                    lines_without_prefixes.join("\n"),
13283                    wrap_column,
13284                    tab_size,
13285                    options.preserve_existing_whitespace,
13286                );
13287
13288                if let Some((indent, delimiter)) = first_line_delimiter {
13289                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13290                }
13291                if let Some(last_line) = last_line_delimiter {
13292                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13293                }
13294
13295                wrapped_text
13296            };
13297
13298            // TODO: should always use char-based diff while still supporting cursor behavior that
13299            // matches vim.
13300            let mut diff_options = DiffOptions::default();
13301            if options.override_language_settings {
13302                diff_options.max_word_diff_len = 0;
13303                diff_options.max_word_diff_line_count = 0;
13304            } else {
13305                diff_options.max_word_diff_len = usize::MAX;
13306                diff_options.max_word_diff_line_count = usize::MAX;
13307            }
13308
13309            for (old_range, new_text) in
13310                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13311            {
13312                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13313                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13314                edits.push((edit_start..edit_end, new_text));
13315            }
13316
13317            rewrapped_row_ranges.push(start_row..=end_row);
13318        }
13319
13320        self.buffer
13321            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13322    }
13323
13324    pub fn cut_common(
13325        &mut self,
13326        cut_no_selection_line: bool,
13327        window: &mut Window,
13328        cx: &mut Context<Self>,
13329    ) -> ClipboardItem {
13330        let mut text = String::new();
13331        let buffer = self.buffer.read(cx).snapshot(cx);
13332        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13333        let mut clipboard_selections = Vec::with_capacity(selections.len());
13334        {
13335            let max_point = buffer.max_point();
13336            let mut is_first = true;
13337            let mut prev_selection_was_entire_line = false;
13338            for selection in &mut selections {
13339                let is_entire_line =
13340                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13341                if is_entire_line {
13342                    selection.start = Point::new(selection.start.row, 0);
13343                    if !selection.is_empty() && selection.end.column == 0 {
13344                        selection.end = cmp::min(max_point, selection.end);
13345                    } else {
13346                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13347                    }
13348                    selection.goal = SelectionGoal::None;
13349                }
13350                if is_first {
13351                    is_first = false;
13352                } else if !prev_selection_was_entire_line {
13353                    text += "\n";
13354                }
13355                prev_selection_was_entire_line = is_entire_line;
13356                let mut len = 0;
13357                for chunk in buffer.text_for_range(selection.start..selection.end) {
13358                    text.push_str(chunk);
13359                    len += chunk.len();
13360                }
13361
13362                clipboard_selections.push(ClipboardSelection::for_buffer(
13363                    len,
13364                    is_entire_line,
13365                    selection.range(),
13366                    &buffer,
13367                    self.project.as_ref(),
13368                    cx,
13369                ));
13370            }
13371        }
13372
13373        self.transact(window, cx, |this, window, cx| {
13374            this.change_selections(Default::default(), window, cx, |s| {
13375                s.select(selections);
13376            });
13377            this.insert("", window, cx);
13378        });
13379        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13380    }
13381
13382    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13383        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13384        let item = self.cut_common(true, window, cx);
13385        cx.write_to_clipboard(item);
13386    }
13387
13388    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13389        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13390        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13391            s.move_with(|snapshot, sel| {
13392                if sel.is_empty() {
13393                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13394                }
13395                if sel.is_empty() {
13396                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13397                }
13398            });
13399        });
13400        let item = self.cut_common(false, window, cx);
13401        cx.set_global(KillRing(item))
13402    }
13403
13404    pub fn kill_ring_yank(
13405        &mut self,
13406        _: &KillRingYank,
13407        window: &mut Window,
13408        cx: &mut Context<Self>,
13409    ) {
13410        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13411        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13412            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13413                (kill_ring.text().to_string(), kill_ring.metadata_json())
13414            } else {
13415                return;
13416            }
13417        } else {
13418            return;
13419        };
13420        self.do_paste(&text, metadata, false, window, cx);
13421    }
13422
13423    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13424        self.do_copy(true, cx);
13425    }
13426
13427    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13428        self.do_copy(false, cx);
13429    }
13430
13431    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13432        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13433        let buffer = self.buffer.read(cx).read(cx);
13434        let mut text = String::new();
13435
13436        let mut clipboard_selections = Vec::with_capacity(selections.len());
13437        {
13438            let max_point = buffer.max_point();
13439            let mut is_first = true;
13440            let mut prev_selection_was_entire_line = false;
13441            for selection in &selections {
13442                let mut start = selection.start;
13443                let mut end = selection.end;
13444                let is_entire_line = selection.is_empty() || self.selections.line_mode();
13445                let mut add_trailing_newline = false;
13446                if is_entire_line {
13447                    start = Point::new(start.row, 0);
13448                    let next_line_start = Point::new(end.row + 1, 0);
13449                    if next_line_start <= max_point {
13450                        end = next_line_start;
13451                    } else {
13452                        // We're on the last line without a trailing newline.
13453                        // Copy to the end of the line and add a newline afterwards.
13454                        end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13455                        add_trailing_newline = true;
13456                    }
13457                }
13458
13459                let mut trimmed_selections = Vec::new();
13460                if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13461                    let row = MultiBufferRow(start.row);
13462                    let first_indent = buffer.indent_size_for_line(row);
13463                    if first_indent.len == 0 || start.column > first_indent.len {
13464                        trimmed_selections.push(start..end);
13465                    } else {
13466                        trimmed_selections.push(
13467                            Point::new(row.0, first_indent.len)
13468                                ..Point::new(row.0, buffer.line_len(row)),
13469                        );
13470                        for row in start.row + 1..=end.row {
13471                            let mut line_len = buffer.line_len(MultiBufferRow(row));
13472                            if row == end.row {
13473                                line_len = end.column;
13474                            }
13475                            if line_len == 0 {
13476                                trimmed_selections
13477                                    .push(Point::new(row, 0)..Point::new(row, line_len));
13478                                continue;
13479                            }
13480                            let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13481                            if row_indent_size.len >= first_indent.len {
13482                                trimmed_selections.push(
13483                                    Point::new(row, first_indent.len)..Point::new(row, line_len),
13484                                );
13485                            } else {
13486                                trimmed_selections.clear();
13487                                trimmed_selections.push(start..end);
13488                                break;
13489                            }
13490                        }
13491                    }
13492                } else {
13493                    trimmed_selections.push(start..end);
13494                }
13495
13496                let is_multiline_trim = trimmed_selections.len() > 1;
13497                for trimmed_range in trimmed_selections {
13498                    if is_first {
13499                        is_first = false;
13500                    } else if is_multiline_trim || !prev_selection_was_entire_line {
13501                        text += "\n";
13502                    }
13503                    prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13504                    let mut len = 0;
13505                    for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13506                        text.push_str(chunk);
13507                        len += chunk.len();
13508                    }
13509                    if add_trailing_newline {
13510                        text.push('\n');
13511                        len += 1;
13512                    }
13513                    clipboard_selections.push(ClipboardSelection::for_buffer(
13514                        len,
13515                        is_entire_line,
13516                        trimmed_range,
13517                        &buffer,
13518                        self.project.as_ref(),
13519                        cx,
13520                    ));
13521                }
13522            }
13523        }
13524
13525        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13526            text,
13527            clipboard_selections,
13528        ));
13529    }
13530
13531    pub fn do_paste(
13532        &mut self,
13533        text: &String,
13534        clipboard_selections: Option<Vec<ClipboardSelection>>,
13535        handle_entire_lines: bool,
13536        window: &mut Window,
13537        cx: &mut Context<Self>,
13538    ) {
13539        if self.read_only(cx) {
13540            return;
13541        }
13542
13543        let clipboard_text = Cow::Borrowed(text.as_str());
13544
13545        self.transact(window, cx, |this, window, cx| {
13546            let had_active_edit_prediction = this.has_active_edit_prediction();
13547            let display_map = this.display_snapshot(cx);
13548            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13549            let cursor_offset = this
13550                .selections
13551                .last::<MultiBufferOffset>(&display_map)
13552                .head();
13553
13554            if let Some(mut clipboard_selections) = clipboard_selections {
13555                let all_selections_were_entire_line =
13556                    clipboard_selections.iter().all(|s| s.is_entire_line);
13557                let first_selection_indent_column =
13558                    clipboard_selections.first().map(|s| s.first_line_indent);
13559                if clipboard_selections.len() != old_selections.len() {
13560                    clipboard_selections.drain(..);
13561                }
13562                let mut auto_indent_on_paste = true;
13563
13564                this.buffer.update(cx, |buffer, cx| {
13565                    let snapshot = buffer.read(cx);
13566                    auto_indent_on_paste = snapshot
13567                        .language_settings_at(cursor_offset, cx)
13568                        .auto_indent_on_paste;
13569
13570                    let mut start_offset = 0;
13571                    let mut edits = Vec::new();
13572                    let mut original_indent_columns = Vec::new();
13573                    for (ix, selection) in old_selections.iter().enumerate() {
13574                        let to_insert;
13575                        let entire_line;
13576                        let original_indent_column;
13577                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13578                            let end_offset = start_offset + clipboard_selection.len;
13579                            to_insert = &clipboard_text[start_offset..end_offset];
13580                            entire_line = clipboard_selection.is_entire_line;
13581                            start_offset = if entire_line {
13582                                end_offset
13583                            } else {
13584                                end_offset + 1
13585                            };
13586                            original_indent_column = Some(clipboard_selection.first_line_indent);
13587                        } else {
13588                            to_insert = &*clipboard_text;
13589                            entire_line = all_selections_were_entire_line;
13590                            original_indent_column = first_selection_indent_column
13591                        }
13592
13593                        let (range, to_insert) =
13594                            if selection.is_empty() && handle_entire_lines && entire_line {
13595                                // If the corresponding selection was empty when this slice of the
13596                                // clipboard text was written, then the entire line containing the
13597                                // selection was copied. If this selection is also currently empty,
13598                                // then paste the line before the current line of the buffer.
13599                                let column = selection.start.to_point(&snapshot).column as usize;
13600                                let line_start = selection.start - column;
13601                                (line_start..line_start, Cow::Borrowed(to_insert))
13602                            } else {
13603                                let language = snapshot.language_at(selection.head());
13604                                let range = selection.range();
13605                                if let Some(language) = language
13606                                    && language.name() == "Markdown".into()
13607                                {
13608                                    edit_for_markdown_paste(
13609                                        &snapshot,
13610                                        range,
13611                                        to_insert,
13612                                        url::Url::parse(to_insert).ok(),
13613                                    )
13614                                } else {
13615                                    (range, Cow::Borrowed(to_insert))
13616                                }
13617                            };
13618
13619                        edits.push((range, to_insert));
13620                        original_indent_columns.push(original_indent_column);
13621                    }
13622                    drop(snapshot);
13623
13624                    buffer.edit(
13625                        edits,
13626                        if auto_indent_on_paste {
13627                            Some(AutoindentMode::Block {
13628                                original_indent_columns,
13629                            })
13630                        } else {
13631                            None
13632                        },
13633                        cx,
13634                    );
13635                });
13636
13637                let selections = this
13638                    .selections
13639                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13640                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13641            } else {
13642                let url = url::Url::parse(&clipboard_text).ok();
13643
13644                let auto_indent_mode = if !clipboard_text.is_empty() {
13645                    Some(AutoindentMode::Block {
13646                        original_indent_columns: Vec::new(),
13647                    })
13648                } else {
13649                    None
13650                };
13651
13652                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13653                    let snapshot = buffer.snapshot(cx);
13654
13655                    let anchors = old_selections
13656                        .iter()
13657                        .map(|s| {
13658                            let anchor = snapshot.anchor_after(s.head());
13659                            s.map(|_| anchor)
13660                        })
13661                        .collect::<Vec<_>>();
13662
13663                    let mut edits = Vec::new();
13664
13665                    for selection in old_selections.iter() {
13666                        let language = snapshot.language_at(selection.head());
13667                        let range = selection.range();
13668
13669                        let (edit_range, edit_text) = if let Some(language) = language
13670                            && language.name() == "Markdown".into()
13671                        {
13672                            edit_for_markdown_paste(&snapshot, range, &clipboard_text, url.clone())
13673                        } else {
13674                            (range, clipboard_text.clone())
13675                        };
13676
13677                        edits.push((edit_range, edit_text));
13678                    }
13679
13680                    drop(snapshot);
13681                    buffer.edit(edits, auto_indent_mode, cx);
13682
13683                    anchors
13684                });
13685
13686                this.change_selections(Default::default(), window, cx, |s| {
13687                    s.select_anchors(selection_anchors);
13688                });
13689            }
13690
13691            //   🤔                 |    ..     | show_in_menu |
13692            // | ..                  |   true        true
13693            // | had_edit_prediction |   false       true
13694
13695            let trigger_in_words =
13696                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13697
13698            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13699        });
13700    }
13701
13702    pub fn diff_clipboard_with_selection(
13703        &mut self,
13704        _: &DiffClipboardWithSelection,
13705        window: &mut Window,
13706        cx: &mut Context<Self>,
13707    ) {
13708        let selections = self
13709            .selections
13710            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13711
13712        if selections.is_empty() {
13713            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13714            return;
13715        };
13716
13717        let clipboard_text = match cx.read_from_clipboard() {
13718            Some(item) => match item.entries().first() {
13719                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13720                _ => None,
13721            },
13722            None => None,
13723        };
13724
13725        let Some(clipboard_text) = clipboard_text else {
13726            log::warn!("Clipboard doesn't contain text.");
13727            return;
13728        };
13729
13730        window.dispatch_action(
13731            Box::new(DiffClipboardWithSelectionData {
13732                clipboard_text,
13733                editor: cx.entity(),
13734            }),
13735            cx,
13736        );
13737    }
13738
13739    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13740        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13741        if let Some(item) = cx.read_from_clipboard() {
13742            let entries = item.entries();
13743
13744            match entries.first() {
13745                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13746                // of all the pasted entries.
13747                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13748                    .do_paste(
13749                        clipboard_string.text(),
13750                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13751                        true,
13752                        window,
13753                        cx,
13754                    ),
13755                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
13756            }
13757        }
13758    }
13759
13760    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
13761        if self.read_only(cx) {
13762            return;
13763        }
13764
13765        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13766
13767        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
13768            if let Some((selections, _)) =
13769                self.selection_history.transaction(transaction_id).cloned()
13770            {
13771                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13772                    s.select_anchors(selections.to_vec());
13773                });
13774            } else {
13775                log::error!(
13776                    "No entry in selection_history found for undo. \
13777                     This may correspond to a bug where undo does not update the selection. \
13778                     If this is occurring, please add details to \
13779                     https://github.com/zed-industries/zed/issues/22692"
13780                );
13781            }
13782            self.request_autoscroll(Autoscroll::fit(), cx);
13783            self.unmark_text(window, cx);
13784            self.refresh_edit_prediction(true, false, window, cx);
13785            cx.emit(EditorEvent::Edited { transaction_id });
13786            cx.emit(EditorEvent::TransactionUndone { transaction_id });
13787        }
13788    }
13789
13790    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
13791        if self.read_only(cx) {
13792            return;
13793        }
13794
13795        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13796
13797        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
13798            if let Some((_, Some(selections))) =
13799                self.selection_history.transaction(transaction_id).cloned()
13800            {
13801                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13802                    s.select_anchors(selections.to_vec());
13803                });
13804            } else {
13805                log::error!(
13806                    "No entry in selection_history found for redo. \
13807                     This may correspond to a bug where undo does not update the selection. \
13808                     If this is occurring, please add details to \
13809                     https://github.com/zed-industries/zed/issues/22692"
13810                );
13811            }
13812            self.request_autoscroll(Autoscroll::fit(), cx);
13813            self.unmark_text(window, cx);
13814            self.refresh_edit_prediction(true, false, window, cx);
13815            cx.emit(EditorEvent::Edited { transaction_id });
13816        }
13817    }
13818
13819    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
13820        self.buffer
13821            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
13822    }
13823
13824    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
13825        self.buffer
13826            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
13827    }
13828
13829    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
13830        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13831        self.change_selections(Default::default(), window, cx, |s| {
13832            s.move_with(|map, selection| {
13833                let cursor = if selection.is_empty() {
13834                    movement::left(map, selection.start)
13835                } else {
13836                    selection.start
13837                };
13838                selection.collapse_to(cursor, SelectionGoal::None);
13839            });
13840        })
13841    }
13842
13843    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
13844        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13845        self.change_selections(Default::default(), window, cx, |s| {
13846            s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
13847        })
13848    }
13849
13850    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
13851        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13852        self.change_selections(Default::default(), window, cx, |s| {
13853            s.move_with(|map, selection| {
13854                let cursor = if selection.is_empty() {
13855                    movement::right(map, selection.end)
13856                } else {
13857                    selection.end
13858                };
13859                selection.collapse_to(cursor, SelectionGoal::None)
13860            });
13861        })
13862    }
13863
13864    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
13865        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13866        self.change_selections(Default::default(), window, cx, |s| {
13867            s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
13868        });
13869    }
13870
13871    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
13872        if self.take_rename(true, window, cx).is_some() {
13873            return;
13874        }
13875
13876        if self.mode.is_single_line() {
13877            cx.propagate();
13878            return;
13879        }
13880
13881        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13882
13883        let text_layout_details = &self.text_layout_details(window, cx);
13884        let selection_count = self.selections.count();
13885        let first_selection = self.selections.first_anchor();
13886
13887        self.change_selections(Default::default(), window, cx, |s| {
13888            s.move_with(|map, selection| {
13889                if !selection.is_empty() {
13890                    selection.goal = SelectionGoal::None;
13891                }
13892                let (cursor, goal) = movement::up(
13893                    map,
13894                    selection.start,
13895                    selection.goal,
13896                    false,
13897                    text_layout_details,
13898                );
13899                selection.collapse_to(cursor, goal);
13900            });
13901        });
13902
13903        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
13904        {
13905            cx.propagate();
13906        }
13907    }
13908
13909    pub fn move_up_by_lines(
13910        &mut self,
13911        action: &MoveUpByLines,
13912        window: &mut Window,
13913        cx: &mut Context<Self>,
13914    ) {
13915        if self.take_rename(true, window, cx).is_some() {
13916            return;
13917        }
13918
13919        if self.mode.is_single_line() {
13920            cx.propagate();
13921            return;
13922        }
13923
13924        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13925
13926        let text_layout_details = &self.text_layout_details(window, cx);
13927
13928        self.change_selections(Default::default(), window, cx, |s| {
13929            s.move_with(|map, selection| {
13930                if !selection.is_empty() {
13931                    selection.goal = SelectionGoal::None;
13932                }
13933                let (cursor, goal) = movement::up_by_rows(
13934                    map,
13935                    selection.start,
13936                    action.lines,
13937                    selection.goal,
13938                    false,
13939                    text_layout_details,
13940                );
13941                selection.collapse_to(cursor, goal);
13942            });
13943        })
13944    }
13945
13946    pub fn move_down_by_lines(
13947        &mut self,
13948        action: &MoveDownByLines,
13949        window: &mut Window,
13950        cx: &mut Context<Self>,
13951    ) {
13952        if self.take_rename(true, window, cx).is_some() {
13953            return;
13954        }
13955
13956        if self.mode.is_single_line() {
13957            cx.propagate();
13958            return;
13959        }
13960
13961        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13962
13963        let text_layout_details = &self.text_layout_details(window, cx);
13964
13965        self.change_selections(Default::default(), window, cx, |s| {
13966            s.move_with(|map, selection| {
13967                if !selection.is_empty() {
13968                    selection.goal = SelectionGoal::None;
13969                }
13970                let (cursor, goal) = movement::down_by_rows(
13971                    map,
13972                    selection.start,
13973                    action.lines,
13974                    selection.goal,
13975                    false,
13976                    text_layout_details,
13977                );
13978                selection.collapse_to(cursor, goal);
13979            });
13980        })
13981    }
13982
13983    pub fn select_down_by_lines(
13984        &mut self,
13985        action: &SelectDownByLines,
13986        window: &mut Window,
13987        cx: &mut Context<Self>,
13988    ) {
13989        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13990        let text_layout_details = &self.text_layout_details(window, cx);
13991        self.change_selections(Default::default(), window, cx, |s| {
13992            s.move_heads_with(|map, head, goal| {
13993                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
13994            })
13995        })
13996    }
13997
13998    pub fn select_up_by_lines(
13999        &mut self,
14000        action: &SelectUpByLines,
14001        window: &mut Window,
14002        cx: &mut Context<Self>,
14003    ) {
14004        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14005        let text_layout_details = &self.text_layout_details(window, cx);
14006        self.change_selections(Default::default(), window, cx, |s| {
14007            s.move_heads_with(|map, head, goal| {
14008                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14009            })
14010        })
14011    }
14012
14013    pub fn select_page_up(
14014        &mut self,
14015        _: &SelectPageUp,
14016        window: &mut Window,
14017        cx: &mut Context<Self>,
14018    ) {
14019        let Some(row_count) = self.visible_row_count() else {
14020            return;
14021        };
14022
14023        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14024
14025        let text_layout_details = &self.text_layout_details(window, cx);
14026
14027        self.change_selections(Default::default(), window, cx, |s| {
14028            s.move_heads_with(|map, head, goal| {
14029                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14030            })
14031        })
14032    }
14033
14034    pub fn move_page_up(
14035        &mut self,
14036        action: &MovePageUp,
14037        window: &mut Window,
14038        cx: &mut Context<Self>,
14039    ) {
14040        if self.take_rename(true, window, cx).is_some() {
14041            return;
14042        }
14043
14044        if self
14045            .context_menu
14046            .borrow_mut()
14047            .as_mut()
14048            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14049            .unwrap_or(false)
14050        {
14051            return;
14052        }
14053
14054        if matches!(self.mode, EditorMode::SingleLine) {
14055            cx.propagate();
14056            return;
14057        }
14058
14059        let Some(row_count) = self.visible_row_count() else {
14060            return;
14061        };
14062
14063        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14064
14065        let effects = if action.center_cursor {
14066            SelectionEffects::scroll(Autoscroll::center())
14067        } else {
14068            SelectionEffects::default()
14069        };
14070
14071        let text_layout_details = &self.text_layout_details(window, cx);
14072
14073        self.change_selections(effects, window, cx, |s| {
14074            s.move_with(|map, selection| {
14075                if !selection.is_empty() {
14076                    selection.goal = SelectionGoal::None;
14077                }
14078                let (cursor, goal) = movement::up_by_rows(
14079                    map,
14080                    selection.end,
14081                    row_count,
14082                    selection.goal,
14083                    false,
14084                    text_layout_details,
14085                );
14086                selection.collapse_to(cursor, goal);
14087            });
14088        });
14089    }
14090
14091    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14092        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14093        let text_layout_details = &self.text_layout_details(window, cx);
14094        self.change_selections(Default::default(), window, cx, |s| {
14095            s.move_heads_with(|map, head, goal| {
14096                movement::up(map, head, goal, false, text_layout_details)
14097            })
14098        })
14099    }
14100
14101    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14102        self.take_rename(true, window, cx);
14103
14104        if self.mode.is_single_line() {
14105            cx.propagate();
14106            return;
14107        }
14108
14109        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14110
14111        let text_layout_details = &self.text_layout_details(window, cx);
14112        let selection_count = self.selections.count();
14113        let first_selection = self.selections.first_anchor();
14114
14115        self.change_selections(Default::default(), window, cx, |s| {
14116            s.move_with(|map, selection| {
14117                if !selection.is_empty() {
14118                    selection.goal = SelectionGoal::None;
14119                }
14120                let (cursor, goal) = movement::down(
14121                    map,
14122                    selection.end,
14123                    selection.goal,
14124                    false,
14125                    text_layout_details,
14126                );
14127                selection.collapse_to(cursor, goal);
14128            });
14129        });
14130
14131        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14132        {
14133            cx.propagate();
14134        }
14135    }
14136
14137    pub fn select_page_down(
14138        &mut self,
14139        _: &SelectPageDown,
14140        window: &mut Window,
14141        cx: &mut Context<Self>,
14142    ) {
14143        let Some(row_count) = self.visible_row_count() else {
14144            return;
14145        };
14146
14147        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14148
14149        let text_layout_details = &self.text_layout_details(window, cx);
14150
14151        self.change_selections(Default::default(), window, cx, |s| {
14152            s.move_heads_with(|map, head, goal| {
14153                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14154            })
14155        })
14156    }
14157
14158    pub fn move_page_down(
14159        &mut self,
14160        action: &MovePageDown,
14161        window: &mut Window,
14162        cx: &mut Context<Self>,
14163    ) {
14164        if self.take_rename(true, window, cx).is_some() {
14165            return;
14166        }
14167
14168        if self
14169            .context_menu
14170            .borrow_mut()
14171            .as_mut()
14172            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14173            .unwrap_or(false)
14174        {
14175            return;
14176        }
14177
14178        if matches!(self.mode, EditorMode::SingleLine) {
14179            cx.propagate();
14180            return;
14181        }
14182
14183        let Some(row_count) = self.visible_row_count() else {
14184            return;
14185        };
14186
14187        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14188
14189        let effects = if action.center_cursor {
14190            SelectionEffects::scroll(Autoscroll::center())
14191        } else {
14192            SelectionEffects::default()
14193        };
14194
14195        let text_layout_details = &self.text_layout_details(window, cx);
14196        self.change_selections(effects, window, cx, |s| {
14197            s.move_with(|map, selection| {
14198                if !selection.is_empty() {
14199                    selection.goal = SelectionGoal::None;
14200                }
14201                let (cursor, goal) = movement::down_by_rows(
14202                    map,
14203                    selection.end,
14204                    row_count,
14205                    selection.goal,
14206                    false,
14207                    text_layout_details,
14208                );
14209                selection.collapse_to(cursor, goal);
14210            });
14211        });
14212    }
14213
14214    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14215        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14216        let text_layout_details = &self.text_layout_details(window, cx);
14217        self.change_selections(Default::default(), window, cx, |s| {
14218            s.move_heads_with(|map, head, goal| {
14219                movement::down(map, head, goal, false, text_layout_details)
14220            })
14221        });
14222    }
14223
14224    pub fn context_menu_first(
14225        &mut self,
14226        _: &ContextMenuFirst,
14227        window: &mut Window,
14228        cx: &mut Context<Self>,
14229    ) {
14230        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14231            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14232        }
14233    }
14234
14235    pub fn context_menu_prev(
14236        &mut self,
14237        _: &ContextMenuPrevious,
14238        window: &mut Window,
14239        cx: &mut Context<Self>,
14240    ) {
14241        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14242            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14243        }
14244    }
14245
14246    pub fn context_menu_next(
14247        &mut self,
14248        _: &ContextMenuNext,
14249        window: &mut Window,
14250        cx: &mut Context<Self>,
14251    ) {
14252        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14253            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14254        }
14255    }
14256
14257    pub fn context_menu_last(
14258        &mut self,
14259        _: &ContextMenuLast,
14260        window: &mut Window,
14261        cx: &mut Context<Self>,
14262    ) {
14263        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14264            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14265        }
14266    }
14267
14268    pub fn signature_help_prev(
14269        &mut self,
14270        _: &SignatureHelpPrevious,
14271        _: &mut Window,
14272        cx: &mut Context<Self>,
14273    ) {
14274        if let Some(popover) = self.signature_help_state.popover_mut() {
14275            if popover.current_signature == 0 {
14276                popover.current_signature = popover.signatures.len() - 1;
14277            } else {
14278                popover.current_signature -= 1;
14279            }
14280            cx.notify();
14281        }
14282    }
14283
14284    pub fn signature_help_next(
14285        &mut self,
14286        _: &SignatureHelpNext,
14287        _: &mut Window,
14288        cx: &mut Context<Self>,
14289    ) {
14290        if let Some(popover) = self.signature_help_state.popover_mut() {
14291            if popover.current_signature + 1 == popover.signatures.len() {
14292                popover.current_signature = 0;
14293            } else {
14294                popover.current_signature += 1;
14295            }
14296            cx.notify();
14297        }
14298    }
14299
14300    pub fn move_to_previous_word_start(
14301        &mut self,
14302        _: &MoveToPreviousWordStart,
14303        window: &mut Window,
14304        cx: &mut Context<Self>,
14305    ) {
14306        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14307        self.change_selections(Default::default(), window, cx, |s| {
14308            s.move_cursors_with(|map, head, _| {
14309                (
14310                    movement::previous_word_start(map, head),
14311                    SelectionGoal::None,
14312                )
14313            });
14314        })
14315    }
14316
14317    pub fn move_to_previous_subword_start(
14318        &mut self,
14319        _: &MoveToPreviousSubwordStart,
14320        window: &mut Window,
14321        cx: &mut Context<Self>,
14322    ) {
14323        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14324        self.change_selections(Default::default(), window, cx, |s| {
14325            s.move_cursors_with(|map, head, _| {
14326                (
14327                    movement::previous_subword_start(map, head),
14328                    SelectionGoal::None,
14329                )
14330            });
14331        })
14332    }
14333
14334    pub fn select_to_previous_word_start(
14335        &mut self,
14336        _: &SelectToPreviousWordStart,
14337        window: &mut Window,
14338        cx: &mut Context<Self>,
14339    ) {
14340        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14341        self.change_selections(Default::default(), window, cx, |s| {
14342            s.move_heads_with(|map, head, _| {
14343                (
14344                    movement::previous_word_start(map, head),
14345                    SelectionGoal::None,
14346                )
14347            });
14348        })
14349    }
14350
14351    pub fn select_to_previous_subword_start(
14352        &mut self,
14353        _: &SelectToPreviousSubwordStart,
14354        window: &mut Window,
14355        cx: &mut Context<Self>,
14356    ) {
14357        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14358        self.change_selections(Default::default(), window, cx, |s| {
14359            s.move_heads_with(|map, head, _| {
14360                (
14361                    movement::previous_subword_start(map, head),
14362                    SelectionGoal::None,
14363                )
14364            });
14365        })
14366    }
14367
14368    pub fn delete_to_previous_word_start(
14369        &mut self,
14370        action: &DeleteToPreviousWordStart,
14371        window: &mut Window,
14372        cx: &mut Context<Self>,
14373    ) {
14374        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14375        self.transact(window, cx, |this, window, cx| {
14376            this.select_autoclose_pair(window, cx);
14377            this.change_selections(Default::default(), window, cx, |s| {
14378                s.move_with(|map, selection| {
14379                    if selection.is_empty() {
14380                        let mut cursor = if action.ignore_newlines {
14381                            movement::previous_word_start(map, selection.head())
14382                        } else {
14383                            movement::previous_word_start_or_newline(map, selection.head())
14384                        };
14385                        cursor = movement::adjust_greedy_deletion(
14386                            map,
14387                            selection.head(),
14388                            cursor,
14389                            action.ignore_brackets,
14390                        );
14391                        selection.set_head(cursor, SelectionGoal::None);
14392                    }
14393                });
14394            });
14395            this.insert("", window, cx);
14396        });
14397    }
14398
14399    pub fn delete_to_previous_subword_start(
14400        &mut self,
14401        action: &DeleteToPreviousSubwordStart,
14402        window: &mut Window,
14403        cx: &mut Context<Self>,
14404    ) {
14405        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14406        self.transact(window, cx, |this, window, cx| {
14407            this.select_autoclose_pair(window, cx);
14408            this.change_selections(Default::default(), window, cx, |s| {
14409                s.move_with(|map, selection| {
14410                    if selection.is_empty() {
14411                        let mut cursor = if action.ignore_newlines {
14412                            movement::previous_subword_start(map, selection.head())
14413                        } else {
14414                            movement::previous_subword_start_or_newline(map, selection.head())
14415                        };
14416                        cursor = movement::adjust_greedy_deletion(
14417                            map,
14418                            selection.head(),
14419                            cursor,
14420                            action.ignore_brackets,
14421                        );
14422                        selection.set_head(cursor, SelectionGoal::None);
14423                    }
14424                });
14425            });
14426            this.insert("", window, cx);
14427        });
14428    }
14429
14430    pub fn move_to_next_word_end(
14431        &mut self,
14432        _: &MoveToNextWordEnd,
14433        window: &mut Window,
14434        cx: &mut Context<Self>,
14435    ) {
14436        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14437        self.change_selections(Default::default(), window, cx, |s| {
14438            s.move_cursors_with(|map, head, _| {
14439                (movement::next_word_end(map, head), SelectionGoal::None)
14440            });
14441        })
14442    }
14443
14444    pub fn move_to_next_subword_end(
14445        &mut self,
14446        _: &MoveToNextSubwordEnd,
14447        window: &mut Window,
14448        cx: &mut Context<Self>,
14449    ) {
14450        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14451        self.change_selections(Default::default(), window, cx, |s| {
14452            s.move_cursors_with(|map, head, _| {
14453                (movement::next_subword_end(map, head), SelectionGoal::None)
14454            });
14455        })
14456    }
14457
14458    pub fn select_to_next_word_end(
14459        &mut self,
14460        _: &SelectToNextWordEnd,
14461        window: &mut Window,
14462        cx: &mut Context<Self>,
14463    ) {
14464        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14465        self.change_selections(Default::default(), window, cx, |s| {
14466            s.move_heads_with(|map, head, _| {
14467                (movement::next_word_end(map, head), SelectionGoal::None)
14468            });
14469        })
14470    }
14471
14472    pub fn select_to_next_subword_end(
14473        &mut self,
14474        _: &SelectToNextSubwordEnd,
14475        window: &mut Window,
14476        cx: &mut Context<Self>,
14477    ) {
14478        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14479        self.change_selections(Default::default(), window, cx, |s| {
14480            s.move_heads_with(|map, head, _| {
14481                (movement::next_subword_end(map, head), SelectionGoal::None)
14482            });
14483        })
14484    }
14485
14486    pub fn delete_to_next_word_end(
14487        &mut self,
14488        action: &DeleteToNextWordEnd,
14489        window: &mut Window,
14490        cx: &mut Context<Self>,
14491    ) {
14492        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14493        self.transact(window, cx, |this, window, cx| {
14494            this.change_selections(Default::default(), window, cx, |s| {
14495                s.move_with(|map, selection| {
14496                    if selection.is_empty() {
14497                        let mut cursor = if action.ignore_newlines {
14498                            movement::next_word_end(map, selection.head())
14499                        } else {
14500                            movement::next_word_end_or_newline(map, selection.head())
14501                        };
14502                        cursor = movement::adjust_greedy_deletion(
14503                            map,
14504                            selection.head(),
14505                            cursor,
14506                            action.ignore_brackets,
14507                        );
14508                        selection.set_head(cursor, SelectionGoal::None);
14509                    }
14510                });
14511            });
14512            this.insert("", window, cx);
14513        });
14514    }
14515
14516    pub fn delete_to_next_subword_end(
14517        &mut self,
14518        action: &DeleteToNextSubwordEnd,
14519        window: &mut Window,
14520        cx: &mut Context<Self>,
14521    ) {
14522        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14523        self.transact(window, cx, |this, window, cx| {
14524            this.change_selections(Default::default(), window, cx, |s| {
14525                s.move_with(|map, selection| {
14526                    if selection.is_empty() {
14527                        let mut cursor = if action.ignore_newlines {
14528                            movement::next_subword_end(map, selection.head())
14529                        } else {
14530                            movement::next_subword_end_or_newline(map, selection.head())
14531                        };
14532                        cursor = movement::adjust_greedy_deletion(
14533                            map,
14534                            selection.head(),
14535                            cursor,
14536                            action.ignore_brackets,
14537                        );
14538                        selection.set_head(cursor, SelectionGoal::None);
14539                    }
14540                });
14541            });
14542            this.insert("", window, cx);
14543        });
14544    }
14545
14546    pub fn move_to_beginning_of_line(
14547        &mut self,
14548        action: &MoveToBeginningOfLine,
14549        window: &mut Window,
14550        cx: &mut Context<Self>,
14551    ) {
14552        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14553        self.change_selections(Default::default(), window, cx, |s| {
14554            s.move_cursors_with(|map, head, _| {
14555                (
14556                    movement::indented_line_beginning(
14557                        map,
14558                        head,
14559                        action.stop_at_soft_wraps,
14560                        action.stop_at_indent,
14561                    ),
14562                    SelectionGoal::None,
14563                )
14564            });
14565        })
14566    }
14567
14568    pub fn select_to_beginning_of_line(
14569        &mut self,
14570        action: &SelectToBeginningOfLine,
14571        window: &mut Window,
14572        cx: &mut Context<Self>,
14573    ) {
14574        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14575        self.change_selections(Default::default(), window, cx, |s| {
14576            s.move_heads_with(|map, head, _| {
14577                (
14578                    movement::indented_line_beginning(
14579                        map,
14580                        head,
14581                        action.stop_at_soft_wraps,
14582                        action.stop_at_indent,
14583                    ),
14584                    SelectionGoal::None,
14585                )
14586            });
14587        });
14588    }
14589
14590    pub fn delete_to_beginning_of_line(
14591        &mut self,
14592        action: &DeleteToBeginningOfLine,
14593        window: &mut Window,
14594        cx: &mut Context<Self>,
14595    ) {
14596        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14597        self.transact(window, cx, |this, window, cx| {
14598            this.change_selections(Default::default(), window, cx, |s| {
14599                s.move_with(|_, selection| {
14600                    selection.reversed = true;
14601                });
14602            });
14603
14604            this.select_to_beginning_of_line(
14605                &SelectToBeginningOfLine {
14606                    stop_at_soft_wraps: false,
14607                    stop_at_indent: action.stop_at_indent,
14608                },
14609                window,
14610                cx,
14611            );
14612            this.backspace(&Backspace, window, cx);
14613        });
14614    }
14615
14616    pub fn move_to_end_of_line(
14617        &mut self,
14618        action: &MoveToEndOfLine,
14619        window: &mut Window,
14620        cx: &mut Context<Self>,
14621    ) {
14622        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14623        self.change_selections(Default::default(), window, cx, |s| {
14624            s.move_cursors_with(|map, head, _| {
14625                (
14626                    movement::line_end(map, head, action.stop_at_soft_wraps),
14627                    SelectionGoal::None,
14628                )
14629            });
14630        })
14631    }
14632
14633    pub fn select_to_end_of_line(
14634        &mut self,
14635        action: &SelectToEndOfLine,
14636        window: &mut Window,
14637        cx: &mut Context<Self>,
14638    ) {
14639        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14640        self.change_selections(Default::default(), window, cx, |s| {
14641            s.move_heads_with(|map, head, _| {
14642                (
14643                    movement::line_end(map, head, action.stop_at_soft_wraps),
14644                    SelectionGoal::None,
14645                )
14646            });
14647        })
14648    }
14649
14650    pub fn delete_to_end_of_line(
14651        &mut self,
14652        _: &DeleteToEndOfLine,
14653        window: &mut Window,
14654        cx: &mut Context<Self>,
14655    ) {
14656        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14657        self.transact(window, cx, |this, window, cx| {
14658            this.select_to_end_of_line(
14659                &SelectToEndOfLine {
14660                    stop_at_soft_wraps: false,
14661                },
14662                window,
14663                cx,
14664            );
14665            this.delete(&Delete, window, cx);
14666        });
14667    }
14668
14669    pub fn cut_to_end_of_line(
14670        &mut self,
14671        action: &CutToEndOfLine,
14672        window: &mut Window,
14673        cx: &mut Context<Self>,
14674    ) {
14675        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14676        self.transact(window, cx, |this, window, cx| {
14677            this.select_to_end_of_line(
14678                &SelectToEndOfLine {
14679                    stop_at_soft_wraps: false,
14680                },
14681                window,
14682                cx,
14683            );
14684            if !action.stop_at_newlines {
14685                this.change_selections(Default::default(), window, cx, |s| {
14686                    s.move_with(|_, sel| {
14687                        if sel.is_empty() {
14688                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14689                        }
14690                    });
14691                });
14692            }
14693            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14694            let item = this.cut_common(false, window, cx);
14695            cx.write_to_clipboard(item);
14696        });
14697    }
14698
14699    pub fn move_to_start_of_paragraph(
14700        &mut self,
14701        _: &MoveToStartOfParagraph,
14702        window: &mut Window,
14703        cx: &mut Context<Self>,
14704    ) {
14705        if matches!(self.mode, EditorMode::SingleLine) {
14706            cx.propagate();
14707            return;
14708        }
14709        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14710        self.change_selections(Default::default(), window, cx, |s| {
14711            s.move_with(|map, selection| {
14712                selection.collapse_to(
14713                    movement::start_of_paragraph(map, selection.head(), 1),
14714                    SelectionGoal::None,
14715                )
14716            });
14717        })
14718    }
14719
14720    pub fn move_to_end_of_paragraph(
14721        &mut self,
14722        _: &MoveToEndOfParagraph,
14723        window: &mut Window,
14724        cx: &mut Context<Self>,
14725    ) {
14726        if matches!(self.mode, EditorMode::SingleLine) {
14727            cx.propagate();
14728            return;
14729        }
14730        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14731        self.change_selections(Default::default(), window, cx, |s| {
14732            s.move_with(|map, selection| {
14733                selection.collapse_to(
14734                    movement::end_of_paragraph(map, selection.head(), 1),
14735                    SelectionGoal::None,
14736                )
14737            });
14738        })
14739    }
14740
14741    pub fn select_to_start_of_paragraph(
14742        &mut self,
14743        _: &SelectToStartOfParagraph,
14744        window: &mut Window,
14745        cx: &mut Context<Self>,
14746    ) {
14747        if matches!(self.mode, EditorMode::SingleLine) {
14748            cx.propagate();
14749            return;
14750        }
14751        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14752        self.change_selections(Default::default(), window, cx, |s| {
14753            s.move_heads_with(|map, head, _| {
14754                (
14755                    movement::start_of_paragraph(map, head, 1),
14756                    SelectionGoal::None,
14757                )
14758            });
14759        })
14760    }
14761
14762    pub fn select_to_end_of_paragraph(
14763        &mut self,
14764        _: &SelectToEndOfParagraph,
14765        window: &mut Window,
14766        cx: &mut Context<Self>,
14767    ) {
14768        if matches!(self.mode, EditorMode::SingleLine) {
14769            cx.propagate();
14770            return;
14771        }
14772        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14773        self.change_selections(Default::default(), window, cx, |s| {
14774            s.move_heads_with(|map, head, _| {
14775                (
14776                    movement::end_of_paragraph(map, head, 1),
14777                    SelectionGoal::None,
14778                )
14779            });
14780        })
14781    }
14782
14783    pub fn move_to_start_of_excerpt(
14784        &mut self,
14785        _: &MoveToStartOfExcerpt,
14786        window: &mut Window,
14787        cx: &mut Context<Self>,
14788    ) {
14789        if matches!(self.mode, EditorMode::SingleLine) {
14790            cx.propagate();
14791            return;
14792        }
14793        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14794        self.change_selections(Default::default(), window, cx, |s| {
14795            s.move_with(|map, selection| {
14796                selection.collapse_to(
14797                    movement::start_of_excerpt(
14798                        map,
14799                        selection.head(),
14800                        workspace::searchable::Direction::Prev,
14801                    ),
14802                    SelectionGoal::None,
14803                )
14804            });
14805        })
14806    }
14807
14808    pub fn move_to_start_of_next_excerpt(
14809        &mut self,
14810        _: &MoveToStartOfNextExcerpt,
14811        window: &mut Window,
14812        cx: &mut Context<Self>,
14813    ) {
14814        if matches!(self.mode, EditorMode::SingleLine) {
14815            cx.propagate();
14816            return;
14817        }
14818
14819        self.change_selections(Default::default(), window, cx, |s| {
14820            s.move_with(|map, selection| {
14821                selection.collapse_to(
14822                    movement::start_of_excerpt(
14823                        map,
14824                        selection.head(),
14825                        workspace::searchable::Direction::Next,
14826                    ),
14827                    SelectionGoal::None,
14828                )
14829            });
14830        })
14831    }
14832
14833    pub fn move_to_end_of_excerpt(
14834        &mut self,
14835        _: &MoveToEndOfExcerpt,
14836        window: &mut Window,
14837        cx: &mut Context<Self>,
14838    ) {
14839        if matches!(self.mode, EditorMode::SingleLine) {
14840            cx.propagate();
14841            return;
14842        }
14843        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14844        self.change_selections(Default::default(), window, cx, |s| {
14845            s.move_with(|map, selection| {
14846                selection.collapse_to(
14847                    movement::end_of_excerpt(
14848                        map,
14849                        selection.head(),
14850                        workspace::searchable::Direction::Next,
14851                    ),
14852                    SelectionGoal::None,
14853                )
14854            });
14855        })
14856    }
14857
14858    pub fn move_to_end_of_previous_excerpt(
14859        &mut self,
14860        _: &MoveToEndOfPreviousExcerpt,
14861        window: &mut Window,
14862        cx: &mut Context<Self>,
14863    ) {
14864        if matches!(self.mode, EditorMode::SingleLine) {
14865            cx.propagate();
14866            return;
14867        }
14868        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14869        self.change_selections(Default::default(), window, cx, |s| {
14870            s.move_with(|map, selection| {
14871                selection.collapse_to(
14872                    movement::end_of_excerpt(
14873                        map,
14874                        selection.head(),
14875                        workspace::searchable::Direction::Prev,
14876                    ),
14877                    SelectionGoal::None,
14878                )
14879            });
14880        })
14881    }
14882
14883    pub fn select_to_start_of_excerpt(
14884        &mut self,
14885        _: &SelectToStartOfExcerpt,
14886        window: &mut Window,
14887        cx: &mut Context<Self>,
14888    ) {
14889        if matches!(self.mode, EditorMode::SingleLine) {
14890            cx.propagate();
14891            return;
14892        }
14893        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14894        self.change_selections(Default::default(), window, cx, |s| {
14895            s.move_heads_with(|map, head, _| {
14896                (
14897                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
14898                    SelectionGoal::None,
14899                )
14900            });
14901        })
14902    }
14903
14904    pub fn select_to_start_of_next_excerpt(
14905        &mut self,
14906        _: &SelectToStartOfNextExcerpt,
14907        window: &mut Window,
14908        cx: &mut Context<Self>,
14909    ) {
14910        if matches!(self.mode, EditorMode::SingleLine) {
14911            cx.propagate();
14912            return;
14913        }
14914        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14915        self.change_selections(Default::default(), window, cx, |s| {
14916            s.move_heads_with(|map, head, _| {
14917                (
14918                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
14919                    SelectionGoal::None,
14920                )
14921            });
14922        })
14923    }
14924
14925    pub fn select_to_end_of_excerpt(
14926        &mut self,
14927        _: &SelectToEndOfExcerpt,
14928        window: &mut Window,
14929        cx: &mut Context<Self>,
14930    ) {
14931        if matches!(self.mode, EditorMode::SingleLine) {
14932            cx.propagate();
14933            return;
14934        }
14935        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14936        self.change_selections(Default::default(), window, cx, |s| {
14937            s.move_heads_with(|map, head, _| {
14938                (
14939                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
14940                    SelectionGoal::None,
14941                )
14942            });
14943        })
14944    }
14945
14946    pub fn select_to_end_of_previous_excerpt(
14947        &mut self,
14948        _: &SelectToEndOfPreviousExcerpt,
14949        window: &mut Window,
14950        cx: &mut Context<Self>,
14951    ) {
14952        if matches!(self.mode, EditorMode::SingleLine) {
14953            cx.propagate();
14954            return;
14955        }
14956        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14957        self.change_selections(Default::default(), window, cx, |s| {
14958            s.move_heads_with(|map, head, _| {
14959                (
14960                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
14961                    SelectionGoal::None,
14962                )
14963            });
14964        })
14965    }
14966
14967    pub fn move_to_beginning(
14968        &mut self,
14969        _: &MoveToBeginning,
14970        window: &mut Window,
14971        cx: &mut Context<Self>,
14972    ) {
14973        if matches!(self.mode, EditorMode::SingleLine) {
14974            cx.propagate();
14975            return;
14976        }
14977        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14978        self.change_selections(Default::default(), window, cx, |s| {
14979            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
14980        });
14981    }
14982
14983    pub fn select_to_beginning(
14984        &mut self,
14985        _: &SelectToBeginning,
14986        window: &mut Window,
14987        cx: &mut Context<Self>,
14988    ) {
14989        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
14990        selection.set_head(Point::zero(), SelectionGoal::None);
14991        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14992        self.change_selections(Default::default(), window, cx, |s| {
14993            s.select(vec![selection]);
14994        });
14995    }
14996
14997    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
14998        if matches!(self.mode, EditorMode::SingleLine) {
14999            cx.propagate();
15000            return;
15001        }
15002        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15003        let cursor = self.buffer.read(cx).read(cx).len();
15004        self.change_selections(Default::default(), window, cx, |s| {
15005            s.select_ranges(vec![cursor..cursor])
15006        });
15007    }
15008
15009    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15010        self.nav_history = nav_history;
15011    }
15012
15013    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15014        self.nav_history.as_ref()
15015    }
15016
15017    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15018        self.push_to_nav_history(
15019            self.selections.newest_anchor().head(),
15020            None,
15021            false,
15022            true,
15023            cx,
15024        );
15025    }
15026
15027    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15028        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15029        let buffer = self.buffer.read(cx).read(cx);
15030        let cursor_position = cursor_anchor.to_point(&buffer);
15031        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15032        let scroll_top_row = scroll_anchor.top_row(&buffer);
15033        drop(buffer);
15034
15035        NavigationData {
15036            cursor_anchor,
15037            cursor_position,
15038            scroll_anchor,
15039            scroll_top_row,
15040        }
15041    }
15042
15043    fn navigation_entry(
15044        &self,
15045        cursor_anchor: Anchor,
15046        cx: &mut Context<Self>,
15047    ) -> Option<NavigationEntry> {
15048        let Some(history) = self.nav_history.clone() else {
15049            return None;
15050        };
15051        let data = self.navigation_data(cursor_anchor, cx);
15052        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15053    }
15054
15055    fn push_to_nav_history(
15056        &mut self,
15057        cursor_anchor: Anchor,
15058        new_position: Option<Point>,
15059        is_deactivate: bool,
15060        always: bool,
15061        cx: &mut Context<Self>,
15062    ) {
15063        let data = self.navigation_data(cursor_anchor, cx);
15064        if let Some(nav_history) = self.nav_history.as_mut() {
15065            if let Some(new_position) = new_position {
15066                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15067                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15068                    return;
15069                }
15070            }
15071
15072            nav_history.push(Some(data), cx);
15073            cx.emit(EditorEvent::PushedToNavHistory {
15074                anchor: cursor_anchor,
15075                is_deactivate,
15076            })
15077        }
15078    }
15079
15080    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15081        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15082        let buffer = self.buffer.read(cx).snapshot(cx);
15083        let mut selection = self
15084            .selections
15085            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15086        selection.set_head(buffer.len(), SelectionGoal::None);
15087        self.change_selections(Default::default(), window, cx, |s| {
15088            s.select(vec![selection]);
15089        });
15090    }
15091
15092    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15093        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15094        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15095            s.select_ranges(vec![Anchor::min()..Anchor::max()]);
15096        });
15097    }
15098
15099    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15100        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15101        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15102        let mut selections = self.selections.all::<Point>(&display_map);
15103        let max_point = display_map.buffer_snapshot().max_point();
15104        for selection in &mut selections {
15105            let rows = selection.spanned_rows(true, &display_map);
15106            selection.start = Point::new(rows.start.0, 0);
15107            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15108            selection.reversed = false;
15109        }
15110        self.change_selections(Default::default(), window, cx, |s| {
15111            s.select(selections);
15112        });
15113    }
15114
15115    pub fn split_selection_into_lines(
15116        &mut self,
15117        action: &SplitSelectionIntoLines,
15118        window: &mut Window,
15119        cx: &mut Context<Self>,
15120    ) {
15121        let selections = self
15122            .selections
15123            .all::<Point>(&self.display_snapshot(cx))
15124            .into_iter()
15125            .map(|selection| selection.start..selection.end)
15126            .collect::<Vec<_>>();
15127        self.unfold_ranges(&selections, true, true, cx);
15128
15129        let mut new_selection_ranges = Vec::new();
15130        {
15131            let buffer = self.buffer.read(cx).read(cx);
15132            for selection in selections {
15133                for row in selection.start.row..selection.end.row {
15134                    let line_start = Point::new(row, 0);
15135                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15136
15137                    if action.keep_selections {
15138                        // Keep the selection range for each line
15139                        let selection_start = if row == selection.start.row {
15140                            selection.start
15141                        } else {
15142                            line_start
15143                        };
15144                        new_selection_ranges.push(selection_start..line_end);
15145                    } else {
15146                        // Collapse to cursor at end of line
15147                        new_selection_ranges.push(line_end..line_end);
15148                    }
15149                }
15150
15151                let is_multiline_selection = selection.start.row != selection.end.row;
15152                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15153                // so this action feels more ergonomic when paired with other selection operations
15154                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15155                if !should_skip_last {
15156                    if action.keep_selections {
15157                        if is_multiline_selection {
15158                            let line_start = Point::new(selection.end.row, 0);
15159                            new_selection_ranges.push(line_start..selection.end);
15160                        } else {
15161                            new_selection_ranges.push(selection.start..selection.end);
15162                        }
15163                    } else {
15164                        new_selection_ranges.push(selection.end..selection.end);
15165                    }
15166                }
15167            }
15168        }
15169        self.change_selections(Default::default(), window, cx, |s| {
15170            s.select_ranges(new_selection_ranges);
15171        });
15172    }
15173
15174    pub fn add_selection_above(
15175        &mut self,
15176        action: &AddSelectionAbove,
15177        window: &mut Window,
15178        cx: &mut Context<Self>,
15179    ) {
15180        self.add_selection(true, action.skip_soft_wrap, window, cx);
15181    }
15182
15183    pub fn add_selection_below(
15184        &mut self,
15185        action: &AddSelectionBelow,
15186        window: &mut Window,
15187        cx: &mut Context<Self>,
15188    ) {
15189        self.add_selection(false, action.skip_soft_wrap, window, cx);
15190    }
15191
15192    fn add_selection(
15193        &mut self,
15194        above: bool,
15195        skip_soft_wrap: bool,
15196        window: &mut Window,
15197        cx: &mut Context<Self>,
15198    ) {
15199        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15200
15201        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15202        let all_selections = self.selections.all::<Point>(&display_map);
15203        let text_layout_details = self.text_layout_details(window, cx);
15204
15205        let (mut columnar_selections, new_selections_to_columnarize) = {
15206            if let Some(state) = self.add_selections_state.as_ref() {
15207                let columnar_selection_ids: HashSet<_> = state
15208                    .groups
15209                    .iter()
15210                    .flat_map(|group| group.stack.iter())
15211                    .copied()
15212                    .collect();
15213
15214                all_selections
15215                    .into_iter()
15216                    .partition(|s| columnar_selection_ids.contains(&s.id))
15217            } else {
15218                (Vec::new(), all_selections)
15219            }
15220        };
15221
15222        let mut state = self
15223            .add_selections_state
15224            .take()
15225            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15226
15227        for selection in new_selections_to_columnarize {
15228            let range = selection.display_range(&display_map).sorted();
15229            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15230            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15231            let positions = start_x.min(end_x)..start_x.max(end_x);
15232            let mut stack = Vec::new();
15233            for row in range.start.row().0..=range.end.row().0 {
15234                if let Some(selection) = self.selections.build_columnar_selection(
15235                    &display_map,
15236                    DisplayRow(row),
15237                    &positions,
15238                    selection.reversed,
15239                    &text_layout_details,
15240                ) {
15241                    stack.push(selection.id);
15242                    columnar_selections.push(selection);
15243                }
15244            }
15245            if !stack.is_empty() {
15246                if above {
15247                    stack.reverse();
15248                }
15249                state.groups.push(AddSelectionsGroup { above, stack });
15250            }
15251        }
15252
15253        let mut final_selections = Vec::new();
15254        let end_row = if above {
15255            DisplayRow(0)
15256        } else {
15257            display_map.max_point().row()
15258        };
15259
15260        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15261        // positions to place new selections, so we need to keep track of the
15262        // column range of the oldest selection in each group, because
15263        // intermediate selections may have been clamped to shorter lines.
15264        // selections may have been clamped to shorter lines.
15265        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15266            let mut map = HashMap::default();
15267            for group in state.groups.iter() {
15268                if let Some(oldest_id) = group.stack.first() {
15269                    if let Some(oldest_selection) =
15270                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15271                    {
15272                        let start_col = oldest_selection.start.column;
15273                        let end_col = oldest_selection.end.column;
15274                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15275                        for id in &group.stack {
15276                            map.insert(*id, goal_columns.clone());
15277                        }
15278                    }
15279                }
15280            }
15281            map
15282        } else {
15283            HashMap::default()
15284        };
15285
15286        let mut last_added_item_per_group = HashMap::default();
15287        for group in state.groups.iter_mut() {
15288            if let Some(last_id) = group.stack.last() {
15289                last_added_item_per_group.insert(*last_id, group);
15290            }
15291        }
15292
15293        for selection in columnar_selections {
15294            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15295                if above == group.above {
15296                    let range = selection.display_range(&display_map).sorted();
15297                    debug_assert_eq!(range.start.row(), range.end.row());
15298                    let row = range.start.row();
15299                    let positions =
15300                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15301                            Pixels::from(start)..Pixels::from(end)
15302                        } else {
15303                            let start_x =
15304                                display_map.x_for_display_point(range.start, &text_layout_details);
15305                            let end_x =
15306                                display_map.x_for_display_point(range.end, &text_layout_details);
15307                            start_x.min(end_x)..start_x.max(end_x)
15308                        };
15309
15310                    let maybe_new_selection = if skip_soft_wrap {
15311                        let goal_columns = goal_columns_by_selection_id
15312                            .remove(&selection.id)
15313                            .unwrap_or_else(|| {
15314                                let start_col = selection.start.column;
15315                                let end_col = selection.end.column;
15316                                start_col.min(end_col)..start_col.max(end_col)
15317                            });
15318                        self.selections.find_next_columnar_selection_by_buffer_row(
15319                            &display_map,
15320                            row,
15321                            end_row,
15322                            above,
15323                            &goal_columns,
15324                            selection.reversed,
15325                            &text_layout_details,
15326                        )
15327                    } else {
15328                        self.selections.find_next_columnar_selection_by_display_row(
15329                            &display_map,
15330                            row,
15331                            end_row,
15332                            above,
15333                            &positions,
15334                            selection.reversed,
15335                            &text_layout_details,
15336                        )
15337                    };
15338
15339                    if let Some(new_selection) = maybe_new_selection {
15340                        group.stack.push(new_selection.id);
15341                        if above {
15342                            final_selections.push(new_selection);
15343                            final_selections.push(selection);
15344                        } else {
15345                            final_selections.push(selection);
15346                            final_selections.push(new_selection);
15347                        }
15348                    } else {
15349                        final_selections.push(selection);
15350                    }
15351                } else {
15352                    group.stack.pop();
15353                }
15354            } else {
15355                final_selections.push(selection);
15356            }
15357        }
15358
15359        self.change_selections(Default::default(), window, cx, |s| {
15360            s.select(final_selections);
15361        });
15362
15363        let final_selection_ids: HashSet<_> = self
15364            .selections
15365            .all::<Point>(&display_map)
15366            .iter()
15367            .map(|s| s.id)
15368            .collect();
15369        state.groups.retain_mut(|group| {
15370            // selections might get merged above so we remove invalid items from stacks
15371            group.stack.retain(|id| final_selection_ids.contains(id));
15372
15373            // single selection in stack can be treated as initial state
15374            group.stack.len() > 1
15375        });
15376
15377        if !state.groups.is_empty() {
15378            self.add_selections_state = Some(state);
15379        }
15380    }
15381
15382    pub fn insert_snippet_at_selections(
15383        &mut self,
15384        action: &InsertSnippet,
15385        window: &mut Window,
15386        cx: &mut Context<Self>,
15387    ) {
15388        self.try_insert_snippet_at_selections(action, window, cx)
15389            .log_err();
15390    }
15391
15392    fn try_insert_snippet_at_selections(
15393        &mut self,
15394        action: &InsertSnippet,
15395        window: &mut Window,
15396        cx: &mut Context<Self>,
15397    ) -> Result<()> {
15398        let insertion_ranges = self
15399            .selections
15400            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15401            .into_iter()
15402            .map(|selection| selection.range())
15403            .collect_vec();
15404
15405        let snippet = if let Some(snippet_body) = &action.snippet {
15406            if action.language.is_none() && action.name.is_none() {
15407                Snippet::parse(snippet_body)?
15408            } else {
15409                bail!("`snippet` is mutually exclusive with `language` and `name`")
15410            }
15411        } else if let Some(name) = &action.name {
15412            let project = self.project().context("no project")?;
15413            let snippet_store = project.read(cx).snippets().read(cx);
15414            let snippet = snippet_store
15415                .snippets_for(action.language.clone(), cx)
15416                .into_iter()
15417                .find(|snippet| snippet.name == *name)
15418                .context("snippet not found")?;
15419            Snippet::parse(&snippet.body)?
15420        } else {
15421            // todo(andrew): open modal to select snippet
15422            bail!("`name` or `snippet` is required")
15423        };
15424
15425        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15426    }
15427
15428    fn select_match_ranges(
15429        &mut self,
15430        range: Range<MultiBufferOffset>,
15431        reversed: bool,
15432        replace_newest: bool,
15433        auto_scroll: Option<Autoscroll>,
15434        window: &mut Window,
15435        cx: &mut Context<Editor>,
15436    ) {
15437        self.unfold_ranges(
15438            std::slice::from_ref(&range),
15439            false,
15440            auto_scroll.is_some(),
15441            cx,
15442        );
15443        let effects = if let Some(scroll) = auto_scroll {
15444            SelectionEffects::scroll(scroll)
15445        } else {
15446            SelectionEffects::no_scroll()
15447        };
15448        self.change_selections(effects, window, cx, |s| {
15449            if replace_newest {
15450                s.delete(s.newest_anchor().id);
15451            }
15452            if reversed {
15453                s.insert_range(range.end..range.start);
15454            } else {
15455                s.insert_range(range);
15456            }
15457        });
15458    }
15459
15460    pub fn select_next_match_internal(
15461        &mut self,
15462        display_map: &DisplaySnapshot,
15463        replace_newest: bool,
15464        autoscroll: Option<Autoscroll>,
15465        window: &mut Window,
15466        cx: &mut Context<Self>,
15467    ) -> Result<()> {
15468        let buffer = display_map.buffer_snapshot();
15469        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15470        if let Some(mut select_next_state) = self.select_next_state.take() {
15471            let query = &select_next_state.query;
15472            if !select_next_state.done {
15473                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15474                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15475                let mut next_selected_range = None;
15476
15477                let bytes_after_last_selection =
15478                    buffer.bytes_in_range(last_selection.end..buffer.len());
15479                let bytes_before_first_selection =
15480                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15481                let query_matches = query
15482                    .stream_find_iter(bytes_after_last_selection)
15483                    .map(|result| (last_selection.end, result))
15484                    .chain(
15485                        query
15486                            .stream_find_iter(bytes_before_first_selection)
15487                            .map(|result| (MultiBufferOffset(0), result)),
15488                    );
15489
15490                for (start_offset, query_match) in query_matches {
15491                    let query_match = query_match.unwrap(); // can only fail due to I/O
15492                    let offset_range =
15493                        start_offset + query_match.start()..start_offset + query_match.end();
15494
15495                    if !select_next_state.wordwise
15496                        || (!buffer.is_inside_word(offset_range.start, None)
15497                            && !buffer.is_inside_word(offset_range.end, None))
15498                    {
15499                        let idx = selections
15500                            .partition_point(|selection| selection.end <= offset_range.start);
15501                        let overlaps = selections
15502                            .get(idx)
15503                            .map_or(false, |selection| selection.start < offset_range.end);
15504
15505                        if !overlaps {
15506                            next_selected_range = Some(offset_range);
15507                            break;
15508                        }
15509                    }
15510                }
15511
15512                if let Some(next_selected_range) = next_selected_range {
15513                    self.select_match_ranges(
15514                        next_selected_range,
15515                        last_selection.reversed,
15516                        replace_newest,
15517                        autoscroll,
15518                        window,
15519                        cx,
15520                    );
15521                } else {
15522                    select_next_state.done = true;
15523                }
15524            }
15525
15526            self.select_next_state = Some(select_next_state);
15527        } else {
15528            let mut only_carets = true;
15529            let mut same_text_selected = true;
15530            let mut selected_text = None;
15531
15532            let mut selections_iter = selections.iter().peekable();
15533            while let Some(selection) = selections_iter.next() {
15534                if selection.start != selection.end {
15535                    only_carets = false;
15536                }
15537
15538                if same_text_selected {
15539                    if selected_text.is_none() {
15540                        selected_text =
15541                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15542                    }
15543
15544                    if let Some(next_selection) = selections_iter.peek() {
15545                        if next_selection.len() == selection.len() {
15546                            let next_selected_text = buffer
15547                                .text_for_range(next_selection.range())
15548                                .collect::<String>();
15549                            if Some(next_selected_text) != selected_text {
15550                                same_text_selected = false;
15551                                selected_text = None;
15552                            }
15553                        } else {
15554                            same_text_selected = false;
15555                            selected_text = None;
15556                        }
15557                    }
15558                }
15559            }
15560
15561            if only_carets {
15562                for selection in &mut selections {
15563                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15564                    selection.start = word_range.start;
15565                    selection.end = word_range.end;
15566                    selection.goal = SelectionGoal::None;
15567                    selection.reversed = false;
15568                    self.select_match_ranges(
15569                        selection.start..selection.end,
15570                        selection.reversed,
15571                        replace_newest,
15572                        autoscroll,
15573                        window,
15574                        cx,
15575                    );
15576                }
15577
15578                if selections.len() == 1 {
15579                    let selection = selections
15580                        .last()
15581                        .expect("ensured that there's only one selection");
15582                    let query = buffer
15583                        .text_for_range(selection.start..selection.end)
15584                        .collect::<String>();
15585                    let is_empty = query.is_empty();
15586                    let select_state = SelectNextState {
15587                        query: self.build_query(&[query], cx)?,
15588                        wordwise: true,
15589                        done: is_empty,
15590                    };
15591                    self.select_next_state = Some(select_state);
15592                } else {
15593                    self.select_next_state = None;
15594                }
15595            } else if let Some(selected_text) = selected_text {
15596                self.select_next_state = Some(SelectNextState {
15597                    query: self.build_query(&[selected_text], cx)?,
15598                    wordwise: false,
15599                    done: false,
15600                });
15601                self.select_next_match_internal(
15602                    display_map,
15603                    replace_newest,
15604                    autoscroll,
15605                    window,
15606                    cx,
15607                )?;
15608            }
15609        }
15610        Ok(())
15611    }
15612
15613    pub fn select_all_matches(
15614        &mut self,
15615        _action: &SelectAllMatches,
15616        window: &mut Window,
15617        cx: &mut Context<Self>,
15618    ) -> Result<()> {
15619        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15620
15621        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15622
15623        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15624        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15625        else {
15626            return Ok(());
15627        };
15628
15629        let mut new_selections = Vec::new();
15630
15631        let reversed = self
15632            .selections
15633            .oldest::<MultiBufferOffset>(&display_map)
15634            .reversed;
15635        let buffer = display_map.buffer_snapshot();
15636        let query_matches = select_next_state
15637            .query
15638            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15639
15640        for query_match in query_matches.into_iter() {
15641            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15642            let offset_range = if reversed {
15643                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15644            } else {
15645                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15646            };
15647
15648            if !select_next_state.wordwise
15649                || (!buffer.is_inside_word(offset_range.start, None)
15650                    && !buffer.is_inside_word(offset_range.end, None))
15651            {
15652                new_selections.push(offset_range.start..offset_range.end);
15653            }
15654        }
15655
15656        select_next_state.done = true;
15657
15658        if new_selections.is_empty() {
15659            log::error!("bug: new_selections is empty in select_all_matches");
15660            return Ok(());
15661        }
15662
15663        self.unfold_ranges(&new_selections, false, false, cx);
15664        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15665            selections.select_ranges(new_selections)
15666        });
15667
15668        Ok(())
15669    }
15670
15671    pub fn select_next(
15672        &mut self,
15673        action: &SelectNext,
15674        window: &mut Window,
15675        cx: &mut Context<Self>,
15676    ) -> Result<()> {
15677        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15678        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15679        self.select_next_match_internal(
15680            &display_map,
15681            action.replace_newest,
15682            Some(Autoscroll::newest()),
15683            window,
15684            cx,
15685        )
15686    }
15687
15688    pub fn select_previous(
15689        &mut self,
15690        action: &SelectPrevious,
15691        window: &mut Window,
15692        cx: &mut Context<Self>,
15693    ) -> Result<()> {
15694        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15695        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15696        let buffer = display_map.buffer_snapshot();
15697        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15698        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15699            let query = &select_prev_state.query;
15700            if !select_prev_state.done {
15701                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15702                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15703                let mut next_selected_range = None;
15704                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15705                let bytes_before_last_selection =
15706                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15707                let bytes_after_first_selection =
15708                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15709                let query_matches = query
15710                    .stream_find_iter(bytes_before_last_selection)
15711                    .map(|result| (last_selection.start, result))
15712                    .chain(
15713                        query
15714                            .stream_find_iter(bytes_after_first_selection)
15715                            .map(|result| (buffer.len(), result)),
15716                    );
15717                for (end_offset, query_match) in query_matches {
15718                    let query_match = query_match.unwrap(); // can only fail due to I/O
15719                    let offset_range =
15720                        end_offset - query_match.end()..end_offset - query_match.start();
15721
15722                    if !select_prev_state.wordwise
15723                        || (!buffer.is_inside_word(offset_range.start, None)
15724                            && !buffer.is_inside_word(offset_range.end, None))
15725                    {
15726                        next_selected_range = Some(offset_range);
15727                        break;
15728                    }
15729                }
15730
15731                if let Some(next_selected_range) = next_selected_range {
15732                    self.select_match_ranges(
15733                        next_selected_range,
15734                        last_selection.reversed,
15735                        action.replace_newest,
15736                        Some(Autoscroll::newest()),
15737                        window,
15738                        cx,
15739                    );
15740                } else {
15741                    select_prev_state.done = true;
15742                }
15743            }
15744
15745            self.select_prev_state = Some(select_prev_state);
15746        } else {
15747            let mut only_carets = true;
15748            let mut same_text_selected = true;
15749            let mut selected_text = None;
15750
15751            let mut selections_iter = selections.iter().peekable();
15752            while let Some(selection) = selections_iter.next() {
15753                if selection.start != selection.end {
15754                    only_carets = false;
15755                }
15756
15757                if same_text_selected {
15758                    if selected_text.is_none() {
15759                        selected_text =
15760                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15761                    }
15762
15763                    if let Some(next_selection) = selections_iter.peek() {
15764                        if next_selection.len() == selection.len() {
15765                            let next_selected_text = buffer
15766                                .text_for_range(next_selection.range())
15767                                .collect::<String>();
15768                            if Some(next_selected_text) != selected_text {
15769                                same_text_selected = false;
15770                                selected_text = None;
15771                            }
15772                        } else {
15773                            same_text_selected = false;
15774                            selected_text = None;
15775                        }
15776                    }
15777                }
15778            }
15779
15780            if only_carets {
15781                for selection in &mut selections {
15782                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15783                    selection.start = word_range.start;
15784                    selection.end = word_range.end;
15785                    selection.goal = SelectionGoal::None;
15786                    selection.reversed = false;
15787                    self.select_match_ranges(
15788                        selection.start..selection.end,
15789                        selection.reversed,
15790                        action.replace_newest,
15791                        Some(Autoscroll::newest()),
15792                        window,
15793                        cx,
15794                    );
15795                }
15796                if selections.len() == 1 {
15797                    let selection = selections
15798                        .last()
15799                        .expect("ensured that there's only one selection");
15800                    let query = buffer
15801                        .text_for_range(selection.start..selection.end)
15802                        .collect::<String>();
15803                    let is_empty = query.is_empty();
15804                    let select_state = SelectNextState {
15805                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
15806                        wordwise: true,
15807                        done: is_empty,
15808                    };
15809                    self.select_prev_state = Some(select_state);
15810                } else {
15811                    self.select_prev_state = None;
15812                }
15813            } else if let Some(selected_text) = selected_text {
15814                self.select_prev_state = Some(SelectNextState {
15815                    query: self
15816                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
15817                    wordwise: false,
15818                    done: false,
15819                });
15820                self.select_previous(action, window, cx)?;
15821            }
15822        }
15823        Ok(())
15824    }
15825
15826    /// Builds an `AhoCorasick` automaton from the provided patterns, while
15827    /// setting the case sensitivity based on the global
15828    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
15829    /// editor's settings.
15830    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
15831    where
15832        I: IntoIterator<Item = P>,
15833        P: AsRef<[u8]>,
15834    {
15835        let case_sensitive = self
15836            .select_next_is_case_sensitive
15837            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
15838
15839        let mut builder = AhoCorasickBuilder::new();
15840        builder.ascii_case_insensitive(!case_sensitive);
15841        builder.build(patterns)
15842    }
15843
15844    pub fn find_next_match(
15845        &mut self,
15846        _: &FindNextMatch,
15847        window: &mut Window,
15848        cx: &mut Context<Self>,
15849    ) -> Result<()> {
15850        let selections = self.selections.disjoint_anchors_arc();
15851        match selections.first() {
15852            Some(first) if selections.len() >= 2 => {
15853                self.change_selections(Default::default(), window, cx, |s| {
15854                    s.select_ranges([first.range()]);
15855                });
15856            }
15857            _ => self.select_next(
15858                &SelectNext {
15859                    replace_newest: true,
15860                },
15861                window,
15862                cx,
15863            )?,
15864        }
15865        Ok(())
15866    }
15867
15868    pub fn find_previous_match(
15869        &mut self,
15870        _: &FindPreviousMatch,
15871        window: &mut Window,
15872        cx: &mut Context<Self>,
15873    ) -> Result<()> {
15874        let selections = self.selections.disjoint_anchors_arc();
15875        match selections.last() {
15876            Some(last) if selections.len() >= 2 => {
15877                self.change_selections(Default::default(), window, cx, |s| {
15878                    s.select_ranges([last.range()]);
15879                });
15880            }
15881            _ => self.select_previous(
15882                &SelectPrevious {
15883                    replace_newest: true,
15884                },
15885                window,
15886                cx,
15887            )?,
15888        }
15889        Ok(())
15890    }
15891
15892    pub fn toggle_comments(
15893        &mut self,
15894        action: &ToggleComments,
15895        window: &mut Window,
15896        cx: &mut Context<Self>,
15897    ) {
15898        if self.read_only(cx) {
15899            return;
15900        }
15901        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15902        let text_layout_details = &self.text_layout_details(window, cx);
15903        self.transact(window, cx, |this, window, cx| {
15904            let mut selections = this
15905                .selections
15906                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
15907            let mut edits = Vec::new();
15908            let mut selection_edit_ranges = Vec::new();
15909            let mut last_toggled_row = None;
15910            let snapshot = this.buffer.read(cx).read(cx);
15911            let empty_str: Arc<str> = Arc::default();
15912            let mut suffixes_inserted = Vec::new();
15913            let ignore_indent = action.ignore_indent;
15914
15915            fn comment_prefix_range(
15916                snapshot: &MultiBufferSnapshot,
15917                row: MultiBufferRow,
15918                comment_prefix: &str,
15919                comment_prefix_whitespace: &str,
15920                ignore_indent: bool,
15921            ) -> Range<Point> {
15922                let indent_size = if ignore_indent {
15923                    0
15924                } else {
15925                    snapshot.indent_size_for_line(row).len
15926                };
15927
15928                let start = Point::new(row.0, indent_size);
15929
15930                let mut line_bytes = snapshot
15931                    .bytes_in_range(start..snapshot.max_point())
15932                    .flatten()
15933                    .copied();
15934
15935                // If this line currently begins with the line comment prefix, then record
15936                // the range containing the prefix.
15937                if line_bytes
15938                    .by_ref()
15939                    .take(comment_prefix.len())
15940                    .eq(comment_prefix.bytes())
15941                {
15942                    // Include any whitespace that matches the comment prefix.
15943                    let matching_whitespace_len = line_bytes
15944                        .zip(comment_prefix_whitespace.bytes())
15945                        .take_while(|(a, b)| a == b)
15946                        .count() as u32;
15947                    let end = Point::new(
15948                        start.row,
15949                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
15950                    );
15951                    start..end
15952                } else {
15953                    start..start
15954                }
15955            }
15956
15957            fn comment_suffix_range(
15958                snapshot: &MultiBufferSnapshot,
15959                row: MultiBufferRow,
15960                comment_suffix: &str,
15961                comment_suffix_has_leading_space: bool,
15962            ) -> Range<Point> {
15963                let end = Point::new(row.0, snapshot.line_len(row));
15964                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
15965
15966                let mut line_end_bytes = snapshot
15967                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
15968                    .flatten()
15969                    .copied();
15970
15971                let leading_space_len = if suffix_start_column > 0
15972                    && line_end_bytes.next() == Some(b' ')
15973                    && comment_suffix_has_leading_space
15974                {
15975                    1
15976                } else {
15977                    0
15978                };
15979
15980                // If this line currently begins with the line comment prefix, then record
15981                // the range containing the prefix.
15982                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
15983                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
15984                    start..end
15985                } else {
15986                    end..end
15987                }
15988            }
15989
15990            // TODO: Handle selections that cross excerpts
15991            for selection in &mut selections {
15992                let start_column = snapshot
15993                    .indent_size_for_line(MultiBufferRow(selection.start.row))
15994                    .len;
15995                let language = if let Some(language) =
15996                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
15997                {
15998                    language
15999                } else {
16000                    continue;
16001                };
16002
16003                selection_edit_ranges.clear();
16004
16005                // If multiple selections contain a given row, avoid processing that
16006                // row more than once.
16007                let mut start_row = MultiBufferRow(selection.start.row);
16008                if last_toggled_row == Some(start_row) {
16009                    start_row = start_row.next_row();
16010                }
16011                let end_row =
16012                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16013                        MultiBufferRow(selection.end.row - 1)
16014                    } else {
16015                        MultiBufferRow(selection.end.row)
16016                    };
16017                last_toggled_row = Some(end_row);
16018
16019                if start_row > end_row {
16020                    continue;
16021                }
16022
16023                // If the language has line comments, toggle those.
16024                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16025
16026                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16027                if ignore_indent {
16028                    full_comment_prefixes = full_comment_prefixes
16029                        .into_iter()
16030                        .map(|s| Arc::from(s.trim_end()))
16031                        .collect();
16032                }
16033
16034                if !full_comment_prefixes.is_empty() {
16035                    let first_prefix = full_comment_prefixes
16036                        .first()
16037                        .expect("prefixes is non-empty");
16038                    let prefix_trimmed_lengths = full_comment_prefixes
16039                        .iter()
16040                        .map(|p| p.trim_end_matches(' ').len())
16041                        .collect::<SmallVec<[usize; 4]>>();
16042
16043                    let mut all_selection_lines_are_comments = true;
16044
16045                    for row in start_row.0..=end_row.0 {
16046                        let row = MultiBufferRow(row);
16047                        if start_row < end_row && snapshot.is_line_blank(row) {
16048                            continue;
16049                        }
16050
16051                        let prefix_range = full_comment_prefixes
16052                            .iter()
16053                            .zip(prefix_trimmed_lengths.iter().copied())
16054                            .map(|(prefix, trimmed_prefix_len)| {
16055                                comment_prefix_range(
16056                                    snapshot.deref(),
16057                                    row,
16058                                    &prefix[..trimmed_prefix_len],
16059                                    &prefix[trimmed_prefix_len..],
16060                                    ignore_indent,
16061                                )
16062                            })
16063                            .max_by_key(|range| range.end.column - range.start.column)
16064                            .expect("prefixes is non-empty");
16065
16066                        if prefix_range.is_empty() {
16067                            all_selection_lines_are_comments = false;
16068                        }
16069
16070                        selection_edit_ranges.push(prefix_range);
16071                    }
16072
16073                    if all_selection_lines_are_comments {
16074                        edits.extend(
16075                            selection_edit_ranges
16076                                .iter()
16077                                .cloned()
16078                                .map(|range| (range, empty_str.clone())),
16079                        );
16080                    } else {
16081                        let min_column = selection_edit_ranges
16082                            .iter()
16083                            .map(|range| range.start.column)
16084                            .min()
16085                            .unwrap_or(0);
16086                        edits.extend(selection_edit_ranges.iter().map(|range| {
16087                            let position = Point::new(range.start.row, min_column);
16088                            (position..position, first_prefix.clone())
16089                        }));
16090                    }
16091                } else if let Some(BlockCommentConfig {
16092                    start: full_comment_prefix,
16093                    end: comment_suffix,
16094                    ..
16095                }) = language.block_comment()
16096                {
16097                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16098                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16099                    let prefix_range = comment_prefix_range(
16100                        snapshot.deref(),
16101                        start_row,
16102                        comment_prefix,
16103                        comment_prefix_whitespace,
16104                        ignore_indent,
16105                    );
16106                    let suffix_range = comment_suffix_range(
16107                        snapshot.deref(),
16108                        end_row,
16109                        comment_suffix.trim_start_matches(' '),
16110                        comment_suffix.starts_with(' '),
16111                    );
16112
16113                    if prefix_range.is_empty() || suffix_range.is_empty() {
16114                        edits.push((
16115                            prefix_range.start..prefix_range.start,
16116                            full_comment_prefix.clone(),
16117                        ));
16118                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16119                        suffixes_inserted.push((end_row, comment_suffix.len()));
16120                    } else {
16121                        edits.push((prefix_range, empty_str.clone()));
16122                        edits.push((suffix_range, empty_str.clone()));
16123                    }
16124                } else {
16125                    continue;
16126                }
16127            }
16128
16129            drop(snapshot);
16130            this.buffer.update(cx, |buffer, cx| {
16131                buffer.edit(edits, None, cx);
16132            });
16133
16134            // Adjust selections so that they end before any comment suffixes that
16135            // were inserted.
16136            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16137            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16138            let snapshot = this.buffer.read(cx).read(cx);
16139            for selection in &mut selections {
16140                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16141                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16142                        Ordering::Less => {
16143                            suffixes_inserted.next();
16144                            continue;
16145                        }
16146                        Ordering::Greater => break,
16147                        Ordering::Equal => {
16148                            if selection.end.column == snapshot.line_len(row) {
16149                                if selection.is_empty() {
16150                                    selection.start.column -= suffix_len as u32;
16151                                }
16152                                selection.end.column -= suffix_len as u32;
16153                            }
16154                            break;
16155                        }
16156                    }
16157                }
16158            }
16159
16160            drop(snapshot);
16161            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16162
16163            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16164            let selections_on_single_row = selections.windows(2).all(|selections| {
16165                selections[0].start.row == selections[1].start.row
16166                    && selections[0].end.row == selections[1].end.row
16167                    && selections[0].start.row == selections[0].end.row
16168            });
16169            let selections_selecting = selections
16170                .iter()
16171                .any(|selection| selection.start != selection.end);
16172            let advance_downwards = action.advance_downwards
16173                && selections_on_single_row
16174                && !selections_selecting
16175                && !matches!(this.mode, EditorMode::SingleLine);
16176
16177            if advance_downwards {
16178                let snapshot = this.buffer.read(cx).snapshot(cx);
16179
16180                this.change_selections(Default::default(), window, cx, |s| {
16181                    s.move_cursors_with(|display_snapshot, display_point, _| {
16182                        let mut point = display_point.to_point(display_snapshot);
16183                        point.row += 1;
16184                        point = snapshot.clip_point(point, Bias::Left);
16185                        let display_point = point.to_display_point(display_snapshot);
16186                        let goal = SelectionGoal::HorizontalPosition(
16187                            display_snapshot
16188                                .x_for_display_point(display_point, text_layout_details)
16189                                .into(),
16190                        );
16191                        (display_point, goal)
16192                    })
16193                });
16194            }
16195        });
16196    }
16197
16198    pub fn select_enclosing_symbol(
16199        &mut self,
16200        _: &SelectEnclosingSymbol,
16201        window: &mut Window,
16202        cx: &mut Context<Self>,
16203    ) {
16204        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16205
16206        let buffer = self.buffer.read(cx).snapshot(cx);
16207        let old_selections = self
16208            .selections
16209            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16210            .into_boxed_slice();
16211
16212        fn update_selection(
16213            selection: &Selection<MultiBufferOffset>,
16214            buffer_snap: &MultiBufferSnapshot,
16215        ) -> Option<Selection<MultiBufferOffset>> {
16216            let cursor = selection.head();
16217            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16218            for symbol in symbols.iter().rev() {
16219                let start = symbol.range.start.to_offset(buffer_snap);
16220                let end = symbol.range.end.to_offset(buffer_snap);
16221                let new_range = start..end;
16222                if start < selection.start || end > selection.end {
16223                    return Some(Selection {
16224                        id: selection.id,
16225                        start: new_range.start,
16226                        end: new_range.end,
16227                        goal: SelectionGoal::None,
16228                        reversed: selection.reversed,
16229                    });
16230                }
16231            }
16232            None
16233        }
16234
16235        let mut selected_larger_symbol = false;
16236        let new_selections = old_selections
16237            .iter()
16238            .map(|selection| match update_selection(selection, &buffer) {
16239                Some(new_selection) => {
16240                    if new_selection.range() != selection.range() {
16241                        selected_larger_symbol = true;
16242                    }
16243                    new_selection
16244                }
16245                None => selection.clone(),
16246            })
16247            .collect::<Vec<_>>();
16248
16249        if selected_larger_symbol {
16250            self.change_selections(Default::default(), window, cx, |s| {
16251                s.select(new_selections);
16252            });
16253        }
16254    }
16255
16256    pub fn select_larger_syntax_node(
16257        &mut self,
16258        _: &SelectLargerSyntaxNode,
16259        window: &mut Window,
16260        cx: &mut Context<Self>,
16261    ) {
16262        let Some(visible_row_count) = self.visible_row_count() else {
16263            return;
16264        };
16265        let old_selections: Box<[_]> = self
16266            .selections
16267            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16268            .into();
16269        if old_selections.is_empty() {
16270            return;
16271        }
16272
16273        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16274
16275        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16276        let buffer = self.buffer.read(cx).snapshot(cx);
16277
16278        let mut selected_larger_node = false;
16279        let mut new_selections = old_selections
16280            .iter()
16281            .map(|selection| {
16282                let old_range = selection.start..selection.end;
16283
16284                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16285                    // manually select word at selection
16286                    if ["string_content", "inline"].contains(&node.kind()) {
16287                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16288                        // ignore if word is already selected
16289                        if !word_range.is_empty() && old_range != word_range {
16290                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16291                            // only select word if start and end point belongs to same word
16292                            if word_range == last_word_range {
16293                                selected_larger_node = true;
16294                                return Selection {
16295                                    id: selection.id,
16296                                    start: word_range.start,
16297                                    end: word_range.end,
16298                                    goal: SelectionGoal::None,
16299                                    reversed: selection.reversed,
16300                                };
16301                            }
16302                        }
16303                    }
16304                }
16305
16306                let mut new_range = old_range.clone();
16307                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16308                    new_range = range;
16309                    if !node.is_named() {
16310                        continue;
16311                    }
16312                    if !display_map.intersects_fold(new_range.start)
16313                        && !display_map.intersects_fold(new_range.end)
16314                    {
16315                        break;
16316                    }
16317                }
16318
16319                selected_larger_node |= new_range != old_range;
16320                Selection {
16321                    id: selection.id,
16322                    start: new_range.start,
16323                    end: new_range.end,
16324                    goal: SelectionGoal::None,
16325                    reversed: selection.reversed,
16326                }
16327            })
16328            .collect::<Vec<_>>();
16329
16330        if !selected_larger_node {
16331            return; // don't put this call in the history
16332        }
16333
16334        // scroll based on transformation done to the last selection created by the user
16335        let (last_old, last_new) = old_selections
16336            .last()
16337            .zip(new_selections.last().cloned())
16338            .expect("old_selections isn't empty");
16339
16340        // revert selection
16341        let is_selection_reversed = {
16342            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16343            new_selections.last_mut().expect("checked above").reversed =
16344                should_newest_selection_be_reversed;
16345            should_newest_selection_be_reversed
16346        };
16347
16348        if selected_larger_node {
16349            self.select_syntax_node_history.disable_clearing = true;
16350            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16351                s.select(new_selections.clone());
16352            });
16353            self.select_syntax_node_history.disable_clearing = false;
16354        }
16355
16356        let start_row = last_new.start.to_display_point(&display_map).row().0;
16357        let end_row = last_new.end.to_display_point(&display_map).row().0;
16358        let selection_height = end_row - start_row + 1;
16359        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16360
16361        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16362        let scroll_behavior = if fits_on_the_screen {
16363            self.request_autoscroll(Autoscroll::fit(), cx);
16364            SelectSyntaxNodeScrollBehavior::FitSelection
16365        } else if is_selection_reversed {
16366            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16367            SelectSyntaxNodeScrollBehavior::CursorTop
16368        } else {
16369            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16370            SelectSyntaxNodeScrollBehavior::CursorBottom
16371        };
16372
16373        self.select_syntax_node_history.push((
16374            old_selections,
16375            scroll_behavior,
16376            is_selection_reversed,
16377        ));
16378    }
16379
16380    pub fn select_smaller_syntax_node(
16381        &mut self,
16382        _: &SelectSmallerSyntaxNode,
16383        window: &mut Window,
16384        cx: &mut Context<Self>,
16385    ) {
16386        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16387
16388        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16389            self.select_syntax_node_history.pop()
16390        {
16391            if let Some(selection) = selections.last_mut() {
16392                selection.reversed = is_selection_reversed;
16393            }
16394
16395            self.select_syntax_node_history.disable_clearing = true;
16396            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16397                s.select(selections.to_vec());
16398            });
16399            self.select_syntax_node_history.disable_clearing = false;
16400
16401            match scroll_behavior {
16402                SelectSyntaxNodeScrollBehavior::CursorTop => {
16403                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16404                }
16405                SelectSyntaxNodeScrollBehavior::FitSelection => {
16406                    self.request_autoscroll(Autoscroll::fit(), cx);
16407                }
16408                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16409                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16410                }
16411            }
16412        }
16413    }
16414
16415    pub fn unwrap_syntax_node(
16416        &mut self,
16417        _: &UnwrapSyntaxNode,
16418        window: &mut Window,
16419        cx: &mut Context<Self>,
16420    ) {
16421        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16422
16423        let buffer = self.buffer.read(cx).snapshot(cx);
16424        let selections = self
16425            .selections
16426            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16427            .into_iter()
16428            // subtracting the offset requires sorting
16429            .sorted_by_key(|i| i.start);
16430
16431        let full_edits = selections
16432            .into_iter()
16433            .filter_map(|selection| {
16434                let child = if selection.is_empty()
16435                    && let Some((_, ancestor_range)) =
16436                        buffer.syntax_ancestor(selection.start..selection.end)
16437                {
16438                    ancestor_range
16439                } else {
16440                    selection.range()
16441                };
16442
16443                let mut parent = child.clone();
16444                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16445                    parent = ancestor_range;
16446                    if parent.start < child.start || parent.end > child.end {
16447                        break;
16448                    }
16449                }
16450
16451                if parent == child {
16452                    return None;
16453                }
16454                let text = buffer.text_for_range(child).collect::<String>();
16455                Some((selection.id, parent, text))
16456            })
16457            .collect::<Vec<_>>();
16458        if full_edits.is_empty() {
16459            return;
16460        }
16461
16462        self.transact(window, cx, |this, window, cx| {
16463            this.buffer.update(cx, |buffer, cx| {
16464                buffer.edit(
16465                    full_edits
16466                        .iter()
16467                        .map(|(_, p, t)| (p.clone(), t.clone()))
16468                        .collect::<Vec<_>>(),
16469                    None,
16470                    cx,
16471                );
16472            });
16473            this.change_selections(Default::default(), window, cx, |s| {
16474                let mut offset = 0;
16475                let mut selections = vec![];
16476                for (id, parent, text) in full_edits {
16477                    let start = parent.start - offset;
16478                    offset += (parent.end - parent.start) - text.len();
16479                    selections.push(Selection {
16480                        id,
16481                        start,
16482                        end: start + text.len(),
16483                        reversed: false,
16484                        goal: Default::default(),
16485                    });
16486                }
16487                s.select(selections);
16488            });
16489        });
16490    }
16491
16492    pub fn select_next_syntax_node(
16493        &mut self,
16494        _: &SelectNextSyntaxNode,
16495        window: &mut Window,
16496        cx: &mut Context<Self>,
16497    ) {
16498        let old_selections: Box<[_]> = self
16499            .selections
16500            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16501            .into();
16502        if old_selections.is_empty() {
16503            return;
16504        }
16505
16506        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16507
16508        let buffer = self.buffer.read(cx).snapshot(cx);
16509        let mut selected_sibling = false;
16510
16511        let new_selections = old_selections
16512            .iter()
16513            .map(|selection| {
16514                let old_range = selection.start..selection.end;
16515
16516                let old_range =
16517                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16518                let excerpt = buffer.excerpt_containing(old_range.clone());
16519
16520                if let Some(mut excerpt) = excerpt
16521                    && let Some(node) = excerpt
16522                        .buffer()
16523                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16524                {
16525                    let new_range = excerpt.map_range_from_buffer(
16526                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16527                    );
16528                    selected_sibling = true;
16529                    Selection {
16530                        id: selection.id,
16531                        start: new_range.start,
16532                        end: new_range.end,
16533                        goal: SelectionGoal::None,
16534                        reversed: selection.reversed,
16535                    }
16536                } else {
16537                    selection.clone()
16538                }
16539            })
16540            .collect::<Vec<_>>();
16541
16542        if selected_sibling {
16543            self.change_selections(
16544                SelectionEffects::scroll(Autoscroll::fit()),
16545                window,
16546                cx,
16547                |s| {
16548                    s.select(new_selections);
16549                },
16550            );
16551        }
16552    }
16553
16554    pub fn select_prev_syntax_node(
16555        &mut self,
16556        _: &SelectPreviousSyntaxNode,
16557        window: &mut Window,
16558        cx: &mut Context<Self>,
16559    ) {
16560        let old_selections: Box<[_]> = self
16561            .selections
16562            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16563            .into();
16564        if old_selections.is_empty() {
16565            return;
16566        }
16567
16568        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16569
16570        let buffer = self.buffer.read(cx).snapshot(cx);
16571        let mut selected_sibling = false;
16572
16573        let new_selections = old_selections
16574            .iter()
16575            .map(|selection| {
16576                let old_range = selection.start..selection.end;
16577                let old_range =
16578                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16579                let excerpt = buffer.excerpt_containing(old_range.clone());
16580
16581                if let Some(mut excerpt) = excerpt
16582                    && let Some(node) = excerpt
16583                        .buffer()
16584                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16585                {
16586                    let new_range = excerpt.map_range_from_buffer(
16587                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16588                    );
16589                    selected_sibling = true;
16590                    Selection {
16591                        id: selection.id,
16592                        start: new_range.start,
16593                        end: new_range.end,
16594                        goal: SelectionGoal::None,
16595                        reversed: selection.reversed,
16596                    }
16597                } else {
16598                    selection.clone()
16599                }
16600            })
16601            .collect::<Vec<_>>();
16602
16603        if selected_sibling {
16604            self.change_selections(
16605                SelectionEffects::scroll(Autoscroll::fit()),
16606                window,
16607                cx,
16608                |s| {
16609                    s.select(new_selections);
16610                },
16611            );
16612        }
16613    }
16614
16615    pub fn move_to_start_of_larger_syntax_node(
16616        &mut self,
16617        _: &MoveToStartOfLargerSyntaxNode,
16618        window: &mut Window,
16619        cx: &mut Context<Self>,
16620    ) {
16621        self.move_cursors_to_syntax_nodes(window, cx, false);
16622    }
16623
16624    pub fn move_to_end_of_larger_syntax_node(
16625        &mut self,
16626        _: &MoveToEndOfLargerSyntaxNode,
16627        window: &mut Window,
16628        cx: &mut Context<Self>,
16629    ) {
16630        self.move_cursors_to_syntax_nodes(window, cx, true);
16631    }
16632
16633    fn find_syntax_node_boundary(
16634        &self,
16635        selection_pos: MultiBufferOffset,
16636        move_to_end: bool,
16637        display_map: &DisplaySnapshot,
16638        buffer: &MultiBufferSnapshot,
16639    ) -> MultiBufferOffset {
16640        let old_range = selection_pos..selection_pos;
16641        let mut new_pos = selection_pos;
16642        let mut search_range = old_range;
16643        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16644            search_range = range.clone();
16645            if !node.is_named()
16646                || display_map.intersects_fold(range.start)
16647                || display_map.intersects_fold(range.end)
16648                // If cursor is already at the end of the syntax node, continue searching
16649                || (move_to_end && range.end == selection_pos)
16650                // If cursor is already at the start of the syntax node, continue searching
16651                || (!move_to_end && range.start == selection_pos)
16652            {
16653                continue;
16654            }
16655
16656            // If we found a string_content node, find the largest parent that is still string_content
16657            // Enables us to skip to the end of strings without taking multiple steps inside the string
16658            let (_, final_range) = if node.kind() == "string_content" {
16659                let mut current_node = node;
16660                let mut current_range = range;
16661                while let Some((parent, parent_range)) =
16662                    buffer.syntax_ancestor(current_range.clone())
16663                {
16664                    if parent.kind() == "string_content" {
16665                        current_node = parent;
16666                        current_range = parent_range;
16667                    } else {
16668                        break;
16669                    }
16670                }
16671
16672                (current_node, current_range)
16673            } else {
16674                (node, range)
16675            };
16676
16677            new_pos = if move_to_end {
16678                final_range.end
16679            } else {
16680                final_range.start
16681            };
16682
16683            break;
16684        }
16685
16686        new_pos
16687    }
16688
16689    fn move_cursors_to_syntax_nodes(
16690        &mut self,
16691        window: &mut Window,
16692        cx: &mut Context<Self>,
16693        move_to_end: bool,
16694    ) -> bool {
16695        let old_selections: Box<[_]> = self
16696            .selections
16697            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16698            .into();
16699        if old_selections.is_empty() {
16700            return false;
16701        }
16702
16703        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16704
16705        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16706        let buffer = self.buffer.read(cx).snapshot(cx);
16707
16708        let mut any_cursor_moved = false;
16709        let new_selections = old_selections
16710            .iter()
16711            .map(|selection| {
16712                if !selection.is_empty() {
16713                    return selection.clone();
16714                }
16715
16716                let selection_pos = selection.head();
16717                let new_pos = self.find_syntax_node_boundary(
16718                    selection_pos,
16719                    move_to_end,
16720                    &display_map,
16721                    &buffer,
16722                );
16723
16724                any_cursor_moved |= new_pos != selection_pos;
16725
16726                Selection {
16727                    id: selection.id,
16728                    start: new_pos,
16729                    end: new_pos,
16730                    goal: SelectionGoal::None,
16731                    reversed: false,
16732                }
16733            })
16734            .collect::<Vec<_>>();
16735
16736        self.change_selections(Default::default(), window, cx, |s| {
16737            s.select(new_selections);
16738        });
16739        self.request_autoscroll(Autoscroll::newest(), cx);
16740
16741        any_cursor_moved
16742    }
16743
16744    pub fn select_to_start_of_larger_syntax_node(
16745        &mut self,
16746        _: &SelectToStartOfLargerSyntaxNode,
16747        window: &mut Window,
16748        cx: &mut Context<Self>,
16749    ) {
16750        self.select_to_syntax_nodes(window, cx, false);
16751    }
16752
16753    pub fn select_to_end_of_larger_syntax_node(
16754        &mut self,
16755        _: &SelectToEndOfLargerSyntaxNode,
16756        window: &mut Window,
16757        cx: &mut Context<Self>,
16758    ) {
16759        self.select_to_syntax_nodes(window, cx, true);
16760    }
16761
16762    fn select_to_syntax_nodes(
16763        &mut self,
16764        window: &mut Window,
16765        cx: &mut Context<Self>,
16766        move_to_end: bool,
16767    ) {
16768        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16769
16770        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16771        let buffer = self.buffer.read(cx).snapshot(cx);
16772        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
16773
16774        let new_selections = old_selections
16775            .iter()
16776            .map(|selection| {
16777                let new_pos = self.find_syntax_node_boundary(
16778                    selection.head(),
16779                    move_to_end,
16780                    &display_map,
16781                    &buffer,
16782                );
16783
16784                let mut new_selection = selection.clone();
16785                new_selection.set_head(new_pos, SelectionGoal::None);
16786                new_selection
16787            })
16788            .collect::<Vec<_>>();
16789
16790        self.change_selections(Default::default(), window, cx, |s| {
16791            s.select(new_selections);
16792        });
16793    }
16794
16795    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
16796        if !EditorSettings::get_global(cx).gutter.runnables {
16797            self.clear_tasks();
16798            return Task::ready(());
16799        }
16800        let project = self.project().map(Entity::downgrade);
16801        let task_sources = self.lsp_task_sources(cx);
16802        let multi_buffer = self.buffer.downgrade();
16803        cx.spawn_in(window, async move |editor, cx| {
16804            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
16805            let Some(project) = project.and_then(|p| p.upgrade()) else {
16806                return;
16807            };
16808            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
16809                this.display_map.update(cx, |map, cx| map.snapshot(cx))
16810            }) else {
16811                return;
16812            };
16813
16814            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
16815            if hide_runnables {
16816                return;
16817            }
16818            let new_rows =
16819                cx.background_spawn({
16820                    let snapshot = display_snapshot.clone();
16821                    async move {
16822                        Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
16823                    }
16824                })
16825                    .await;
16826            let Ok(lsp_tasks) =
16827                cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
16828            else {
16829                return;
16830            };
16831            let lsp_tasks = lsp_tasks.await;
16832
16833            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
16834                lsp_tasks
16835                    .into_iter()
16836                    .flat_map(|(kind, tasks)| {
16837                        tasks.into_iter().filter_map(move |(location, task)| {
16838                            Some((kind.clone(), location?, task))
16839                        })
16840                    })
16841                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
16842                        let buffer = location.target.buffer;
16843                        let buffer_snapshot = buffer.read(cx).snapshot();
16844                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
16845                            |(excerpt_id, snapshot, _)| {
16846                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
16847                                    display_snapshot
16848                                        .buffer_snapshot()
16849                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
16850                                } else {
16851                                    None
16852                                }
16853                            },
16854                        );
16855                        if let Some(offset) = offset {
16856                            let task_buffer_range =
16857                                location.target.range.to_point(&buffer_snapshot);
16858                            let context_buffer_range =
16859                                task_buffer_range.to_offset(&buffer_snapshot);
16860                            let context_range = BufferOffset(context_buffer_range.start)
16861                                ..BufferOffset(context_buffer_range.end);
16862
16863                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
16864                                .or_insert_with(|| RunnableTasks {
16865                                    templates: Vec::new(),
16866                                    offset,
16867                                    column: task_buffer_range.start.column,
16868                                    extra_variables: HashMap::default(),
16869                                    context_range,
16870                                })
16871                                .templates
16872                                .push((kind, task.original_task().clone()));
16873                        }
16874
16875                        acc
16876                    })
16877            }) else {
16878                return;
16879            };
16880
16881            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
16882                buffer.language_settings(cx).tasks.prefer_lsp
16883            }) else {
16884                return;
16885            };
16886
16887            let rows = Self::runnable_rows(
16888                project,
16889                display_snapshot,
16890                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
16891                new_rows,
16892                cx.clone(),
16893            )
16894            .await;
16895            editor
16896                .update(cx, |editor, _| {
16897                    editor.clear_tasks();
16898                    for (key, mut value) in rows {
16899                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
16900                            value.templates.extend(lsp_tasks.templates);
16901                        }
16902
16903                        editor.insert_tasks(key, value);
16904                    }
16905                    for (key, value) in lsp_tasks_by_rows {
16906                        editor.insert_tasks(key, value);
16907                    }
16908                })
16909                .ok();
16910        })
16911    }
16912    fn fetch_runnable_ranges(
16913        snapshot: &DisplaySnapshot,
16914        range: Range<Anchor>,
16915    ) -> Vec<(Range<MultiBufferOffset>, language::RunnableRange)> {
16916        snapshot.buffer_snapshot().runnable_ranges(range).collect()
16917    }
16918
16919    fn runnable_rows(
16920        project: Entity<Project>,
16921        snapshot: DisplaySnapshot,
16922        prefer_lsp: bool,
16923        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
16924        cx: AsyncWindowContext,
16925    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
16926        cx.spawn(async move |cx| {
16927            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
16928            for (run_range, mut runnable) in runnable_ranges {
16929                let Some(tasks) = cx
16930                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
16931                    .ok()
16932                else {
16933                    continue;
16934                };
16935                let mut tasks = tasks.await;
16936
16937                if prefer_lsp {
16938                    tasks.retain(|(task_kind, _)| {
16939                        !matches!(task_kind, TaskSourceKind::Language { .. })
16940                    });
16941                }
16942                if tasks.is_empty() {
16943                    continue;
16944                }
16945
16946                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
16947                let Some(row) = snapshot
16948                    .buffer_snapshot()
16949                    .buffer_line_for_row(MultiBufferRow(point.row))
16950                    .map(|(_, range)| range.start.row)
16951                else {
16952                    continue;
16953                };
16954
16955                let context_range =
16956                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
16957                runnable_rows.push((
16958                    (runnable.buffer_id, row),
16959                    RunnableTasks {
16960                        templates: tasks,
16961                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
16962                        context_range,
16963                        column: point.column,
16964                        extra_variables: runnable.extra_captures,
16965                    },
16966                ));
16967            }
16968            runnable_rows
16969        })
16970    }
16971
16972    fn templates_with_tags(
16973        project: &Entity<Project>,
16974        runnable: &mut Runnable,
16975        cx: &mut App,
16976    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
16977        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
16978            let (worktree_id, file) = project
16979                .buffer_for_id(runnable.buffer, cx)
16980                .and_then(|buffer| buffer.read(cx).file())
16981                .map(|file| (file.worktree_id(cx), file.clone()))
16982                .unzip();
16983
16984            (
16985                project.task_store().read(cx).task_inventory().cloned(),
16986                worktree_id,
16987                file,
16988            )
16989        });
16990
16991        let tags = mem::take(&mut runnable.tags);
16992        let language = runnable.language.clone();
16993        cx.spawn(async move |cx| {
16994            let mut templates_with_tags = Vec::new();
16995            if let Some(inventory) = inventory {
16996                for RunnableTag(tag) in tags {
16997                    let new_tasks = inventory.update(cx, |inventory, cx| {
16998                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
16999                    });
17000                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
17001                        move |(_, template)| {
17002                            template.tags.iter().any(|source_tag| source_tag == &tag)
17003                        },
17004                    ));
17005                }
17006            }
17007            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
17008
17009            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
17010                // Strongest source wins; if we have worktree tag binding, prefer that to
17011                // global and language bindings;
17012                // if we have a global binding, prefer that to language binding.
17013                let first_mismatch = templates_with_tags
17014                    .iter()
17015                    .position(|(tag_source, _)| tag_source != leading_tag_source);
17016                if let Some(index) = first_mismatch {
17017                    templates_with_tags.truncate(index);
17018                }
17019            }
17020
17021            templates_with_tags
17022        })
17023    }
17024
17025    pub fn move_to_enclosing_bracket(
17026        &mut self,
17027        _: &MoveToEnclosingBracket,
17028        window: &mut Window,
17029        cx: &mut Context<Self>,
17030    ) {
17031        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17032        self.change_selections(Default::default(), window, cx, |s| {
17033            s.move_offsets_with(|snapshot, selection| {
17034                let Some(enclosing_bracket_ranges) =
17035                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17036                else {
17037                    return;
17038                };
17039
17040                let mut best_length = usize::MAX;
17041                let mut best_inside = false;
17042                let mut best_in_bracket_range = false;
17043                let mut best_destination = None;
17044                for (open, close) in enclosing_bracket_ranges {
17045                    let close = close.to_inclusive();
17046                    let length = *close.end() - open.start;
17047                    let inside = selection.start >= open.end && selection.end <= *close.start();
17048                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17049                        || close.contains(&selection.head());
17050
17051                    // If best is next to a bracket and current isn't, skip
17052                    if !in_bracket_range && best_in_bracket_range {
17053                        continue;
17054                    }
17055
17056                    // Prefer smaller lengths unless best is inside and current isn't
17057                    if length > best_length && (best_inside || !inside) {
17058                        continue;
17059                    }
17060
17061                    best_length = length;
17062                    best_inside = inside;
17063                    best_in_bracket_range = in_bracket_range;
17064                    best_destination = Some(
17065                        if close.contains(&selection.start) && close.contains(&selection.end) {
17066                            if inside { open.end } else { open.start }
17067                        } else if inside {
17068                            *close.start()
17069                        } else {
17070                            *close.end()
17071                        },
17072                    );
17073                }
17074
17075                if let Some(destination) = best_destination {
17076                    selection.collapse_to(destination, SelectionGoal::None);
17077                }
17078            })
17079        });
17080    }
17081
17082    pub fn undo_selection(
17083        &mut self,
17084        _: &UndoSelection,
17085        window: &mut Window,
17086        cx: &mut Context<Self>,
17087    ) {
17088        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17089        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17090            self.selection_history.mode = SelectionHistoryMode::Undoing;
17091            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17092                this.end_selection(window, cx);
17093                this.change_selections(
17094                    SelectionEffects::scroll(Autoscroll::newest()),
17095                    window,
17096                    cx,
17097                    |s| s.select_anchors(entry.selections.to_vec()),
17098                );
17099            });
17100            self.selection_history.mode = SelectionHistoryMode::Normal;
17101
17102            self.select_next_state = entry.select_next_state;
17103            self.select_prev_state = entry.select_prev_state;
17104            self.add_selections_state = entry.add_selections_state;
17105        }
17106    }
17107
17108    pub fn redo_selection(
17109        &mut self,
17110        _: &RedoSelection,
17111        window: &mut Window,
17112        cx: &mut Context<Self>,
17113    ) {
17114        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17115        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17116            self.selection_history.mode = SelectionHistoryMode::Redoing;
17117            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17118                this.end_selection(window, cx);
17119                this.change_selections(
17120                    SelectionEffects::scroll(Autoscroll::newest()),
17121                    window,
17122                    cx,
17123                    |s| s.select_anchors(entry.selections.to_vec()),
17124                );
17125            });
17126            self.selection_history.mode = SelectionHistoryMode::Normal;
17127
17128            self.select_next_state = entry.select_next_state;
17129            self.select_prev_state = entry.select_prev_state;
17130            self.add_selections_state = entry.add_selections_state;
17131        }
17132    }
17133
17134    pub fn expand_excerpts(
17135        &mut self,
17136        action: &ExpandExcerpts,
17137        _: &mut Window,
17138        cx: &mut Context<Self>,
17139    ) {
17140        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17141    }
17142
17143    pub fn expand_excerpts_down(
17144        &mut self,
17145        action: &ExpandExcerptsDown,
17146        _: &mut Window,
17147        cx: &mut Context<Self>,
17148    ) {
17149        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17150    }
17151
17152    pub fn expand_excerpts_up(
17153        &mut self,
17154        action: &ExpandExcerptsUp,
17155        _: &mut Window,
17156        cx: &mut Context<Self>,
17157    ) {
17158        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17159    }
17160
17161    pub fn expand_excerpts_for_direction(
17162        &mut self,
17163        lines: u32,
17164        direction: ExpandExcerptDirection,
17165        cx: &mut Context<Self>,
17166    ) {
17167        let selections = self.selections.disjoint_anchors_arc();
17168
17169        let lines = if lines == 0 {
17170            EditorSettings::get_global(cx).expand_excerpt_lines
17171        } else {
17172            lines
17173        };
17174
17175        let snapshot = self.buffer.read(cx).snapshot(cx);
17176        let mut excerpt_ids = selections
17177            .iter()
17178            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17179            .collect::<Vec<_>>();
17180        excerpt_ids.sort();
17181        excerpt_ids.dedup();
17182
17183        if self.delegate_expand_excerpts {
17184            cx.emit(EditorEvent::ExpandExcerptsRequested {
17185                excerpt_ids,
17186                lines,
17187                direction,
17188            });
17189            return;
17190        }
17191
17192        self.buffer.update(cx, |buffer, cx| {
17193            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17194        })
17195    }
17196
17197    pub fn expand_excerpt(
17198        &mut self,
17199        excerpt: ExcerptId,
17200        direction: ExpandExcerptDirection,
17201        window: &mut Window,
17202        cx: &mut Context<Self>,
17203    ) {
17204        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17205
17206        if self.delegate_expand_excerpts {
17207            cx.emit(EditorEvent::ExpandExcerptsRequested {
17208                excerpt_ids: vec![excerpt],
17209                lines: lines_to_expand,
17210                direction,
17211            });
17212            return;
17213        }
17214
17215        let current_scroll_position = self.scroll_position(cx);
17216        let mut scroll = None;
17217
17218        if direction == ExpandExcerptDirection::Down {
17219            let multi_buffer = self.buffer.read(cx);
17220            let snapshot = multi_buffer.snapshot(cx);
17221            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17222                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17223                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17224            {
17225                let buffer_snapshot = buffer.read(cx).snapshot();
17226                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17227                let last_row = buffer_snapshot.max_point().row;
17228                let lines_below = last_row.saturating_sub(excerpt_end_row);
17229                if lines_below >= lines_to_expand {
17230                    scroll = Some(
17231                        current_scroll_position
17232                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17233                    );
17234                }
17235            }
17236        }
17237        if direction == ExpandExcerptDirection::Up
17238            && self
17239                .buffer
17240                .read(cx)
17241                .snapshot(cx)
17242                .excerpt_before(excerpt)
17243                .is_none()
17244        {
17245            scroll = Some(current_scroll_position);
17246        }
17247
17248        self.buffer.update(cx, |buffer, cx| {
17249            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17250        });
17251
17252        if let Some(new_scroll_position) = scroll {
17253            self.set_scroll_position(new_scroll_position, window, cx);
17254        }
17255    }
17256
17257    pub fn go_to_singleton_buffer_point(
17258        &mut self,
17259        point: Point,
17260        window: &mut Window,
17261        cx: &mut Context<Self>,
17262    ) {
17263        self.go_to_singleton_buffer_range(point..point, window, cx);
17264    }
17265
17266    pub fn go_to_singleton_buffer_range(
17267        &mut self,
17268        range: Range<Point>,
17269        window: &mut Window,
17270        cx: &mut Context<Self>,
17271    ) {
17272        let multibuffer = self.buffer().read(cx);
17273        let Some(buffer) = multibuffer.as_singleton() else {
17274            return;
17275        };
17276        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17277            return;
17278        };
17279        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17280            return;
17281        };
17282        self.change_selections(
17283            SelectionEffects::default().nav_history(true),
17284            window,
17285            cx,
17286            |s| s.select_anchor_ranges([start..end]),
17287        );
17288    }
17289
17290    pub fn go_to_diagnostic(
17291        &mut self,
17292        action: &GoToDiagnostic,
17293        window: &mut Window,
17294        cx: &mut Context<Self>,
17295    ) {
17296        if !self.diagnostics_enabled() {
17297            return;
17298        }
17299        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17300        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17301    }
17302
17303    pub fn go_to_prev_diagnostic(
17304        &mut self,
17305        action: &GoToPreviousDiagnostic,
17306        window: &mut Window,
17307        cx: &mut Context<Self>,
17308    ) {
17309        if !self.diagnostics_enabled() {
17310            return;
17311        }
17312        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17313        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17314    }
17315
17316    pub fn go_to_diagnostic_impl(
17317        &mut self,
17318        direction: Direction,
17319        severity: GoToDiagnosticSeverityFilter,
17320        window: &mut Window,
17321        cx: &mut Context<Self>,
17322    ) {
17323        let buffer = self.buffer.read(cx).snapshot(cx);
17324        let selection = self
17325            .selections
17326            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17327
17328        let mut active_group_id = None;
17329        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17330            && active_group.active_range.start.to_offset(&buffer) == selection.start
17331        {
17332            active_group_id = Some(active_group.group_id);
17333        }
17334
17335        fn filtered<'a>(
17336            severity: GoToDiagnosticSeverityFilter,
17337            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17338        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17339            diagnostics
17340                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17341                .filter(|entry| entry.range.start != entry.range.end)
17342                .filter(|entry| !entry.diagnostic.is_unnecessary)
17343        }
17344
17345        let before = filtered(
17346            severity,
17347            buffer
17348                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17349                .filter(|entry| entry.range.start <= selection.start),
17350        );
17351        let after = filtered(
17352            severity,
17353            buffer
17354                .diagnostics_in_range(selection.start..buffer.len())
17355                .filter(|entry| entry.range.start >= selection.start),
17356        );
17357
17358        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17359        if direction == Direction::Prev {
17360            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17361            {
17362                for diagnostic in prev_diagnostics.into_iter().rev() {
17363                    if diagnostic.range.start != selection.start
17364                        || active_group_id
17365                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17366                    {
17367                        found = Some(diagnostic);
17368                        break 'outer;
17369                    }
17370                }
17371            }
17372        } else {
17373            for diagnostic in after.chain(before) {
17374                if diagnostic.range.start != selection.start
17375                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17376                {
17377                    found = Some(diagnostic);
17378                    break;
17379                }
17380            }
17381        }
17382        let Some(next_diagnostic) = found else {
17383            return;
17384        };
17385
17386        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17387        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17388            return;
17389        };
17390        let snapshot = self.snapshot(window, cx);
17391        if snapshot.intersects_fold(next_diagnostic.range.start) {
17392            self.unfold_ranges(
17393                std::slice::from_ref(&next_diagnostic.range),
17394                true,
17395                false,
17396                cx,
17397            );
17398        }
17399        self.change_selections(Default::default(), window, cx, |s| {
17400            s.select_ranges(vec![
17401                next_diagnostic.range.start..next_diagnostic.range.start,
17402            ])
17403        });
17404        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17405        self.refresh_edit_prediction(false, true, window, cx);
17406    }
17407
17408    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17409        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17410        let snapshot = self.snapshot(window, cx);
17411        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17412        self.go_to_hunk_before_or_after_position(
17413            &snapshot,
17414            selection.head(),
17415            Direction::Next,
17416            window,
17417            cx,
17418        );
17419    }
17420
17421    pub fn go_to_hunk_before_or_after_position(
17422        &mut self,
17423        snapshot: &EditorSnapshot,
17424        position: Point,
17425        direction: Direction,
17426        window: &mut Window,
17427        cx: &mut Context<Editor>,
17428    ) {
17429        let row = if direction == Direction::Next {
17430            self.hunk_after_position(snapshot, position)
17431                .map(|hunk| hunk.row_range.start)
17432        } else {
17433            self.hunk_before_position(snapshot, position)
17434        };
17435
17436        if let Some(row) = row {
17437            let destination = Point::new(row.0, 0);
17438            let autoscroll = Autoscroll::center();
17439
17440            self.unfold_ranges(&[destination..destination], false, false, cx);
17441            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17442                s.select_ranges([destination..destination]);
17443            });
17444        }
17445    }
17446
17447    fn hunk_after_position(
17448        &mut self,
17449        snapshot: &EditorSnapshot,
17450        position: Point,
17451    ) -> Option<MultiBufferDiffHunk> {
17452        snapshot
17453            .buffer_snapshot()
17454            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17455            .find(|hunk| hunk.row_range.start.0 > position.row)
17456            .or_else(|| {
17457                snapshot
17458                    .buffer_snapshot()
17459                    .diff_hunks_in_range(Point::zero()..position)
17460                    .find(|hunk| hunk.row_range.end.0 < position.row)
17461            })
17462    }
17463
17464    fn go_to_prev_hunk(
17465        &mut self,
17466        _: &GoToPreviousHunk,
17467        window: &mut Window,
17468        cx: &mut Context<Self>,
17469    ) {
17470        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17471        let snapshot = self.snapshot(window, cx);
17472        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17473        self.go_to_hunk_before_or_after_position(
17474            &snapshot,
17475            selection.head(),
17476            Direction::Prev,
17477            window,
17478            cx,
17479        );
17480    }
17481
17482    fn hunk_before_position(
17483        &mut self,
17484        snapshot: &EditorSnapshot,
17485        position: Point,
17486    ) -> Option<MultiBufferRow> {
17487        snapshot
17488            .buffer_snapshot()
17489            .diff_hunk_before(position)
17490            .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17491    }
17492
17493    fn go_to_next_change(
17494        &mut self,
17495        _: &GoToNextChange,
17496        window: &mut Window,
17497        cx: &mut Context<Self>,
17498    ) {
17499        if let Some(selections) = self
17500            .change_list
17501            .next_change(1, Direction::Next)
17502            .map(|s| s.to_vec())
17503        {
17504            self.change_selections(Default::default(), window, cx, |s| {
17505                let map = s.display_snapshot();
17506                s.select_display_ranges(selections.iter().map(|a| {
17507                    let point = a.to_display_point(&map);
17508                    point..point
17509                }))
17510            })
17511        }
17512    }
17513
17514    fn go_to_previous_change(
17515        &mut self,
17516        _: &GoToPreviousChange,
17517        window: &mut Window,
17518        cx: &mut Context<Self>,
17519    ) {
17520        if let Some(selections) = self
17521            .change_list
17522            .next_change(1, Direction::Prev)
17523            .map(|s| s.to_vec())
17524        {
17525            self.change_selections(Default::default(), window, cx, |s| {
17526                let map = s.display_snapshot();
17527                s.select_display_ranges(selections.iter().map(|a| {
17528                    let point = a.to_display_point(&map);
17529                    point..point
17530                }))
17531            })
17532        }
17533    }
17534
17535    pub fn go_to_next_document_highlight(
17536        &mut self,
17537        _: &GoToNextDocumentHighlight,
17538        window: &mut Window,
17539        cx: &mut Context<Self>,
17540    ) {
17541        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17542    }
17543
17544    pub fn go_to_prev_document_highlight(
17545        &mut self,
17546        _: &GoToPreviousDocumentHighlight,
17547        window: &mut Window,
17548        cx: &mut Context<Self>,
17549    ) {
17550        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17551    }
17552
17553    pub fn go_to_document_highlight_before_or_after_position(
17554        &mut self,
17555        direction: Direction,
17556        window: &mut Window,
17557        cx: &mut Context<Editor>,
17558    ) {
17559        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17560        let snapshot = self.snapshot(window, cx);
17561        let buffer = &snapshot.buffer_snapshot();
17562        let position = self
17563            .selections
17564            .newest::<Point>(&snapshot.display_snapshot)
17565            .head();
17566        let anchor_position = buffer.anchor_after(position);
17567
17568        // Get all document highlights (both read and write)
17569        let mut all_highlights = Vec::new();
17570
17571        if let Some((_, read_highlights)) = self
17572            .background_highlights
17573            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
17574        {
17575            all_highlights.extend(read_highlights.iter());
17576        }
17577
17578        if let Some((_, write_highlights)) = self
17579            .background_highlights
17580            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
17581        {
17582            all_highlights.extend(write_highlights.iter());
17583        }
17584
17585        if all_highlights.is_empty() {
17586            return;
17587        }
17588
17589        // Sort highlights by position
17590        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17591
17592        let target_highlight = match direction {
17593            Direction::Next => {
17594                // Find the first highlight after the current position
17595                all_highlights
17596                    .iter()
17597                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17598            }
17599            Direction::Prev => {
17600                // Find the last highlight before the current position
17601                all_highlights
17602                    .iter()
17603                    .rev()
17604                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17605            }
17606        };
17607
17608        if let Some(highlight) = target_highlight {
17609            let destination = highlight.start.to_point(buffer);
17610            let autoscroll = Autoscroll::center();
17611
17612            self.unfold_ranges(&[destination..destination], false, false, cx);
17613            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17614                s.select_ranges([destination..destination]);
17615            });
17616        }
17617    }
17618
17619    fn go_to_line<T: 'static>(
17620        &mut self,
17621        position: Anchor,
17622        highlight_color: Option<Hsla>,
17623        window: &mut Window,
17624        cx: &mut Context<Self>,
17625    ) {
17626        let snapshot = self.snapshot(window, cx).display_snapshot;
17627        let position = position.to_point(&snapshot.buffer_snapshot());
17628        let start = snapshot
17629            .buffer_snapshot()
17630            .clip_point(Point::new(position.row, 0), Bias::Left);
17631        let end = start + Point::new(1, 0);
17632        let start = snapshot.buffer_snapshot().anchor_before(start);
17633        let end = snapshot.buffer_snapshot().anchor_before(end);
17634
17635        self.highlight_rows::<T>(
17636            start..end,
17637            highlight_color
17638                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17639            Default::default(),
17640            cx,
17641        );
17642
17643        if self.buffer.read(cx).is_singleton() {
17644            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17645        }
17646    }
17647
17648    pub fn go_to_definition(
17649        &mut self,
17650        _: &GoToDefinition,
17651        window: &mut Window,
17652        cx: &mut Context<Self>,
17653    ) -> Task<Result<Navigated>> {
17654        let definition =
17655            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17656        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17657        cx.spawn_in(window, async move |editor, cx| {
17658            if definition.await? == Navigated::Yes {
17659                return Ok(Navigated::Yes);
17660            }
17661            match fallback_strategy {
17662                GoToDefinitionFallback::None => Ok(Navigated::No),
17663                GoToDefinitionFallback::FindAllReferences => {
17664                    match editor.update_in(cx, |editor, window, cx| {
17665                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17666                    })? {
17667                        Some(references) => references.await,
17668                        None => Ok(Navigated::No),
17669                    }
17670                }
17671            }
17672        })
17673    }
17674
17675    pub fn go_to_declaration(
17676        &mut self,
17677        _: &GoToDeclaration,
17678        window: &mut Window,
17679        cx: &mut Context<Self>,
17680    ) -> Task<Result<Navigated>> {
17681        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17682    }
17683
17684    pub fn go_to_declaration_split(
17685        &mut self,
17686        _: &GoToDeclaration,
17687        window: &mut Window,
17688        cx: &mut Context<Self>,
17689    ) -> Task<Result<Navigated>> {
17690        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17691    }
17692
17693    pub fn go_to_implementation(
17694        &mut self,
17695        _: &GoToImplementation,
17696        window: &mut Window,
17697        cx: &mut Context<Self>,
17698    ) -> Task<Result<Navigated>> {
17699        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17700    }
17701
17702    pub fn go_to_implementation_split(
17703        &mut self,
17704        _: &GoToImplementationSplit,
17705        window: &mut Window,
17706        cx: &mut Context<Self>,
17707    ) -> Task<Result<Navigated>> {
17708        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17709    }
17710
17711    pub fn go_to_type_definition(
17712        &mut self,
17713        _: &GoToTypeDefinition,
17714        window: &mut Window,
17715        cx: &mut Context<Self>,
17716    ) -> Task<Result<Navigated>> {
17717        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17718    }
17719
17720    pub fn go_to_definition_split(
17721        &mut self,
17722        _: &GoToDefinitionSplit,
17723        window: &mut Window,
17724        cx: &mut Context<Self>,
17725    ) -> Task<Result<Navigated>> {
17726        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17727    }
17728
17729    pub fn go_to_type_definition_split(
17730        &mut self,
17731        _: &GoToTypeDefinitionSplit,
17732        window: &mut Window,
17733        cx: &mut Context<Self>,
17734    ) -> Task<Result<Navigated>> {
17735        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17736    }
17737
17738    fn go_to_definition_of_kind(
17739        &mut self,
17740        kind: GotoDefinitionKind,
17741        split: bool,
17742        window: &mut Window,
17743        cx: &mut Context<Self>,
17744    ) -> Task<Result<Navigated>> {
17745        let Some(provider) = self.semantics_provider.clone() else {
17746            return Task::ready(Ok(Navigated::No));
17747        };
17748        let head = self
17749            .selections
17750            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
17751            .head();
17752        let buffer = self.buffer.read(cx);
17753        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
17754            return Task::ready(Ok(Navigated::No));
17755        };
17756        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
17757            return Task::ready(Ok(Navigated::No));
17758        };
17759
17760        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
17761
17762        cx.spawn_in(window, async move |editor, cx| {
17763            let Some(definitions) = definitions.await? else {
17764                return Ok(Navigated::No);
17765            };
17766            let navigated = editor
17767                .update_in(cx, |editor, window, cx| {
17768                    editor.navigate_to_hover_links(
17769                        Some(kind),
17770                        definitions
17771                            .into_iter()
17772                            .filter(|location| {
17773                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
17774                            })
17775                            .map(HoverLink::Text)
17776                            .collect::<Vec<_>>(),
17777                        nav_entry,
17778                        split,
17779                        window,
17780                        cx,
17781                    )
17782                })?
17783                .await?;
17784            anyhow::Ok(navigated)
17785        })
17786    }
17787
17788    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
17789        let selection = self.selections.newest_anchor();
17790        let head = selection.head();
17791        let tail = selection.tail();
17792
17793        let Some((buffer, start_position)) =
17794            self.buffer.read(cx).text_anchor_for_position(head, cx)
17795        else {
17796            return;
17797        };
17798
17799        let end_position = if head != tail {
17800            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
17801                return;
17802            };
17803            Some(pos)
17804        } else {
17805            None
17806        };
17807
17808        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
17809            let url = if let Some(end_pos) = end_position {
17810                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
17811            } else {
17812                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
17813            };
17814
17815            if let Some(url) = url {
17816                cx.update(|window, cx| {
17817                    if parse_zed_link(&url, cx).is_some() {
17818                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
17819                    } else {
17820                        cx.open_url(&url);
17821                    }
17822                })?;
17823            }
17824
17825            anyhow::Ok(())
17826        });
17827
17828        url_finder.detach();
17829    }
17830
17831    pub fn open_selected_filename(
17832        &mut self,
17833        _: &OpenSelectedFilename,
17834        window: &mut Window,
17835        cx: &mut Context<Self>,
17836    ) {
17837        let Some(workspace) = self.workspace() else {
17838            return;
17839        };
17840
17841        let position = self.selections.newest_anchor().head();
17842
17843        let Some((buffer, buffer_position)) =
17844            self.buffer.read(cx).text_anchor_for_position(position, cx)
17845        else {
17846            return;
17847        };
17848
17849        let project = self.project.clone();
17850
17851        cx.spawn_in(window, async move |_, cx| {
17852            let result = find_file(&buffer, project, buffer_position, cx).await;
17853
17854            if let Some((_, path)) = result {
17855                workspace
17856                    .update_in(cx, |workspace, window, cx| {
17857                        workspace.open_resolved_path(path, window, cx)
17858                    })?
17859                    .await?;
17860            }
17861            anyhow::Ok(())
17862        })
17863        .detach();
17864    }
17865
17866    pub(crate) fn navigate_to_hover_links(
17867        &mut self,
17868        kind: Option<GotoDefinitionKind>,
17869        definitions: Vec<HoverLink>,
17870        origin: Option<NavigationEntry>,
17871        split: bool,
17872        window: &mut Window,
17873        cx: &mut Context<Editor>,
17874    ) -> Task<Result<Navigated>> {
17875        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
17876        let mut first_url_or_file = None;
17877        let definitions: Vec<_> = definitions
17878            .into_iter()
17879            .filter_map(|def| match def {
17880                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
17881                HoverLink::InlayHint(lsp_location, server_id) => {
17882                    let computation =
17883                        self.compute_target_location(lsp_location, server_id, window, cx);
17884                    Some(cx.background_spawn(computation))
17885                }
17886                HoverLink::Url(url) => {
17887                    first_url_or_file = Some(Either::Left(url));
17888                    None
17889                }
17890                HoverLink::File(path) => {
17891                    first_url_or_file = Some(Either::Right(path));
17892                    None
17893                }
17894            })
17895            .collect();
17896
17897        let workspace = self.workspace();
17898
17899        cx.spawn_in(window, async move |editor, cx| {
17900            let locations: Vec<Location> = future::join_all(definitions)
17901                .await
17902                .into_iter()
17903                .filter_map(|location| location.transpose())
17904                .collect::<Result<_>>()
17905                .context("location tasks")?;
17906            let mut locations = cx.update(|_, cx| {
17907                locations
17908                    .into_iter()
17909                    .map(|location| {
17910                        let buffer = location.buffer.read(cx);
17911                        (location.buffer, location.range.to_point(buffer))
17912                    })
17913                    .into_group_map()
17914            })?;
17915            let mut num_locations = 0;
17916            for ranges in locations.values_mut() {
17917                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
17918                ranges.dedup();
17919                num_locations += ranges.len();
17920            }
17921
17922            if num_locations > 1 {
17923                let tab_kind = match kind {
17924                    Some(GotoDefinitionKind::Implementation) => "Implementations",
17925                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
17926                    Some(GotoDefinitionKind::Declaration) => "Declarations",
17927                    Some(GotoDefinitionKind::Type) => "Types",
17928                };
17929                let title = editor
17930                    .update_in(cx, |_, _, cx| {
17931                        let target = locations
17932                            .iter()
17933                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
17934                            .map(|(buffer, location)| {
17935                                buffer
17936                                    .read(cx)
17937                                    .text_for_range(location.clone())
17938                                    .collect::<String>()
17939                            })
17940                            .filter(|text| !text.contains('\n'))
17941                            .unique()
17942                            .take(3)
17943                            .join(", ");
17944                        if target.is_empty() {
17945                            tab_kind.to_owned()
17946                        } else {
17947                            format!("{tab_kind} for {target}")
17948                        }
17949                    })
17950                    .context("buffer title")?;
17951
17952                let Some(workspace) = workspace else {
17953                    return Ok(Navigated::No);
17954                };
17955
17956                let opened = workspace
17957                    .update_in(cx, |workspace, window, cx| {
17958                        let allow_preview = PreviewTabsSettings::get_global(cx)
17959                            .enable_preview_multibuffer_from_code_navigation;
17960                        if let Some((target_editor, target_pane)) =
17961                            Self::open_locations_in_multibuffer(
17962                                workspace,
17963                                locations,
17964                                title,
17965                                split,
17966                                allow_preview,
17967                                MultibufferSelectionMode::First,
17968                                window,
17969                                cx,
17970                            )
17971                        {
17972                            // We create our own nav history instead of using
17973                            // `target_editor.nav_history` because `nav_history`
17974                            // seems to be populated asynchronously when an item
17975                            // is added to a pane
17976                            let mut nav_history = target_pane
17977                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
17978                            target_editor.update(cx, |editor, cx| {
17979                                let nav_data = editor
17980                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
17981                                let target =
17982                                    Some(nav_history.navigation_entry(Some(
17983                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
17984                                    )));
17985                                nav_history.push_tag(origin, target);
17986                            })
17987                        }
17988                    })
17989                    .is_ok();
17990
17991                anyhow::Ok(Navigated::from_bool(opened))
17992            } else if num_locations == 0 {
17993                // If there is one url or file, open it directly
17994                match first_url_or_file {
17995                    Some(Either::Left(url)) => {
17996                        cx.update(|window, cx| {
17997                            if parse_zed_link(&url, cx).is_some() {
17998                                window
17999                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18000                            } else {
18001                                cx.open_url(&url);
18002                            }
18003                        })?;
18004                        Ok(Navigated::Yes)
18005                    }
18006                    Some(Either::Right(path)) => {
18007                        // TODO(andrew): respect preview tab settings
18008                        //               `enable_keep_preview_on_code_navigation` and
18009                        //               `enable_preview_file_from_code_navigation`
18010                        let Some(workspace) = workspace else {
18011                            return Ok(Navigated::No);
18012                        };
18013                        workspace
18014                            .update_in(cx, |workspace, window, cx| {
18015                                workspace.open_resolved_path(path, window, cx)
18016                            })?
18017                            .await?;
18018                        Ok(Navigated::Yes)
18019                    }
18020                    None => Ok(Navigated::No),
18021                }
18022            } else {
18023                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18024                let target_range = target_ranges.first().unwrap().clone();
18025
18026                editor.update_in(cx, |editor, window, cx| {
18027                    let range = editor.range_for_match(&target_range);
18028                    let range = collapse_multiline_range(range);
18029
18030                    if !split
18031                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18032                    {
18033                        editor.go_to_singleton_buffer_range(range, window, cx);
18034
18035                        let target =
18036                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18037                        if let Some(mut nav_history) = editor.nav_history.clone() {
18038                            nav_history.push_tag(origin, target);
18039                        }
18040                    } else {
18041                        let Some(workspace) = workspace else {
18042                            return Navigated::No;
18043                        };
18044                        let pane = workspace.read(cx).active_pane().clone();
18045                        window.defer(cx, move |window, cx| {
18046                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18047                                workspace.update(cx, |workspace, cx| {
18048                                    let pane = if split {
18049                                        workspace.adjacent_pane(window, cx)
18050                                    } else {
18051                                        workspace.active_pane().clone()
18052                                    };
18053
18054                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18055                                    let keep_old_preview = preview_tabs_settings
18056                                        .enable_keep_preview_on_code_navigation;
18057                                    let allow_new_preview = preview_tabs_settings
18058                                        .enable_preview_file_from_code_navigation;
18059
18060                                    let editor = workspace.open_project_item(
18061                                        pane.clone(),
18062                                        target_buffer.clone(),
18063                                        true,
18064                                        true,
18065                                        keep_old_preview,
18066                                        allow_new_preview,
18067                                        window,
18068                                        cx,
18069                                    );
18070                                    (editor, pane)
18071                                });
18072                            // We create our own nav history instead of using
18073                            // `target_editor.nav_history` because `nav_history`
18074                            // seems to be populated asynchronously when an item
18075                            // is added to a pane
18076                            let mut nav_history = target_pane
18077                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18078                            target_editor.update(cx, |target_editor, cx| {
18079                                // When selecting a definition in a different buffer, disable the nav history
18080                                // to avoid creating a history entry at the previous cursor location.
18081                                pane.update(cx, |pane, _| pane.disable_history());
18082                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18083
18084                                let nav_data = target_editor.navigation_data(
18085                                    target_editor.selections.newest_anchor().head(),
18086                                    cx,
18087                                );
18088                                let target =
18089                                    Some(nav_history.navigation_entry(Some(
18090                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18091                                    )));
18092                                nav_history.push_tag(origin, target);
18093                                pane.update(cx, |pane, _| pane.enable_history());
18094                            });
18095                        });
18096                    }
18097                    Navigated::Yes
18098                })
18099            }
18100        })
18101    }
18102
18103    fn compute_target_location(
18104        &self,
18105        lsp_location: lsp::Location,
18106        server_id: LanguageServerId,
18107        window: &mut Window,
18108        cx: &mut Context<Self>,
18109    ) -> Task<anyhow::Result<Option<Location>>> {
18110        let Some(project) = self.project.clone() else {
18111            return Task::ready(Ok(None));
18112        };
18113
18114        cx.spawn_in(window, async move |editor, cx| {
18115            let location_task = editor.update(cx, |_, cx| {
18116                project.update(cx, |project, cx| {
18117                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18118                })
18119            })?;
18120            let location = Some({
18121                let target_buffer_handle = location_task.await.context("open local buffer")?;
18122                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18123                    let target_start = target_buffer
18124                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18125                    let target_end = target_buffer
18126                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18127                    target_buffer.anchor_after(target_start)
18128                        ..target_buffer.anchor_before(target_end)
18129                });
18130                Location {
18131                    buffer: target_buffer_handle,
18132                    range,
18133                }
18134            });
18135            Ok(location)
18136        })
18137    }
18138
18139    fn go_to_next_reference(
18140        &mut self,
18141        _: &GoToNextReference,
18142        window: &mut Window,
18143        cx: &mut Context<Self>,
18144    ) {
18145        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18146        if let Some(task) = task {
18147            task.detach();
18148        };
18149    }
18150
18151    fn go_to_prev_reference(
18152        &mut self,
18153        _: &GoToPreviousReference,
18154        window: &mut Window,
18155        cx: &mut Context<Self>,
18156    ) {
18157        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18158        if let Some(task) = task {
18159            task.detach();
18160        };
18161    }
18162
18163    pub fn go_to_reference_before_or_after_position(
18164        &mut self,
18165        direction: Direction,
18166        count: usize,
18167        window: &mut Window,
18168        cx: &mut Context<Self>,
18169    ) -> Option<Task<Result<()>>> {
18170        let selection = self.selections.newest_anchor();
18171        let head = selection.head();
18172
18173        let multi_buffer = self.buffer.read(cx);
18174
18175        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18176        let workspace = self.workspace()?;
18177        let project = workspace.read(cx).project().clone();
18178        let references =
18179            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18180        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18181            let Some(locations) = references.await? else {
18182                return Ok(());
18183            };
18184
18185            if locations.is_empty() {
18186                // totally normal - the cursor may be on something which is not
18187                // a symbol (e.g. a keyword)
18188                log::info!("no references found under cursor");
18189                return Ok(());
18190            }
18191
18192            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18193
18194            let (locations, current_location_index) =
18195                multi_buffer.update(cx, |multi_buffer, cx| {
18196                    let mut locations = locations
18197                        .into_iter()
18198                        .filter_map(|loc| {
18199                            let start = multi_buffer.buffer_anchor_to_anchor(
18200                                &loc.buffer,
18201                                loc.range.start,
18202                                cx,
18203                            )?;
18204                            let end = multi_buffer.buffer_anchor_to_anchor(
18205                                &loc.buffer,
18206                                loc.range.end,
18207                                cx,
18208                            )?;
18209                            Some(start..end)
18210                        })
18211                        .collect::<Vec<_>>();
18212
18213                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18214                    // There is an O(n) implementation, but given this list will be
18215                    // small (usually <100 items), the extra O(log(n)) factor isn't
18216                    // worth the (surprisingly large amount of) extra complexity.
18217                    locations
18218                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18219
18220                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18221
18222                    let current_location_index = locations.iter().position(|loc| {
18223                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18224                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18225                    });
18226
18227                    (locations, current_location_index)
18228                });
18229
18230            let Some(current_location_index) = current_location_index else {
18231                // This indicates something has gone wrong, because we already
18232                // handle the "no references" case above
18233                log::error!(
18234                    "failed to find current reference under cursor. Total references: {}",
18235                    locations.len()
18236                );
18237                return Ok(());
18238            };
18239
18240            let destination_location_index = match direction {
18241                Direction::Next => (current_location_index + count) % locations.len(),
18242                Direction::Prev => {
18243                    (current_location_index + locations.len() - count % locations.len())
18244                        % locations.len()
18245                }
18246            };
18247
18248            // TODO(cameron): is this needed?
18249            // the thinking is to avoid "jumping to the current location" (avoid
18250            // polluting "jumplist" in vim terms)
18251            if current_location_index == destination_location_index {
18252                return Ok(());
18253            }
18254
18255            let Range { start, end } = locations[destination_location_index];
18256
18257            editor.update_in(cx, |editor, window, cx| {
18258                let effects = SelectionEffects::default();
18259
18260                editor.unfold_ranges(&[start..end], false, false, cx);
18261                editor.change_selections(effects, window, cx, |s| {
18262                    s.select_ranges([start..start]);
18263                });
18264            })?;
18265
18266            Ok(())
18267        }))
18268    }
18269
18270    pub fn find_all_references(
18271        &mut self,
18272        action: &FindAllReferences,
18273        window: &mut Window,
18274        cx: &mut Context<Self>,
18275    ) -> Option<Task<Result<Navigated>>> {
18276        let always_open_multibuffer = action.always_open_multibuffer;
18277        let selection = self.selections.newest_anchor();
18278        let multi_buffer = self.buffer.read(cx);
18279        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18280        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18281        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18282        let head = selection_offset.head();
18283
18284        let head_anchor = multi_buffer_snapshot.anchor_at(
18285            head,
18286            if head < selection_offset.tail() {
18287                Bias::Right
18288            } else {
18289                Bias::Left
18290            },
18291        );
18292
18293        match self
18294            .find_all_references_task_sources
18295            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18296        {
18297            Ok(_) => {
18298                log::info!(
18299                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18300                );
18301                return None;
18302            }
18303            Err(i) => {
18304                self.find_all_references_task_sources.insert(i, head_anchor);
18305            }
18306        }
18307
18308        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18309        let workspace = self.workspace()?;
18310        let project = workspace.read(cx).project().clone();
18311        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18312        Some(cx.spawn_in(window, async move |editor, cx| {
18313            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18314                if let Ok(i) = editor
18315                    .find_all_references_task_sources
18316                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18317                {
18318                    editor.find_all_references_task_sources.remove(i);
18319                }
18320            });
18321
18322            let Some(locations) = references.await? else {
18323                return anyhow::Ok(Navigated::No);
18324            };
18325            let mut locations = cx.update(|_, cx| {
18326                locations
18327                    .into_iter()
18328                    .map(|location| {
18329                        let buffer = location.buffer.read(cx);
18330                        (location.buffer, location.range.to_point(buffer))
18331                    })
18332                    // if special-casing the single-match case, remove ranges
18333                    // that intersect current selection
18334                    .filter(|(location_buffer, location)| {
18335                        if always_open_multibuffer || &buffer != location_buffer {
18336                            return true;
18337                        }
18338
18339                        !location.contains_inclusive(&selection_point.range())
18340                    })
18341                    .into_group_map()
18342            })?;
18343            if locations.is_empty() {
18344                return anyhow::Ok(Navigated::No);
18345            }
18346            for ranges in locations.values_mut() {
18347                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18348                ranges.dedup();
18349            }
18350            let mut num_locations = 0;
18351            for ranges in locations.values_mut() {
18352                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18353                ranges.dedup();
18354                num_locations += ranges.len();
18355            }
18356
18357            if num_locations == 1 && !always_open_multibuffer {
18358                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18359                let target_range = target_ranges.first().unwrap().clone();
18360
18361                return editor.update_in(cx, |editor, window, cx| {
18362                    let range = target_range.to_point(target_buffer.read(cx));
18363                    let range = editor.range_for_match(&range);
18364                    let range = range.start..range.start;
18365
18366                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18367                        editor.go_to_singleton_buffer_range(range, window, cx);
18368                    } else {
18369                        let pane = workspace.read(cx).active_pane().clone();
18370                        window.defer(cx, move |window, cx| {
18371                            let target_editor: Entity<Self> =
18372                                workspace.update(cx, |workspace, cx| {
18373                                    let pane = workspace.active_pane().clone();
18374
18375                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18376                                    let keep_old_preview = preview_tabs_settings
18377                                        .enable_keep_preview_on_code_navigation;
18378                                    let allow_new_preview = preview_tabs_settings
18379                                        .enable_preview_file_from_code_navigation;
18380
18381                                    workspace.open_project_item(
18382                                        pane,
18383                                        target_buffer.clone(),
18384                                        true,
18385                                        true,
18386                                        keep_old_preview,
18387                                        allow_new_preview,
18388                                        window,
18389                                        cx,
18390                                    )
18391                                });
18392                            target_editor.update(cx, |target_editor, cx| {
18393                                // When selecting a definition in a different buffer, disable the nav history
18394                                // to avoid creating a history entry at the previous cursor location.
18395                                pane.update(cx, |pane, _| pane.disable_history());
18396                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18397                                pane.update(cx, |pane, _| pane.enable_history());
18398                            });
18399                        });
18400                    }
18401                    Navigated::No
18402                });
18403            }
18404
18405            workspace.update_in(cx, |workspace, window, cx| {
18406                let target = locations
18407                    .iter()
18408                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18409                    .map(|(buffer, location)| {
18410                        buffer
18411                            .read(cx)
18412                            .text_for_range(location.clone())
18413                            .collect::<String>()
18414                    })
18415                    .filter(|text| !text.contains('\n'))
18416                    .unique()
18417                    .take(3)
18418                    .join(", ");
18419                let title = if target.is_empty() {
18420                    "References".to_owned()
18421                } else {
18422                    format!("References to {target}")
18423                };
18424                let allow_preview = PreviewTabsSettings::get_global(cx)
18425                    .enable_preview_multibuffer_from_code_navigation;
18426                Self::open_locations_in_multibuffer(
18427                    workspace,
18428                    locations,
18429                    title,
18430                    false,
18431                    allow_preview,
18432                    MultibufferSelectionMode::First,
18433                    window,
18434                    cx,
18435                );
18436                Navigated::Yes
18437            })
18438        }))
18439    }
18440
18441    /// Opens a multibuffer with the given project locations in it.
18442    pub fn open_locations_in_multibuffer(
18443        workspace: &mut Workspace,
18444        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18445        title: String,
18446        split: bool,
18447        allow_preview: bool,
18448        multibuffer_selection_mode: MultibufferSelectionMode,
18449        window: &mut Window,
18450        cx: &mut Context<Workspace>,
18451    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18452        if locations.is_empty() {
18453            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18454            return None;
18455        }
18456
18457        let capability = workspace.project().read(cx).capability();
18458        let mut ranges = <Vec<Range<Anchor>>>::new();
18459
18460        // a key to find existing multibuffer editors with the same set of locations
18461        // to prevent us from opening more and more multibuffer tabs for searches and the like
18462        let mut key = (title.clone(), vec![]);
18463        let excerpt_buffer = cx.new(|cx| {
18464            let key = &mut key.1;
18465            let mut multibuffer = MultiBuffer::new(capability);
18466            for (buffer, mut ranges_for_buffer) in locations {
18467                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18468                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18469                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18470                    PathKey::for_buffer(&buffer, cx),
18471                    buffer.clone(),
18472                    ranges_for_buffer,
18473                    multibuffer_context_lines(cx),
18474                    cx,
18475                );
18476                ranges.extend(new_ranges)
18477            }
18478
18479            multibuffer.with_title(title)
18480        });
18481        let existing = workspace.active_pane().update(cx, |pane, cx| {
18482            pane.items()
18483                .filter_map(|item| item.downcast::<Editor>())
18484                .find(|editor| {
18485                    editor
18486                        .read(cx)
18487                        .lookup_key
18488                        .as_ref()
18489                        .and_then(|it| {
18490                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18491                        })
18492                        .is_some_and(|it| *it == key)
18493                })
18494        });
18495        let was_existing = existing.is_some();
18496        let editor = existing.unwrap_or_else(|| {
18497            cx.new(|cx| {
18498                let mut editor = Editor::for_multibuffer(
18499                    excerpt_buffer,
18500                    Some(workspace.project().clone()),
18501                    window,
18502                    cx,
18503                );
18504                editor.lookup_key = Some(Box::new(key));
18505                editor
18506            })
18507        });
18508        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18509            MultibufferSelectionMode::First => {
18510                if let Some(first_range) = ranges.first() {
18511                    editor.change_selections(
18512                        SelectionEffects::no_scroll(),
18513                        window,
18514                        cx,
18515                        |selections| {
18516                            selections.clear_disjoint();
18517                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18518                        },
18519                    );
18520                }
18521                editor.highlight_background::<Self>(
18522                    &ranges,
18523                    |_, theme| theme.colors().editor_highlighted_line_background,
18524                    cx,
18525                );
18526            }
18527            MultibufferSelectionMode::All => {
18528                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18529                    selections.clear_disjoint();
18530                    selections.select_anchor_ranges(ranges);
18531                });
18532            }
18533        });
18534
18535        let item = Box::new(editor.clone());
18536
18537        let pane = if split {
18538            workspace.adjacent_pane(window, cx)
18539        } else {
18540            workspace.active_pane().clone()
18541        };
18542        let activate_pane = split;
18543
18544        let mut destination_index = None;
18545        pane.update(cx, |pane, cx| {
18546            if allow_preview && !was_existing {
18547                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18548            }
18549            if was_existing && !allow_preview {
18550                pane.unpreview_item_if_preview(item.item_id());
18551            }
18552            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18553        });
18554
18555        Some((editor, pane))
18556    }
18557
18558    pub fn rename(
18559        &mut self,
18560        _: &Rename,
18561        window: &mut Window,
18562        cx: &mut Context<Self>,
18563    ) -> Option<Task<Result<()>>> {
18564        use language::ToOffset as _;
18565
18566        let provider = self.semantics_provider.clone()?;
18567        let selection = self.selections.newest_anchor().clone();
18568        let (cursor_buffer, cursor_buffer_position) = self
18569            .buffer
18570            .read(cx)
18571            .text_anchor_for_position(selection.head(), cx)?;
18572        let (tail_buffer, cursor_buffer_position_end) = self
18573            .buffer
18574            .read(cx)
18575            .text_anchor_for_position(selection.tail(), cx)?;
18576        if tail_buffer != cursor_buffer {
18577            return None;
18578        }
18579
18580        let snapshot = cursor_buffer.read(cx).snapshot();
18581        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18582        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18583        let prepare_rename = provider
18584            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18585            .unwrap_or_else(|| Task::ready(Ok(None)));
18586        drop(snapshot);
18587
18588        Some(cx.spawn_in(window, async move |this, cx| {
18589            let rename_range = if let Some(range) = prepare_rename.await? {
18590                Some(range)
18591            } else {
18592                this.update(cx, |this, cx| {
18593                    let buffer = this.buffer.read(cx).snapshot(cx);
18594                    let mut buffer_highlights = this
18595                        .document_highlights_for_position(selection.head(), &buffer)
18596                        .filter(|highlight| {
18597                            highlight.start.excerpt_id == selection.head().excerpt_id
18598                                && highlight.end.excerpt_id == selection.head().excerpt_id
18599                        });
18600                    buffer_highlights
18601                        .next()
18602                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18603                })?
18604            };
18605            if let Some(rename_range) = rename_range {
18606                this.update_in(cx, |this, window, cx| {
18607                    let snapshot = cursor_buffer.read(cx).snapshot();
18608                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18609                    let cursor_offset_in_rename_range =
18610                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18611                    let cursor_offset_in_rename_range_end =
18612                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18613
18614                    this.take_rename(false, window, cx);
18615                    let buffer = this.buffer.read(cx).read(cx);
18616                    let cursor_offset = selection.head().to_offset(&buffer);
18617                    let rename_start =
18618                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18619                    let rename_end = rename_start + rename_buffer_range.len();
18620                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18621                    let mut old_highlight_id = None;
18622                    let old_name: Arc<str> = buffer
18623                        .chunks(rename_start..rename_end, true)
18624                        .map(|chunk| {
18625                            if old_highlight_id.is_none() {
18626                                old_highlight_id = chunk.syntax_highlight_id;
18627                            }
18628                            chunk.text
18629                        })
18630                        .collect::<String>()
18631                        .into();
18632
18633                    drop(buffer);
18634
18635                    // Position the selection in the rename editor so that it matches the current selection.
18636                    this.show_local_selections = false;
18637                    let rename_editor = cx.new(|cx| {
18638                        let mut editor = Editor::single_line(window, cx);
18639                        editor.buffer.update(cx, |buffer, cx| {
18640                            buffer.edit(
18641                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18642                                None,
18643                                cx,
18644                            )
18645                        });
18646                        let cursor_offset_in_rename_range =
18647                            MultiBufferOffset(cursor_offset_in_rename_range);
18648                        let cursor_offset_in_rename_range_end =
18649                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18650                        let rename_selection_range = match cursor_offset_in_rename_range
18651                            .cmp(&cursor_offset_in_rename_range_end)
18652                        {
18653                            Ordering::Equal => {
18654                                editor.select_all(&SelectAll, window, cx);
18655                                return editor;
18656                            }
18657                            Ordering::Less => {
18658                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18659                            }
18660                            Ordering::Greater => {
18661                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18662                            }
18663                        };
18664                        if rename_selection_range.end.0 > old_name.len() {
18665                            editor.select_all(&SelectAll, window, cx);
18666                        } else {
18667                            editor.change_selections(Default::default(), window, cx, |s| {
18668                                s.select_ranges([rename_selection_range]);
18669                            });
18670                        }
18671                        editor
18672                    });
18673                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18674                        if e == &EditorEvent::Focused {
18675                            cx.emit(EditorEvent::FocusedIn)
18676                        }
18677                    })
18678                    .detach();
18679
18680                    let write_highlights =
18681                        this.clear_background_highlights::<DocumentHighlightWrite>(cx);
18682                    let read_highlights =
18683                        this.clear_background_highlights::<DocumentHighlightRead>(cx);
18684                    let ranges = write_highlights
18685                        .iter()
18686                        .flat_map(|(_, ranges)| ranges.iter())
18687                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18688                        .cloned()
18689                        .collect();
18690
18691                    this.highlight_text::<Rename>(
18692                        ranges,
18693                        HighlightStyle {
18694                            fade_out: Some(0.6),
18695                            ..Default::default()
18696                        },
18697                        cx,
18698                    );
18699                    let rename_focus_handle = rename_editor.focus_handle(cx);
18700                    window.focus(&rename_focus_handle, cx);
18701                    let block_id = this.insert_blocks(
18702                        [BlockProperties {
18703                            style: BlockStyle::Flex,
18704                            placement: BlockPlacement::Below(range.start),
18705                            height: Some(1),
18706                            render: Arc::new({
18707                                let rename_editor = rename_editor.clone();
18708                                move |cx: &mut BlockContext| {
18709                                    let mut text_style = cx.editor_style.text.clone();
18710                                    if let Some(highlight_style) = old_highlight_id
18711                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18712                                    {
18713                                        text_style = text_style.highlight(highlight_style);
18714                                    }
18715                                    div()
18716                                        .block_mouse_except_scroll()
18717                                        .pl(cx.anchor_x)
18718                                        .child(EditorElement::new(
18719                                            &rename_editor,
18720                                            EditorStyle {
18721                                                background: cx.theme().system().transparent,
18722                                                local_player: cx.editor_style.local_player,
18723                                                text: text_style,
18724                                                scrollbar_width: cx.editor_style.scrollbar_width,
18725                                                syntax: cx.editor_style.syntax.clone(),
18726                                                status: cx.editor_style.status.clone(),
18727                                                inlay_hints_style: HighlightStyle {
18728                                                    font_weight: Some(FontWeight::BOLD),
18729                                                    ..make_inlay_hints_style(cx.app)
18730                                                },
18731                                                edit_prediction_styles: make_suggestion_styles(
18732                                                    cx.app,
18733                                                ),
18734                                                ..EditorStyle::default()
18735                                            },
18736                                        ))
18737                                        .into_any_element()
18738                                }
18739                            }),
18740                            priority: 0,
18741                        }],
18742                        Some(Autoscroll::fit()),
18743                        cx,
18744                    )[0];
18745                    this.pending_rename = Some(RenameState {
18746                        range,
18747                        old_name,
18748                        editor: rename_editor,
18749                        block_id,
18750                    });
18751                })?;
18752            }
18753
18754            Ok(())
18755        }))
18756    }
18757
18758    pub fn confirm_rename(
18759        &mut self,
18760        _: &ConfirmRename,
18761        window: &mut Window,
18762        cx: &mut Context<Self>,
18763    ) -> Option<Task<Result<()>>> {
18764        let rename = self.take_rename(false, window, cx)?;
18765        let workspace = self.workspace()?.downgrade();
18766        let (buffer, start) = self
18767            .buffer
18768            .read(cx)
18769            .text_anchor_for_position(rename.range.start, cx)?;
18770        let (end_buffer, _) = self
18771            .buffer
18772            .read(cx)
18773            .text_anchor_for_position(rename.range.end, cx)?;
18774        if buffer != end_buffer {
18775            return None;
18776        }
18777
18778        let old_name = rename.old_name;
18779        let new_name = rename.editor.read(cx).text(cx);
18780
18781        let rename = self.semantics_provider.as_ref()?.perform_rename(
18782            &buffer,
18783            start,
18784            new_name.clone(),
18785            cx,
18786        )?;
18787
18788        Some(cx.spawn_in(window, async move |editor, cx| {
18789            let project_transaction = rename.await?;
18790            Self::open_project_transaction(
18791                &editor,
18792                workspace,
18793                project_transaction,
18794                format!("Rename: {}{}", old_name, new_name),
18795                cx,
18796            )
18797            .await?;
18798
18799            editor.update(cx, |editor, cx| {
18800                editor.refresh_document_highlights(cx);
18801            })?;
18802            Ok(())
18803        }))
18804    }
18805
18806    fn take_rename(
18807        &mut self,
18808        moving_cursor: bool,
18809        window: &mut Window,
18810        cx: &mut Context<Self>,
18811    ) -> Option<RenameState> {
18812        let rename = self.pending_rename.take()?;
18813        if rename.editor.focus_handle(cx).is_focused(window) {
18814            window.focus(&self.focus_handle, cx);
18815        }
18816
18817        self.remove_blocks(
18818            [rename.block_id].into_iter().collect(),
18819            Some(Autoscroll::fit()),
18820            cx,
18821        );
18822        self.clear_highlights::<Rename>(cx);
18823        self.show_local_selections = true;
18824
18825        if moving_cursor {
18826            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
18827                editor
18828                    .selections
18829                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
18830                    .head()
18831            });
18832
18833            // Update the selection to match the position of the selection inside
18834            // the rename editor.
18835            let snapshot = self.buffer.read(cx).read(cx);
18836            let rename_range = rename.range.to_offset(&snapshot);
18837            let cursor_in_editor = snapshot
18838                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
18839                .min(rename_range.end);
18840            drop(snapshot);
18841
18842            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
18843                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
18844            });
18845        } else {
18846            self.refresh_document_highlights(cx);
18847        }
18848
18849        Some(rename)
18850    }
18851
18852    pub fn pending_rename(&self) -> Option<&RenameState> {
18853        self.pending_rename.as_ref()
18854    }
18855
18856    fn format(
18857        &mut self,
18858        _: &Format,
18859        window: &mut Window,
18860        cx: &mut Context<Self>,
18861    ) -> Option<Task<Result<()>>> {
18862        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18863
18864        let project = match &self.project {
18865            Some(project) => project.clone(),
18866            None => return None,
18867        };
18868
18869        Some(self.perform_format(
18870            project,
18871            FormatTrigger::Manual,
18872            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
18873            window,
18874            cx,
18875        ))
18876    }
18877
18878    fn format_selections(
18879        &mut self,
18880        _: &FormatSelections,
18881        window: &mut Window,
18882        cx: &mut Context<Self>,
18883    ) -> Option<Task<Result<()>>> {
18884        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18885
18886        let project = match &self.project {
18887            Some(project) => project.clone(),
18888            None => return None,
18889        };
18890
18891        let ranges = self
18892            .selections
18893            .all_adjusted(&self.display_snapshot(cx))
18894            .into_iter()
18895            .map(|selection| selection.range())
18896            .collect_vec();
18897
18898        Some(self.perform_format(
18899            project,
18900            FormatTrigger::Manual,
18901            FormatTarget::Ranges(ranges),
18902            window,
18903            cx,
18904        ))
18905    }
18906
18907    fn perform_format(
18908        &mut self,
18909        project: Entity<Project>,
18910        trigger: FormatTrigger,
18911        target: FormatTarget,
18912        window: &mut Window,
18913        cx: &mut Context<Self>,
18914    ) -> Task<Result<()>> {
18915        let buffer = self.buffer.clone();
18916        let (buffers, target) = match target {
18917            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
18918            FormatTarget::Ranges(selection_ranges) => {
18919                let multi_buffer = buffer.read(cx);
18920                let snapshot = multi_buffer.read(cx);
18921                let mut buffers = HashSet::default();
18922                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
18923                    BTreeMap::new();
18924                for selection_range in selection_ranges {
18925                    for (buffer, buffer_range, _) in
18926                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
18927                    {
18928                        let buffer_id = buffer.remote_id();
18929                        let start = buffer.anchor_before(buffer_range.start);
18930                        let end = buffer.anchor_after(buffer_range.end);
18931                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
18932                        buffer_id_to_ranges
18933                            .entry(buffer_id)
18934                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
18935                            .or_insert_with(|| vec![start..end]);
18936                    }
18937                }
18938                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
18939            }
18940        };
18941
18942        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
18943        let selections_prev = transaction_id_prev
18944            .and_then(|transaction_id_prev| {
18945                // default to selections as they were after the last edit, if we have them,
18946                // instead of how they are now.
18947                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
18948                // will take you back to where you made the last edit, instead of staying where you scrolled
18949                self.selection_history
18950                    .transaction(transaction_id_prev)
18951                    .map(|t| t.0.clone())
18952            })
18953            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
18954
18955        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
18956        let format = project.update(cx, |project, cx| {
18957            project.format(buffers, target, true, trigger, cx)
18958        });
18959
18960        cx.spawn_in(window, async move |editor, cx| {
18961            let transaction = futures::select_biased! {
18962                transaction = format.log_err().fuse() => transaction,
18963                () = timeout => {
18964                    log::warn!("timed out waiting for formatting");
18965                    None
18966                }
18967            };
18968
18969            buffer.update(cx, |buffer, cx| {
18970                if let Some(transaction) = transaction
18971                    && !buffer.is_singleton()
18972                {
18973                    buffer.push_transaction(&transaction.0, cx);
18974                }
18975                cx.notify();
18976            });
18977
18978            if let Some(transaction_id_now) =
18979                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
18980            {
18981                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
18982                if has_new_transaction {
18983                    editor
18984                        .update(cx, |editor, _| {
18985                            editor
18986                                .selection_history
18987                                .insert_transaction(transaction_id_now, selections_prev);
18988                        })
18989                        .ok();
18990                }
18991            }
18992
18993            Ok(())
18994        })
18995    }
18996
18997    fn organize_imports(
18998        &mut self,
18999        _: &OrganizeImports,
19000        window: &mut Window,
19001        cx: &mut Context<Self>,
19002    ) -> Option<Task<Result<()>>> {
19003        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19004        let project = match &self.project {
19005            Some(project) => project.clone(),
19006            None => return None,
19007        };
19008        Some(self.perform_code_action_kind(
19009            project,
19010            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19011            window,
19012            cx,
19013        ))
19014    }
19015
19016    fn perform_code_action_kind(
19017        &mut self,
19018        project: Entity<Project>,
19019        kind: CodeActionKind,
19020        window: &mut Window,
19021        cx: &mut Context<Self>,
19022    ) -> Task<Result<()>> {
19023        let buffer = self.buffer.clone();
19024        let buffers = buffer.read(cx).all_buffers();
19025        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19026        let apply_action = project.update(cx, |project, cx| {
19027            project.apply_code_action_kind(buffers, kind, true, cx)
19028        });
19029        cx.spawn_in(window, async move |_, cx| {
19030            let transaction = futures::select_biased! {
19031                () = timeout => {
19032                    log::warn!("timed out waiting for executing code action");
19033                    None
19034                }
19035                transaction = apply_action.log_err().fuse() => transaction,
19036            };
19037            buffer.update(cx, |buffer, cx| {
19038                // check if we need this
19039                if let Some(transaction) = transaction
19040                    && !buffer.is_singleton()
19041                {
19042                    buffer.push_transaction(&transaction.0, cx);
19043                }
19044                cx.notify();
19045            });
19046            Ok(())
19047        })
19048    }
19049
19050    pub fn restart_language_server(
19051        &mut self,
19052        _: &RestartLanguageServer,
19053        _: &mut Window,
19054        cx: &mut Context<Self>,
19055    ) {
19056        if let Some(project) = self.project.clone() {
19057            self.buffer.update(cx, |multi_buffer, cx| {
19058                project.update(cx, |project, cx| {
19059                    project.restart_language_servers_for_buffers(
19060                        multi_buffer.all_buffers().into_iter().collect(),
19061                        HashSet::default(),
19062                        cx,
19063                    );
19064                });
19065            })
19066        }
19067    }
19068
19069    pub fn stop_language_server(
19070        &mut self,
19071        _: &StopLanguageServer,
19072        _: &mut Window,
19073        cx: &mut Context<Self>,
19074    ) {
19075        if let Some(project) = self.project.clone() {
19076            self.buffer.update(cx, |multi_buffer, cx| {
19077                project.update(cx, |project, cx| {
19078                    project.stop_language_servers_for_buffers(
19079                        multi_buffer.all_buffers().into_iter().collect(),
19080                        HashSet::default(),
19081                        cx,
19082                    );
19083                });
19084            });
19085            self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
19086        }
19087    }
19088
19089    fn cancel_language_server_work(
19090        workspace: &mut Workspace,
19091        _: &actions::CancelLanguageServerWork,
19092        _: &mut Window,
19093        cx: &mut Context<Workspace>,
19094    ) {
19095        let project = workspace.project();
19096        let buffers = workspace
19097            .active_item(cx)
19098            .and_then(|item| item.act_as::<Editor>(cx))
19099            .map_or(HashSet::default(), |editor| {
19100                editor.read(cx).buffer.read(cx).all_buffers()
19101            });
19102        project.update(cx, |project, cx| {
19103            project.cancel_language_server_work_for_buffers(buffers, cx);
19104        });
19105    }
19106
19107    fn show_character_palette(
19108        &mut self,
19109        _: &ShowCharacterPalette,
19110        window: &mut Window,
19111        _: &mut Context<Self>,
19112    ) {
19113        window.show_character_palette();
19114    }
19115
19116    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19117        if !self.diagnostics_enabled() {
19118            return;
19119        }
19120
19121        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19122            let buffer = self.buffer.read(cx).snapshot(cx);
19123            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19124            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19125            let is_valid = buffer
19126                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19127                .any(|entry| {
19128                    entry.diagnostic.is_primary
19129                        && !entry.range.is_empty()
19130                        && entry.range.start == primary_range_start
19131                        && entry.diagnostic.message == active_diagnostics.active_message
19132                });
19133
19134            if !is_valid {
19135                self.dismiss_diagnostics(cx);
19136            }
19137        }
19138    }
19139
19140    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19141        match &self.active_diagnostics {
19142            ActiveDiagnostic::Group(group) => Some(group),
19143            _ => None,
19144        }
19145    }
19146
19147    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19148        if !self.diagnostics_enabled() {
19149            return;
19150        }
19151        self.dismiss_diagnostics(cx);
19152        self.active_diagnostics = ActiveDiagnostic::All;
19153    }
19154
19155    fn activate_diagnostics(
19156        &mut self,
19157        buffer_id: BufferId,
19158        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19159        window: &mut Window,
19160        cx: &mut Context<Self>,
19161    ) {
19162        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19163            return;
19164        }
19165        self.dismiss_diagnostics(cx);
19166        let snapshot = self.snapshot(window, cx);
19167        let buffer = self.buffer.read(cx).snapshot(cx);
19168        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19169            return;
19170        };
19171
19172        let diagnostic_group = buffer
19173            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19174            .collect::<Vec<_>>();
19175
19176        let language_registry = self
19177            .project()
19178            .map(|project| project.read(cx).languages().clone());
19179
19180        let blocks = renderer.render_group(
19181            diagnostic_group,
19182            buffer_id,
19183            snapshot,
19184            cx.weak_entity(),
19185            language_registry,
19186            cx,
19187        );
19188
19189        let blocks = self.display_map.update(cx, |display_map, cx| {
19190            display_map.insert_blocks(blocks, cx).into_iter().collect()
19191        });
19192        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19193            active_range: buffer.anchor_before(diagnostic.range.start)
19194                ..buffer.anchor_after(diagnostic.range.end),
19195            active_message: diagnostic.diagnostic.message.clone(),
19196            group_id: diagnostic.diagnostic.group_id,
19197            blocks,
19198        });
19199        cx.notify();
19200    }
19201
19202    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19203        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19204            return;
19205        };
19206
19207        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19208        if let ActiveDiagnostic::Group(group) = prev {
19209            self.display_map.update(cx, |display_map, cx| {
19210                display_map.remove_blocks(group.blocks, cx);
19211            });
19212            cx.notify();
19213        }
19214    }
19215
19216    /// Disable inline diagnostics rendering for this editor.
19217    pub fn disable_inline_diagnostics(&mut self) {
19218        self.inline_diagnostics_enabled = false;
19219        self.inline_diagnostics_update = Task::ready(());
19220        self.inline_diagnostics.clear();
19221    }
19222
19223    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19224        self.diagnostics_enabled = false;
19225        self.dismiss_diagnostics(cx);
19226        self.inline_diagnostics_update = Task::ready(());
19227        self.inline_diagnostics.clear();
19228    }
19229
19230    pub fn disable_word_completions(&mut self) {
19231        self.word_completions_enabled = false;
19232    }
19233
19234    pub fn diagnostics_enabled(&self) -> bool {
19235        self.diagnostics_enabled && self.mode.is_full()
19236    }
19237
19238    pub fn inline_diagnostics_enabled(&self) -> bool {
19239        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19240    }
19241
19242    pub fn show_inline_diagnostics(&self) -> bool {
19243        self.show_inline_diagnostics
19244    }
19245
19246    pub fn toggle_inline_diagnostics(
19247        &mut self,
19248        _: &ToggleInlineDiagnostics,
19249        window: &mut Window,
19250        cx: &mut Context<Editor>,
19251    ) {
19252        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19253        self.refresh_inline_diagnostics(false, window, cx);
19254    }
19255
19256    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19257        self.diagnostics_max_severity = severity;
19258        self.display_map.update(cx, |display_map, _| {
19259            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19260        });
19261    }
19262
19263    pub fn toggle_diagnostics(
19264        &mut self,
19265        _: &ToggleDiagnostics,
19266        window: &mut Window,
19267        cx: &mut Context<Editor>,
19268    ) {
19269        if !self.diagnostics_enabled() {
19270            return;
19271        }
19272
19273        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19274            EditorSettings::get_global(cx)
19275                .diagnostics_max_severity
19276                .filter(|severity| severity != &DiagnosticSeverity::Off)
19277                .unwrap_or(DiagnosticSeverity::Hint)
19278        } else {
19279            DiagnosticSeverity::Off
19280        };
19281        self.set_max_diagnostics_severity(new_severity, cx);
19282        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19283            self.active_diagnostics = ActiveDiagnostic::None;
19284            self.inline_diagnostics_update = Task::ready(());
19285            self.inline_diagnostics.clear();
19286        } else {
19287            self.refresh_inline_diagnostics(false, window, cx);
19288        }
19289
19290        cx.notify();
19291    }
19292
19293    pub fn toggle_minimap(
19294        &mut self,
19295        _: &ToggleMinimap,
19296        window: &mut Window,
19297        cx: &mut Context<Editor>,
19298    ) {
19299        if self.supports_minimap(cx) {
19300            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19301        }
19302    }
19303
19304    fn refresh_inline_diagnostics(
19305        &mut self,
19306        debounce: bool,
19307        window: &mut Window,
19308        cx: &mut Context<Self>,
19309    ) {
19310        let max_severity = ProjectSettings::get_global(cx)
19311            .diagnostics
19312            .inline
19313            .max_severity
19314            .unwrap_or(self.diagnostics_max_severity);
19315
19316        if !self.inline_diagnostics_enabled()
19317            || !self.diagnostics_enabled()
19318            || !self.show_inline_diagnostics
19319            || max_severity == DiagnosticSeverity::Off
19320        {
19321            self.inline_diagnostics_update = Task::ready(());
19322            self.inline_diagnostics.clear();
19323            return;
19324        }
19325
19326        let debounce_ms = ProjectSettings::get_global(cx)
19327            .diagnostics
19328            .inline
19329            .update_debounce_ms;
19330        let debounce = if debounce && debounce_ms > 0 {
19331            Some(Duration::from_millis(debounce_ms))
19332        } else {
19333            None
19334        };
19335        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19336            if let Some(debounce) = debounce {
19337                cx.background_executor().timer(debounce).await;
19338            }
19339            let Some(snapshot) = editor.upgrade().map(|editor| {
19340                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19341            }) else {
19342                return;
19343            };
19344
19345            let new_inline_diagnostics = cx
19346                .background_spawn(async move {
19347                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19348                    for diagnostic_entry in
19349                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19350                    {
19351                        let message = diagnostic_entry
19352                            .diagnostic
19353                            .message
19354                            .split_once('\n')
19355                            .map(|(line, _)| line)
19356                            .map(SharedString::new)
19357                            .unwrap_or_else(|| {
19358                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19359                            });
19360                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19361                        let (Ok(i) | Err(i)) = inline_diagnostics
19362                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19363                        inline_diagnostics.insert(
19364                            i,
19365                            (
19366                                start_anchor,
19367                                InlineDiagnostic {
19368                                    message,
19369                                    group_id: diagnostic_entry.diagnostic.group_id,
19370                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19371                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19372                                    severity: diagnostic_entry.diagnostic.severity,
19373                                },
19374                            ),
19375                        );
19376                    }
19377                    inline_diagnostics
19378                })
19379                .await;
19380
19381            editor
19382                .update(cx, |editor, cx| {
19383                    editor.inline_diagnostics = new_inline_diagnostics;
19384                    cx.notify();
19385                })
19386                .ok();
19387        });
19388    }
19389
19390    fn pull_diagnostics(
19391        &mut self,
19392        buffer_id: BufferId,
19393        _window: &Window,
19394        cx: &mut Context<Self>,
19395    ) -> Option<()> {
19396        if self.ignore_lsp_data() || !self.diagnostics_enabled() {
19397            return None;
19398        }
19399        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19400            .diagnostics
19401            .lsp_pull_diagnostics;
19402        if !pull_diagnostics_settings.enabled {
19403            return None;
19404        }
19405        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19406        let project = self.project()?.downgrade();
19407        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19408
19409        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19410            cx.background_executor().timer(debounce).await;
19411            if let Ok(task) = project.update(cx, |project, cx| {
19412                project.lsp_store().update(cx, |lsp_store, cx| {
19413                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19414                })
19415            }) {
19416                task.await.log_err();
19417            }
19418            project
19419                .update(cx, |project, cx| {
19420                    project.lsp_store().update(cx, |lsp_store, cx| {
19421                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19422                    })
19423                })
19424                .log_err();
19425        });
19426
19427        Some(())
19428    }
19429
19430    pub fn set_selections_from_remote(
19431        &mut self,
19432        selections: Vec<Selection<Anchor>>,
19433        pending_selection: Option<Selection<Anchor>>,
19434        window: &mut Window,
19435        cx: &mut Context<Self>,
19436    ) {
19437        let old_cursor_position = self.selections.newest_anchor().head();
19438        self.selections
19439            .change_with(&self.display_snapshot(cx), |s| {
19440                s.select_anchors(selections);
19441                if let Some(pending_selection) = pending_selection {
19442                    s.set_pending(pending_selection, SelectMode::Character);
19443                } else {
19444                    s.clear_pending();
19445                }
19446            });
19447        self.selections_did_change(
19448            false,
19449            &old_cursor_position,
19450            SelectionEffects::default(),
19451            window,
19452            cx,
19453        );
19454    }
19455
19456    pub fn transact(
19457        &mut self,
19458        window: &mut Window,
19459        cx: &mut Context<Self>,
19460        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19461    ) -> Option<TransactionId> {
19462        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19463            this.start_transaction_at(Instant::now(), window, cx);
19464            update(this, window, cx);
19465            this.end_transaction_at(Instant::now(), cx)
19466        })
19467    }
19468
19469    pub fn start_transaction_at(
19470        &mut self,
19471        now: Instant,
19472        window: &mut Window,
19473        cx: &mut Context<Self>,
19474    ) -> Option<TransactionId> {
19475        self.end_selection(window, cx);
19476        if let Some(tx_id) = self
19477            .buffer
19478            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19479        {
19480            self.selection_history
19481                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19482            cx.emit(EditorEvent::TransactionBegun {
19483                transaction_id: tx_id,
19484            });
19485            Some(tx_id)
19486        } else {
19487            None
19488        }
19489    }
19490
19491    pub fn end_transaction_at(
19492        &mut self,
19493        now: Instant,
19494        cx: &mut Context<Self>,
19495    ) -> Option<TransactionId> {
19496        if let Some(transaction_id) = self
19497            .buffer
19498            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19499        {
19500            if let Some((_, end_selections)) =
19501                self.selection_history.transaction_mut(transaction_id)
19502            {
19503                *end_selections = Some(self.selections.disjoint_anchors_arc());
19504            } else {
19505                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19506            }
19507
19508            cx.emit(EditorEvent::Edited { transaction_id });
19509            Some(transaction_id)
19510        } else {
19511            None
19512        }
19513    }
19514
19515    pub fn modify_transaction_selection_history(
19516        &mut self,
19517        transaction_id: TransactionId,
19518        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19519    ) -> bool {
19520        self.selection_history
19521            .transaction_mut(transaction_id)
19522            .map(modify)
19523            .is_some()
19524    }
19525
19526    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19527        if self.selection_mark_mode {
19528            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19529                s.move_with(|_, sel| {
19530                    sel.collapse_to(sel.head(), SelectionGoal::None);
19531                });
19532            })
19533        }
19534        self.selection_mark_mode = true;
19535        cx.notify();
19536    }
19537
19538    pub fn swap_selection_ends(
19539        &mut self,
19540        _: &actions::SwapSelectionEnds,
19541        window: &mut Window,
19542        cx: &mut Context<Self>,
19543    ) {
19544        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19545            s.move_with(|_, sel| {
19546                if sel.start != sel.end {
19547                    sel.reversed = !sel.reversed
19548                }
19549            });
19550        });
19551        self.request_autoscroll(Autoscroll::newest(), cx);
19552        cx.notify();
19553    }
19554
19555    pub fn toggle_focus(
19556        workspace: &mut Workspace,
19557        _: &actions::ToggleFocus,
19558        window: &mut Window,
19559        cx: &mut Context<Workspace>,
19560    ) {
19561        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19562            return;
19563        };
19564        workspace.activate_item(&item, true, true, window, cx);
19565    }
19566
19567    pub fn toggle_fold(
19568        &mut self,
19569        _: &actions::ToggleFold,
19570        window: &mut Window,
19571        cx: &mut Context<Self>,
19572    ) {
19573        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19574            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19575            let selection = self.selections.newest::<Point>(&display_map);
19576
19577            let range = if selection.is_empty() {
19578                let point = selection.head().to_display_point(&display_map);
19579                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19580                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19581                    .to_point(&display_map);
19582                start..end
19583            } else {
19584                selection.range()
19585            };
19586            if display_map.folds_in_range(range).next().is_some() {
19587                self.unfold_lines(&Default::default(), window, cx)
19588            } else {
19589                self.fold(&Default::default(), window, cx)
19590            }
19591        } else {
19592            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19593            let buffer_ids: HashSet<_> = self
19594                .selections
19595                .disjoint_anchor_ranges()
19596                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19597                .collect();
19598
19599            let should_unfold = buffer_ids
19600                .iter()
19601                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19602
19603            for buffer_id in buffer_ids {
19604                if should_unfold {
19605                    self.unfold_buffer(buffer_id, cx);
19606                } else {
19607                    self.fold_buffer(buffer_id, cx);
19608                }
19609            }
19610        }
19611    }
19612
19613    pub fn toggle_fold_recursive(
19614        &mut self,
19615        _: &actions::ToggleFoldRecursive,
19616        window: &mut Window,
19617        cx: &mut Context<Self>,
19618    ) {
19619        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19620
19621        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19622        let range = if selection.is_empty() {
19623            let point = selection.head().to_display_point(&display_map);
19624            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19625            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19626                .to_point(&display_map);
19627            start..end
19628        } else {
19629            selection.range()
19630        };
19631        if display_map.folds_in_range(range).next().is_some() {
19632            self.unfold_recursive(&Default::default(), window, cx)
19633        } else {
19634            self.fold_recursive(&Default::default(), window, cx)
19635        }
19636    }
19637
19638    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19639        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19640            let mut to_fold = Vec::new();
19641            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19642            let selections = self.selections.all_adjusted(&display_map);
19643
19644            for selection in selections {
19645                let range = selection.range().sorted();
19646                let buffer_start_row = range.start.row;
19647
19648                if range.start.row != range.end.row {
19649                    let mut found = false;
19650                    let mut row = range.start.row;
19651                    while row <= range.end.row {
19652                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19653                        {
19654                            found = true;
19655                            row = crease.range().end.row + 1;
19656                            to_fold.push(crease);
19657                        } else {
19658                            row += 1
19659                        }
19660                    }
19661                    if found {
19662                        continue;
19663                    }
19664                }
19665
19666                for row in (0..=range.start.row).rev() {
19667                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19668                        && crease.range().end.row >= buffer_start_row
19669                    {
19670                        to_fold.push(crease);
19671                        if row <= range.start.row {
19672                            break;
19673                        }
19674                    }
19675                }
19676            }
19677
19678            self.fold_creases(to_fold, true, window, cx);
19679        } else {
19680            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19681            let buffer_ids = self
19682                .selections
19683                .disjoint_anchor_ranges()
19684                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19685                .collect::<HashSet<_>>();
19686            for buffer_id in buffer_ids {
19687                self.fold_buffer(buffer_id, cx);
19688            }
19689        }
19690    }
19691
19692    pub fn toggle_fold_all(
19693        &mut self,
19694        _: &actions::ToggleFoldAll,
19695        window: &mut Window,
19696        cx: &mut Context<Self>,
19697    ) {
19698        let has_folds = if self.buffer.read(cx).is_singleton() {
19699            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19700            let has_folds = display_map
19701                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19702                .next()
19703                .is_some();
19704            has_folds
19705        } else {
19706            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19707            let has_folds = buffer_ids
19708                .iter()
19709                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19710            has_folds
19711        };
19712
19713        if has_folds {
19714            self.unfold_all(&actions::UnfoldAll, window, cx);
19715        } else {
19716            self.fold_all(&actions::FoldAll, window, cx);
19717        }
19718    }
19719
19720    fn fold_at_level(
19721        &mut self,
19722        fold_at: &FoldAtLevel,
19723        window: &mut Window,
19724        cx: &mut Context<Self>,
19725    ) {
19726        if !self.buffer.read(cx).is_singleton() {
19727            return;
19728        }
19729
19730        let fold_at_level = fold_at.0;
19731        let snapshot = self.buffer.read(cx).snapshot(cx);
19732        let mut to_fold = Vec::new();
19733        let mut stack = vec![(0, snapshot.max_row().0, 1)];
19734
19735        let row_ranges_to_keep: Vec<Range<u32>> = self
19736            .selections
19737            .all::<Point>(&self.display_snapshot(cx))
19738            .into_iter()
19739            .map(|sel| sel.start.row..sel.end.row)
19740            .collect();
19741
19742        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
19743            while start_row < end_row {
19744                match self
19745                    .snapshot(window, cx)
19746                    .crease_for_buffer_row(MultiBufferRow(start_row))
19747                {
19748                    Some(crease) => {
19749                        let nested_start_row = crease.range().start.row + 1;
19750                        let nested_end_row = crease.range().end.row;
19751
19752                        if current_level < fold_at_level {
19753                            stack.push((nested_start_row, nested_end_row, current_level + 1));
19754                        } else if current_level == fold_at_level {
19755                            // Fold iff there is no selection completely contained within the fold region
19756                            if !row_ranges_to_keep.iter().any(|selection| {
19757                                selection.end >= nested_start_row
19758                                    && selection.start <= nested_end_row
19759                            }) {
19760                                to_fold.push(crease);
19761                            }
19762                        }
19763
19764                        start_row = nested_end_row + 1;
19765                    }
19766                    None => start_row += 1,
19767                }
19768            }
19769        }
19770
19771        self.fold_creases(to_fold, true, window, cx);
19772    }
19773
19774    pub fn fold_at_level_1(
19775        &mut self,
19776        _: &actions::FoldAtLevel1,
19777        window: &mut Window,
19778        cx: &mut Context<Self>,
19779    ) {
19780        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
19781    }
19782
19783    pub fn fold_at_level_2(
19784        &mut self,
19785        _: &actions::FoldAtLevel2,
19786        window: &mut Window,
19787        cx: &mut Context<Self>,
19788    ) {
19789        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
19790    }
19791
19792    pub fn fold_at_level_3(
19793        &mut self,
19794        _: &actions::FoldAtLevel3,
19795        window: &mut Window,
19796        cx: &mut Context<Self>,
19797    ) {
19798        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
19799    }
19800
19801    pub fn fold_at_level_4(
19802        &mut self,
19803        _: &actions::FoldAtLevel4,
19804        window: &mut Window,
19805        cx: &mut Context<Self>,
19806    ) {
19807        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
19808    }
19809
19810    pub fn fold_at_level_5(
19811        &mut self,
19812        _: &actions::FoldAtLevel5,
19813        window: &mut Window,
19814        cx: &mut Context<Self>,
19815    ) {
19816        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
19817    }
19818
19819    pub fn fold_at_level_6(
19820        &mut self,
19821        _: &actions::FoldAtLevel6,
19822        window: &mut Window,
19823        cx: &mut Context<Self>,
19824    ) {
19825        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
19826    }
19827
19828    pub fn fold_at_level_7(
19829        &mut self,
19830        _: &actions::FoldAtLevel7,
19831        window: &mut Window,
19832        cx: &mut Context<Self>,
19833    ) {
19834        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
19835    }
19836
19837    pub fn fold_at_level_8(
19838        &mut self,
19839        _: &actions::FoldAtLevel8,
19840        window: &mut Window,
19841        cx: &mut Context<Self>,
19842    ) {
19843        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
19844    }
19845
19846    pub fn fold_at_level_9(
19847        &mut self,
19848        _: &actions::FoldAtLevel9,
19849        window: &mut Window,
19850        cx: &mut Context<Self>,
19851    ) {
19852        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
19853    }
19854
19855    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
19856        if self.buffer.read(cx).is_singleton() {
19857            let mut fold_ranges = Vec::new();
19858            let snapshot = self.buffer.read(cx).snapshot(cx);
19859
19860            for row in 0..snapshot.max_row().0 {
19861                if let Some(foldable_range) = self
19862                    .snapshot(window, cx)
19863                    .crease_for_buffer_row(MultiBufferRow(row))
19864                {
19865                    fold_ranges.push(foldable_range);
19866                }
19867            }
19868
19869            self.fold_creases(fold_ranges, true, window, cx);
19870        } else {
19871            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
19872                editor
19873                    .update_in(cx, |editor, _, cx| {
19874                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
19875                            editor.fold_buffer(buffer_id, cx);
19876                        }
19877                    })
19878                    .ok();
19879            });
19880        }
19881        cx.emit(SearchEvent::ResultsCollapsedChanged(
19882            CollapseDirection::Collapsed,
19883        ));
19884    }
19885
19886    pub fn fold_function_bodies(
19887        &mut self,
19888        _: &actions::FoldFunctionBodies,
19889        window: &mut Window,
19890        cx: &mut Context<Self>,
19891    ) {
19892        let snapshot = self.buffer.read(cx).snapshot(cx);
19893
19894        let ranges = snapshot
19895            .text_object_ranges(
19896                MultiBufferOffset(0)..snapshot.len(),
19897                TreeSitterOptions::default(),
19898            )
19899            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
19900            .collect::<Vec<_>>();
19901
19902        let creases = ranges
19903            .into_iter()
19904            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
19905            .collect();
19906
19907        self.fold_creases(creases, true, window, cx);
19908    }
19909
19910    pub fn fold_recursive(
19911        &mut self,
19912        _: &actions::FoldRecursive,
19913        window: &mut Window,
19914        cx: &mut Context<Self>,
19915    ) {
19916        let mut to_fold = Vec::new();
19917        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19918        let selections = self.selections.all_adjusted(&display_map);
19919
19920        for selection in selections {
19921            let range = selection.range().sorted();
19922            let buffer_start_row = range.start.row;
19923
19924            if range.start.row != range.end.row {
19925                let mut found = false;
19926                for row in range.start.row..=range.end.row {
19927                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19928                        found = true;
19929                        to_fold.push(crease);
19930                    }
19931                }
19932                if found {
19933                    continue;
19934                }
19935            }
19936
19937            for row in (0..=range.start.row).rev() {
19938                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19939                    if crease.range().end.row >= buffer_start_row {
19940                        to_fold.push(crease);
19941                    } else {
19942                        break;
19943                    }
19944                }
19945            }
19946        }
19947
19948        self.fold_creases(to_fold, true, window, cx);
19949    }
19950
19951    pub fn fold_at(
19952        &mut self,
19953        buffer_row: MultiBufferRow,
19954        window: &mut Window,
19955        cx: &mut Context<Self>,
19956    ) {
19957        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19958
19959        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
19960            let autoscroll = self
19961                .selections
19962                .all::<Point>(&display_map)
19963                .iter()
19964                .any(|selection| crease.range().overlaps(&selection.range()));
19965
19966            self.fold_creases(vec![crease], autoscroll, window, cx);
19967        }
19968    }
19969
19970    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
19971        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19972            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19973            let buffer = display_map.buffer_snapshot();
19974            let selections = self.selections.all::<Point>(&display_map);
19975            let ranges = selections
19976                .iter()
19977                .map(|s| {
19978                    let range = s.display_range(&display_map).sorted();
19979                    let mut start = range.start.to_point(&display_map);
19980                    let mut end = range.end.to_point(&display_map);
19981                    start.column = 0;
19982                    end.column = buffer.line_len(MultiBufferRow(end.row));
19983                    start..end
19984                })
19985                .collect::<Vec<_>>();
19986
19987            self.unfold_ranges(&ranges, true, true, cx);
19988        } else {
19989            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19990            let buffer_ids = self
19991                .selections
19992                .disjoint_anchor_ranges()
19993                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19994                .collect::<HashSet<_>>();
19995            for buffer_id in buffer_ids {
19996                self.unfold_buffer(buffer_id, cx);
19997            }
19998        }
19999    }
20000
20001    pub fn unfold_recursive(
20002        &mut self,
20003        _: &UnfoldRecursive,
20004        _window: &mut Window,
20005        cx: &mut Context<Self>,
20006    ) {
20007        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20008        let selections = self.selections.all::<Point>(&display_map);
20009        let ranges = selections
20010            .iter()
20011            .map(|s| {
20012                let mut range = s.display_range(&display_map).sorted();
20013                *range.start.column_mut() = 0;
20014                *range.end.column_mut() = display_map.line_len(range.end.row());
20015                let start = range.start.to_point(&display_map);
20016                let end = range.end.to_point(&display_map);
20017                start..end
20018            })
20019            .collect::<Vec<_>>();
20020
20021        self.unfold_ranges(&ranges, true, true, cx);
20022    }
20023
20024    pub fn unfold_at(
20025        &mut self,
20026        buffer_row: MultiBufferRow,
20027        _window: &mut Window,
20028        cx: &mut Context<Self>,
20029    ) {
20030        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20031
20032        let intersection_range = Point::new(buffer_row.0, 0)
20033            ..Point::new(
20034                buffer_row.0,
20035                display_map.buffer_snapshot().line_len(buffer_row),
20036            );
20037
20038        let autoscroll = self
20039            .selections
20040            .all::<Point>(&display_map)
20041            .iter()
20042            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20043
20044        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20045    }
20046
20047    pub fn unfold_all(
20048        &mut self,
20049        _: &actions::UnfoldAll,
20050        _window: &mut Window,
20051        cx: &mut Context<Self>,
20052    ) {
20053        if self.buffer.read(cx).is_singleton() {
20054            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20055            self.unfold_ranges(
20056                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20057                true,
20058                true,
20059                cx,
20060            );
20061        } else {
20062            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20063                editor
20064                    .update(cx, |editor, cx| {
20065                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20066                            editor.unfold_buffer(buffer_id, cx);
20067                        }
20068                    })
20069                    .ok();
20070            });
20071        }
20072        cx.emit(SearchEvent::ResultsCollapsedChanged(
20073            CollapseDirection::Expanded,
20074        ));
20075    }
20076
20077    pub fn fold_selected_ranges(
20078        &mut self,
20079        _: &FoldSelectedRanges,
20080        window: &mut Window,
20081        cx: &mut Context<Self>,
20082    ) {
20083        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20084        let selections = self.selections.all_adjusted(&display_map);
20085        let ranges = selections
20086            .into_iter()
20087            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20088            .collect::<Vec<_>>();
20089        self.fold_creases(ranges, true, window, cx);
20090    }
20091
20092    pub fn fold_ranges<T: ToOffset + Clone>(
20093        &mut self,
20094        ranges: Vec<Range<T>>,
20095        auto_scroll: bool,
20096        window: &mut Window,
20097        cx: &mut Context<Self>,
20098    ) {
20099        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20100        let ranges = ranges
20101            .into_iter()
20102            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20103            .collect::<Vec<_>>();
20104        self.fold_creases(ranges, auto_scroll, window, cx);
20105    }
20106
20107    pub fn fold_creases<T: ToOffset + Clone>(
20108        &mut self,
20109        creases: Vec<Crease<T>>,
20110        auto_scroll: bool,
20111        _window: &mut Window,
20112        cx: &mut Context<Self>,
20113    ) {
20114        if creases.is_empty() {
20115            return;
20116        }
20117
20118        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20119
20120        if auto_scroll {
20121            self.request_autoscroll(Autoscroll::fit(), cx);
20122        }
20123
20124        cx.notify();
20125
20126        self.scrollbar_marker_state.dirty = true;
20127        self.folds_did_change(cx);
20128    }
20129
20130    /// Removes any folds whose ranges intersect any of the given ranges.
20131    pub fn unfold_ranges<T: ToOffset + Clone>(
20132        &mut self,
20133        ranges: &[Range<T>],
20134        inclusive: bool,
20135        auto_scroll: bool,
20136        cx: &mut Context<Self>,
20137    ) {
20138        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20139            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20140        });
20141        self.folds_did_change(cx);
20142    }
20143
20144    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20145        if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
20146            return;
20147        }
20148
20149        let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20150        self.display_map.update(cx, |display_map, cx| {
20151            display_map.fold_buffers([buffer_id], cx)
20152        });
20153
20154        let snapshot = self.display_snapshot(cx);
20155        self.selections.change_with(&snapshot, |selections| {
20156            selections.remove_selections_from_buffer(buffer_id);
20157        });
20158
20159        cx.emit(EditorEvent::BufferFoldToggled {
20160            ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
20161            folded: true,
20162        });
20163        cx.notify();
20164    }
20165
20166    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20167        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20168            return;
20169        }
20170        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20171        self.display_map.update(cx, |display_map, cx| {
20172            display_map.unfold_buffers([buffer_id], cx);
20173        });
20174        cx.emit(EditorEvent::BufferFoldToggled {
20175            ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
20176            folded: false,
20177        });
20178        cx.notify();
20179    }
20180
20181    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20182        self.display_map.read(cx).is_buffer_folded(buffer)
20183    }
20184
20185    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20186        self.display_map.read(cx).folded_buffers()
20187    }
20188
20189    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20190        self.display_map.update(cx, |display_map, cx| {
20191            display_map.disable_header_for_buffer(buffer_id, cx);
20192        });
20193        cx.notify();
20194    }
20195
20196    /// Removes any folds with the given ranges.
20197    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20198        &mut self,
20199        ranges: &[Range<T>],
20200        type_id: TypeId,
20201        auto_scroll: bool,
20202        cx: &mut Context<Self>,
20203    ) {
20204        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20205            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20206        });
20207        self.folds_did_change(cx);
20208    }
20209
20210    fn remove_folds_with<T: ToOffset + Clone>(
20211        &mut self,
20212        ranges: &[Range<T>],
20213        auto_scroll: bool,
20214        cx: &mut Context<Self>,
20215        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20216    ) {
20217        if ranges.is_empty() {
20218            return;
20219        }
20220
20221        let mut buffers_affected = HashSet::default();
20222        let multi_buffer = self.buffer().read(cx);
20223        for range in ranges {
20224            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20225                buffers_affected.insert(buffer.read(cx).remote_id());
20226            };
20227        }
20228
20229        self.display_map.update(cx, update);
20230
20231        if auto_scroll {
20232            self.request_autoscroll(Autoscroll::fit(), cx);
20233        }
20234
20235        cx.notify();
20236        self.scrollbar_marker_state.dirty = true;
20237        self.active_indent_guides_state.dirty = true;
20238    }
20239
20240    pub fn update_renderer_widths(
20241        &mut self,
20242        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20243        cx: &mut Context<Self>,
20244    ) -> bool {
20245        self.display_map
20246            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20247    }
20248
20249    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20250        self.display_map.read(cx).fold_placeholder.clone()
20251    }
20252
20253    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20254        self.buffer.update(cx, |buffer, cx| {
20255            buffer.set_all_diff_hunks_expanded(cx);
20256        });
20257    }
20258
20259    pub fn expand_all_diff_hunks(
20260        &mut self,
20261        _: &ExpandAllDiffHunks,
20262        _window: &mut Window,
20263        cx: &mut Context<Self>,
20264    ) {
20265        self.buffer.update(cx, |buffer, cx| {
20266            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20267        });
20268    }
20269
20270    pub fn collapse_all_diff_hunks(
20271        &mut self,
20272        _: &CollapseAllDiffHunks,
20273        _window: &mut Window,
20274        cx: &mut Context<Self>,
20275    ) {
20276        self.buffer.update(cx, |buffer, cx| {
20277            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20278        });
20279    }
20280
20281    pub fn toggle_selected_diff_hunks(
20282        &mut self,
20283        _: &ToggleSelectedDiffHunks,
20284        _window: &mut Window,
20285        cx: &mut Context<Self>,
20286    ) {
20287        let ranges: Vec<_> = self
20288            .selections
20289            .disjoint_anchors()
20290            .iter()
20291            .map(|s| s.range())
20292            .collect();
20293        self.toggle_diff_hunks_in_ranges(ranges, cx);
20294    }
20295
20296    pub fn diff_hunks_in_ranges<'a>(
20297        &'a self,
20298        ranges: &'a [Range<Anchor>],
20299        buffer: &'a MultiBufferSnapshot,
20300    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20301        ranges.iter().flat_map(move |range| {
20302            let end_excerpt_id = range.end.excerpt_id;
20303            let range = range.to_point(buffer);
20304            let mut peek_end = range.end;
20305            if range.end.row < buffer.max_row().0 {
20306                peek_end = Point::new(range.end.row + 1, 0);
20307            }
20308            buffer
20309                .diff_hunks_in_range(range.start..peek_end)
20310                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20311        })
20312    }
20313
20314    pub fn has_stageable_diff_hunks_in_ranges(
20315        &self,
20316        ranges: &[Range<Anchor>],
20317        snapshot: &MultiBufferSnapshot,
20318    ) -> bool {
20319        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20320        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20321    }
20322
20323    pub fn toggle_staged_selected_diff_hunks(
20324        &mut self,
20325        _: &::git::ToggleStaged,
20326        _: &mut Window,
20327        cx: &mut Context<Self>,
20328    ) {
20329        let snapshot = self.buffer.read(cx).snapshot(cx);
20330        let ranges: Vec<_> = self
20331            .selections
20332            .disjoint_anchors()
20333            .iter()
20334            .map(|s| s.range())
20335            .collect();
20336        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20337        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20338    }
20339
20340    pub fn set_render_diff_hunk_controls(
20341        &mut self,
20342        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20343        cx: &mut Context<Self>,
20344    ) {
20345        self.render_diff_hunk_controls = render_diff_hunk_controls;
20346        cx.notify();
20347    }
20348
20349    pub fn stage_and_next(
20350        &mut self,
20351        _: &::git::StageAndNext,
20352        window: &mut Window,
20353        cx: &mut Context<Self>,
20354    ) {
20355        self.do_stage_or_unstage_and_next(true, window, cx);
20356    }
20357
20358    pub fn unstage_and_next(
20359        &mut self,
20360        _: &::git::UnstageAndNext,
20361        window: &mut Window,
20362        cx: &mut Context<Self>,
20363    ) {
20364        self.do_stage_or_unstage_and_next(false, window, cx);
20365    }
20366
20367    pub fn stage_or_unstage_diff_hunks(
20368        &mut self,
20369        stage: bool,
20370        ranges: Vec<Range<Anchor>>,
20371        cx: &mut Context<Self>,
20372    ) {
20373        if self.delegate_stage_and_restore {
20374            let snapshot = self.buffer.read(cx).snapshot(cx);
20375            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20376            if !hunks.is_empty() {
20377                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20378            }
20379            return;
20380        }
20381        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20382        cx.spawn(async move |this, cx| {
20383            task.await?;
20384            this.update(cx, |this, cx| {
20385                let snapshot = this.buffer.read(cx).snapshot(cx);
20386                let chunk_by = this
20387                    .diff_hunks_in_ranges(&ranges, &snapshot)
20388                    .chunk_by(|hunk| hunk.buffer_id);
20389                for (buffer_id, hunks) in &chunk_by {
20390                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20391                }
20392            })
20393        })
20394        .detach_and_log_err(cx);
20395    }
20396
20397    fn save_buffers_for_ranges_if_needed(
20398        &mut self,
20399        ranges: &[Range<Anchor>],
20400        cx: &mut Context<Editor>,
20401    ) -> Task<Result<()>> {
20402        let multibuffer = self.buffer.read(cx);
20403        let snapshot = multibuffer.read(cx);
20404        let buffer_ids: HashSet<_> = ranges
20405            .iter()
20406            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20407            .collect();
20408        drop(snapshot);
20409
20410        let mut buffers = HashSet::default();
20411        for buffer_id in buffer_ids {
20412            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20413                let buffer = buffer_entity.read(cx);
20414                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20415                {
20416                    buffers.insert(buffer_entity);
20417                }
20418            }
20419        }
20420
20421        if let Some(project) = &self.project {
20422            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20423        } else {
20424            Task::ready(Ok(()))
20425        }
20426    }
20427
20428    fn do_stage_or_unstage_and_next(
20429        &mut self,
20430        stage: bool,
20431        window: &mut Window,
20432        cx: &mut Context<Self>,
20433    ) {
20434        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20435
20436        if ranges.iter().any(|range| range.start != range.end) {
20437            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20438            return;
20439        }
20440
20441        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20442        let snapshot = self.snapshot(window, cx);
20443        let position = self
20444            .selections
20445            .newest::<Point>(&snapshot.display_snapshot)
20446            .head();
20447        let mut row = snapshot
20448            .buffer_snapshot()
20449            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
20450            .find(|hunk| hunk.row_range.start.0 > position.row)
20451            .map(|hunk| hunk.row_range.start);
20452
20453        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20454        // Outside of the project diff editor, wrap around to the beginning.
20455        if !all_diff_hunks_expanded {
20456            row = row.or_else(|| {
20457                snapshot
20458                    .buffer_snapshot()
20459                    .diff_hunks_in_range(Point::zero()..position)
20460                    .find(|hunk| hunk.row_range.end.0 < position.row)
20461                    .map(|hunk| hunk.row_range.start)
20462            });
20463        }
20464
20465        if let Some(row) = row {
20466            let destination = Point::new(row.0, 0);
20467            let autoscroll = Autoscroll::center();
20468
20469            self.unfold_ranges(&[destination..destination], false, false, cx);
20470            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
20471                s.select_ranges([destination..destination]);
20472            });
20473        }
20474    }
20475
20476    pub(crate) fn do_stage_or_unstage(
20477        &self,
20478        stage: bool,
20479        buffer_id: BufferId,
20480        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20481        cx: &mut App,
20482    ) -> Option<()> {
20483        let project = self.project()?;
20484        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20485        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20486        let buffer_snapshot = buffer.read(cx).snapshot();
20487        let file_exists = buffer_snapshot
20488            .file()
20489            .is_some_and(|file| file.disk_state().exists());
20490        diff.update(cx, |diff, cx| {
20491            diff.stage_or_unstage_hunks(
20492                stage,
20493                &hunks
20494                    .map(|hunk| buffer_diff::DiffHunk {
20495                        buffer_range: hunk.buffer_range,
20496                        // We don't need to pass in word diffs here because they're only used for rendering and
20497                        // this function changes internal state
20498                        base_word_diffs: Vec::default(),
20499                        buffer_word_diffs: Vec::default(),
20500                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20501                            ..hunk.diff_base_byte_range.end.0,
20502                        secondary_status: hunk.status.secondary,
20503                        range: Point::zero()..Point::zero(), // unused
20504                    })
20505                    .collect::<Vec<_>>(),
20506                &buffer_snapshot,
20507                file_exists,
20508                cx,
20509            )
20510        });
20511        None
20512    }
20513
20514    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20515        let ranges: Vec<_> = self
20516            .selections
20517            .disjoint_anchors()
20518            .iter()
20519            .map(|s| s.range())
20520            .collect();
20521        self.buffer
20522            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20523    }
20524
20525    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20526        self.buffer.update(cx, |buffer, cx| {
20527            let ranges = vec![Anchor::min()..Anchor::max()];
20528            if !buffer.all_diff_hunks_expanded()
20529                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20530            {
20531                buffer.collapse_diff_hunks(ranges, cx);
20532                true
20533            } else {
20534                false
20535            }
20536        })
20537    }
20538
20539    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20540        if self.buffer.read(cx).all_diff_hunks_expanded() {
20541            return true;
20542        }
20543        let ranges = vec![Anchor::min()..Anchor::max()];
20544        self.buffer
20545            .read(cx)
20546            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20547    }
20548
20549    fn toggle_diff_hunks_in_ranges(
20550        &mut self,
20551        ranges: Vec<Range<Anchor>>,
20552        cx: &mut Context<Editor>,
20553    ) {
20554        self.buffer.update(cx, |buffer, cx| {
20555            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20556            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20557        })
20558    }
20559
20560    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20561        self.buffer.update(cx, |buffer, cx| {
20562            let snapshot = buffer.snapshot(cx);
20563            let excerpt_id = range.end.excerpt_id;
20564            let point_range = range.to_point(&snapshot);
20565            let expand = !buffer.single_hunk_is_expanded(range, cx);
20566            buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
20567        })
20568    }
20569
20570    pub(crate) fn apply_all_diff_hunks(
20571        &mut self,
20572        _: &ApplyAllDiffHunks,
20573        window: &mut Window,
20574        cx: &mut Context<Self>,
20575    ) {
20576        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20577
20578        let buffers = self.buffer.read(cx).all_buffers();
20579        for branch_buffer in buffers {
20580            branch_buffer.update(cx, |branch_buffer, cx| {
20581                branch_buffer.merge_into_base(Vec::new(), cx);
20582            });
20583        }
20584
20585        if let Some(project) = self.project.clone() {
20586            self.save(
20587                SaveOptions {
20588                    format: true,
20589                    autosave: false,
20590                },
20591                project,
20592                window,
20593                cx,
20594            )
20595            .detach_and_log_err(cx);
20596        }
20597    }
20598
20599    pub(crate) fn apply_selected_diff_hunks(
20600        &mut self,
20601        _: &ApplyDiffHunk,
20602        window: &mut Window,
20603        cx: &mut Context<Self>,
20604    ) {
20605        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20606        let snapshot = self.snapshot(window, cx);
20607        let hunks = snapshot.hunks_for_ranges(
20608            self.selections
20609                .all(&snapshot.display_snapshot)
20610                .into_iter()
20611                .map(|selection| selection.range()),
20612        );
20613        let mut ranges_by_buffer = HashMap::default();
20614        self.transact(window, cx, |editor, _window, cx| {
20615            for hunk in hunks {
20616                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20617                    ranges_by_buffer
20618                        .entry(buffer.clone())
20619                        .or_insert_with(Vec::new)
20620                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20621                }
20622            }
20623
20624            for (buffer, ranges) in ranges_by_buffer {
20625                buffer.update(cx, |buffer, cx| {
20626                    buffer.merge_into_base(ranges, cx);
20627                });
20628            }
20629        });
20630
20631        if let Some(project) = self.project.clone() {
20632            self.save(
20633                SaveOptions {
20634                    format: true,
20635                    autosave: false,
20636                },
20637                project,
20638                window,
20639                cx,
20640            )
20641            .detach_and_log_err(cx);
20642        }
20643    }
20644
20645    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20646        if hovered != self.gutter_hovered {
20647            self.gutter_hovered = hovered;
20648            cx.notify();
20649        }
20650    }
20651
20652    pub fn insert_blocks(
20653        &mut self,
20654        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20655        autoscroll: Option<Autoscroll>,
20656        cx: &mut Context<Self>,
20657    ) -> Vec<CustomBlockId> {
20658        let blocks = self
20659            .display_map
20660            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20661        if let Some(autoscroll) = autoscroll {
20662            self.request_autoscroll(autoscroll, cx);
20663        }
20664        cx.notify();
20665        blocks
20666    }
20667
20668    pub fn resize_blocks(
20669        &mut self,
20670        heights: HashMap<CustomBlockId, u32>,
20671        autoscroll: Option<Autoscroll>,
20672        cx: &mut Context<Self>,
20673    ) {
20674        self.display_map
20675            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20676        if let Some(autoscroll) = autoscroll {
20677            self.request_autoscroll(autoscroll, cx);
20678        }
20679        cx.notify();
20680    }
20681
20682    pub fn replace_blocks(
20683        &mut self,
20684        renderers: HashMap<CustomBlockId, RenderBlock>,
20685        autoscroll: Option<Autoscroll>,
20686        cx: &mut Context<Self>,
20687    ) {
20688        self.display_map
20689            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20690        if let Some(autoscroll) = autoscroll {
20691            self.request_autoscroll(autoscroll, cx);
20692        }
20693        cx.notify();
20694    }
20695
20696    pub fn remove_blocks(
20697        &mut self,
20698        block_ids: HashSet<CustomBlockId>,
20699        autoscroll: Option<Autoscroll>,
20700        cx: &mut Context<Self>,
20701    ) {
20702        self.display_map.update(cx, |display_map, cx| {
20703            display_map.remove_blocks(block_ids, cx)
20704        });
20705        if let Some(autoscroll) = autoscroll {
20706            self.request_autoscroll(autoscroll, cx);
20707        }
20708        cx.notify();
20709    }
20710
20711    pub fn row_for_block(
20712        &self,
20713        block_id: CustomBlockId,
20714        cx: &mut Context<Self>,
20715    ) -> Option<DisplayRow> {
20716        self.display_map
20717            .update(cx, |map, cx| map.row_for_block(block_id, cx))
20718    }
20719
20720    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
20721        self.focused_block = Some(focused_block);
20722    }
20723
20724    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
20725        self.focused_block.take()
20726    }
20727
20728    pub fn insert_creases(
20729        &mut self,
20730        creases: impl IntoIterator<Item = Crease<Anchor>>,
20731        cx: &mut Context<Self>,
20732    ) -> Vec<CreaseId> {
20733        self.display_map
20734            .update(cx, |map, cx| map.insert_creases(creases, cx))
20735    }
20736
20737    pub fn remove_creases(
20738        &mut self,
20739        ids: impl IntoIterator<Item = CreaseId>,
20740        cx: &mut Context<Self>,
20741    ) -> Vec<(CreaseId, Range<Anchor>)> {
20742        self.display_map
20743            .update(cx, |map, cx| map.remove_creases(ids, cx))
20744    }
20745
20746    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
20747        self.display_map
20748            .update(cx, |map, cx| map.snapshot(cx))
20749            .longest_row()
20750    }
20751
20752    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
20753        self.display_map
20754            .update(cx, |map, cx| map.snapshot(cx))
20755            .max_point()
20756    }
20757
20758    pub fn text(&self, cx: &App) -> String {
20759        self.buffer.read(cx).read(cx).text()
20760    }
20761
20762    pub fn is_empty(&self, cx: &App) -> bool {
20763        self.buffer.read(cx).read(cx).is_empty()
20764    }
20765
20766    pub fn text_option(&self, cx: &App) -> Option<String> {
20767        let text = self.text(cx);
20768        let text = text.trim();
20769
20770        if text.is_empty() {
20771            return None;
20772        }
20773
20774        Some(text.to_string())
20775    }
20776
20777    pub fn set_text(
20778        &mut self,
20779        text: impl Into<Arc<str>>,
20780        window: &mut Window,
20781        cx: &mut Context<Self>,
20782    ) {
20783        self.transact(window, cx, |this, _, cx| {
20784            this.buffer
20785                .read(cx)
20786                .as_singleton()
20787                .expect("you can only call set_text on editors for singleton buffers")
20788                .update(cx, |buffer, cx| buffer.set_text(text, cx));
20789        });
20790    }
20791
20792    pub fn display_text(&self, cx: &mut App) -> String {
20793        self.display_map
20794            .update(cx, |map, cx| map.snapshot(cx))
20795            .text()
20796    }
20797
20798    fn create_minimap(
20799        &self,
20800        minimap_settings: MinimapSettings,
20801        window: &mut Window,
20802        cx: &mut Context<Self>,
20803    ) -> Option<Entity<Self>> {
20804        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
20805            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
20806    }
20807
20808    fn initialize_new_minimap(
20809        &self,
20810        minimap_settings: MinimapSettings,
20811        window: &mut Window,
20812        cx: &mut Context<Self>,
20813    ) -> Entity<Self> {
20814        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
20815        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
20816
20817        let mut minimap = Editor::new_internal(
20818            EditorMode::Minimap {
20819                parent: cx.weak_entity(),
20820            },
20821            self.buffer.clone(),
20822            None,
20823            Some(self.display_map.clone()),
20824            window,
20825            cx,
20826        );
20827        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20828        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
20829        minimap.scroll_manager.clone_state(
20830            &self.scroll_manager,
20831            &my_snapshot,
20832            &minimap_snapshot,
20833            cx,
20834        );
20835        minimap.set_text_style_refinement(TextStyleRefinement {
20836            font_size: Some(MINIMAP_FONT_SIZE),
20837            font_weight: Some(MINIMAP_FONT_WEIGHT),
20838            font_family: Some(MINIMAP_FONT_FAMILY),
20839            ..Default::default()
20840        });
20841        minimap.update_minimap_configuration(minimap_settings, cx);
20842        cx.new(|_| minimap)
20843    }
20844
20845    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
20846        let current_line_highlight = minimap_settings
20847            .current_line_highlight
20848            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
20849        self.set_current_line_highlight(Some(current_line_highlight));
20850    }
20851
20852    pub fn minimap(&self) -> Option<&Entity<Self>> {
20853        self.minimap
20854            .as_ref()
20855            .filter(|_| self.minimap_visibility.visible())
20856    }
20857
20858    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
20859        let mut wrap_guides = smallvec![];
20860
20861        if self.show_wrap_guides == Some(false) {
20862            return wrap_guides;
20863        }
20864
20865        let settings = self.buffer.read(cx).language_settings(cx);
20866        if settings.show_wrap_guides {
20867            match self.soft_wrap_mode(cx) {
20868                SoftWrap::Column(soft_wrap) => {
20869                    wrap_guides.push((soft_wrap as usize, true));
20870                }
20871                SoftWrap::Bounded(soft_wrap) => {
20872                    wrap_guides.push((soft_wrap as usize, true));
20873                }
20874                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
20875            }
20876            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
20877        }
20878
20879        wrap_guides
20880    }
20881
20882    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
20883        let settings = self.buffer.read(cx).language_settings(cx);
20884        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
20885        match mode {
20886            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
20887                SoftWrap::None
20888            }
20889            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
20890            language_settings::SoftWrap::PreferredLineLength => {
20891                SoftWrap::Column(settings.preferred_line_length)
20892            }
20893            language_settings::SoftWrap::Bounded => {
20894                SoftWrap::Bounded(settings.preferred_line_length)
20895            }
20896        }
20897    }
20898
20899    pub fn set_soft_wrap_mode(
20900        &mut self,
20901        mode: language_settings::SoftWrap,
20902        cx: &mut Context<Self>,
20903    ) {
20904        self.soft_wrap_mode_override = Some(mode);
20905        cx.notify();
20906    }
20907
20908    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
20909        self.hard_wrap = hard_wrap;
20910        cx.notify();
20911    }
20912
20913    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
20914        self.text_style_refinement = Some(style);
20915    }
20916
20917    /// called by the Element so we know what style we were most recently rendered with.
20918    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
20919        // We intentionally do not inform the display map about the minimap style
20920        // so that wrapping is not recalculated and stays consistent for the editor
20921        // and its linked minimap.
20922        if !self.mode.is_minimap() {
20923            let font = style.text.font();
20924            let font_size = style.text.font_size.to_pixels(window.rem_size());
20925            let display_map = self
20926                .placeholder_display_map
20927                .as_ref()
20928                .filter(|_| self.is_empty(cx))
20929                .unwrap_or(&self.display_map);
20930
20931            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
20932        }
20933        self.style = Some(style);
20934    }
20935
20936    pub fn style(&mut self, cx: &App) -> &EditorStyle {
20937        if self.style.is_none() {
20938            self.style = Some(self.create_style(cx));
20939        }
20940        self.style.as_ref().unwrap()
20941    }
20942
20943    // Called by the element. This method is not designed to be called outside of the editor
20944    // element's layout code because it does not notify when rewrapping is computed synchronously.
20945    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
20946        if self.is_empty(cx) {
20947            self.placeholder_display_map
20948                .as_ref()
20949                .map_or(false, |display_map| {
20950                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
20951                })
20952        } else {
20953            self.display_map
20954                .update(cx, |map, cx| map.set_wrap_width(width, cx))
20955        }
20956    }
20957
20958    pub fn set_soft_wrap(&mut self) {
20959        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
20960    }
20961
20962    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
20963        if self.soft_wrap_mode_override.is_some() {
20964            self.soft_wrap_mode_override.take();
20965        } else {
20966            let soft_wrap = match self.soft_wrap_mode(cx) {
20967                SoftWrap::GitDiff => return,
20968                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
20969                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
20970                    language_settings::SoftWrap::None
20971                }
20972            };
20973            self.soft_wrap_mode_override = Some(soft_wrap);
20974        }
20975        cx.notify();
20976    }
20977
20978    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
20979        let Some(workspace) = self.workspace() else {
20980            return;
20981        };
20982        let fs = workspace.read(cx).app_state().fs.clone();
20983        let current_show = TabBarSettings::get_global(cx).show;
20984        update_settings_file(fs, cx, move |setting, _| {
20985            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
20986        });
20987    }
20988
20989    pub fn toggle_indent_guides(
20990        &mut self,
20991        _: &ToggleIndentGuides,
20992        _: &mut Window,
20993        cx: &mut Context<Self>,
20994    ) {
20995        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
20996            self.buffer
20997                .read(cx)
20998                .language_settings(cx)
20999                .indent_guides
21000                .enabled
21001        });
21002        self.show_indent_guides = Some(!currently_enabled);
21003        cx.notify();
21004    }
21005
21006    fn should_show_indent_guides(&self) -> Option<bool> {
21007        self.show_indent_guides
21008    }
21009
21010    pub fn disable_indent_guides_for_buffer(
21011        &mut self,
21012        buffer_id: BufferId,
21013        cx: &mut Context<Self>,
21014    ) {
21015        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21016        cx.notify();
21017    }
21018
21019    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21020        self.buffers_with_disabled_indent_guides
21021            .contains(&buffer_id)
21022    }
21023
21024    pub fn toggle_line_numbers(
21025        &mut self,
21026        _: &ToggleLineNumbers,
21027        _: &mut Window,
21028        cx: &mut Context<Self>,
21029    ) {
21030        let mut editor_settings = EditorSettings::get_global(cx).clone();
21031        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21032        EditorSettings::override_global(editor_settings, cx);
21033    }
21034
21035    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21036        if let Some(show_line_numbers) = self.show_line_numbers {
21037            return show_line_numbers;
21038        }
21039        EditorSettings::get_global(cx).gutter.line_numbers
21040    }
21041
21042    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21043        match (
21044            self.use_relative_line_numbers,
21045            EditorSettings::get_global(cx).relative_line_numbers,
21046        ) {
21047            (None, setting) => setting,
21048            (Some(false), _) => RelativeLineNumbers::Disabled,
21049            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21050            (Some(true), _) => RelativeLineNumbers::Enabled,
21051        }
21052    }
21053
21054    pub fn toggle_relative_line_numbers(
21055        &mut self,
21056        _: &ToggleRelativeLineNumbers,
21057        _: &mut Window,
21058        cx: &mut Context<Self>,
21059    ) {
21060        let is_relative = self.relative_line_numbers(cx);
21061        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21062    }
21063
21064    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21065        self.use_relative_line_numbers = is_relative;
21066        cx.notify();
21067    }
21068
21069    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21070        self.show_gutter = show_gutter;
21071        cx.notify();
21072    }
21073
21074    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21075        self.show_scrollbars = ScrollbarAxes {
21076            horizontal: show,
21077            vertical: show,
21078        };
21079        cx.notify();
21080    }
21081
21082    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21083        self.show_scrollbars.vertical = show;
21084        cx.notify();
21085    }
21086
21087    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21088        self.show_scrollbars.horizontal = show;
21089        cx.notify();
21090    }
21091
21092    pub fn set_minimap_visibility(
21093        &mut self,
21094        minimap_visibility: MinimapVisibility,
21095        window: &mut Window,
21096        cx: &mut Context<Self>,
21097    ) {
21098        if self.minimap_visibility != minimap_visibility {
21099            if minimap_visibility.visible() && self.minimap.is_none() {
21100                let minimap_settings = EditorSettings::get_global(cx).minimap;
21101                self.minimap =
21102                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21103            }
21104            self.minimap_visibility = minimap_visibility;
21105            cx.notify();
21106        }
21107    }
21108
21109    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21110        self.set_show_scrollbars(false, cx);
21111        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21112    }
21113
21114    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21115        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21116    }
21117
21118    /// Normally the text in full mode and auto height editors is padded on the
21119    /// left side by roughly half a character width for improved hit testing.
21120    ///
21121    /// Use this method to disable this for cases where this is not wanted (e.g.
21122    /// if you want to align the editor text with some other text above or below)
21123    /// or if you want to add this padding to single-line editors.
21124    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21125        self.offset_content = offset_content;
21126        cx.notify();
21127    }
21128
21129    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21130        self.show_line_numbers = Some(show_line_numbers);
21131        cx.notify();
21132    }
21133
21134    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21135        self.disable_expand_excerpt_buttons = true;
21136        cx.notify();
21137    }
21138
21139    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21140        self.number_deleted_lines = number;
21141        cx.notify();
21142    }
21143
21144    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21145        self.delegate_expand_excerpts = delegate;
21146    }
21147
21148    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21149        self.delegate_stage_and_restore = delegate;
21150    }
21151
21152    pub fn set_on_local_selections_changed(
21153        &mut self,
21154        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21155    ) {
21156        self.on_local_selections_changed = callback;
21157    }
21158
21159    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21160        self.suppress_selection_callback = suppress;
21161    }
21162
21163    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21164        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21165        cx.notify();
21166    }
21167
21168    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21169        self.show_code_actions = Some(show_code_actions);
21170        cx.notify();
21171    }
21172
21173    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21174        self.show_runnables = Some(show_runnables);
21175        cx.notify();
21176    }
21177
21178    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21179        self.show_breakpoints = Some(show_breakpoints);
21180        cx.notify();
21181    }
21182
21183    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21184        self.show_diff_review_button = show;
21185        cx.notify();
21186    }
21187
21188    pub fn show_diff_review_button(&self) -> bool {
21189        self.show_diff_review_button
21190    }
21191
21192    pub fn render_diff_review_button(
21193        &self,
21194        display_row: DisplayRow,
21195        width: Pixels,
21196        cx: &mut Context<Self>,
21197    ) -> impl IntoElement {
21198        let text_color = cx.theme().colors().text;
21199        let icon_color = cx.theme().colors().icon_accent;
21200
21201        h_flex()
21202            .id("diff_review_button")
21203            .cursor_pointer()
21204            .w(width - px(1.))
21205            .h(relative(0.9))
21206            .justify_center()
21207            .rounded_sm()
21208            .border_1()
21209            .border_color(text_color.opacity(0.1))
21210            .bg(text_color.opacity(0.15))
21211            .hover(|s| {
21212                s.bg(icon_color.opacity(0.4))
21213                    .border_color(icon_color.opacity(0.5))
21214            })
21215            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21216            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21217            .on_mouse_down(
21218                gpui::MouseButton::Left,
21219                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21220                    editor.start_diff_review_drag(display_row, window, cx);
21221                }),
21222            )
21223    }
21224
21225    pub fn start_diff_review_drag(
21226        &mut self,
21227        display_row: DisplayRow,
21228        window: &mut Window,
21229        cx: &mut Context<Self>,
21230    ) {
21231        let snapshot = self.snapshot(window, cx);
21232        let point = snapshot
21233            .display_snapshot
21234            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21235        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21236        self.diff_review_drag_state = Some(DiffReviewDragState {
21237            start_anchor: anchor,
21238            current_anchor: anchor,
21239        });
21240        cx.notify();
21241    }
21242
21243    pub fn update_diff_review_drag(
21244        &mut self,
21245        display_row: DisplayRow,
21246        window: &mut Window,
21247        cx: &mut Context<Self>,
21248    ) {
21249        if self.diff_review_drag_state.is_none() {
21250            return;
21251        }
21252        let snapshot = self.snapshot(window, cx);
21253        let point = snapshot
21254            .display_snapshot
21255            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21256        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21257        if let Some(drag_state) = &mut self.diff_review_drag_state {
21258            drag_state.current_anchor = anchor;
21259            cx.notify();
21260        }
21261    }
21262
21263    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21264        if let Some(drag_state) = self.diff_review_drag_state.take() {
21265            let snapshot = self.snapshot(window, cx);
21266            let range = drag_state.row_range(&snapshot.display_snapshot);
21267            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21268        }
21269        cx.notify();
21270    }
21271
21272    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21273        self.diff_review_drag_state = None;
21274        cx.notify();
21275    }
21276
21277    /// Calculates the appropriate block height for the diff review overlay.
21278    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21279    /// and 2 lines per comment when expanded.
21280    fn calculate_overlay_height(
21281        &self,
21282        hunk_key: &DiffHunkKey,
21283        comments_expanded: bool,
21284        snapshot: &MultiBufferSnapshot,
21285    ) -> u32 {
21286        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21287        let base_height: u32 = 2; // Input row with avatar and buttons
21288
21289        if comment_count == 0 {
21290            base_height
21291        } else if comments_expanded {
21292            // Header (1 line) + 2 lines per comment
21293            base_height + 1 + (comment_count as u32 * 2)
21294        } else {
21295            // Just header when collapsed
21296            base_height + 1
21297        }
21298    }
21299
21300    pub fn show_diff_review_overlay(
21301        &mut self,
21302        display_range: Range<DisplayRow>,
21303        window: &mut Window,
21304        cx: &mut Context<Self>,
21305    ) {
21306        let Range { start, end } = display_range.sorted();
21307
21308        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21309        let editor_snapshot = self.snapshot(window, cx);
21310
21311        // Convert display rows to multibuffer points
21312        let start_point = editor_snapshot
21313            .display_snapshot
21314            .display_point_to_point(start.as_display_point(), Bias::Left);
21315        let end_point = editor_snapshot
21316            .display_snapshot
21317            .display_point_to_point(end.as_display_point(), Bias::Left);
21318        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21319
21320        // Create anchor range for the selected lines (start of first line to end of last line)
21321        let line_end = Point::new(
21322            end_point.row,
21323            buffer_snapshot.line_len(end_multi_buffer_row),
21324        );
21325        let anchor_range =
21326            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21327
21328        // Compute the hunk key for this display row
21329        let file_path = buffer_snapshot
21330            .file_at(start_point)
21331            .map(|file: &Arc<dyn language::File>| file.path().clone())
21332            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21333        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21334        let new_hunk_key = DiffHunkKey {
21335            file_path,
21336            hunk_start_anchor,
21337        };
21338
21339        // Check if we already have an overlay for this hunk
21340        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21341            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21342        }) {
21343            // Just focus the existing overlay's prompt editor
21344            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21345            window.focus(&focus_handle, cx);
21346            return;
21347        }
21348
21349        // Dismiss overlays that have no comments for their hunks
21350        self.dismiss_overlays_without_comments(cx);
21351
21352        // Get the current user's avatar URI from the project's user_store
21353        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21354            let user_store = project.read(cx).user_store();
21355            user_store
21356                .read(cx)
21357                .current_user()
21358                .map(|user| user.avatar_uri.clone())
21359        });
21360
21361        // Create anchor at the end of the last row so the block appears immediately below it
21362        // Use multibuffer coordinates for anchor creation
21363        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21364        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21365
21366        // Use the hunk key we already computed
21367        let hunk_key = new_hunk_key;
21368
21369        // Create the prompt editor for the review input
21370        let prompt_editor = cx.new(|cx| {
21371            let mut editor = Editor::single_line(window, cx);
21372            editor.set_placeholder_text("Add a review comment...", window, cx);
21373            editor
21374        });
21375
21376        // Register the Newline action on the prompt editor to submit the review
21377        let parent_editor = cx.entity().downgrade();
21378        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21379            prompt_editor.register_action({
21380                let parent_editor = parent_editor.clone();
21381                move |_: &crate::actions::Newline, window, cx| {
21382                    if let Some(editor) = parent_editor.upgrade() {
21383                        editor.update(cx, |editor, cx| {
21384                            editor.submit_diff_review_comment(window, cx);
21385                        });
21386                    }
21387                }
21388            })
21389        });
21390
21391        // Calculate initial height based on existing comments for this hunk
21392        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21393
21394        // Create the overlay block
21395        let prompt_editor_for_render = prompt_editor.clone();
21396        let hunk_key_for_render = hunk_key.clone();
21397        let editor_handle = cx.entity().downgrade();
21398        let block = BlockProperties {
21399            style: BlockStyle::Sticky,
21400            placement: BlockPlacement::Below(anchor),
21401            height: Some(initial_height),
21402            render: Arc::new(move |cx| {
21403                Self::render_diff_review_overlay(
21404                    &prompt_editor_for_render,
21405                    &hunk_key_for_render,
21406                    &editor_handle,
21407                    cx,
21408                )
21409            }),
21410            priority: 0,
21411        };
21412
21413        let block_ids = self.insert_blocks([block], None, cx);
21414        let Some(block_id) = block_ids.into_iter().next() else {
21415            log::error!("Failed to insert diff review overlay block");
21416            return;
21417        };
21418
21419        self.diff_review_overlays.push(DiffReviewOverlay {
21420            anchor_range,
21421            block_id,
21422            prompt_editor: prompt_editor.clone(),
21423            hunk_key,
21424            comments_expanded: true,
21425            inline_edit_editors: HashMap::default(),
21426            inline_edit_subscriptions: HashMap::default(),
21427            user_avatar_uri,
21428            _subscription: subscription,
21429        });
21430
21431        // Focus the prompt editor
21432        let focus_handle = prompt_editor.focus_handle(cx);
21433        window.focus(&focus_handle, cx);
21434
21435        cx.notify();
21436    }
21437
21438    /// Dismisses all diff review overlays.
21439    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21440        if self.diff_review_overlays.is_empty() {
21441            return;
21442        }
21443        let block_ids: HashSet<_> = self
21444            .diff_review_overlays
21445            .drain(..)
21446            .map(|overlay| overlay.block_id)
21447            .collect();
21448        self.remove_blocks(block_ids, None, cx);
21449        cx.notify();
21450    }
21451
21452    /// Dismisses overlays that have no comments stored for their hunks.
21453    /// Keeps overlays that have at least one comment.
21454    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21455        let snapshot = self.buffer.read(cx).snapshot(cx);
21456
21457        // First, compute which overlays have comments (to avoid borrow issues with retain)
21458        let overlays_with_comments: Vec<bool> = self
21459            .diff_review_overlays
21460            .iter()
21461            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21462            .collect();
21463
21464        // Now collect block IDs to remove and retain overlays
21465        let mut block_ids_to_remove = HashSet::default();
21466        let mut index = 0;
21467        self.diff_review_overlays.retain(|overlay| {
21468            let has_comments = overlays_with_comments[index];
21469            index += 1;
21470            if !has_comments {
21471                block_ids_to_remove.insert(overlay.block_id);
21472            }
21473            has_comments
21474        });
21475
21476        if !block_ids_to_remove.is_empty() {
21477            self.remove_blocks(block_ids_to_remove, None, cx);
21478            cx.notify();
21479        }
21480    }
21481
21482    /// Refreshes the diff review overlay block to update its height and render function.
21483    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21484    fn refresh_diff_review_overlay_height(
21485        &mut self,
21486        hunk_key: &DiffHunkKey,
21487        _window: &mut Window,
21488        cx: &mut Context<Self>,
21489    ) {
21490        // Extract all needed data from overlay first to avoid borrow conflicts
21491        let snapshot = self.buffer.read(cx).snapshot(cx);
21492        let (comments_expanded, block_id, prompt_editor) = {
21493            let Some(overlay) = self
21494                .diff_review_overlays
21495                .iter()
21496                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21497            else {
21498                return;
21499            };
21500
21501            (
21502                overlay.comments_expanded,
21503                overlay.block_id,
21504                overlay.prompt_editor.clone(),
21505            )
21506        };
21507
21508        // Calculate new height
21509        let snapshot = self.buffer.read(cx).snapshot(cx);
21510        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21511
21512        // Update the block height using resize_blocks (avoids flicker)
21513        let mut heights = HashMap::default();
21514        heights.insert(block_id, new_height);
21515        self.resize_blocks(heights, None, cx);
21516
21517        // Update the render function using replace_blocks (avoids flicker)
21518        let hunk_key_for_render = hunk_key.clone();
21519        let editor_handle = cx.entity().downgrade();
21520        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21521            Arc::new(move |cx| {
21522                Self::render_diff_review_overlay(
21523                    &prompt_editor,
21524                    &hunk_key_for_render,
21525                    &editor_handle,
21526                    cx,
21527                )
21528            });
21529
21530        let mut renderers = HashMap::default();
21531        renderers.insert(block_id, render);
21532        self.replace_blocks(renderers, None, cx);
21533    }
21534
21535    /// Action handler for SubmitDiffReviewComment.
21536    pub fn submit_diff_review_comment_action(
21537        &mut self,
21538        _: &SubmitDiffReviewComment,
21539        window: &mut Window,
21540        cx: &mut Context<Self>,
21541    ) {
21542        self.submit_diff_review_comment(window, cx);
21543    }
21544
21545    /// Stores the diff review comment locally.
21546    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21547    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21548        // Find the overlay that currently has focus
21549        let overlay_index = self
21550            .diff_review_overlays
21551            .iter()
21552            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21553        let Some(overlay_index) = overlay_index else {
21554            return;
21555        };
21556        let overlay = &self.diff_review_overlays[overlay_index];
21557
21558        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21559        if comment_text.is_empty() {
21560            return;
21561        }
21562
21563        let anchor_range = overlay.anchor_range.clone();
21564        let hunk_key = overlay.hunk_key.clone();
21565
21566        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21567
21568        // Clear the prompt editor but keep the overlay open
21569        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21570            overlay.prompt_editor.update(cx, |editor, cx| {
21571                editor.clear(window, cx);
21572            });
21573        }
21574
21575        // Refresh the overlay to update the block height for the new comment
21576        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21577
21578        cx.notify();
21579    }
21580
21581    /// Returns the prompt editor for the diff review overlay, if one is active.
21582    /// This is primarily used for testing.
21583    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21584        self.diff_review_overlays
21585            .first()
21586            .map(|overlay| &overlay.prompt_editor)
21587    }
21588
21589    /// Returns the line range for the first diff review overlay, if one is active.
21590    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21591    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21592        let overlay = self.diff_review_overlays.first()?;
21593        let snapshot = self.buffer.read(cx).snapshot(cx);
21594        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21595        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21596        let start_row = snapshot
21597            .point_to_buffer_point(start_point)
21598            .map(|(_, p, _)| p.row)
21599            .unwrap_or(start_point.row);
21600        let end_row = snapshot
21601            .point_to_buffer_point(end_point)
21602            .map(|(_, p, _)| p.row)
21603            .unwrap_or(end_point.row);
21604        Some((start_row, end_row))
21605    }
21606
21607    /// Sets whether the comments section is expanded in the diff review overlay.
21608    /// This is primarily used for testing.
21609    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21610        for overlay in &mut self.diff_review_overlays {
21611            overlay.comments_expanded = expanded;
21612        }
21613        cx.notify();
21614    }
21615
21616    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21617    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21618        a.file_path == b.file_path
21619            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21620    }
21621
21622    /// Returns comments for a specific hunk, ordered by creation time.
21623    pub fn comments_for_hunk<'a>(
21624        &'a self,
21625        key: &DiffHunkKey,
21626        snapshot: &MultiBufferSnapshot,
21627    ) -> &'a [StoredReviewComment] {
21628        let key_point = key.hunk_start_anchor.to_point(snapshot);
21629        self.stored_review_comments
21630            .iter()
21631            .find(|(k, _)| {
21632                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21633            })
21634            .map(|(_, comments)| comments.as_slice())
21635            .unwrap_or(&[])
21636    }
21637
21638    /// Returns the total count of stored review comments across all hunks.
21639    pub fn total_review_comment_count(&self) -> usize {
21640        self.stored_review_comments
21641            .iter()
21642            .map(|(_, v)| v.len())
21643            .sum()
21644    }
21645
21646    /// Returns the count of comments for a specific hunk.
21647    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21648        let key_point = key.hunk_start_anchor.to_point(snapshot);
21649        self.stored_review_comments
21650            .iter()
21651            .find(|(k, _)| {
21652                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21653            })
21654            .map(|(_, v)| v.len())
21655            .unwrap_or(0)
21656    }
21657
21658    /// Adds a new review comment to a specific hunk.
21659    pub fn add_review_comment(
21660        &mut self,
21661        hunk_key: DiffHunkKey,
21662        comment: String,
21663        anchor_range: Range<Anchor>,
21664        cx: &mut Context<Self>,
21665    ) -> usize {
21666        let id = self.next_review_comment_id;
21667        self.next_review_comment_id += 1;
21668
21669        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21670
21671        let snapshot = self.buffer.read(cx).snapshot(cx);
21672        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21673
21674        // Find existing entry for this hunk or add a new one
21675        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21676            k.file_path == hunk_key.file_path
21677                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21678        }) {
21679            comments.push(stored_comment);
21680        } else {
21681            self.stored_review_comments
21682                .push((hunk_key, vec![stored_comment]));
21683        }
21684
21685        cx.emit(EditorEvent::ReviewCommentsChanged {
21686            total_count: self.total_review_comment_count(),
21687        });
21688        cx.notify();
21689        id
21690    }
21691
21692    /// Removes a review comment by ID from any hunk.
21693    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21694        for (_, comments) in self.stored_review_comments.iter_mut() {
21695            if let Some(index) = comments.iter().position(|c| c.id == id) {
21696                comments.remove(index);
21697                cx.emit(EditorEvent::ReviewCommentsChanged {
21698                    total_count: self.total_review_comment_count(),
21699                });
21700                cx.notify();
21701                return true;
21702            }
21703        }
21704        false
21705    }
21706
21707    /// Updates a review comment's text by ID.
21708    pub fn update_review_comment(
21709        &mut self,
21710        id: usize,
21711        new_comment: String,
21712        cx: &mut Context<Self>,
21713    ) -> bool {
21714        for (_, comments) in self.stored_review_comments.iter_mut() {
21715            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21716                comment.comment = new_comment;
21717                comment.is_editing = false;
21718                cx.emit(EditorEvent::ReviewCommentsChanged {
21719                    total_count: self.total_review_comment_count(),
21720                });
21721                cx.notify();
21722                return true;
21723            }
21724        }
21725        false
21726    }
21727
21728    /// Sets a comment's editing state.
21729    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
21730        for (_, comments) in self.stored_review_comments.iter_mut() {
21731            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21732                comment.is_editing = is_editing;
21733                cx.notify();
21734                return;
21735            }
21736        }
21737    }
21738
21739    /// Takes all stored comments from all hunks, clearing the storage.
21740    /// Returns a Vec of (hunk_key, comments) pairs.
21741    pub fn take_all_review_comments(
21742        &mut self,
21743        cx: &mut Context<Self>,
21744    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
21745        // Dismiss all overlays when taking comments (e.g., when sending to agent)
21746        self.dismiss_all_diff_review_overlays(cx);
21747        let comments = std::mem::take(&mut self.stored_review_comments);
21748        // Reset the ID counter since all comments have been taken
21749        self.next_review_comment_id = 0;
21750        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
21751        cx.notify();
21752        comments
21753    }
21754
21755    /// Removes review comments whose anchors are no longer valid or whose
21756    /// associated diff hunks no longer exist.
21757    ///
21758    /// This should be called when the buffer changes to prevent orphaned comments
21759    /// from accumulating.
21760    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
21761        let snapshot = self.buffer.read(cx).snapshot(cx);
21762        let original_count = self.total_review_comment_count();
21763
21764        // Remove comments with invalid hunk anchors
21765        self.stored_review_comments
21766            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
21767
21768        // Also clean up individual comments with invalid anchor ranges
21769        for (_, comments) in &mut self.stored_review_comments {
21770            comments.retain(|comment| {
21771                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
21772            });
21773        }
21774
21775        // Remove empty hunk entries
21776        self.stored_review_comments
21777            .retain(|(_, comments)| !comments.is_empty());
21778
21779        let new_count = self.total_review_comment_count();
21780        if new_count != original_count {
21781            cx.emit(EditorEvent::ReviewCommentsChanged {
21782                total_count: new_count,
21783            });
21784            cx.notify();
21785        }
21786    }
21787
21788    /// Toggles the expanded state of the comments section in the overlay.
21789    pub fn toggle_review_comments_expanded(
21790        &mut self,
21791        _: &ToggleReviewCommentsExpanded,
21792        window: &mut Window,
21793        cx: &mut Context<Self>,
21794    ) {
21795        // Find the overlay that currently has focus, or use the first one
21796        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
21797            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
21798                overlay.comments_expanded = !overlay.comments_expanded;
21799                Some(overlay.hunk_key.clone())
21800            } else {
21801                None
21802            }
21803        });
21804
21805        // If no focused overlay found, toggle the first one
21806        let hunk_key = overlay_info.or_else(|| {
21807            self.diff_review_overlays.first_mut().map(|overlay| {
21808                overlay.comments_expanded = !overlay.comments_expanded;
21809                overlay.hunk_key.clone()
21810            })
21811        });
21812
21813        if let Some(hunk_key) = hunk_key {
21814            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21815            cx.notify();
21816        }
21817    }
21818
21819    /// Handles the EditReviewComment action - sets a comment into editing mode.
21820    pub fn edit_review_comment(
21821        &mut self,
21822        action: &EditReviewComment,
21823        window: &mut Window,
21824        cx: &mut Context<Self>,
21825    ) {
21826        let comment_id = action.id;
21827
21828        // Set the comment to editing mode
21829        self.set_comment_editing(comment_id, true, cx);
21830
21831        // Find the overlay that contains this comment and create an inline editor if needed
21832        // First, find which hunk this comment belongs to
21833        let hunk_key = self
21834            .stored_review_comments
21835            .iter()
21836            .find_map(|(key, comments)| {
21837                if comments.iter().any(|c| c.id == comment_id) {
21838                    Some(key.clone())
21839                } else {
21840                    None
21841                }
21842            });
21843
21844        let snapshot = self.buffer.read(cx).snapshot(cx);
21845        if let Some(hunk_key) = hunk_key {
21846            if let Some(overlay) = self
21847                .diff_review_overlays
21848                .iter_mut()
21849                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21850            {
21851                if let std::collections::hash_map::Entry::Vacant(entry) =
21852                    overlay.inline_edit_editors.entry(comment_id)
21853                {
21854                    // Find the comment text
21855                    let comment_text = self
21856                        .stored_review_comments
21857                        .iter()
21858                        .flat_map(|(_, comments)| comments)
21859                        .find(|c| c.id == comment_id)
21860                        .map(|c| c.comment.clone())
21861                        .unwrap_or_default();
21862
21863                    // Create inline editor
21864                    let parent_editor = cx.entity().downgrade();
21865                    let inline_editor = cx.new(|cx| {
21866                        let mut editor = Editor::single_line(window, cx);
21867                        editor.set_text(&*comment_text, window, cx);
21868                        // Select all text for easy replacement
21869                        editor.select_all(&crate::actions::SelectAll, window, cx);
21870                        editor
21871                    });
21872
21873                    // Register the Newline action to confirm the edit
21874                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
21875                        inline_editor.register_action({
21876                            let parent_editor = parent_editor.clone();
21877                            move |_: &crate::actions::Newline, window, cx| {
21878                                if let Some(editor) = parent_editor.upgrade() {
21879                                    editor.update(cx, |editor, cx| {
21880                                        editor.confirm_edit_review_comment(comment_id, window, cx);
21881                                    });
21882                                }
21883                            }
21884                        })
21885                    });
21886
21887                    // Store the subscription to keep the action handler alive
21888                    overlay
21889                        .inline_edit_subscriptions
21890                        .insert(comment_id, subscription);
21891
21892                    // Focus the inline editor
21893                    let focus_handle = inline_editor.focus_handle(cx);
21894                    window.focus(&focus_handle, cx);
21895
21896                    entry.insert(inline_editor);
21897                }
21898            }
21899        }
21900
21901        cx.notify();
21902    }
21903
21904    /// Confirms an inline edit of a review comment.
21905    pub fn confirm_edit_review_comment(
21906        &mut self,
21907        comment_id: usize,
21908        _window: &mut Window,
21909        cx: &mut Context<Self>,
21910    ) {
21911        // Get the new text from the inline editor
21912        // Find the overlay containing this comment's inline editor
21913        let snapshot = self.buffer.read(cx).snapshot(cx);
21914        let hunk_key = self
21915            .stored_review_comments
21916            .iter()
21917            .find_map(|(key, comments)| {
21918                if comments.iter().any(|c| c.id == comment_id) {
21919                    Some(key.clone())
21920                } else {
21921                    None
21922                }
21923            });
21924
21925        let new_text = hunk_key
21926            .as_ref()
21927            .and_then(|hunk_key| {
21928                self.diff_review_overlays
21929                    .iter()
21930                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21931            })
21932            .as_ref()
21933            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
21934            .map(|editor| editor.read(cx).text(cx).trim().to_string());
21935
21936        if let Some(new_text) = new_text {
21937            if !new_text.is_empty() {
21938                self.update_review_comment(comment_id, new_text, cx);
21939            }
21940        }
21941
21942        // Remove the inline editor and its subscription
21943        if let Some(hunk_key) = hunk_key {
21944            if let Some(overlay) = self
21945                .diff_review_overlays
21946                .iter_mut()
21947                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21948            {
21949                overlay.inline_edit_editors.remove(&comment_id);
21950                overlay.inline_edit_subscriptions.remove(&comment_id);
21951            }
21952        }
21953
21954        // Clear editing state
21955        self.set_comment_editing(comment_id, false, cx);
21956    }
21957
21958    /// Cancels an inline edit of a review comment.
21959    pub fn cancel_edit_review_comment(
21960        &mut self,
21961        comment_id: usize,
21962        _window: &mut Window,
21963        cx: &mut Context<Self>,
21964    ) {
21965        // Find which hunk this comment belongs to
21966        let hunk_key = self
21967            .stored_review_comments
21968            .iter()
21969            .find_map(|(key, comments)| {
21970                if comments.iter().any(|c| c.id == comment_id) {
21971                    Some(key.clone())
21972                } else {
21973                    None
21974                }
21975            });
21976
21977        // Remove the inline editor and its subscription
21978        if let Some(hunk_key) = hunk_key {
21979            let snapshot = self.buffer.read(cx).snapshot(cx);
21980            if let Some(overlay) = self
21981                .diff_review_overlays
21982                .iter_mut()
21983                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21984            {
21985                overlay.inline_edit_editors.remove(&comment_id);
21986                overlay.inline_edit_subscriptions.remove(&comment_id);
21987            }
21988        }
21989
21990        // Clear editing state
21991        self.set_comment_editing(comment_id, false, cx);
21992    }
21993
21994    /// Action handler for ConfirmEditReviewComment.
21995    pub fn confirm_edit_review_comment_action(
21996        &mut self,
21997        action: &ConfirmEditReviewComment,
21998        window: &mut Window,
21999        cx: &mut Context<Self>,
22000    ) {
22001        self.confirm_edit_review_comment(action.id, window, cx);
22002    }
22003
22004    /// Action handler for CancelEditReviewComment.
22005    pub fn cancel_edit_review_comment_action(
22006        &mut self,
22007        action: &CancelEditReviewComment,
22008        window: &mut Window,
22009        cx: &mut Context<Self>,
22010    ) {
22011        self.cancel_edit_review_comment(action.id, window, cx);
22012    }
22013
22014    /// Handles the DeleteReviewComment action - removes a comment.
22015    pub fn delete_review_comment(
22016        &mut self,
22017        action: &DeleteReviewComment,
22018        window: &mut Window,
22019        cx: &mut Context<Self>,
22020    ) {
22021        // Get the hunk key before removing the comment
22022        // Find the hunk key from the comment itself
22023        let comment_id = action.id;
22024        let hunk_key = self
22025            .stored_review_comments
22026            .iter()
22027            .find_map(|(key, comments)| {
22028                if comments.iter().any(|c| c.id == comment_id) {
22029                    Some(key.clone())
22030                } else {
22031                    None
22032                }
22033            });
22034
22035        // Also get it from the overlay for refresh purposes
22036        let overlay_hunk_key = self
22037            .diff_review_overlays
22038            .first()
22039            .map(|o| o.hunk_key.clone());
22040
22041        self.remove_review_comment(action.id, cx);
22042
22043        // Refresh the overlay height after removing a comment
22044        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22045            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22046        }
22047    }
22048
22049    fn render_diff_review_overlay(
22050        prompt_editor: &Entity<Editor>,
22051        hunk_key: &DiffHunkKey,
22052        editor_handle: &WeakEntity<Editor>,
22053        cx: &mut BlockContext,
22054    ) -> AnyElement {
22055        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22056            if ranges.is_empty() {
22057                return None;
22058            }
22059            let formatted: Vec<String> = ranges
22060                .iter()
22061                .map(|(start, end)| {
22062                    let start_line = start + 1;
22063                    let end_line = end + 1;
22064                    if start_line == end_line {
22065                        format!("Line {start_line}")
22066                    } else {
22067                        format!("Lines {start_line}-{end_line}")
22068                    }
22069                })
22070                .collect();
22071            // Don't show label for single line in single excerpt
22072            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22073                return None;
22074            }
22075            Some(formatted.join(""))
22076        }
22077
22078        let theme = cx.theme();
22079        let colors = theme.colors();
22080
22081        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22082            editor_handle
22083                .upgrade()
22084                .map(|editor| {
22085                    let editor = editor.read(cx);
22086                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22087                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22088                    let (expanded, editors, avatar_uri, line_ranges) = editor
22089                        .diff_review_overlays
22090                        .iter()
22091                        .find(|overlay| {
22092                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22093                        })
22094                        .map(|o| {
22095                            let start_point = o.anchor_range.start.to_point(&snapshot);
22096                            let end_point = o.anchor_range.end.to_point(&snapshot);
22097                            // Get line ranges per excerpt to detect discontinuities
22098                            let buffer_ranges =
22099                                snapshot.range_to_buffer_ranges(start_point..end_point);
22100                            let ranges: Vec<(u32, u32)> = buffer_ranges
22101                                .iter()
22102                                .map(|(buffer, range, _)| {
22103                                    let start = buffer.offset_to_point(range.start.0).row;
22104                                    let end = buffer.offset_to_point(range.end.0).row;
22105                                    (start, end)
22106                                })
22107                                .collect();
22108                            (
22109                                o.comments_expanded,
22110                                o.inline_edit_editors.clone(),
22111                                o.user_avatar_uri.clone(),
22112                                if ranges.is_empty() {
22113                                    None
22114                                } else {
22115                                    Some(ranges)
22116                                },
22117                            )
22118                        })
22119                        .unwrap_or((true, HashMap::default(), None, None));
22120                    (comments, expanded, editors, avatar_uri, line_ranges)
22121                })
22122                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22123
22124        let comment_count = comments.len();
22125        let avatar_size = px(20.);
22126        let action_icon_size = IconSize::XSmall;
22127
22128        v_flex()
22129            .w_full()
22130            .bg(colors.editor_background)
22131            .border_b_1()
22132            .border_color(colors.border)
22133            .px_2()
22134            .pb_2()
22135            .gap_2()
22136            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22137            .when_some(line_ranges, |el, ranges| {
22138                let label = format_line_ranges(&ranges);
22139                if let Some(label) = label {
22140                    el.child(
22141                        h_flex()
22142                            .w_full()
22143                            .px_2()
22144                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22145                    )
22146                } else {
22147                    el
22148                }
22149            })
22150            // Top row: editable input with user's avatar
22151            .child(
22152                h_flex()
22153                    .w_full()
22154                    .items_center()
22155                    .gap_2()
22156                    .px_2()
22157                    .py_1p5()
22158                    .rounded_md()
22159                    .bg(colors.surface_background)
22160                    .child(
22161                        div()
22162                            .size(avatar_size)
22163                            .flex_shrink_0()
22164                            .rounded_full()
22165                            .overflow_hidden()
22166                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22167                                Avatar::new(avatar_uri.clone())
22168                                    .size(avatar_size)
22169                                    .into_any_element()
22170                            } else {
22171                                Icon::new(IconName::Person)
22172                                    .size(IconSize::Small)
22173                                    .color(ui::Color::Muted)
22174                                    .into_any_element()
22175                            }),
22176                    )
22177                    .child(
22178                        div()
22179                            .flex_1()
22180                            .border_1()
22181                            .border_color(colors.border)
22182                            .rounded_md()
22183                            .bg(colors.editor_background)
22184                            .px_2()
22185                            .py_1()
22186                            .child(prompt_editor.clone()),
22187                    )
22188                    .child(
22189                        h_flex()
22190                            .flex_shrink_0()
22191                            .gap_1()
22192                            .child(
22193                                IconButton::new("diff-review-close", IconName::Close)
22194                                    .icon_color(ui::Color::Muted)
22195                                    .icon_size(action_icon_size)
22196                                    .tooltip(Tooltip::text("Close"))
22197                                    .on_click(|_, window, cx| {
22198                                        window
22199                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22200                                    }),
22201                            )
22202                            .child(
22203                                IconButton::new("diff-review-add", IconName::Return)
22204                                    .icon_color(ui::Color::Muted)
22205                                    .icon_size(action_icon_size)
22206                                    .tooltip(Tooltip::text("Add comment"))
22207                                    .on_click(|_, window, cx| {
22208                                        window.dispatch_action(
22209                                            Box::new(crate::actions::SubmitDiffReviewComment),
22210                                            cx,
22211                                        );
22212                                    }),
22213                            ),
22214                    ),
22215            )
22216            // Expandable comments section (only shown when there are comments)
22217            .when(comment_count > 0, |el| {
22218                el.child(Self::render_comments_section(
22219                    comments,
22220                    comments_expanded,
22221                    inline_editors,
22222                    user_avatar_uri,
22223                    avatar_size,
22224                    action_icon_size,
22225                    colors,
22226                ))
22227            })
22228            .into_any_element()
22229    }
22230
22231    fn render_comments_section(
22232        comments: Vec<StoredReviewComment>,
22233        expanded: bool,
22234        inline_editors: HashMap<usize, Entity<Editor>>,
22235        user_avatar_uri: Option<SharedUri>,
22236        avatar_size: Pixels,
22237        action_icon_size: IconSize,
22238        colors: &theme::ThemeColors,
22239    ) -> impl IntoElement {
22240        let comment_count = comments.len();
22241
22242        v_flex()
22243            .w_full()
22244            .gap_1()
22245            // Header with expand/collapse toggle
22246            .child(
22247                h_flex()
22248                    .id("review-comments-header")
22249                    .w_full()
22250                    .items_center()
22251                    .gap_1()
22252                    .px_2()
22253                    .py_1()
22254                    .cursor_pointer()
22255                    .rounded_md()
22256                    .hover(|style| style.bg(colors.ghost_element_hover))
22257                    .on_click(|_, window: &mut Window, cx| {
22258                        window.dispatch_action(
22259                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22260                            cx,
22261                        );
22262                    })
22263                    .child(
22264                        Icon::new(if expanded {
22265                            IconName::ChevronDown
22266                        } else {
22267                            IconName::ChevronRight
22268                        })
22269                        .size(IconSize::Small)
22270                        .color(ui::Color::Muted),
22271                    )
22272                    .child(
22273                        Label::new(format!(
22274                            "{} Comment{}",
22275                            comment_count,
22276                            if comment_count == 1 { "" } else { "s" }
22277                        ))
22278                        .size(LabelSize::Small)
22279                        .color(Color::Muted),
22280                    ),
22281            )
22282            // Comments list (when expanded)
22283            .when(expanded, |el| {
22284                el.children(comments.into_iter().map(|comment| {
22285                    let inline_editor = inline_editors.get(&comment.id).cloned();
22286                    Self::render_comment_row(
22287                        comment,
22288                        inline_editor,
22289                        user_avatar_uri.clone(),
22290                        avatar_size,
22291                        action_icon_size,
22292                        colors,
22293                    )
22294                }))
22295            })
22296    }
22297
22298    fn render_comment_row(
22299        comment: StoredReviewComment,
22300        inline_editor: Option<Entity<Editor>>,
22301        user_avatar_uri: Option<SharedUri>,
22302        avatar_size: Pixels,
22303        action_icon_size: IconSize,
22304        colors: &theme::ThemeColors,
22305    ) -> impl IntoElement {
22306        let comment_id = comment.id;
22307        let is_editing = inline_editor.is_some();
22308
22309        h_flex()
22310            .w_full()
22311            .items_center()
22312            .gap_2()
22313            .px_2()
22314            .py_1p5()
22315            .rounded_md()
22316            .bg(colors.surface_background)
22317            .child(
22318                div()
22319                    .size(avatar_size)
22320                    .flex_shrink_0()
22321                    .rounded_full()
22322                    .overflow_hidden()
22323                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22324                        Avatar::new(avatar_uri.clone())
22325                            .size(avatar_size)
22326                            .into_any_element()
22327                    } else {
22328                        Icon::new(IconName::Person)
22329                            .size(IconSize::Small)
22330                            .color(ui::Color::Muted)
22331                            .into_any_element()
22332                    }),
22333            )
22334            .child(if let Some(editor) = inline_editor {
22335                // Inline edit mode: show an editable text field
22336                div()
22337                    .flex_1()
22338                    .border_1()
22339                    .border_color(colors.border)
22340                    .rounded_md()
22341                    .bg(colors.editor_background)
22342                    .px_2()
22343                    .py_1()
22344                    .child(editor)
22345                    .into_any_element()
22346            } else {
22347                // Display mode: show the comment text
22348                div()
22349                    .flex_1()
22350                    .text_sm()
22351                    .text_color(colors.text)
22352                    .child(comment.comment)
22353                    .into_any_element()
22354            })
22355            .child(if is_editing {
22356                // Editing mode: show close and confirm buttons
22357                h_flex()
22358                    .gap_1()
22359                    .child(
22360                        IconButton::new(
22361                            format!("diff-review-cancel-edit-{comment_id}"),
22362                            IconName::Close,
22363                        )
22364                        .icon_color(ui::Color::Muted)
22365                        .icon_size(action_icon_size)
22366                        .tooltip(Tooltip::text("Cancel"))
22367                        .on_click(move |_, window, cx| {
22368                            window.dispatch_action(
22369                                Box::new(crate::actions::CancelEditReviewComment {
22370                                    id: comment_id,
22371                                }),
22372                                cx,
22373                            );
22374                        }),
22375                    )
22376                    .child(
22377                        IconButton::new(
22378                            format!("diff-review-confirm-edit-{comment_id}"),
22379                            IconName::Return,
22380                        )
22381                        .icon_color(ui::Color::Muted)
22382                        .icon_size(action_icon_size)
22383                        .tooltip(Tooltip::text("Confirm"))
22384                        .on_click(move |_, window, cx| {
22385                            window.dispatch_action(
22386                                Box::new(crate::actions::ConfirmEditReviewComment {
22387                                    id: comment_id,
22388                                }),
22389                                cx,
22390                            );
22391                        }),
22392                    )
22393                    .into_any_element()
22394            } else {
22395                // Display mode: no action buttons for now (edit/delete not yet implemented)
22396                gpui::Empty.into_any_element()
22397            })
22398    }
22399
22400    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22401        if self.display_map.read(cx).masked != masked {
22402            self.display_map.update(cx, |map, _| map.masked = masked);
22403        }
22404        cx.notify()
22405    }
22406
22407    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22408        self.show_wrap_guides = Some(show_wrap_guides);
22409        cx.notify();
22410    }
22411
22412    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22413        self.show_indent_guides = Some(show_indent_guides);
22414        cx.notify();
22415    }
22416
22417    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22418        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22419            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22420                && let Some(dir) = file.abs_path(cx).parent()
22421            {
22422                return Some(dir.to_owned());
22423            }
22424        }
22425
22426        None
22427    }
22428
22429    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22430        self.active_excerpt(cx)?
22431            .1
22432            .read(cx)
22433            .file()
22434            .and_then(|f| f.as_local())
22435    }
22436
22437    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22438        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22439            let buffer = buffer.read(cx);
22440            if let Some(project_path) = buffer.project_path(cx) {
22441                let project = self.project()?.read(cx);
22442                project.absolute_path(&project_path, cx)
22443            } else {
22444                buffer
22445                    .file()
22446                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22447            }
22448        })
22449    }
22450
22451    pub fn reveal_in_finder(
22452        &mut self,
22453        _: &RevealInFileManager,
22454        _window: &mut Window,
22455        cx: &mut Context<Self>,
22456    ) {
22457        if let Some(path) = self.target_file_abs_path(cx) {
22458            if let Some(project) = self.project() {
22459                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22460            } else {
22461                cx.reveal_path(&path);
22462            }
22463        }
22464    }
22465
22466    pub fn copy_path(
22467        &mut self,
22468        _: &zed_actions::workspace::CopyPath,
22469        _window: &mut Window,
22470        cx: &mut Context<Self>,
22471    ) {
22472        if let Some(path) = self.target_file_abs_path(cx)
22473            && let Some(path) = path.to_str()
22474        {
22475            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22476        } else {
22477            cx.propagate();
22478        }
22479    }
22480
22481    pub fn copy_relative_path(
22482        &mut self,
22483        _: &zed_actions::workspace::CopyRelativePath,
22484        _window: &mut Window,
22485        cx: &mut Context<Self>,
22486    ) {
22487        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22488            let project = self.project()?.read(cx);
22489            let path = buffer.read(cx).file()?.path();
22490            let path = path.display(project.path_style(cx));
22491            Some(path)
22492        }) {
22493            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22494        } else {
22495            cx.propagate();
22496        }
22497    }
22498
22499    /// Returns the project path for the editor's buffer, if any buffer is
22500    /// opened in the editor.
22501    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22502        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22503            buffer.read(cx).project_path(cx)
22504        } else {
22505            None
22506        }
22507    }
22508
22509    // Returns true if the editor handled a go-to-line request
22510    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22511        maybe!({
22512            let breakpoint_store = self.breakpoint_store.as_ref()?;
22513
22514            let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
22515            else {
22516                self.clear_row_highlights::<ActiveDebugLine>();
22517                return None;
22518            };
22519
22520            let position = active_stack_frame.position;
22521            let buffer_id = position.buffer_id?;
22522            let snapshot = self
22523                .project
22524                .as_ref()?
22525                .read(cx)
22526                .buffer_for_id(buffer_id, cx)?
22527                .read(cx)
22528                .snapshot();
22529
22530            let mut handled = false;
22531            for (id, ExcerptRange { context, .. }) in
22532                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22533            {
22534                if context.start.cmp(&position, &snapshot).is_ge()
22535                    || context.end.cmp(&position, &snapshot).is_lt()
22536                {
22537                    continue;
22538                }
22539                let snapshot = self.buffer.read(cx).snapshot(cx);
22540                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22541
22542                handled = true;
22543                self.clear_row_highlights::<ActiveDebugLine>();
22544
22545                self.go_to_line::<ActiveDebugLine>(
22546                    multibuffer_anchor,
22547                    Some(cx.theme().colors().editor_debugger_active_line_background),
22548                    window,
22549                    cx,
22550                );
22551
22552                cx.notify();
22553            }
22554
22555            handled.then_some(())
22556        })
22557        .is_some()
22558    }
22559
22560    pub fn copy_file_name_without_extension(
22561        &mut self,
22562        _: &CopyFileNameWithoutExtension,
22563        _: &mut Window,
22564        cx: &mut Context<Self>,
22565    ) {
22566        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22567            let file = buffer.read(cx).file()?;
22568            file.path().file_stem()
22569        }) {
22570            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22571        }
22572    }
22573
22574    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22575        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22576            let file = buffer.read(cx).file()?;
22577            Some(file.file_name(cx))
22578        }) {
22579            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22580        }
22581    }
22582
22583    pub fn toggle_git_blame(
22584        &mut self,
22585        _: &::git::Blame,
22586        window: &mut Window,
22587        cx: &mut Context<Self>,
22588    ) {
22589        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22590
22591        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22592            self.start_git_blame(true, window, cx);
22593        }
22594
22595        cx.notify();
22596    }
22597
22598    pub fn toggle_git_blame_inline(
22599        &mut self,
22600        _: &ToggleGitBlameInline,
22601        window: &mut Window,
22602        cx: &mut Context<Self>,
22603    ) {
22604        self.toggle_git_blame_inline_internal(true, window, cx);
22605        cx.notify();
22606    }
22607
22608    pub fn open_git_blame_commit(
22609        &mut self,
22610        _: &OpenGitBlameCommit,
22611        window: &mut Window,
22612        cx: &mut Context<Self>,
22613    ) {
22614        self.open_git_blame_commit_internal(window, cx);
22615    }
22616
22617    fn open_git_blame_commit_internal(
22618        &mut self,
22619        window: &mut Window,
22620        cx: &mut Context<Self>,
22621    ) -> Option<()> {
22622        let blame = self.blame.as_ref()?;
22623        let snapshot = self.snapshot(window, cx);
22624        let cursor = self
22625            .selections
22626            .newest::<Point>(&snapshot.display_snapshot)
22627            .head();
22628        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22629        let (_, blame_entry) = blame
22630            .update(cx, |blame, cx| {
22631                blame
22632                    .blame_for_rows(
22633                        &[RowInfo {
22634                            buffer_id: Some(buffer.remote_id()),
22635                            buffer_row: Some(point.row),
22636                            ..Default::default()
22637                        }],
22638                        cx,
22639                    )
22640                    .next()
22641            })
22642            .flatten()?;
22643        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22644        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22645        let workspace = self.workspace()?.downgrade();
22646        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22647        None
22648    }
22649
22650    pub fn git_blame_inline_enabled(&self) -> bool {
22651        self.git_blame_inline_enabled
22652    }
22653
22654    pub fn toggle_selection_menu(
22655        &mut self,
22656        _: &ToggleSelectionMenu,
22657        _: &mut Window,
22658        cx: &mut Context<Self>,
22659    ) {
22660        self.show_selection_menu = self
22661            .show_selection_menu
22662            .map(|show_selections_menu| !show_selections_menu)
22663            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22664
22665        cx.notify();
22666    }
22667
22668    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22669        self.show_selection_menu
22670            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22671    }
22672
22673    fn start_git_blame(
22674        &mut self,
22675        user_triggered: bool,
22676        window: &mut Window,
22677        cx: &mut Context<Self>,
22678    ) {
22679        if let Some(project) = self.project() {
22680            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22681                && buffer.read(cx).file().is_none()
22682            {
22683                return;
22684            }
22685
22686            let focused = self.focus_handle(cx).contains_focused(window, cx);
22687
22688            let project = project.clone();
22689            let blame = cx
22690                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
22691            self.blame_subscription =
22692                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
22693            self.blame = Some(blame);
22694        }
22695    }
22696
22697    fn toggle_git_blame_inline_internal(
22698        &mut self,
22699        user_triggered: bool,
22700        window: &mut Window,
22701        cx: &mut Context<Self>,
22702    ) {
22703        if self.git_blame_inline_enabled {
22704            self.git_blame_inline_enabled = false;
22705            self.show_git_blame_inline = false;
22706            self.show_git_blame_inline_delay_task.take();
22707        } else {
22708            self.git_blame_inline_enabled = true;
22709            self.start_git_blame_inline(user_triggered, window, cx);
22710        }
22711
22712        cx.notify();
22713    }
22714
22715    fn start_git_blame_inline(
22716        &mut self,
22717        user_triggered: bool,
22718        window: &mut Window,
22719        cx: &mut Context<Self>,
22720    ) {
22721        self.start_git_blame(user_triggered, window, cx);
22722
22723        if ProjectSettings::get_global(cx)
22724            .git
22725            .inline_blame_delay()
22726            .is_some()
22727        {
22728            self.start_inline_blame_timer(window, cx);
22729        } else {
22730            self.show_git_blame_inline = true
22731        }
22732    }
22733
22734    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
22735        self.blame.as_ref()
22736    }
22737
22738    pub fn show_git_blame_gutter(&self) -> bool {
22739        self.show_git_blame_gutter
22740    }
22741
22742    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
22743        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
22744    }
22745
22746    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
22747        self.show_git_blame_inline
22748            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
22749            && !self.newest_selection_head_on_empty_line(cx)
22750            && self.has_blame_entries(cx)
22751    }
22752
22753    fn has_blame_entries(&self, cx: &App) -> bool {
22754        self.blame()
22755            .is_some_and(|blame| blame.read(cx).has_generated_entries())
22756    }
22757
22758    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
22759        let cursor_anchor = self.selections.newest_anchor().head();
22760
22761        let snapshot = self.buffer.read(cx).snapshot(cx);
22762        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
22763
22764        snapshot.line_len(buffer_row) == 0
22765    }
22766
22767    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
22768        let buffer_and_selection = maybe!({
22769            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
22770            let selection_range = selection.range();
22771
22772            let multi_buffer = self.buffer().read(cx);
22773            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
22774            let buffer_ranges = multi_buffer_snapshot
22775                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
22776
22777            let (buffer, range, _) = if selection.reversed {
22778                buffer_ranges.first()
22779            } else {
22780                buffer_ranges.last()
22781            }?;
22782
22783            let buffer_range = range.to_point(buffer);
22784
22785            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
22786                return Some((
22787                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
22788                    buffer_range.start.row..buffer_range.end.row,
22789                ));
22790            };
22791
22792            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
22793            let start =
22794                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
22795            let end =
22796                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
22797
22798            Some((
22799                multi_buffer.buffer(buffer.remote_id()).unwrap(),
22800                start.row..end.row,
22801            ))
22802        });
22803
22804        let Some((buffer, selection)) = buffer_and_selection else {
22805            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
22806        };
22807
22808        let Some(project) = self.project() else {
22809            return Task::ready(Err(anyhow!("editor does not have project")));
22810        };
22811
22812        project.update(cx, |project, cx| {
22813            project.get_permalink_to_line(&buffer, selection, cx)
22814        })
22815    }
22816
22817    pub fn copy_permalink_to_line(
22818        &mut self,
22819        _: &CopyPermalinkToLine,
22820        window: &mut Window,
22821        cx: &mut Context<Self>,
22822    ) {
22823        let permalink_task = self.get_permalink_to_line(cx);
22824        let workspace = self.workspace();
22825
22826        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22827            Ok(permalink) => {
22828                cx.update(|_, cx| {
22829                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
22830                })
22831                .ok();
22832            }
22833            Err(err) => {
22834                let message = format!("Failed to copy permalink: {err}");
22835
22836                anyhow::Result::<()>::Err(err).log_err();
22837
22838                if let Some(workspace) = workspace {
22839                    workspace
22840                        .update_in(cx, |workspace, _, cx| {
22841                            struct CopyPermalinkToLine;
22842
22843                            workspace.show_toast(
22844                                Toast::new(
22845                                    NotificationId::unique::<CopyPermalinkToLine>(),
22846                                    message,
22847                                ),
22848                                cx,
22849                            )
22850                        })
22851                        .ok();
22852                }
22853            }
22854        })
22855        .detach();
22856    }
22857
22858    pub fn copy_file_location(
22859        &mut self,
22860        _: &CopyFileLocation,
22861        _: &mut Window,
22862        cx: &mut Context<Self>,
22863    ) {
22864        let selection = self
22865            .selections
22866            .newest::<Point>(&self.display_snapshot(cx))
22867            .start
22868            .row
22869            + 1;
22870        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22871            let project = self.project()?.read(cx);
22872            let file = buffer.read(cx).file()?;
22873            let path = file.path().display(project.path_style(cx));
22874
22875            Some(format!("{path}:{selection}"))
22876        }) {
22877            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
22878        }
22879    }
22880
22881    pub fn open_permalink_to_line(
22882        &mut self,
22883        _: &OpenPermalinkToLine,
22884        window: &mut Window,
22885        cx: &mut Context<Self>,
22886    ) {
22887        let permalink_task = self.get_permalink_to_line(cx);
22888        let workspace = self.workspace();
22889
22890        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22891            Ok(permalink) => {
22892                cx.update(|_, cx| {
22893                    cx.open_url(permalink.as_ref());
22894                })
22895                .ok();
22896            }
22897            Err(err) => {
22898                let message = format!("Failed to open permalink: {err}");
22899
22900                anyhow::Result::<()>::Err(err).log_err();
22901
22902                if let Some(workspace) = workspace {
22903                    workspace.update(cx, |workspace, cx| {
22904                        struct OpenPermalinkToLine;
22905
22906                        workspace.show_toast(
22907                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
22908                            cx,
22909                        )
22910                    });
22911                }
22912            }
22913        })
22914        .detach();
22915    }
22916
22917    pub fn insert_uuid_v4(
22918        &mut self,
22919        _: &InsertUuidV4,
22920        window: &mut Window,
22921        cx: &mut Context<Self>,
22922    ) {
22923        self.insert_uuid(UuidVersion::V4, window, cx);
22924    }
22925
22926    pub fn insert_uuid_v7(
22927        &mut self,
22928        _: &InsertUuidV7,
22929        window: &mut Window,
22930        cx: &mut Context<Self>,
22931    ) {
22932        self.insert_uuid(UuidVersion::V7, window, cx);
22933    }
22934
22935    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
22936        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
22937        self.transact(window, cx, |this, window, cx| {
22938            let edits = this
22939                .selections
22940                .all::<Point>(&this.display_snapshot(cx))
22941                .into_iter()
22942                .map(|selection| {
22943                    let uuid = match version {
22944                        UuidVersion::V4 => uuid::Uuid::new_v4(),
22945                        UuidVersion::V7 => uuid::Uuid::now_v7(),
22946                    };
22947
22948                    (selection.range(), uuid.to_string())
22949                });
22950            this.edit(edits, cx);
22951            this.refresh_edit_prediction(true, false, window, cx);
22952        });
22953    }
22954
22955    pub fn open_selections_in_multibuffer(
22956        &mut self,
22957        _: &OpenSelectionsInMultibuffer,
22958        window: &mut Window,
22959        cx: &mut Context<Self>,
22960    ) {
22961        let multibuffer = self.buffer.read(cx);
22962
22963        let Some(buffer) = multibuffer.as_singleton() else {
22964            return;
22965        };
22966
22967        let Some(workspace) = self.workspace() else {
22968            return;
22969        };
22970
22971        let title = multibuffer.title(cx).to_string();
22972
22973        let locations = self
22974            .selections
22975            .all_anchors(&self.display_snapshot(cx))
22976            .iter()
22977            .map(|selection| {
22978                (
22979                    buffer.clone(),
22980                    (selection.start.text_anchor..selection.end.text_anchor)
22981                        .to_point(buffer.read(cx)),
22982                )
22983            })
22984            .into_group_map();
22985
22986        cx.spawn_in(window, async move |_, cx| {
22987            workspace.update_in(cx, |workspace, window, cx| {
22988                Self::open_locations_in_multibuffer(
22989                    workspace,
22990                    locations,
22991                    format!("Selections for '{title}'"),
22992                    false,
22993                    false,
22994                    MultibufferSelectionMode::All,
22995                    window,
22996                    cx,
22997                );
22998            })
22999        })
23000        .detach();
23001    }
23002
23003    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23004    /// last highlight added will be used.
23005    ///
23006    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23007    pub fn highlight_rows<T: 'static>(
23008        &mut self,
23009        range: Range<Anchor>,
23010        color: Hsla,
23011        options: RowHighlightOptions,
23012        cx: &mut Context<Self>,
23013    ) {
23014        let snapshot = self.buffer().read(cx).snapshot(cx);
23015        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23016        let ix = row_highlights.binary_search_by(|highlight| {
23017            Ordering::Equal
23018                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23019                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23020        });
23021
23022        if let Err(mut ix) = ix {
23023            let index = post_inc(&mut self.highlight_order);
23024
23025            // If this range intersects with the preceding highlight, then merge it with
23026            // the preceding highlight. Otherwise insert a new highlight.
23027            let mut merged = false;
23028            if ix > 0 {
23029                let prev_highlight = &mut row_highlights[ix - 1];
23030                if prev_highlight
23031                    .range
23032                    .end
23033                    .cmp(&range.start, &snapshot)
23034                    .is_ge()
23035                {
23036                    ix -= 1;
23037                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23038                        prev_highlight.range.end = range.end;
23039                    }
23040                    merged = true;
23041                    prev_highlight.index = index;
23042                    prev_highlight.color = color;
23043                    prev_highlight.options = options;
23044                }
23045            }
23046
23047            if !merged {
23048                row_highlights.insert(
23049                    ix,
23050                    RowHighlight {
23051                        range,
23052                        index,
23053                        color,
23054                        options,
23055                        type_id: TypeId::of::<T>(),
23056                    },
23057                );
23058            }
23059
23060            // If any of the following highlights intersect with this one, merge them.
23061            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23062                let highlight = &row_highlights[ix];
23063                if next_highlight
23064                    .range
23065                    .start
23066                    .cmp(&highlight.range.end, &snapshot)
23067                    .is_le()
23068                {
23069                    if next_highlight
23070                        .range
23071                        .end
23072                        .cmp(&highlight.range.end, &snapshot)
23073                        .is_gt()
23074                    {
23075                        row_highlights[ix].range.end = next_highlight.range.end;
23076                    }
23077                    row_highlights.remove(ix + 1);
23078                } else {
23079                    break;
23080                }
23081            }
23082        }
23083    }
23084
23085    /// Remove any highlighted row ranges of the given type that intersect the
23086    /// given ranges.
23087    pub fn remove_highlighted_rows<T: 'static>(
23088        &mut self,
23089        ranges_to_remove: Vec<Range<Anchor>>,
23090        cx: &mut Context<Self>,
23091    ) {
23092        let snapshot = self.buffer().read(cx).snapshot(cx);
23093        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23094        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23095        row_highlights.retain(|highlight| {
23096            while let Some(range_to_remove) = ranges_to_remove.peek() {
23097                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23098                    Ordering::Less | Ordering::Equal => {
23099                        ranges_to_remove.next();
23100                    }
23101                    Ordering::Greater => {
23102                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23103                            Ordering::Less | Ordering::Equal => {
23104                                return false;
23105                            }
23106                            Ordering::Greater => break,
23107                        }
23108                    }
23109                }
23110            }
23111
23112            true
23113        })
23114    }
23115
23116    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23117    pub fn clear_row_highlights<T: 'static>(&mut self) {
23118        self.highlighted_rows.remove(&TypeId::of::<T>());
23119    }
23120
23121    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23122    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23123        self.highlighted_rows
23124            .get(&TypeId::of::<T>())
23125            .map_or(&[] as &[_], |vec| vec.as_slice())
23126            .iter()
23127            .map(|highlight| (highlight.range.clone(), highlight.color))
23128    }
23129
23130    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23131    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23132    /// Allows to ignore certain kinds of highlights.
23133    pub fn highlighted_display_rows(
23134        &self,
23135        window: &mut Window,
23136        cx: &mut App,
23137    ) -> BTreeMap<DisplayRow, LineHighlight> {
23138        let snapshot = self.snapshot(window, cx);
23139        let mut used_highlight_orders = HashMap::default();
23140        self.highlighted_rows
23141            .iter()
23142            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23143            .fold(
23144                BTreeMap::<DisplayRow, LineHighlight>::new(),
23145                |mut unique_rows, highlight| {
23146                    let start = highlight.range.start.to_display_point(&snapshot);
23147                    let end = highlight.range.end.to_display_point(&snapshot);
23148                    let start_row = start.row().0;
23149                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23150                    {
23151                        end.row().0.saturating_sub(1)
23152                    } else {
23153                        end.row().0
23154                    };
23155                    for row in start_row..=end_row {
23156                        let used_index =
23157                            used_highlight_orders.entry(row).or_insert(highlight.index);
23158                        if highlight.index >= *used_index {
23159                            *used_index = highlight.index;
23160                            unique_rows.insert(
23161                                DisplayRow(row),
23162                                LineHighlight {
23163                                    include_gutter: highlight.options.include_gutter,
23164                                    border: None,
23165                                    background: highlight.color.into(),
23166                                    type_id: Some(highlight.type_id),
23167                                },
23168                            );
23169                        }
23170                    }
23171                    unique_rows
23172                },
23173            )
23174    }
23175
23176    pub fn highlighted_display_row_for_autoscroll(
23177        &self,
23178        snapshot: &DisplaySnapshot,
23179    ) -> Option<DisplayRow> {
23180        self.highlighted_rows
23181            .values()
23182            .flat_map(|highlighted_rows| highlighted_rows.iter())
23183            .filter_map(|highlight| {
23184                if highlight.options.autoscroll {
23185                    Some(highlight.range.start.to_display_point(snapshot).row())
23186                } else {
23187                    None
23188                }
23189            })
23190            .min()
23191    }
23192
23193    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23194        self.highlight_background::<SearchWithinRange>(
23195            ranges,
23196            |_, colors| colors.colors().editor_document_highlight_read_background,
23197            cx,
23198        )
23199    }
23200
23201    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23202        self.breadcrumb_header = Some(new_header);
23203    }
23204
23205    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23206        self.clear_background_highlights::<SearchWithinRange>(cx);
23207    }
23208
23209    pub fn highlight_background<T: 'static>(
23210        &mut self,
23211        ranges: &[Range<Anchor>],
23212        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23213        cx: &mut Context<Self>,
23214    ) {
23215        self.background_highlights.insert(
23216            HighlightKey::Type(TypeId::of::<T>()),
23217            (Arc::new(color_fetcher), Arc::from(ranges)),
23218        );
23219        self.scrollbar_marker_state.dirty = true;
23220        cx.notify();
23221    }
23222
23223    pub fn highlight_background_key<T: 'static>(
23224        &mut self,
23225        key: usize,
23226        ranges: &[Range<Anchor>],
23227        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23228        cx: &mut Context<Self>,
23229    ) {
23230        self.background_highlights.insert(
23231            HighlightKey::TypePlus(TypeId::of::<T>(), key),
23232            (Arc::new(color_fetcher), Arc::from(ranges)),
23233        );
23234        self.scrollbar_marker_state.dirty = true;
23235        cx.notify();
23236    }
23237
23238    pub fn clear_background_highlights<T: 'static>(
23239        &mut self,
23240        cx: &mut Context<Self>,
23241    ) -> Option<BackgroundHighlight> {
23242        let text_highlights = self
23243            .background_highlights
23244            .remove(&HighlightKey::Type(TypeId::of::<T>()))?;
23245        if !text_highlights.1.is_empty() {
23246            self.scrollbar_marker_state.dirty = true;
23247            cx.notify();
23248        }
23249        Some(text_highlights)
23250    }
23251
23252    pub fn clear_background_highlights_key<T: 'static>(
23253        &mut self,
23254        key: usize,
23255        cx: &mut Context<Self>,
23256    ) -> Option<BackgroundHighlight> {
23257        let text_highlights = self
23258            .background_highlights
23259            .remove(&HighlightKey::TypePlus(TypeId::of::<T>(), key))?;
23260        if !text_highlights.1.is_empty() {
23261            self.scrollbar_marker_state.dirty = true;
23262            cx.notify();
23263        }
23264        Some(text_highlights)
23265    }
23266
23267    pub fn highlight_gutter<T: 'static>(
23268        &mut self,
23269        ranges: impl Into<Vec<Range<Anchor>>>,
23270        color_fetcher: fn(&App) -> Hsla,
23271        cx: &mut Context<Self>,
23272    ) {
23273        self.gutter_highlights
23274            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23275        cx.notify();
23276    }
23277
23278    pub fn clear_gutter_highlights<T: 'static>(
23279        &mut self,
23280        cx: &mut Context<Self>,
23281    ) -> Option<GutterHighlight> {
23282        cx.notify();
23283        self.gutter_highlights.remove(&TypeId::of::<T>())
23284    }
23285
23286    pub fn insert_gutter_highlight<T: 'static>(
23287        &mut self,
23288        range: Range<Anchor>,
23289        color_fetcher: fn(&App) -> Hsla,
23290        cx: &mut Context<Self>,
23291    ) {
23292        let snapshot = self.buffer().read(cx).snapshot(cx);
23293        let mut highlights = self
23294            .gutter_highlights
23295            .remove(&TypeId::of::<T>())
23296            .map(|(_, highlights)| highlights)
23297            .unwrap_or_default();
23298        let ix = highlights.binary_search_by(|highlight| {
23299            Ordering::Equal
23300                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23301                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23302        });
23303        if let Err(ix) = ix {
23304            highlights.insert(ix, range);
23305        }
23306        self.gutter_highlights
23307            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23308    }
23309
23310    pub fn remove_gutter_highlights<T: 'static>(
23311        &mut self,
23312        ranges_to_remove: Vec<Range<Anchor>>,
23313        cx: &mut Context<Self>,
23314    ) {
23315        let snapshot = self.buffer().read(cx).snapshot(cx);
23316        let Some((color_fetcher, mut gutter_highlights)) =
23317            self.gutter_highlights.remove(&TypeId::of::<T>())
23318        else {
23319            return;
23320        };
23321        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23322        gutter_highlights.retain(|highlight| {
23323            while let Some(range_to_remove) = ranges_to_remove.peek() {
23324                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23325                    Ordering::Less | Ordering::Equal => {
23326                        ranges_to_remove.next();
23327                    }
23328                    Ordering::Greater => {
23329                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23330                            Ordering::Less | Ordering::Equal => {
23331                                return false;
23332                            }
23333                            Ordering::Greater => break,
23334                        }
23335                    }
23336                }
23337            }
23338
23339            true
23340        });
23341        self.gutter_highlights
23342            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23343    }
23344
23345    #[cfg(feature = "test-support")]
23346    pub fn all_text_highlights(
23347        &self,
23348        window: &mut Window,
23349        cx: &mut Context<Self>,
23350    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23351        let snapshot = self.snapshot(window, cx);
23352        self.display_map.update(cx, |display_map, _| {
23353            display_map
23354                .all_text_highlights()
23355                .map(|highlight| {
23356                    let (style, ranges) = highlight.as_ref();
23357                    (
23358                        *style,
23359                        ranges
23360                            .iter()
23361                            .map(|range| range.clone().to_display_points(&snapshot))
23362                            .collect(),
23363                    )
23364                })
23365                .collect()
23366        })
23367    }
23368
23369    #[cfg(feature = "test-support")]
23370    pub fn all_text_background_highlights(
23371        &self,
23372        window: &mut Window,
23373        cx: &mut Context<Self>,
23374    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23375        let snapshot = self.snapshot(window, cx);
23376        let buffer = &snapshot.buffer_snapshot();
23377        let start = buffer.anchor_before(MultiBufferOffset(0));
23378        let end = buffer.anchor_after(buffer.len());
23379        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23380    }
23381
23382    #[cfg(any(test, feature = "test-support"))]
23383    pub fn sorted_background_highlights_in_range(
23384        &self,
23385        search_range: Range<Anchor>,
23386        display_snapshot: &DisplaySnapshot,
23387        theme: &Theme,
23388    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23389        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23390        res.sort_by(|a, b| {
23391            a.0.start
23392                .cmp(&b.0.start)
23393                .then_with(|| a.0.end.cmp(&b.0.end))
23394                .then_with(|| a.1.cmp(&b.1))
23395        });
23396        res
23397    }
23398
23399    #[cfg(feature = "test-support")]
23400    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23401        let snapshot = self.buffer().read(cx).snapshot(cx);
23402
23403        let highlights = self
23404            .background_highlights
23405            .get(&HighlightKey::Type(TypeId::of::<
23406                items::BufferSearchHighlights,
23407            >()));
23408
23409        if let Some((_color, ranges)) = highlights {
23410            ranges
23411                .iter()
23412                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23413                .collect_vec()
23414        } else {
23415            vec![]
23416        }
23417    }
23418
23419    fn document_highlights_for_position<'a>(
23420        &'a self,
23421        position: Anchor,
23422        buffer: &'a MultiBufferSnapshot,
23423    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23424        let read_highlights = self
23425            .background_highlights
23426            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
23427            .map(|h| &h.1);
23428        let write_highlights = self
23429            .background_highlights
23430            .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
23431            .map(|h| &h.1);
23432        let left_position = position.bias_left(buffer);
23433        let right_position = position.bias_right(buffer);
23434        read_highlights
23435            .into_iter()
23436            .chain(write_highlights)
23437            .flat_map(move |ranges| {
23438                let start_ix = match ranges.binary_search_by(|probe| {
23439                    let cmp = probe.end.cmp(&left_position, buffer);
23440                    if cmp.is_ge() {
23441                        Ordering::Greater
23442                    } else {
23443                        Ordering::Less
23444                    }
23445                }) {
23446                    Ok(i) | Err(i) => i,
23447                };
23448
23449                ranges[start_ix..]
23450                    .iter()
23451                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23452            })
23453    }
23454
23455    pub fn has_background_highlights<T: 'static>(&self) -> bool {
23456        self.background_highlights
23457            .get(&HighlightKey::Type(TypeId::of::<T>()))
23458            .is_some_and(|(_, highlights)| !highlights.is_empty())
23459    }
23460
23461    /// Returns all background highlights for a given range.
23462    ///
23463    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23464    pub fn background_highlights_in_range(
23465        &self,
23466        search_range: Range<Anchor>,
23467        display_snapshot: &DisplaySnapshot,
23468        theme: &Theme,
23469    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23470        let mut results = Vec::new();
23471        for (color_fetcher, ranges) in self.background_highlights.values() {
23472            let start_ix = match ranges.binary_search_by(|probe| {
23473                let cmp = probe
23474                    .end
23475                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23476                if cmp.is_gt() {
23477                    Ordering::Greater
23478                } else {
23479                    Ordering::Less
23480                }
23481            }) {
23482                Ok(i) | Err(i) => i,
23483            };
23484            for (index, range) in ranges[start_ix..].iter().enumerate() {
23485                if range
23486                    .start
23487                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23488                    .is_ge()
23489                {
23490                    break;
23491                }
23492
23493                let color = color_fetcher(&(start_ix + index), theme);
23494                let start = range.start.to_display_point(display_snapshot);
23495                let end = range.end.to_display_point(display_snapshot);
23496                results.push((start..end, color))
23497            }
23498        }
23499        results
23500    }
23501
23502    pub fn gutter_highlights_in_range(
23503        &self,
23504        search_range: Range<Anchor>,
23505        display_snapshot: &DisplaySnapshot,
23506        cx: &App,
23507    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23508        let mut results = Vec::new();
23509        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23510            let color = color_fetcher(cx);
23511            let start_ix = match ranges.binary_search_by(|probe| {
23512                let cmp = probe
23513                    .end
23514                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23515                if cmp.is_gt() {
23516                    Ordering::Greater
23517                } else {
23518                    Ordering::Less
23519                }
23520            }) {
23521                Ok(i) | Err(i) => i,
23522            };
23523            for range in &ranges[start_ix..] {
23524                if range
23525                    .start
23526                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23527                    .is_ge()
23528                {
23529                    break;
23530                }
23531
23532                let start = range.start.to_display_point(display_snapshot);
23533                let end = range.end.to_display_point(display_snapshot);
23534                results.push((start..end, color))
23535            }
23536        }
23537        results
23538    }
23539
23540    /// Get the text ranges corresponding to the redaction query
23541    pub fn redacted_ranges(
23542        &self,
23543        search_range: Range<Anchor>,
23544        display_snapshot: &DisplaySnapshot,
23545        cx: &App,
23546    ) -> Vec<Range<DisplayPoint>> {
23547        display_snapshot
23548            .buffer_snapshot()
23549            .redacted_ranges(search_range, |file| {
23550                if let Some(file) = file {
23551                    file.is_private()
23552                        && EditorSettings::get(
23553                            Some(SettingsLocation {
23554                                worktree_id: file.worktree_id(cx),
23555                                path: file.path().as_ref(),
23556                            }),
23557                            cx,
23558                        )
23559                        .redact_private_values
23560                } else {
23561                    false
23562                }
23563            })
23564            .map(|range| {
23565                range.start.to_display_point(display_snapshot)
23566                    ..range.end.to_display_point(display_snapshot)
23567            })
23568            .collect()
23569    }
23570
23571    pub fn highlight_text_key<T: 'static>(
23572        &mut self,
23573        key: usize,
23574        ranges: Vec<Range<Anchor>>,
23575        style: HighlightStyle,
23576        merge: bool,
23577        cx: &mut Context<Self>,
23578    ) {
23579        self.display_map.update(cx, |map, cx| {
23580            map.highlight_text(
23581                HighlightKey::TypePlus(TypeId::of::<T>(), key),
23582                ranges,
23583                style,
23584                merge,
23585                cx,
23586            );
23587        });
23588        cx.notify();
23589    }
23590
23591    pub fn highlight_text<T: 'static>(
23592        &mut self,
23593        ranges: Vec<Range<Anchor>>,
23594        style: HighlightStyle,
23595        cx: &mut Context<Self>,
23596    ) {
23597        self.display_map.update(cx, |map, cx| {
23598            map.highlight_text(
23599                HighlightKey::Type(TypeId::of::<T>()),
23600                ranges,
23601                style,
23602                false,
23603                cx,
23604            )
23605        });
23606        cx.notify();
23607    }
23608
23609    pub fn text_highlights<'a, T: 'static>(
23610        &'a self,
23611        cx: &'a App,
23612    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23613        self.display_map.read(cx).text_highlights(TypeId::of::<T>())
23614    }
23615
23616    pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
23617        let cleared = self
23618            .display_map
23619            .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
23620        if cleared {
23621            cx.notify();
23622        }
23623    }
23624
23625    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23626        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23627            && self.focus_handle.is_focused(window)
23628    }
23629
23630    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23631        self.show_cursor_when_unfocused = is_enabled;
23632        cx.notify();
23633    }
23634
23635    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23636        cx.notify();
23637    }
23638
23639    fn on_debug_session_event(
23640        &mut self,
23641        _session: Entity<Session>,
23642        event: &SessionEvent,
23643        cx: &mut Context<Self>,
23644    ) {
23645        if let SessionEvent::InvalidateInlineValue = event {
23646            self.refresh_inline_values(cx);
23647        }
23648    }
23649
23650    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23651        let Some(project) = self.project.clone() else {
23652            return;
23653        };
23654
23655        if !self.inline_value_cache.enabled {
23656            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23657            self.splice_inlays(&inlays, Vec::new(), cx);
23658            return;
23659        }
23660
23661        let current_execution_position = self
23662            .highlighted_rows
23663            .get(&TypeId::of::<ActiveDebugLine>())
23664            .and_then(|lines| lines.last().map(|line| line.range.end));
23665
23666        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23667            let inline_values = editor
23668                .update(cx, |editor, cx| {
23669                    let Some(current_execution_position) = current_execution_position else {
23670                        return Some(Task::ready(Ok(Vec::new())));
23671                    };
23672
23673                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23674                        let snapshot = buffer.snapshot(cx);
23675
23676                        let excerpt = snapshot.excerpt_containing(
23677                            current_execution_position..current_execution_position,
23678                        )?;
23679
23680                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23681                    })?;
23682
23683                    let range =
23684                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23685
23686                    project.inline_values(buffer, range, cx)
23687                })
23688                .ok()
23689                .flatten()?
23690                .await
23691                .context("refreshing debugger inlays")
23692                .log_err()?;
23693
23694            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23695
23696            for (buffer_id, inline_value) in inline_values
23697                .into_iter()
23698                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23699            {
23700                buffer_inline_values
23701                    .entry(buffer_id)
23702                    .or_default()
23703                    .push(inline_value);
23704            }
23705
23706            editor
23707                .update(cx, |editor, cx| {
23708                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23709                    let mut new_inlays = Vec::default();
23710
23711                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23712                        let buffer_id = buffer_snapshot.remote_id();
23713                        buffer_inline_values
23714                            .get(&buffer_id)
23715                            .into_iter()
23716                            .flatten()
23717                            .for_each(|hint| {
23718                                let inlay = Inlay::debugger(
23719                                    post_inc(&mut editor.next_inlay_id),
23720                                    Anchor::in_buffer(excerpt_id, hint.position),
23721                                    hint.text(),
23722                                );
23723                                if !inlay.text().chars().contains(&'\n') {
23724                                    new_inlays.push(inlay);
23725                                }
23726                            });
23727                    }
23728
23729                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
23730                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
23731
23732                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
23733                })
23734                .ok()?;
23735            Some(())
23736        });
23737    }
23738
23739    fn on_buffer_event(
23740        &mut self,
23741        multibuffer: &Entity<MultiBuffer>,
23742        event: &multi_buffer::Event,
23743        window: &mut Window,
23744        cx: &mut Context<Self>,
23745    ) {
23746        match event {
23747            multi_buffer::Event::Edited { edited_buffer } => {
23748                self.scrollbar_marker_state.dirty = true;
23749                self.active_indent_guides_state.dirty = true;
23750                self.refresh_active_diagnostics(cx);
23751                self.refresh_code_actions(window, cx);
23752                self.refresh_single_line_folds(window, cx);
23753                self.refresh_matching_bracket_highlights(window, cx);
23754                if self.has_active_edit_prediction() {
23755                    self.update_visible_edit_prediction(window, cx);
23756                }
23757
23758                // Clean up orphaned review comments after edits
23759                self.cleanup_orphaned_review_comments(cx);
23760
23761                if let Some(buffer) = edited_buffer {
23762                    if buffer.read(cx).file().is_none() {
23763                        cx.emit(EditorEvent::TitleChanged);
23764                    }
23765
23766                    if self.project.is_some() {
23767                        let buffer_id = buffer.read(cx).remote_id();
23768                        self.register_buffer(buffer_id, cx);
23769                        self.update_lsp_data(Some(buffer_id), window, cx);
23770                        self.refresh_inlay_hints(
23771                            InlayHintRefreshReason::BufferEdited(buffer_id),
23772                            cx,
23773                        );
23774                    }
23775                }
23776
23777                cx.emit(EditorEvent::BufferEdited);
23778                cx.emit(SearchEvent::MatchesInvalidated);
23779
23780                let Some(project) = &self.project else { return };
23781                let (telemetry, is_via_ssh) = {
23782                    let project = project.read(cx);
23783                    let telemetry = project.client().telemetry().clone();
23784                    let is_via_ssh = project.is_via_remote_server();
23785                    (telemetry, is_via_ssh)
23786                };
23787                telemetry.log_edit_event("editor", is_via_ssh);
23788            }
23789            multi_buffer::Event::ExcerptsAdded {
23790                buffer,
23791                predecessor,
23792                excerpts,
23793            } => {
23794                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23795                let buffer_id = buffer.read(cx).remote_id();
23796                if self.buffer.read(cx).diff_for(buffer_id).is_none()
23797                    && let Some(project) = &self.project
23798                {
23799                    update_uncommitted_diff_for_buffer(
23800                        cx.entity(),
23801                        project,
23802                        [buffer.clone()],
23803                        self.buffer.clone(),
23804                        cx,
23805                    )
23806                    .detach();
23807                }
23808                self.update_lsp_data(Some(buffer_id), window, cx);
23809                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23810                self.colorize_brackets(false, cx);
23811                self.refresh_selected_text_highlights(true, window, cx);
23812                cx.emit(EditorEvent::ExcerptsAdded {
23813                    buffer: buffer.clone(),
23814                    predecessor: *predecessor,
23815                    excerpts: excerpts.clone(),
23816                });
23817            }
23818            multi_buffer::Event::ExcerptsRemoved {
23819                ids,
23820                removed_buffer_ids,
23821            } => {
23822                if let Some(inlay_hints) = &mut self.inlay_hints {
23823                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
23824                }
23825                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
23826                for buffer_id in removed_buffer_ids {
23827                    self.registered_buffers.remove(buffer_id);
23828                }
23829                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23830                cx.emit(EditorEvent::ExcerptsRemoved {
23831                    ids: ids.clone(),
23832                    removed_buffer_ids: removed_buffer_ids.clone(),
23833                });
23834            }
23835            multi_buffer::Event::ExcerptsEdited {
23836                excerpt_ids,
23837                buffer_ids,
23838            } => {
23839                self.display_map.update(cx, |map, cx| {
23840                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
23841                });
23842                cx.emit(EditorEvent::ExcerptsEdited {
23843                    ids: excerpt_ids.clone(),
23844                });
23845            }
23846            multi_buffer::Event::ExcerptsExpanded { ids } => {
23847                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23848                self.refresh_document_highlights(cx);
23849                for id in ids {
23850                    self.fetched_tree_sitter_chunks.remove(id);
23851                }
23852                self.colorize_brackets(false, cx);
23853                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
23854            }
23855            multi_buffer::Event::Reparsed(buffer_id) => {
23856                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23857                self.refresh_selected_text_highlights(true, window, cx);
23858                self.colorize_brackets(true, cx);
23859                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23860
23861                cx.emit(EditorEvent::Reparsed(*buffer_id));
23862            }
23863            multi_buffer::Event::DiffHunksToggled => {
23864                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
23865            }
23866            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
23867                if !is_fresh_language {
23868                    self.registered_buffers.remove(&buffer_id);
23869                }
23870                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23871                cx.emit(EditorEvent::Reparsed(*buffer_id));
23872                cx.notify();
23873            }
23874            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
23875            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
23876            multi_buffer::Event::FileHandleChanged
23877            | multi_buffer::Event::Reloaded
23878            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
23879            multi_buffer::Event::DiagnosticsUpdated => {
23880                self.update_diagnostics_state(window, cx);
23881            }
23882            _ => {}
23883        };
23884    }
23885
23886    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
23887        if !self.diagnostics_enabled() {
23888            return;
23889        }
23890        self.refresh_active_diagnostics(cx);
23891        self.refresh_inline_diagnostics(true, window, cx);
23892        self.scrollbar_marker_state.dirty = true;
23893        cx.notify();
23894    }
23895
23896    pub fn start_temporary_diff_override(&mut self) {
23897        self.load_diff_task.take();
23898        self.temporary_diff_override = true;
23899    }
23900
23901    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
23902        self.temporary_diff_override = false;
23903        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
23904        self.buffer.update(cx, |buffer, cx| {
23905            buffer.set_all_diff_hunks_collapsed(cx);
23906        });
23907
23908        if let Some(project) = self.project.clone() {
23909            self.load_diff_task = Some(
23910                update_uncommitted_diff_for_buffer(
23911                    cx.entity(),
23912                    &project,
23913                    self.buffer.read(cx).all_buffers(),
23914                    self.buffer.clone(),
23915                    cx,
23916                )
23917                .shared(),
23918            );
23919        }
23920    }
23921
23922    fn on_display_map_changed(
23923        &mut self,
23924        _: Entity<DisplayMap>,
23925        _: &mut Window,
23926        cx: &mut Context<Self>,
23927    ) {
23928        cx.notify();
23929    }
23930
23931    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
23932        if !self.mode.is_full() {
23933            return None;
23934        }
23935
23936        let theme_settings = theme::ThemeSettings::get_global(cx);
23937        let theme = cx.theme();
23938        let accent_colors = theme.accents().clone();
23939
23940        let accent_overrides = theme_settings
23941            .theme_overrides
23942            .get(theme.name.as_ref())
23943            .map(|theme_style| &theme_style.accents)
23944            .into_iter()
23945            .flatten()
23946            .chain(
23947                theme_settings
23948                    .experimental_theme_overrides
23949                    .as_ref()
23950                    .map(|overrides| &overrides.accents)
23951                    .into_iter()
23952                    .flatten(),
23953            )
23954            .flat_map(|accent| accent.0.clone().map(SharedString::from))
23955            .collect();
23956
23957        Some(AccentData {
23958            colors: accent_colors,
23959            overrides: accent_overrides,
23960        })
23961    }
23962
23963    fn fetch_applicable_language_settings(
23964        &self,
23965        cx: &App,
23966    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
23967        if !self.mode.is_full() {
23968            return HashMap::default();
23969        }
23970
23971        self.buffer().read(cx).all_buffers().into_iter().fold(
23972            HashMap::default(),
23973            |mut acc, buffer| {
23974                let buffer = buffer.read(cx);
23975                let language = buffer.language().map(|language| language.name());
23976                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
23977                    let file = buffer.file();
23978                    v.insert(language_settings(language, file, cx).into_owned());
23979                }
23980                acc
23981            },
23982        )
23983    }
23984
23985    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
23986        let new_language_settings = self.fetch_applicable_language_settings(cx);
23987        let language_settings_changed = new_language_settings != self.applicable_language_settings;
23988        self.applicable_language_settings = new_language_settings;
23989
23990        let new_accents = self.fetch_accent_data(cx);
23991        let accents_changed = new_accents != self.accent_data;
23992        self.accent_data = new_accents;
23993
23994        if self.diagnostics_enabled() {
23995            let new_severity = EditorSettings::get_global(cx)
23996                .diagnostics_max_severity
23997                .unwrap_or(DiagnosticSeverity::Hint);
23998            self.set_max_diagnostics_severity(new_severity, cx);
23999        }
24000        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24001        self.update_edit_prediction_settings(cx);
24002        self.refresh_edit_prediction(true, false, window, cx);
24003        self.refresh_inline_values(cx);
24004        self.refresh_inlay_hints(
24005            InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24006                self.selections.newest_anchor().head(),
24007                &self.buffer.read(cx).snapshot(cx),
24008                cx,
24009            )),
24010            cx,
24011        );
24012
24013        let old_cursor_shape = self.cursor_shape;
24014        let old_show_breadcrumbs = self.show_breadcrumbs;
24015
24016        {
24017            let editor_settings = EditorSettings::get_global(cx);
24018            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24019            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24020            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24021            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24022        }
24023
24024        if old_cursor_shape != self.cursor_shape {
24025            cx.emit(EditorEvent::CursorShapeChanged);
24026        }
24027
24028        if old_show_breadcrumbs != self.show_breadcrumbs {
24029            cx.emit(EditorEvent::BreadcrumbsChanged);
24030        }
24031
24032        let project_settings = ProjectSettings::get_global(cx);
24033        self.buffer_serialization = self
24034            .should_serialize_buffer()
24035            .then(|| BufferSerialization::new(project_settings.session.restore_unsaved_buffers));
24036
24037        if self.mode.is_full() {
24038            let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
24039            let inline_blame_enabled = project_settings.git.inline_blame.enabled;
24040            if self.show_inline_diagnostics != show_inline_diagnostics {
24041                self.show_inline_diagnostics = show_inline_diagnostics;
24042                self.refresh_inline_diagnostics(false, window, cx);
24043            }
24044
24045            if self.git_blame_inline_enabled != inline_blame_enabled {
24046                self.toggle_git_blame_inline_internal(false, window, cx);
24047            }
24048
24049            let minimap_settings = EditorSettings::get_global(cx).minimap;
24050            if self.minimap_visibility != MinimapVisibility::Disabled {
24051                if self.minimap_visibility.settings_visibility()
24052                    != minimap_settings.minimap_enabled()
24053                {
24054                    self.set_minimap_visibility(
24055                        MinimapVisibility::for_mode(self.mode(), cx),
24056                        window,
24057                        cx,
24058                    );
24059                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24060                    minimap_entity.update(cx, |minimap_editor, cx| {
24061                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24062                    })
24063                }
24064            }
24065
24066            if language_settings_changed || accents_changed {
24067                self.colorize_brackets(true, cx);
24068            }
24069
24070            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24071                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24072            }) {
24073                if !inlay_splice.is_empty() {
24074                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24075                }
24076                self.refresh_colors_for_visible_range(None, window, cx);
24077            }
24078        }
24079
24080        cx.notify();
24081    }
24082
24083    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24084        if !self.mode.is_full() {
24085            return;
24086        }
24087
24088        let new_accents = self.fetch_accent_data(cx);
24089        if new_accents != self.accent_data {
24090            self.accent_data = new_accents;
24091            self.colorize_brackets(true, cx);
24092        }
24093    }
24094
24095    pub fn set_searchable(&mut self, searchable: bool) {
24096        self.searchable = searchable;
24097    }
24098
24099    pub fn searchable(&self) -> bool {
24100        self.searchable
24101    }
24102
24103    pub fn open_excerpts_in_split(
24104        &mut self,
24105        _: &OpenExcerptsSplit,
24106        window: &mut Window,
24107        cx: &mut Context<Self>,
24108    ) {
24109        self.open_excerpts_common(None, true, window, cx)
24110    }
24111
24112    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24113        self.open_excerpts_common(None, false, window, cx)
24114    }
24115
24116    pub(crate) fn open_excerpts_common(
24117        &mut self,
24118        jump_data: Option<JumpData>,
24119        split: bool,
24120        window: &mut Window,
24121        cx: &mut Context<Self>,
24122    ) {
24123        let Some(workspace) = self.workspace() else {
24124            cx.propagate();
24125            return;
24126        };
24127
24128        if self.buffer.read(cx).is_singleton() {
24129            cx.propagate();
24130            return;
24131        }
24132
24133        let mut new_selections_by_buffer = HashMap::default();
24134        match &jump_data {
24135            Some(JumpData::MultiBufferPoint {
24136                excerpt_id,
24137                position,
24138                anchor,
24139                line_offset_from_top,
24140            }) => {
24141                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24142                if let Some(buffer) = multi_buffer_snapshot
24143                    .buffer_id_for_excerpt(*excerpt_id)
24144                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24145                {
24146                    let buffer_snapshot = buffer.read(cx).snapshot();
24147                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24148                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24149                    } else {
24150                        buffer_snapshot.clip_point(*position, Bias::Left)
24151                    };
24152                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24153                    new_selections_by_buffer.insert(
24154                        buffer,
24155                        (
24156                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24157                            Some(*line_offset_from_top),
24158                        ),
24159                    );
24160                }
24161            }
24162            Some(JumpData::MultiBufferRow {
24163                row,
24164                line_offset_from_top,
24165            }) => {
24166                let point = MultiBufferPoint::new(row.0, 0);
24167                if let Some((buffer, buffer_point, _)) =
24168                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24169                {
24170                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24171                    new_selections_by_buffer
24172                        .entry(buffer)
24173                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24174                        .0
24175                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24176                }
24177            }
24178            None => {
24179                let selections = self
24180                    .selections
24181                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24182                let multi_buffer = self.buffer.read(cx);
24183                for selection in selections {
24184                    for (snapshot, range, _, anchor) in multi_buffer
24185                        .snapshot(cx)
24186                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24187                    {
24188                        if let Some(anchor) = anchor {
24189                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24190                            else {
24191                                continue;
24192                            };
24193                            let offset = text::ToOffset::to_offset(
24194                                &anchor.text_anchor,
24195                                &buffer_handle.read(cx).snapshot(),
24196                            );
24197                            let range = BufferOffset(offset)..BufferOffset(offset);
24198                            new_selections_by_buffer
24199                                .entry(buffer_handle)
24200                                .or_insert((Vec::new(), None))
24201                                .0
24202                                .push(range)
24203                        } else {
24204                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24205                            else {
24206                                continue;
24207                            };
24208                            new_selections_by_buffer
24209                                .entry(buffer_handle)
24210                                .or_insert((Vec::new(), None))
24211                                .0
24212                                .push(range)
24213                        }
24214                    }
24215                }
24216            }
24217        }
24218
24219        new_selections_by_buffer
24220            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24221
24222        if new_selections_by_buffer.is_empty() {
24223            return;
24224        }
24225
24226        // We defer the pane interaction because we ourselves are a workspace item
24227        // and activating a new item causes the pane to call a method on us reentrantly,
24228        // which panics if we're on the stack.
24229        window.defer(cx, move |window, cx| {
24230            workspace.update(cx, |workspace, cx| {
24231                let pane = if split {
24232                    workspace.adjacent_pane(window, cx)
24233                } else {
24234                    workspace.active_pane().clone()
24235                };
24236
24237                for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24238                    let buffer_read = buffer.read(cx);
24239                    let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24240                        (true, project::File::from_dyn(Some(file)).is_some())
24241                    } else {
24242                        (false, false)
24243                    };
24244
24245                    // If project file is none workspace.open_project_item will fail to open the excerpt
24246                    // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24247                    // so we check if there's a tab match in that case first
24248                    let editor = (!has_file || !is_project_file)
24249                        .then(|| {
24250                            // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24251                            // so `workspace.open_project_item` will never find them, always opening a new editor.
24252                            // Instead, we try to activate the existing editor in the pane first.
24253                            let (editor, pane_item_index, pane_item_id) =
24254                                pane.read(cx).items().enumerate().find_map(|(i, item)| {
24255                                    let editor = item.downcast::<Editor>()?;
24256                                    let singleton_buffer =
24257                                        editor.read(cx).buffer().read(cx).as_singleton()?;
24258                                    if singleton_buffer == buffer {
24259                                        Some((editor, i, item.item_id()))
24260                                    } else {
24261                                        None
24262                                    }
24263                                })?;
24264                            pane.update(cx, |pane, cx| {
24265                                pane.activate_item(pane_item_index, true, true, window, cx);
24266                                if !PreviewTabsSettings::get_global(cx)
24267                                    .enable_preview_from_multibuffer
24268                                {
24269                                    pane.unpreview_item_if_preview(pane_item_id);
24270                                }
24271                            });
24272                            Some(editor)
24273                        })
24274                        .flatten()
24275                        .unwrap_or_else(|| {
24276                            let keep_old_preview = PreviewTabsSettings::get_global(cx)
24277                                .enable_keep_preview_on_code_navigation;
24278                            let allow_new_preview =
24279                                PreviewTabsSettings::get_global(cx).enable_preview_from_multibuffer;
24280                            workspace.open_project_item::<Self>(
24281                                pane.clone(),
24282                                buffer,
24283                                true,
24284                                true,
24285                                keep_old_preview,
24286                                allow_new_preview,
24287                                window,
24288                                cx,
24289                            )
24290                        });
24291
24292                    editor.update(cx, |editor, cx| {
24293                        if has_file && !is_project_file {
24294                            editor.set_read_only(true);
24295                        }
24296                        let autoscroll = match scroll_offset {
24297                            Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
24298                            None => Autoscroll::newest(),
24299                        };
24300                        let nav_history = editor.nav_history.take();
24301                        let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24302                        let Some((&excerpt_id, _, buffer_snapshot)) =
24303                            multibuffer_snapshot.as_singleton()
24304                        else {
24305                            return;
24306                        };
24307                        editor.change_selections(
24308                            SelectionEffects::scroll(autoscroll),
24309                            window,
24310                            cx,
24311                            |s| {
24312                                s.select_ranges(ranges.into_iter().map(|range| {
24313                                    let range = buffer_snapshot.anchor_before(range.start)
24314                                        ..buffer_snapshot.anchor_after(range.end);
24315                                    multibuffer_snapshot
24316                                        .anchor_range_in_excerpt(excerpt_id, range)
24317                                        .unwrap()
24318                                }));
24319                            },
24320                        );
24321                        editor.nav_history = nav_history;
24322                    });
24323                }
24324            })
24325        });
24326    }
24327
24328    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24329        let snapshot = self.buffer.read(cx).read(cx);
24330        let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
24331        Some(
24332            ranges
24333                .iter()
24334                .map(move |range| {
24335                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24336                })
24337                .collect(),
24338        )
24339    }
24340
24341    fn selection_replacement_ranges(
24342        &self,
24343        range: Range<MultiBufferOffsetUtf16>,
24344        cx: &mut App,
24345    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24346        let selections = self
24347            .selections
24348            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24349        let newest_selection = selections
24350            .iter()
24351            .max_by_key(|selection| selection.id)
24352            .unwrap();
24353        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24354        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24355        let snapshot = self.buffer.read(cx).read(cx);
24356        selections
24357            .into_iter()
24358            .map(|mut selection| {
24359                selection.start.0.0 =
24360                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24361                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24362                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24363                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24364            })
24365            .collect()
24366    }
24367
24368    fn report_editor_event(
24369        &self,
24370        reported_event: ReportEditorEvent,
24371        file_extension: Option<String>,
24372        cx: &App,
24373    ) {
24374        if cfg!(any(test, feature = "test-support")) {
24375            return;
24376        }
24377
24378        let Some(project) = &self.project else { return };
24379
24380        // If None, we are in a file without an extension
24381        let file = self
24382            .buffer
24383            .read(cx)
24384            .as_singleton()
24385            .and_then(|b| b.read(cx).file());
24386        let file_extension = file_extension.or(file
24387            .as_ref()
24388            .and_then(|file| Path::new(file.file_name(cx)).extension())
24389            .and_then(|e| e.to_str())
24390            .map(|a| a.to_string()));
24391
24392        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24393            .map(|vim_mode| vim_mode.0)
24394            .unwrap_or(false);
24395
24396        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24397        let copilot_enabled = edit_predictions_provider
24398            == language::language_settings::EditPredictionProvider::Copilot;
24399        let copilot_enabled_for_language = self
24400            .buffer
24401            .read(cx)
24402            .language_settings(cx)
24403            .show_edit_predictions;
24404
24405        let project = project.read(cx);
24406        let event_type = reported_event.event_type();
24407
24408        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24409            telemetry::event!(
24410                event_type,
24411                type = if auto_saved {"autosave"} else {"manual"},
24412                file_extension,
24413                vim_mode,
24414                copilot_enabled,
24415                copilot_enabled_for_language,
24416                edit_predictions_provider,
24417                is_via_ssh = project.is_via_remote_server(),
24418            );
24419        } else {
24420            telemetry::event!(
24421                event_type,
24422                file_extension,
24423                vim_mode,
24424                copilot_enabled,
24425                copilot_enabled_for_language,
24426                edit_predictions_provider,
24427                is_via_ssh = project.is_via_remote_server(),
24428            );
24429        };
24430    }
24431
24432    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24433    /// with each line being an array of {text, highlight} objects.
24434    fn copy_highlight_json(
24435        &mut self,
24436        _: &CopyHighlightJson,
24437        window: &mut Window,
24438        cx: &mut Context<Self>,
24439    ) {
24440        #[derive(Serialize)]
24441        struct Chunk<'a> {
24442            text: String,
24443            highlight: Option<&'a str>,
24444        }
24445
24446        let snapshot = self.buffer.read(cx).snapshot(cx);
24447        let range = self
24448            .selected_text_range(false, window, cx)
24449            .and_then(|selection| {
24450                if selection.range.is_empty() {
24451                    None
24452                } else {
24453                    Some(
24454                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24455                            selection.range.start,
24456                        )))
24457                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24458                                selection.range.end,
24459                            ))),
24460                    )
24461                }
24462            })
24463            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24464
24465        let chunks = snapshot.chunks(range, true);
24466        let mut lines = Vec::new();
24467        let mut line: VecDeque<Chunk> = VecDeque::new();
24468
24469        let Some(style) = self.style.as_ref() else {
24470            return;
24471        };
24472
24473        for chunk in chunks {
24474            let highlight = chunk
24475                .syntax_highlight_id
24476                .and_then(|id| id.name(&style.syntax));
24477            let mut chunk_lines = chunk.text.split('\n').peekable();
24478            while let Some(text) = chunk_lines.next() {
24479                let mut merged_with_last_token = false;
24480                if let Some(last_token) = line.back_mut()
24481                    && last_token.highlight == highlight
24482                {
24483                    last_token.text.push_str(text);
24484                    merged_with_last_token = true;
24485                }
24486
24487                if !merged_with_last_token {
24488                    line.push_back(Chunk {
24489                        text: text.into(),
24490                        highlight,
24491                    });
24492                }
24493
24494                if chunk_lines.peek().is_some() {
24495                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24496                        line.pop_front();
24497                    }
24498                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24499                        line.pop_back();
24500                    }
24501
24502                    lines.push(mem::take(&mut line));
24503                }
24504            }
24505        }
24506
24507        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24508            return;
24509        };
24510        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24511    }
24512
24513    pub fn open_context_menu(
24514        &mut self,
24515        _: &OpenContextMenu,
24516        window: &mut Window,
24517        cx: &mut Context<Self>,
24518    ) {
24519        self.request_autoscroll(Autoscroll::newest(), cx);
24520        let position = self
24521            .selections
24522            .newest_display(&self.display_snapshot(cx))
24523            .start;
24524        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24525    }
24526
24527    pub fn replay_insert_event(
24528        &mut self,
24529        text: &str,
24530        relative_utf16_range: Option<Range<isize>>,
24531        window: &mut Window,
24532        cx: &mut Context<Self>,
24533    ) {
24534        if !self.input_enabled {
24535            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24536            return;
24537        }
24538        if let Some(relative_utf16_range) = relative_utf16_range {
24539            let selections = self
24540                .selections
24541                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24542            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24543                let new_ranges = selections.into_iter().map(|range| {
24544                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24545                        range
24546                            .head()
24547                            .0
24548                            .0
24549                            .saturating_add_signed(relative_utf16_range.start),
24550                    ));
24551                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24552                        range
24553                            .head()
24554                            .0
24555                            .0
24556                            .saturating_add_signed(relative_utf16_range.end),
24557                    ));
24558                    start..end
24559                });
24560                s.select_ranges(new_ranges);
24561            });
24562        }
24563
24564        self.handle_input(text, window, cx);
24565    }
24566
24567    pub fn is_focused(&self, window: &Window) -> bool {
24568        self.focus_handle.is_focused(window)
24569    }
24570
24571    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24572        cx.emit(EditorEvent::Focused);
24573
24574        if let Some(descendant) = self
24575            .last_focused_descendant
24576            .take()
24577            .and_then(|descendant| descendant.upgrade())
24578        {
24579            window.focus(&descendant, cx);
24580        } else {
24581            if let Some(blame) = self.blame.as_ref() {
24582                blame.update(cx, GitBlame::focus)
24583            }
24584
24585            self.blink_manager.update(cx, BlinkManager::enable);
24586            self.show_cursor_names(window, cx);
24587            self.buffer.update(cx, |buffer, cx| {
24588                buffer.finalize_last_transaction(cx);
24589                if self.leader_id.is_none() {
24590                    buffer.set_active_selections(
24591                        &self.selections.disjoint_anchors_arc(),
24592                        self.selections.line_mode(),
24593                        self.cursor_shape,
24594                        cx,
24595                    );
24596                }
24597            });
24598
24599            if let Some(position_map) = self.last_position_map.clone() {
24600                EditorElement::mouse_moved(
24601                    self,
24602                    &MouseMoveEvent {
24603                        position: window.mouse_position(),
24604                        pressed_button: None,
24605                        modifiers: window.modifiers(),
24606                    },
24607                    &position_map,
24608                    None,
24609                    window,
24610                    cx,
24611                );
24612            }
24613        }
24614    }
24615
24616    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24617        cx.emit(EditorEvent::FocusedIn)
24618    }
24619
24620    fn handle_focus_out(
24621        &mut self,
24622        event: FocusOutEvent,
24623        _window: &mut Window,
24624        cx: &mut Context<Self>,
24625    ) {
24626        if event.blurred != self.focus_handle {
24627            self.last_focused_descendant = Some(event.blurred);
24628        }
24629        self.selection_drag_state = SelectionDragState::None;
24630        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24631    }
24632
24633    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24634        self.blink_manager.update(cx, BlinkManager::disable);
24635        self.buffer
24636            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
24637
24638        if let Some(blame) = self.blame.as_ref() {
24639            blame.update(cx, GitBlame::blur)
24640        }
24641        if !self.hover_state.focused(window, cx) {
24642            hide_hover(self, cx);
24643        }
24644        if !self
24645            .context_menu
24646            .borrow()
24647            .as_ref()
24648            .is_some_and(|context_menu| context_menu.focused(window, cx))
24649        {
24650            self.hide_context_menu(window, cx);
24651        }
24652        self.take_active_edit_prediction(cx);
24653        cx.emit(EditorEvent::Blurred);
24654        cx.notify();
24655    }
24656
24657    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24658        let mut pending: String = window
24659            .pending_input_keystrokes()
24660            .into_iter()
24661            .flatten()
24662            .filter_map(|keystroke| keystroke.key_char.clone())
24663            .collect();
24664
24665        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
24666            pending = "".to_string();
24667        }
24668
24669        let existing_pending = self
24670            .text_highlights::<PendingInput>(cx)
24671            .map(|(_, ranges)| ranges.to_vec());
24672        if existing_pending.is_none() && pending.is_empty() {
24673            return;
24674        }
24675        let transaction =
24676            self.transact(window, cx, |this, window, cx| {
24677                let selections = this
24678                    .selections
24679                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
24680                let edits = selections
24681                    .iter()
24682                    .map(|selection| (selection.end..selection.end, pending.clone()));
24683                this.edit(edits, cx);
24684                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24685                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
24686                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
24687                    }));
24688                });
24689                if let Some(existing_ranges) = existing_pending {
24690                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
24691                    this.edit(edits, cx);
24692                }
24693            });
24694
24695        let snapshot = self.snapshot(window, cx);
24696        let ranges = self
24697            .selections
24698            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
24699            .into_iter()
24700            .map(|selection| {
24701                snapshot.buffer_snapshot().anchor_after(selection.end)
24702                    ..snapshot
24703                        .buffer_snapshot()
24704                        .anchor_before(selection.end + pending.len())
24705            })
24706            .collect();
24707
24708        if pending.is_empty() {
24709            self.clear_highlights::<PendingInput>(cx);
24710        } else {
24711            self.highlight_text::<PendingInput>(
24712                ranges,
24713                HighlightStyle {
24714                    underline: Some(UnderlineStyle {
24715                        thickness: px(1.),
24716                        color: None,
24717                        wavy: false,
24718                    }),
24719                    ..Default::default()
24720                },
24721                cx,
24722            );
24723        }
24724
24725        self.ime_transaction = self.ime_transaction.or(transaction);
24726        if let Some(transaction) = self.ime_transaction {
24727            self.buffer.update(cx, |buffer, cx| {
24728                buffer.group_until_transaction(transaction, cx);
24729            });
24730        }
24731
24732        if self.text_highlights::<PendingInput>(cx).is_none() {
24733            self.ime_transaction.take();
24734        }
24735    }
24736
24737    pub fn register_action_renderer(
24738        &mut self,
24739        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
24740    ) -> Subscription {
24741        let id = self.next_editor_action_id.post_inc();
24742        self.editor_actions
24743            .borrow_mut()
24744            .insert(id, Box::new(listener));
24745
24746        let editor_actions = self.editor_actions.clone();
24747        Subscription::new(move || {
24748            editor_actions.borrow_mut().remove(&id);
24749        })
24750    }
24751
24752    pub fn register_action<A: Action>(
24753        &mut self,
24754        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
24755    ) -> Subscription {
24756        let id = self.next_editor_action_id.post_inc();
24757        let listener = Arc::new(listener);
24758        self.editor_actions.borrow_mut().insert(
24759            id,
24760            Box::new(move |_, window, _| {
24761                let listener = listener.clone();
24762                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
24763                    let action = action.downcast_ref().unwrap();
24764                    if phase == DispatchPhase::Bubble {
24765                        listener(action, window, cx)
24766                    }
24767                })
24768            }),
24769        );
24770
24771        let editor_actions = self.editor_actions.clone();
24772        Subscription::new(move || {
24773            editor_actions.borrow_mut().remove(&id);
24774        })
24775    }
24776
24777    pub fn file_header_size(&self) -> u32 {
24778        FILE_HEADER_HEIGHT
24779    }
24780
24781    pub fn restore(
24782        &mut self,
24783        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
24784        window: &mut Window,
24785        cx: &mut Context<Self>,
24786    ) {
24787        self.buffer().update(cx, |multi_buffer, cx| {
24788            for (buffer_id, changes) in revert_changes {
24789                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
24790                    buffer.update(cx, |buffer, cx| {
24791                        buffer.edit(
24792                            changes
24793                                .into_iter()
24794                                .map(|(range, text)| (range, text.to_string())),
24795                            None,
24796                            cx,
24797                        );
24798                    });
24799                }
24800            }
24801        });
24802        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
24803            selections.refresh()
24804        });
24805    }
24806
24807    pub fn to_pixel_point(
24808        &mut self,
24809        source: Anchor,
24810        editor_snapshot: &EditorSnapshot,
24811        window: &mut Window,
24812        cx: &mut App,
24813    ) -> Option<gpui::Point<Pixels>> {
24814        let source_point = source.to_display_point(editor_snapshot);
24815        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
24816    }
24817
24818    pub fn display_to_pixel_point(
24819        &mut self,
24820        source: DisplayPoint,
24821        editor_snapshot: &EditorSnapshot,
24822        window: &mut Window,
24823        cx: &mut App,
24824    ) -> Option<gpui::Point<Pixels>> {
24825        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
24826        let text_layout_details = self.text_layout_details(window, cx);
24827        let scroll_top = text_layout_details
24828            .scroll_anchor
24829            .scroll_position(editor_snapshot)
24830            .y;
24831
24832        if source.row().as_f64() < scroll_top.floor() {
24833            return None;
24834        }
24835        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
24836        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
24837        Some(gpui::Point::new(source_x, source_y))
24838    }
24839
24840    pub fn has_visible_completions_menu(&self) -> bool {
24841        !self.edit_prediction_preview_is_active()
24842            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
24843                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
24844            })
24845    }
24846
24847    pub fn register_addon<T: Addon>(&mut self, instance: T) {
24848        if self.mode.is_minimap() {
24849            return;
24850        }
24851        self.addons
24852            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
24853    }
24854
24855    pub fn unregister_addon<T: Addon>(&mut self) {
24856        self.addons.remove(&std::any::TypeId::of::<T>());
24857    }
24858
24859    pub fn addon<T: Addon>(&self) -> Option<&T> {
24860        let type_id = std::any::TypeId::of::<T>();
24861        self.addons
24862            .get(&type_id)
24863            .and_then(|item| item.to_any().downcast_ref::<T>())
24864    }
24865
24866    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
24867        let type_id = std::any::TypeId::of::<T>();
24868        self.addons
24869            .get_mut(&type_id)
24870            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
24871    }
24872
24873    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
24874        let text_layout_details = self.text_layout_details(window, cx);
24875        let style = &text_layout_details.editor_style;
24876        let font_id = window.text_system().resolve_font(&style.text.font());
24877        let font_size = style.text.font_size.to_pixels(window.rem_size());
24878        let line_height = style.text.line_height_in_pixels(window.rem_size());
24879        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
24880        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
24881
24882        CharacterDimensions {
24883            em_width,
24884            em_advance,
24885            line_height,
24886        }
24887    }
24888
24889    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
24890        self.load_diff_task.clone()
24891    }
24892
24893    fn read_metadata_from_db(
24894        &mut self,
24895        item_id: u64,
24896        workspace_id: WorkspaceId,
24897        window: &mut Window,
24898        cx: &mut Context<Editor>,
24899    ) {
24900        if self.buffer_kind(cx) == ItemBufferKind::Singleton
24901            && !self.mode.is_minimap()
24902            && WorkspaceSettings::get(None, cx).restore_on_startup
24903                != RestoreOnStartupBehavior::EmptyTab
24904        {
24905            let buffer_snapshot = OnceCell::new();
24906
24907            if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
24908                && !folds.is_empty()
24909            {
24910                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
24911                let snapshot_len = snapshot.len().0;
24912
24913                // Helper: search for fingerprint in buffer, return offset if found
24914                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
24915                    // Ensure we start at a character boundary (defensive)
24916                    let search_start = snapshot
24917                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
24918                        .0;
24919                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
24920
24921                    let mut byte_offset = search_start;
24922                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
24923                        if byte_offset > search_end {
24924                            break;
24925                        }
24926                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
24927                            return Some(byte_offset);
24928                        }
24929                        byte_offset += ch.len_utf8();
24930                    }
24931                    None
24932                };
24933
24934                // Track search position to handle duplicate fingerprints correctly.
24935                // Folds are stored in document order, so we advance after each match.
24936                let mut search_start = 0usize;
24937
24938                let valid_folds: Vec<_> = folds
24939                    .into_iter()
24940                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
24941                        // Skip folds without fingerprints (old data before migration)
24942                        let sfp = start_fp?;
24943                        let efp = end_fp?;
24944                        let efp_len = efp.len();
24945
24946                        // Fast path: check if fingerprints match at stored offsets
24947                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
24948                        let start_matches = stored_start < snapshot_len
24949                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
24950                        let efp_check_pos = stored_end.saturating_sub(efp_len);
24951                        let end_matches = efp_check_pos >= stored_start
24952                            && stored_end <= snapshot_len
24953                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
24954
24955                        let (new_start, new_end) = if start_matches && end_matches {
24956                            // Offsets unchanged, use stored values
24957                            (stored_start, stored_end)
24958                        } else if sfp == efp {
24959                            // Short fold: identical fingerprints can only match once per search
24960                            // Use stored fold length to compute new_end
24961                            let new_start = find_fingerprint(&sfp, search_start)?;
24962                            let fold_len = stored_end - stored_start;
24963                            let new_end = new_start + fold_len;
24964                            (new_start, new_end)
24965                        } else {
24966                            // Slow path: search for fingerprints in buffer
24967                            let new_start = find_fingerprint(&sfp, search_start)?;
24968                            // Search for end_fp after start, then add efp_len to get actual fold end
24969                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
24970                            let new_end = efp_pos + efp_len;
24971                            (new_start, new_end)
24972                        };
24973
24974                        // Advance search position for next fold
24975                        search_start = new_end;
24976
24977                        // Validate fold makes sense (end must be after start)
24978                        if new_end <= new_start {
24979                            return None;
24980                        }
24981
24982                        Some(
24983                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
24984                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
24985                        )
24986                    })
24987                    .collect();
24988
24989                if !valid_folds.is_empty() {
24990                    self.fold_ranges(valid_folds, false, window, cx);
24991
24992                    // Migrate folds to current entity_id before workspace cleanup runs.
24993                    // Entity IDs change between sessions, but workspace cleanup deletes
24994                    // old editor rows (cascading to folds) based on current entity IDs.
24995                    let new_editor_id = cx.entity().entity_id().as_u64() as ItemId;
24996                    if new_editor_id != item_id {
24997                        cx.spawn(async move |_, _| {
24998                            DB.migrate_editor_folds(item_id, new_editor_id, workspace_id)
24999                                .await
25000                                .log_err();
25001                        })
25002                        .detach();
25003                    }
25004                }
25005            }
25006
25007            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25008                && !selections.is_empty()
25009            {
25010                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25011                // skip adding the initial selection to selection history
25012                self.selection_history.mode = SelectionHistoryMode::Skipping;
25013                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25014                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25015                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25016                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25017                    }));
25018                });
25019                self.selection_history.mode = SelectionHistoryMode::Normal;
25020            };
25021        }
25022
25023        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25024    }
25025
25026    fn update_lsp_data(
25027        &mut self,
25028        for_buffer: Option<BufferId>,
25029        window: &mut Window,
25030        cx: &mut Context<'_, Self>,
25031    ) {
25032        if let Some(buffer_id) = for_buffer {
25033            self.pull_diagnostics(buffer_id, window, cx);
25034        }
25035        self.refresh_colors_for_visible_range(for_buffer, window, cx);
25036    }
25037
25038    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25039        if self.ignore_lsp_data() {
25040            return;
25041        }
25042        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25043            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25044        }
25045    }
25046
25047    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25048        if self.ignore_lsp_data() {
25049            return;
25050        }
25051
25052        if !self.registered_buffers.contains_key(&buffer_id)
25053            && let Some(project) = self.project.as_ref()
25054        {
25055            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25056                project.update(cx, |project, cx| {
25057                    self.registered_buffers.insert(
25058                        buffer_id,
25059                        project.register_buffer_with_language_servers(&buffer, cx),
25060                    );
25061                });
25062            } else {
25063                self.registered_buffers.remove(&buffer_id);
25064            }
25065        }
25066    }
25067
25068    fn ignore_lsp_data(&self) -> bool {
25069        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
25070        // skip any LSP updates for it.
25071        self.active_diagnostics == ActiveDiagnostic::All || !self.mode().is_full()
25072    }
25073
25074    pub(crate) fn create_style(&self, cx: &App) -> EditorStyle {
25075        let settings = ThemeSettings::get_global(cx);
25076
25077        let mut text_style = match self.mode {
25078            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25079                color: cx.theme().colors().editor_foreground,
25080                font_family: settings.ui_font.family.clone(),
25081                font_features: settings.ui_font.features.clone(),
25082                font_fallbacks: settings.ui_font.fallbacks.clone(),
25083                font_size: rems(0.875).into(),
25084                font_weight: settings.ui_font.weight,
25085                line_height: relative(settings.buffer_line_height.value()),
25086                ..Default::default()
25087            },
25088            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25089                color: cx.theme().colors().editor_foreground,
25090                font_family: settings.buffer_font.family.clone(),
25091                font_features: settings.buffer_font.features.clone(),
25092                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25093                font_size: settings.buffer_font_size(cx).into(),
25094                font_weight: settings.buffer_font.weight,
25095                line_height: relative(settings.buffer_line_height.value()),
25096                ..Default::default()
25097            },
25098        };
25099        if let Some(text_style_refinement) = &self.text_style_refinement {
25100            text_style.refine(text_style_refinement)
25101        }
25102
25103        let background = match self.mode {
25104            EditorMode::SingleLine => cx.theme().system().transparent,
25105            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25106            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25107            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25108        };
25109
25110        EditorStyle {
25111            background,
25112            border: cx.theme().colors().border,
25113            local_player: cx.theme().players().local(),
25114            text: text_style,
25115            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25116            syntax: cx.theme().syntax().clone(),
25117            status: cx.theme().status().clone(),
25118            inlay_hints_style: make_inlay_hints_style(cx),
25119            edit_prediction_styles: make_suggestion_styles(cx),
25120            unnecessary_code_fade: settings.unnecessary_code_fade,
25121            show_underlines: self.diagnostics_enabled(),
25122        }
25123    }
25124    fn breadcrumbs_inner(&self, variant: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
25125        let cursor = self.selections.newest_anchor().head();
25126        let multibuffer = self.buffer().read(cx);
25127        let is_singleton = multibuffer.is_singleton();
25128        let (buffer_id, symbols) = multibuffer
25129            .read(cx)
25130            .symbols_containing(cursor, Some(variant.syntax()))?;
25131        let buffer = multibuffer.buffer(buffer_id)?;
25132
25133        let buffer = buffer.read(cx);
25134        let settings = ThemeSettings::get_global(cx);
25135        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25136        let mut breadcrumbs = if is_singleton {
25137            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25138                buffer
25139                    .snapshot()
25140                    .resolve_file_path(
25141                        self.project
25142                            .as_ref()
25143                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25144                            .unwrap_or_default(),
25145                        cx,
25146                    )
25147                    .unwrap_or_else(|| {
25148                        if multibuffer.is_singleton() {
25149                            multibuffer.title(cx).to_string()
25150                        } else {
25151                            "untitled".to_string()
25152                        }
25153                    })
25154            });
25155            vec![BreadcrumbText {
25156                text,
25157                highlights: None,
25158                font: Some(settings.buffer_font.clone()),
25159            }]
25160        } else {
25161            vec![]
25162        };
25163
25164        breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText {
25165            text: symbol.text,
25166            highlights: Some(symbol.highlight_ranges),
25167            font: Some(settings.buffer_font.clone()),
25168        }));
25169        Some(breadcrumbs)
25170    }
25171}
25172
25173fn edit_for_markdown_paste<'a>(
25174    buffer: &MultiBufferSnapshot,
25175    range: Range<MultiBufferOffset>,
25176    to_insert: &'a str,
25177    url: Option<url::Url>,
25178) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25179    if url.is_none() {
25180        return (range, Cow::Borrowed(to_insert));
25181    };
25182
25183    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25184
25185    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25186        Cow::Borrowed(to_insert)
25187    } else {
25188        Cow::Owned(format!("[{old_text}]({to_insert})"))
25189    };
25190    (range, new_text)
25191}
25192
25193fn process_completion_for_edit(
25194    completion: &Completion,
25195    intent: CompletionIntent,
25196    buffer: &Entity<Buffer>,
25197    cursor_position: &text::Anchor,
25198    cx: &mut Context<Editor>,
25199) -> CompletionEdit {
25200    let buffer = buffer.read(cx);
25201    let buffer_snapshot = buffer.snapshot();
25202    let (snippet, new_text) = if completion.is_snippet() {
25203        let mut snippet_source = completion.new_text.clone();
25204        // Workaround for typescript language server issues so that methods don't expand within
25205        // strings and functions with type expressions. The previous point is used because the query
25206        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25207        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25208        let previous_point = if previous_point.column > 0 {
25209            cursor_position.to_previous_offset(&buffer_snapshot)
25210        } else {
25211            cursor_position.to_offset(&buffer_snapshot)
25212        };
25213        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25214            && scope.prefers_label_for_snippet_in_completion()
25215            && let Some(label) = completion.label()
25216            && matches!(
25217                completion.kind(),
25218                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25219            )
25220        {
25221            snippet_source = label;
25222        }
25223        match Snippet::parse(&snippet_source).log_err() {
25224            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25225            None => (None, completion.new_text.clone()),
25226        }
25227    } else {
25228        (None, completion.new_text.clone())
25229    };
25230
25231    let mut range_to_replace = {
25232        let replace_range = &completion.replace_range;
25233        if let CompletionSource::Lsp {
25234            insert_range: Some(insert_range),
25235            ..
25236        } = &completion.source
25237        {
25238            debug_assert_eq!(
25239                insert_range.start, replace_range.start,
25240                "insert_range and replace_range should start at the same position"
25241            );
25242            debug_assert!(
25243                insert_range
25244                    .start
25245                    .cmp(cursor_position, &buffer_snapshot)
25246                    .is_le(),
25247                "insert_range should start before or at cursor position"
25248            );
25249            debug_assert!(
25250                replace_range
25251                    .start
25252                    .cmp(cursor_position, &buffer_snapshot)
25253                    .is_le(),
25254                "replace_range should start before or at cursor position"
25255            );
25256
25257            let should_replace = match intent {
25258                CompletionIntent::CompleteWithInsert => false,
25259                CompletionIntent::CompleteWithReplace => true,
25260                CompletionIntent::Complete | CompletionIntent::Compose => {
25261                    let insert_mode =
25262                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25263                            .completions
25264                            .lsp_insert_mode;
25265                    match insert_mode {
25266                        LspInsertMode::Insert => false,
25267                        LspInsertMode::Replace => true,
25268                        LspInsertMode::ReplaceSubsequence => {
25269                            let mut text_to_replace = buffer.chars_for_range(
25270                                buffer.anchor_before(replace_range.start)
25271                                    ..buffer.anchor_after(replace_range.end),
25272                            );
25273                            let mut current_needle = text_to_replace.next();
25274                            for haystack_ch in completion.label.text.chars() {
25275                                if let Some(needle_ch) = current_needle
25276                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25277                                {
25278                                    current_needle = text_to_replace.next();
25279                                }
25280                            }
25281                            current_needle.is_none()
25282                        }
25283                        LspInsertMode::ReplaceSuffix => {
25284                            if replace_range
25285                                .end
25286                                .cmp(cursor_position, &buffer_snapshot)
25287                                .is_gt()
25288                            {
25289                                let range_after_cursor = *cursor_position..replace_range.end;
25290                                let text_after_cursor = buffer
25291                                    .text_for_range(
25292                                        buffer.anchor_before(range_after_cursor.start)
25293                                            ..buffer.anchor_after(range_after_cursor.end),
25294                                    )
25295                                    .collect::<String>()
25296                                    .to_ascii_lowercase();
25297                                completion
25298                                    .label
25299                                    .text
25300                                    .to_ascii_lowercase()
25301                                    .ends_with(&text_after_cursor)
25302                            } else {
25303                                true
25304                            }
25305                        }
25306                    }
25307                }
25308            };
25309
25310            if should_replace {
25311                replace_range.clone()
25312            } else {
25313                insert_range.clone()
25314            }
25315        } else {
25316            replace_range.clone()
25317        }
25318    };
25319
25320    if range_to_replace
25321        .end
25322        .cmp(cursor_position, &buffer_snapshot)
25323        .is_lt()
25324    {
25325        range_to_replace.end = *cursor_position;
25326    }
25327
25328    let replace_range = range_to_replace.to_offset(buffer);
25329    CompletionEdit {
25330        new_text,
25331        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25332        snippet,
25333    }
25334}
25335
25336struct CompletionEdit {
25337    new_text: String,
25338    replace_range: Range<BufferOffset>,
25339    snippet: Option<Snippet>,
25340}
25341
25342fn comment_delimiter_for_newline(
25343    start_point: &Point,
25344    buffer: &MultiBufferSnapshot,
25345    language: &LanguageScope,
25346) -> Option<Arc<str>> {
25347    let delimiters = language.line_comment_prefixes();
25348    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25349    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25350
25351    let num_of_whitespaces = snapshot
25352        .chars_for_range(range.clone())
25353        .take_while(|c| c.is_whitespace())
25354        .count();
25355    let comment_candidate = snapshot
25356        .chars_for_range(range.clone())
25357        .skip(num_of_whitespaces)
25358        .take(max_len_of_delimiter)
25359        .collect::<String>();
25360    let (delimiter, trimmed_len) = delimiters
25361        .iter()
25362        .filter_map(|delimiter| {
25363            let prefix = delimiter.trim_end();
25364            if comment_candidate.starts_with(prefix) {
25365                Some((delimiter, prefix.len()))
25366            } else {
25367                None
25368            }
25369        })
25370        .max_by_key(|(_, len)| *len)?;
25371
25372    if let Some(BlockCommentConfig {
25373        start: block_start, ..
25374    }) = language.block_comment()
25375    {
25376        let block_start_trimmed = block_start.trim_end();
25377        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25378            let line_content = snapshot
25379                .chars_for_range(range)
25380                .skip(num_of_whitespaces)
25381                .take(block_start_trimmed.len())
25382                .collect::<String>();
25383
25384            if line_content.starts_with(block_start_trimmed) {
25385                return None;
25386            }
25387        }
25388    }
25389
25390    let cursor_is_placed_after_comment_marker =
25391        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25392    if cursor_is_placed_after_comment_marker {
25393        Some(delimiter.clone())
25394    } else {
25395        None
25396    }
25397}
25398
25399fn documentation_delimiter_for_newline(
25400    start_point: &Point,
25401    buffer: &MultiBufferSnapshot,
25402    language: &LanguageScope,
25403    newline_config: &mut NewlineConfig,
25404) -> Option<Arc<str>> {
25405    let BlockCommentConfig {
25406        start: start_tag,
25407        end: end_tag,
25408        prefix: delimiter,
25409        tab_size: len,
25410    } = language.documentation_comment()?;
25411    let is_within_block_comment = buffer
25412        .language_scope_at(*start_point)
25413        .is_some_and(|scope| scope.override_name() == Some("comment"));
25414    if !is_within_block_comment {
25415        return None;
25416    }
25417
25418    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25419
25420    let num_of_whitespaces = snapshot
25421        .chars_for_range(range.clone())
25422        .take_while(|c| c.is_whitespace())
25423        .count();
25424
25425    // 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.
25426    let column = start_point.column;
25427    let cursor_is_after_start_tag = {
25428        let start_tag_len = start_tag.len();
25429        let start_tag_line = snapshot
25430            .chars_for_range(range.clone())
25431            .skip(num_of_whitespaces)
25432            .take(start_tag_len)
25433            .collect::<String>();
25434        if start_tag_line.starts_with(start_tag.as_ref()) {
25435            num_of_whitespaces + start_tag_len <= column as usize
25436        } else {
25437            false
25438        }
25439    };
25440
25441    let cursor_is_after_delimiter = {
25442        let delimiter_trim = delimiter.trim_end();
25443        let delimiter_line = snapshot
25444            .chars_for_range(range.clone())
25445            .skip(num_of_whitespaces)
25446            .take(delimiter_trim.len())
25447            .collect::<String>();
25448        if delimiter_line.starts_with(delimiter_trim) {
25449            num_of_whitespaces + delimiter_trim.len() <= column as usize
25450        } else {
25451            false
25452        }
25453    };
25454
25455    let mut needs_extra_line = false;
25456    let mut extra_line_additional_indent = IndentSize::spaces(0);
25457
25458    let cursor_is_before_end_tag_if_exists = {
25459        let mut char_position = 0u32;
25460        let mut end_tag_offset = None;
25461
25462        'outer: for chunk in snapshot.text_for_range(range) {
25463            if let Some(byte_pos) = chunk.find(&**end_tag) {
25464                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25465                end_tag_offset = Some(char_position + chars_before_match);
25466                break 'outer;
25467            }
25468            char_position += chunk.chars().count() as u32;
25469        }
25470
25471        if let Some(end_tag_offset) = end_tag_offset {
25472            let cursor_is_before_end_tag = column <= end_tag_offset;
25473            if cursor_is_after_start_tag {
25474                if cursor_is_before_end_tag {
25475                    needs_extra_line = true;
25476                }
25477                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
25478                if cursor_is_at_start_of_end_tag {
25479                    extra_line_additional_indent.len = *len;
25480                }
25481            }
25482            cursor_is_before_end_tag
25483        } else {
25484            true
25485        }
25486    };
25487
25488    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
25489        && cursor_is_before_end_tag_if_exists
25490    {
25491        let additional_indent = if cursor_is_after_start_tag {
25492            IndentSize::spaces(*len)
25493        } else {
25494            IndentSize::spaces(0)
25495        };
25496
25497        *newline_config = NewlineConfig::Newline {
25498            additional_indent,
25499            extra_line_additional_indent: if needs_extra_line {
25500                Some(extra_line_additional_indent)
25501            } else {
25502                None
25503            },
25504            prevent_auto_indent: true,
25505        };
25506        Some(delimiter.clone())
25507    } else {
25508        None
25509    }
25510}
25511
25512const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
25513
25514fn list_delimiter_for_newline(
25515    start_point: &Point,
25516    buffer: &MultiBufferSnapshot,
25517    language: &LanguageScope,
25518    newline_config: &mut NewlineConfig,
25519) -> Option<Arc<str>> {
25520    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25521
25522    let num_of_whitespaces = snapshot
25523        .chars_for_range(range.clone())
25524        .take_while(|c| c.is_whitespace())
25525        .count();
25526
25527    let task_list_entries: Vec<_> = language
25528        .task_list()
25529        .into_iter()
25530        .flat_map(|config| {
25531            config
25532                .prefixes
25533                .iter()
25534                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
25535        })
25536        .collect();
25537    let unordered_list_entries: Vec<_> = language
25538        .unordered_list()
25539        .iter()
25540        .map(|marker| (marker.as_ref(), marker.as_ref()))
25541        .collect();
25542
25543    let all_entries: Vec<_> = task_list_entries
25544        .into_iter()
25545        .chain(unordered_list_entries)
25546        .collect();
25547
25548    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
25549        let candidate: String = snapshot
25550            .chars_for_range(range.clone())
25551            .skip(num_of_whitespaces)
25552            .take(max_prefix_len)
25553            .collect();
25554
25555        if let Some((prefix, continuation)) = all_entries
25556            .iter()
25557            .filter(|(prefix, _)| candidate.starts_with(*prefix))
25558            .max_by_key(|(prefix, _)| prefix.len())
25559        {
25560            let end_of_prefix = num_of_whitespaces + prefix.len();
25561            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25562            let has_content_after_marker = snapshot
25563                .chars_for_range(range)
25564                .skip(end_of_prefix)
25565                .any(|c| !c.is_whitespace());
25566
25567            if has_content_after_marker && cursor_is_after_prefix {
25568                return Some((*continuation).into());
25569            }
25570
25571            if start_point.column as usize == end_of_prefix {
25572                if num_of_whitespaces == 0 {
25573                    *newline_config = NewlineConfig::ClearCurrentLine;
25574                } else {
25575                    *newline_config = NewlineConfig::UnindentCurrentLine {
25576                        continuation: (*continuation).into(),
25577                    };
25578                }
25579            }
25580
25581            return None;
25582        }
25583    }
25584
25585    let candidate: String = snapshot
25586        .chars_for_range(range.clone())
25587        .skip(num_of_whitespaces)
25588        .take(ORDERED_LIST_MAX_MARKER_LEN)
25589        .collect();
25590
25591    for ordered_config in language.ordered_list() {
25592        let regex = match Regex::new(&ordered_config.pattern) {
25593            Ok(r) => r,
25594            Err(_) => continue,
25595        };
25596
25597        if let Some(captures) = regex.captures(&candidate) {
25598            let full_match = captures.get(0)?;
25599            let marker_len = full_match.len();
25600            let end_of_prefix = num_of_whitespaces + marker_len;
25601            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25602
25603            let has_content_after_marker = snapshot
25604                .chars_for_range(range)
25605                .skip(end_of_prefix)
25606                .any(|c| !c.is_whitespace());
25607
25608            if has_content_after_marker && cursor_is_after_prefix {
25609                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
25610                let continuation = ordered_config
25611                    .format
25612                    .replace("{1}", &(number + 1).to_string());
25613                return Some(continuation.into());
25614            }
25615
25616            if start_point.column as usize == end_of_prefix {
25617                let continuation = ordered_config.format.replace("{1}", "1");
25618                if num_of_whitespaces == 0 {
25619                    *newline_config = NewlineConfig::ClearCurrentLine;
25620                } else {
25621                    *newline_config = NewlineConfig::UnindentCurrentLine {
25622                        continuation: continuation.into(),
25623                    };
25624                }
25625            }
25626
25627            return None;
25628        }
25629    }
25630
25631    None
25632}
25633
25634fn is_list_prefix_row(
25635    row: MultiBufferRow,
25636    buffer: &MultiBufferSnapshot,
25637    language: &LanguageScope,
25638) -> bool {
25639    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
25640        return false;
25641    };
25642
25643    let num_of_whitespaces = snapshot
25644        .chars_for_range(range.clone())
25645        .take_while(|c| c.is_whitespace())
25646        .count();
25647
25648    let task_list_prefixes: Vec<_> = language
25649        .task_list()
25650        .into_iter()
25651        .flat_map(|config| {
25652            config
25653                .prefixes
25654                .iter()
25655                .map(|p| p.as_ref())
25656                .collect::<Vec<_>>()
25657        })
25658        .collect();
25659    let unordered_list_markers: Vec<_> = language
25660        .unordered_list()
25661        .iter()
25662        .map(|marker| marker.as_ref())
25663        .collect();
25664    let all_prefixes: Vec<_> = task_list_prefixes
25665        .into_iter()
25666        .chain(unordered_list_markers)
25667        .collect();
25668    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
25669        let candidate: String = snapshot
25670            .chars_for_range(range.clone())
25671            .skip(num_of_whitespaces)
25672            .take(max_prefix_len)
25673            .collect();
25674        if all_prefixes
25675            .iter()
25676            .any(|prefix| candidate.starts_with(*prefix))
25677        {
25678            return true;
25679        }
25680    }
25681
25682    let ordered_list_candidate: String = snapshot
25683        .chars_for_range(range)
25684        .skip(num_of_whitespaces)
25685        .take(ORDERED_LIST_MAX_MARKER_LEN)
25686        .collect();
25687    for ordered_config in language.ordered_list() {
25688        let regex = match Regex::new(&ordered_config.pattern) {
25689            Ok(r) => r,
25690            Err(_) => continue,
25691        };
25692        if let Some(captures) = regex.captures(&ordered_list_candidate) {
25693            return captures.get(0).is_some();
25694        }
25695    }
25696
25697    false
25698}
25699
25700#[derive(Debug)]
25701enum NewlineConfig {
25702    /// Insert newline with optional additional indent and optional extra blank line
25703    Newline {
25704        additional_indent: IndentSize,
25705        extra_line_additional_indent: Option<IndentSize>,
25706        prevent_auto_indent: bool,
25707    },
25708    /// Clear the current line
25709    ClearCurrentLine,
25710    /// Unindent the current line and add continuation
25711    UnindentCurrentLine { continuation: Arc<str> },
25712}
25713
25714impl NewlineConfig {
25715    fn has_extra_line(&self) -> bool {
25716        matches!(
25717            self,
25718            Self::Newline {
25719                extra_line_additional_indent: Some(_),
25720                ..
25721            }
25722        )
25723    }
25724
25725    fn insert_extra_newline_brackets(
25726        buffer: &MultiBufferSnapshot,
25727        range: Range<MultiBufferOffset>,
25728        language: &language::LanguageScope,
25729    ) -> bool {
25730        let leading_whitespace_len = buffer
25731            .reversed_chars_at(range.start)
25732            .take_while(|c| c.is_whitespace() && *c != '\n')
25733            .map(|c| c.len_utf8())
25734            .sum::<usize>();
25735        let trailing_whitespace_len = buffer
25736            .chars_at(range.end)
25737            .take_while(|c| c.is_whitespace() && *c != '\n')
25738            .map(|c| c.len_utf8())
25739            .sum::<usize>();
25740        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
25741
25742        language.brackets().any(|(pair, enabled)| {
25743            let pair_start = pair.start.trim_end();
25744            let pair_end = pair.end.trim_start();
25745
25746            enabled
25747                && pair.newline
25748                && buffer.contains_str_at(range.end, pair_end)
25749                && buffer.contains_str_at(
25750                    range.start.saturating_sub_usize(pair_start.len()),
25751                    pair_start,
25752                )
25753        })
25754    }
25755
25756    fn insert_extra_newline_tree_sitter(
25757        buffer: &MultiBufferSnapshot,
25758        range: Range<MultiBufferOffset>,
25759    ) -> bool {
25760        let (buffer, range) = match buffer
25761            .range_to_buffer_ranges(range.start..=range.end)
25762            .as_slice()
25763        {
25764            [(buffer, range, _)] => (*buffer, range.clone()),
25765            _ => return false,
25766        };
25767        let pair = {
25768            let mut result: Option<BracketMatch<usize>> = None;
25769
25770            for pair in buffer
25771                .all_bracket_ranges(range.start.0..range.end.0)
25772                .filter(move |pair| {
25773                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
25774                })
25775            {
25776                let len = pair.close_range.end - pair.open_range.start;
25777
25778                if let Some(existing) = &result {
25779                    let existing_len = existing.close_range.end - existing.open_range.start;
25780                    if len > existing_len {
25781                        continue;
25782                    }
25783                }
25784
25785                result = Some(pair);
25786            }
25787
25788            result
25789        };
25790        let Some(pair) = pair else {
25791            return false;
25792        };
25793        pair.newline_only
25794            && buffer
25795                .chars_for_range(pair.open_range.end..range.start.0)
25796                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
25797                .all(|c| c.is_whitespace() && c != '\n')
25798    }
25799}
25800
25801fn update_uncommitted_diff_for_buffer(
25802    editor: Entity<Editor>,
25803    project: &Entity<Project>,
25804    buffers: impl IntoIterator<Item = Entity<Buffer>>,
25805    buffer: Entity<MultiBuffer>,
25806    cx: &mut App,
25807) -> Task<()> {
25808    let mut tasks = Vec::new();
25809    project.update(cx, |project, cx| {
25810        for buffer in buffers {
25811            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
25812                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
25813            }
25814        }
25815    });
25816    cx.spawn(async move |cx| {
25817        let diffs = future::join_all(tasks).await;
25818        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
25819            return;
25820        }
25821
25822        buffer.update(cx, |buffer, cx| {
25823            for diff in diffs.into_iter().flatten() {
25824                buffer.add_diff(diff, cx);
25825            }
25826        });
25827    })
25828}
25829
25830fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
25831    let tab_size = tab_size.get() as usize;
25832    let mut width = offset;
25833
25834    for ch in text.chars() {
25835        width += if ch == '\t' {
25836            tab_size - (width % tab_size)
25837        } else {
25838            1
25839        };
25840    }
25841
25842    width - offset
25843}
25844
25845#[cfg(test)]
25846mod tests {
25847    use super::*;
25848
25849    #[test]
25850    fn test_string_size_with_expanded_tabs() {
25851        let nz = |val| NonZeroU32::new(val).unwrap();
25852        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
25853        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
25854        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
25855        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
25856        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
25857        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
25858        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
25859        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
25860    }
25861}
25862
25863/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
25864struct WordBreakingTokenizer<'a> {
25865    input: &'a str,
25866}
25867
25868impl<'a> WordBreakingTokenizer<'a> {
25869    fn new(input: &'a str) -> Self {
25870        Self { input }
25871    }
25872}
25873
25874fn is_char_ideographic(ch: char) -> bool {
25875    use unicode_script::Script::*;
25876    use unicode_script::UnicodeScript;
25877    matches!(ch.script(), Han | Tangut | Yi)
25878}
25879
25880fn is_grapheme_ideographic(text: &str) -> bool {
25881    text.chars().any(is_char_ideographic)
25882}
25883
25884fn is_grapheme_whitespace(text: &str) -> bool {
25885    text.chars().any(|x| x.is_whitespace())
25886}
25887
25888fn should_stay_with_preceding_ideograph(text: &str) -> bool {
25889    text.chars()
25890        .next()
25891        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
25892}
25893
25894#[derive(PartialEq, Eq, Debug, Clone, Copy)]
25895enum WordBreakToken<'a> {
25896    Word { token: &'a str, grapheme_len: usize },
25897    InlineWhitespace { token: &'a str, grapheme_len: usize },
25898    Newline,
25899}
25900
25901impl<'a> Iterator for WordBreakingTokenizer<'a> {
25902    /// Yields a span, the count of graphemes in the token, and whether it was
25903    /// whitespace. Note that it also breaks at word boundaries.
25904    type Item = WordBreakToken<'a>;
25905
25906    fn next(&mut self) -> Option<Self::Item> {
25907        use unicode_segmentation::UnicodeSegmentation;
25908        if self.input.is_empty() {
25909            return None;
25910        }
25911
25912        let mut iter = self.input.graphemes(true).peekable();
25913        let mut offset = 0;
25914        let mut grapheme_len = 0;
25915        if let Some(first_grapheme) = iter.next() {
25916            let is_newline = first_grapheme == "\n";
25917            let is_whitespace = is_grapheme_whitespace(first_grapheme);
25918            offset += first_grapheme.len();
25919            grapheme_len += 1;
25920            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
25921                if let Some(grapheme) = iter.peek().copied()
25922                    && should_stay_with_preceding_ideograph(grapheme)
25923                {
25924                    offset += grapheme.len();
25925                    grapheme_len += 1;
25926                }
25927            } else {
25928                let mut words = self.input[offset..].split_word_bound_indices().peekable();
25929                let mut next_word_bound = words.peek().copied();
25930                if next_word_bound.is_some_and(|(i, _)| i == 0) {
25931                    next_word_bound = words.next();
25932                }
25933                while let Some(grapheme) = iter.peek().copied() {
25934                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
25935                        break;
25936                    };
25937                    if is_grapheme_whitespace(grapheme) != is_whitespace
25938                        || (grapheme == "\n") != is_newline
25939                    {
25940                        break;
25941                    };
25942                    offset += grapheme.len();
25943                    grapheme_len += 1;
25944                    iter.next();
25945                }
25946            }
25947            let token = &self.input[..offset];
25948            self.input = &self.input[offset..];
25949            if token == "\n" {
25950                Some(WordBreakToken::Newline)
25951            } else if is_whitespace {
25952                Some(WordBreakToken::InlineWhitespace {
25953                    token,
25954                    grapheme_len,
25955                })
25956            } else {
25957                Some(WordBreakToken::Word {
25958                    token,
25959                    grapheme_len,
25960                })
25961            }
25962        } else {
25963            None
25964        }
25965    }
25966}
25967
25968#[test]
25969fn test_word_breaking_tokenizer() {
25970    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
25971        ("", &[]),
25972        ("  ", &[whitespace("  ", 2)]),
25973        ("Ʒ", &[word("Ʒ", 1)]),
25974        ("Ǽ", &[word("Ǽ", 1)]),
25975        ("", &[word("", 1)]),
25976        ("⋑⋑", &[word("⋑⋑", 2)]),
25977        (
25978            "原理,进而",
25979            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
25980        ),
25981        (
25982            "hello world",
25983            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
25984        ),
25985        (
25986            "hello, world",
25987            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
25988        ),
25989        (
25990            "  hello world",
25991            &[
25992                whitespace("  ", 2),
25993                word("hello", 5),
25994                whitespace(" ", 1),
25995                word("world", 5),
25996            ],
25997        ),
25998        (
25999            "这是什么 \n 钢笔",
26000            &[
26001                word("", 1),
26002                word("", 1),
26003                word("", 1),
26004                word("", 1),
26005                whitespace(" ", 1),
26006                newline(),
26007                whitespace(" ", 1),
26008                word("", 1),
26009                word("", 1),
26010            ],
26011        ),
26012        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26013    ];
26014
26015    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26016        WordBreakToken::Word {
26017            token,
26018            grapheme_len,
26019        }
26020    }
26021
26022    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26023        WordBreakToken::InlineWhitespace {
26024            token,
26025            grapheme_len,
26026        }
26027    }
26028
26029    fn newline() -> WordBreakToken<'static> {
26030        WordBreakToken::Newline
26031    }
26032
26033    for (input, result) in tests {
26034        assert_eq!(
26035            WordBreakingTokenizer::new(input)
26036                .collect::<Vec<_>>()
26037                .as_slice(),
26038            *result,
26039        );
26040    }
26041}
26042
26043fn wrap_with_prefix(
26044    first_line_prefix: String,
26045    subsequent_lines_prefix: String,
26046    unwrapped_text: String,
26047    wrap_column: usize,
26048    tab_size: NonZeroU32,
26049    preserve_existing_whitespace: bool,
26050) -> String {
26051    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26052    let subsequent_lines_prefix_len =
26053        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26054    let mut wrapped_text = String::new();
26055    let mut current_line = first_line_prefix;
26056    let mut is_first_line = true;
26057
26058    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26059    let mut current_line_len = first_line_prefix_len;
26060    let mut in_whitespace = false;
26061    for token in tokenizer {
26062        let have_preceding_whitespace = in_whitespace;
26063        match token {
26064            WordBreakToken::Word {
26065                token,
26066                grapheme_len,
26067            } => {
26068                in_whitespace = false;
26069                let current_prefix_len = if is_first_line {
26070                    first_line_prefix_len
26071                } else {
26072                    subsequent_lines_prefix_len
26073                };
26074                if current_line_len + grapheme_len > wrap_column
26075                    && current_line_len != current_prefix_len
26076                {
26077                    wrapped_text.push_str(current_line.trim_end());
26078                    wrapped_text.push('\n');
26079                    is_first_line = false;
26080                    current_line = subsequent_lines_prefix.clone();
26081                    current_line_len = subsequent_lines_prefix_len;
26082                }
26083                current_line.push_str(token);
26084                current_line_len += grapheme_len;
26085            }
26086            WordBreakToken::InlineWhitespace {
26087                mut token,
26088                mut grapheme_len,
26089            } => {
26090                in_whitespace = true;
26091                if have_preceding_whitespace && !preserve_existing_whitespace {
26092                    continue;
26093                }
26094                if !preserve_existing_whitespace {
26095                    // Keep a single whitespace grapheme as-is
26096                    if let Some(first) =
26097                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26098                    {
26099                        token = first;
26100                    } else {
26101                        token = " ";
26102                    }
26103                    grapheme_len = 1;
26104                }
26105                let current_prefix_len = if is_first_line {
26106                    first_line_prefix_len
26107                } else {
26108                    subsequent_lines_prefix_len
26109                };
26110                if current_line_len + grapheme_len > wrap_column {
26111                    wrapped_text.push_str(current_line.trim_end());
26112                    wrapped_text.push('\n');
26113                    is_first_line = false;
26114                    current_line = subsequent_lines_prefix.clone();
26115                    current_line_len = subsequent_lines_prefix_len;
26116                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26117                    current_line.push_str(token);
26118                    current_line_len += grapheme_len;
26119                }
26120            }
26121            WordBreakToken::Newline => {
26122                in_whitespace = true;
26123                let current_prefix_len = if is_first_line {
26124                    first_line_prefix_len
26125                } else {
26126                    subsequent_lines_prefix_len
26127                };
26128                if preserve_existing_whitespace {
26129                    wrapped_text.push_str(current_line.trim_end());
26130                    wrapped_text.push('\n');
26131                    is_first_line = false;
26132                    current_line = subsequent_lines_prefix.clone();
26133                    current_line_len = subsequent_lines_prefix_len;
26134                } else if have_preceding_whitespace {
26135                    continue;
26136                } else if current_line_len + 1 > wrap_column
26137                    && current_line_len != current_prefix_len
26138                {
26139                    wrapped_text.push_str(current_line.trim_end());
26140                    wrapped_text.push('\n');
26141                    is_first_line = false;
26142                    current_line = subsequent_lines_prefix.clone();
26143                    current_line_len = subsequent_lines_prefix_len;
26144                } else if current_line_len != current_prefix_len {
26145                    current_line.push(' ');
26146                    current_line_len += 1;
26147                }
26148            }
26149        }
26150    }
26151
26152    if !current_line.is_empty() {
26153        wrapped_text.push_str(&current_line);
26154    }
26155    wrapped_text
26156}
26157
26158#[test]
26159fn test_wrap_with_prefix() {
26160    assert_eq!(
26161        wrap_with_prefix(
26162            "# ".to_string(),
26163            "# ".to_string(),
26164            "abcdefg".to_string(),
26165            4,
26166            NonZeroU32::new(4).unwrap(),
26167            false,
26168        ),
26169        "# abcdefg"
26170    );
26171    assert_eq!(
26172        wrap_with_prefix(
26173            "".to_string(),
26174            "".to_string(),
26175            "\thello world".to_string(),
26176            8,
26177            NonZeroU32::new(4).unwrap(),
26178            false,
26179        ),
26180        "hello\nworld"
26181    );
26182    assert_eq!(
26183        wrap_with_prefix(
26184            "// ".to_string(),
26185            "// ".to_string(),
26186            "xx \nyy zz aa bb cc".to_string(),
26187            12,
26188            NonZeroU32::new(4).unwrap(),
26189            false,
26190        ),
26191        "// xx yy zz\n// aa bb cc"
26192    );
26193    assert_eq!(
26194        wrap_with_prefix(
26195            String::new(),
26196            String::new(),
26197            "这是什么 \n 钢笔".to_string(),
26198            3,
26199            NonZeroU32::new(4).unwrap(),
26200            false,
26201        ),
26202        "这是什\n么 钢\n"
26203    );
26204    assert_eq!(
26205        wrap_with_prefix(
26206            String::new(),
26207            String::new(),
26208            format!("foo{}bar", '\u{2009}'), // thin space
26209            80,
26210            NonZeroU32::new(4).unwrap(),
26211            false,
26212        ),
26213        format!("foo{}bar", '\u{2009}')
26214    );
26215}
26216
26217pub trait CollaborationHub {
26218    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26219    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26220    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26221}
26222
26223impl CollaborationHub for Entity<Project> {
26224    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26225        self.read(cx).collaborators()
26226    }
26227
26228    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26229        self.read(cx).user_store().read(cx).participant_indices()
26230    }
26231
26232    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26233        let this = self.read(cx);
26234        let user_ids = this.collaborators().values().map(|c| c.user_id);
26235        this.user_store().read(cx).participant_names(user_ids, cx)
26236    }
26237}
26238
26239pub trait SemanticsProvider {
26240    fn hover(
26241        &self,
26242        buffer: &Entity<Buffer>,
26243        position: text::Anchor,
26244        cx: &mut App,
26245    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26246
26247    fn inline_values(
26248        &self,
26249        buffer_handle: Entity<Buffer>,
26250        range: Range<text::Anchor>,
26251        cx: &mut App,
26252    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26253
26254    fn applicable_inlay_chunks(
26255        &self,
26256        buffer: &Entity<Buffer>,
26257        ranges: &[Range<text::Anchor>],
26258        cx: &mut App,
26259    ) -> Vec<Range<BufferRow>>;
26260
26261    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26262
26263    fn inlay_hints(
26264        &self,
26265        invalidate: InvalidationStrategy,
26266        buffer: Entity<Buffer>,
26267        ranges: Vec<Range<text::Anchor>>,
26268        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26269        cx: &mut App,
26270    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26271
26272    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26273
26274    fn document_highlights(
26275        &self,
26276        buffer: &Entity<Buffer>,
26277        position: text::Anchor,
26278        cx: &mut App,
26279    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26280
26281    fn definitions(
26282        &self,
26283        buffer: &Entity<Buffer>,
26284        position: text::Anchor,
26285        kind: GotoDefinitionKind,
26286        cx: &mut App,
26287    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26288
26289    fn range_for_rename(
26290        &self,
26291        buffer: &Entity<Buffer>,
26292        position: text::Anchor,
26293        cx: &mut App,
26294    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26295
26296    fn perform_rename(
26297        &self,
26298        buffer: &Entity<Buffer>,
26299        position: text::Anchor,
26300        new_name: String,
26301        cx: &mut App,
26302    ) -> Option<Task<Result<ProjectTransaction>>>;
26303}
26304
26305pub trait CompletionProvider {
26306    fn completions(
26307        &self,
26308        excerpt_id: ExcerptId,
26309        buffer: &Entity<Buffer>,
26310        buffer_position: text::Anchor,
26311        trigger: CompletionContext,
26312        window: &mut Window,
26313        cx: &mut Context<Editor>,
26314    ) -> Task<Result<Vec<CompletionResponse>>>;
26315
26316    fn resolve_completions(
26317        &self,
26318        _buffer: Entity<Buffer>,
26319        _completion_indices: Vec<usize>,
26320        _completions: Rc<RefCell<Box<[Completion]>>>,
26321        _cx: &mut Context<Editor>,
26322    ) -> Task<Result<bool>> {
26323        Task::ready(Ok(false))
26324    }
26325
26326    fn apply_additional_edits_for_completion(
26327        &self,
26328        _buffer: Entity<Buffer>,
26329        _completions: Rc<RefCell<Box<[Completion]>>>,
26330        _completion_index: usize,
26331        _push_to_history: bool,
26332        _cx: &mut Context<Editor>,
26333    ) -> Task<Result<Option<language::Transaction>>> {
26334        Task::ready(Ok(None))
26335    }
26336
26337    fn is_completion_trigger(
26338        &self,
26339        buffer: &Entity<Buffer>,
26340        position: language::Anchor,
26341        text: &str,
26342        trigger_in_words: bool,
26343        cx: &mut Context<Editor>,
26344    ) -> bool;
26345
26346    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26347
26348    fn sort_completions(&self) -> bool {
26349        true
26350    }
26351
26352    fn filter_completions(&self) -> bool {
26353        true
26354    }
26355
26356    fn show_snippets(&self) -> bool {
26357        false
26358    }
26359}
26360
26361pub trait CodeActionProvider {
26362    fn id(&self) -> Arc<str>;
26363
26364    fn code_actions(
26365        &self,
26366        buffer: &Entity<Buffer>,
26367        range: Range<text::Anchor>,
26368        window: &mut Window,
26369        cx: &mut App,
26370    ) -> Task<Result<Vec<CodeAction>>>;
26371
26372    fn apply_code_action(
26373        &self,
26374        buffer_handle: Entity<Buffer>,
26375        action: CodeAction,
26376        excerpt_id: ExcerptId,
26377        push_to_history: bool,
26378        window: &mut Window,
26379        cx: &mut App,
26380    ) -> Task<Result<ProjectTransaction>>;
26381}
26382
26383impl CodeActionProvider for Entity<Project> {
26384    fn id(&self) -> Arc<str> {
26385        "project".into()
26386    }
26387
26388    fn code_actions(
26389        &self,
26390        buffer: &Entity<Buffer>,
26391        range: Range<text::Anchor>,
26392        _window: &mut Window,
26393        cx: &mut App,
26394    ) -> Task<Result<Vec<CodeAction>>> {
26395        self.update(cx, |project, cx| {
26396            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26397            let code_actions = project.code_actions(buffer, range, None, cx);
26398            cx.background_spawn(async move {
26399                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26400                Ok(code_lens_actions
26401                    .context("code lens fetch")?
26402                    .into_iter()
26403                    .flatten()
26404                    .chain(
26405                        code_actions
26406                            .context("code action fetch")?
26407                            .into_iter()
26408                            .flatten(),
26409                    )
26410                    .collect())
26411            })
26412        })
26413    }
26414
26415    fn apply_code_action(
26416        &self,
26417        buffer_handle: Entity<Buffer>,
26418        action: CodeAction,
26419        _excerpt_id: ExcerptId,
26420        push_to_history: bool,
26421        _window: &mut Window,
26422        cx: &mut App,
26423    ) -> Task<Result<ProjectTransaction>> {
26424        self.update(cx, |project, cx| {
26425            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26426        })
26427    }
26428}
26429
26430fn snippet_completions(
26431    project: &Project,
26432    buffer: &Entity<Buffer>,
26433    buffer_anchor: text::Anchor,
26434    classifier: CharClassifier,
26435    cx: &mut App,
26436) -> Task<Result<CompletionResponse>> {
26437    let languages = buffer.read(cx).languages_at(buffer_anchor);
26438    let snippet_store = project.snippets().read(cx);
26439
26440    let scopes: Vec<_> = languages
26441        .iter()
26442        .filter_map(|language| {
26443            let language_name = language.lsp_id();
26444            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26445
26446            if snippets.is_empty() {
26447                None
26448            } else {
26449                Some((language.default_scope(), snippets))
26450            }
26451        })
26452        .collect();
26453
26454    if scopes.is_empty() {
26455        return Task::ready(Ok(CompletionResponse {
26456            completions: vec![],
26457            display_options: CompletionDisplayOptions::default(),
26458            is_incomplete: false,
26459        }));
26460    }
26461
26462    let snapshot = buffer.read(cx).text_snapshot();
26463    let executor = cx.background_executor().clone();
26464
26465    cx.background_spawn(async move {
26466        let is_word_char = |c| classifier.is_word(c);
26467
26468        let mut is_incomplete = false;
26469        let mut completions: Vec<Completion> = Vec::new();
26470
26471        const MAX_PREFIX_LEN: usize = 128;
26472        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
26473        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
26474        let window_start = snapshot.clip_offset(window_start, Bias::Left);
26475
26476        let max_buffer_window: String = snapshot
26477            .text_for_range(window_start..buffer_offset)
26478            .collect();
26479
26480        if max_buffer_window.is_empty() {
26481            return Ok(CompletionResponse {
26482                completions: vec![],
26483                display_options: CompletionDisplayOptions::default(),
26484                is_incomplete: true,
26485            });
26486        }
26487
26488        for (_scope, snippets) in scopes.into_iter() {
26489            // Sort snippets by word count to match longer snippet prefixes first.
26490            let mut sorted_snippet_candidates = snippets
26491                .iter()
26492                .enumerate()
26493                .flat_map(|(snippet_ix, snippet)| {
26494                    snippet
26495                        .prefix
26496                        .iter()
26497                        .enumerate()
26498                        .map(move |(prefix_ix, prefix)| {
26499                            let word_count =
26500                                snippet_candidate_suffixes(prefix, is_word_char).count();
26501                            ((snippet_ix, prefix_ix), prefix, word_count)
26502                        })
26503                })
26504                .collect_vec();
26505            sorted_snippet_candidates
26506                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
26507
26508            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
26509
26510            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, is_word_char)
26511                .take(
26512                    sorted_snippet_candidates
26513                        .first()
26514                        .map(|(_, _, word_count)| *word_count)
26515                        .unwrap_or_default(),
26516                )
26517                .collect_vec();
26518
26519            const MAX_RESULTS: usize = 100;
26520            // Each match also remembers how many characters from the buffer it consumed
26521            let mut matches: Vec<(StringMatch, usize)> = vec![];
26522
26523            let mut snippet_list_cutoff_index = 0;
26524            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
26525                let word_count = buffer_index + 1;
26526                // Increase `snippet_list_cutoff_index` until we have all of the
26527                // snippets with sufficiently many words.
26528                while sorted_snippet_candidates
26529                    .get(snippet_list_cutoff_index)
26530                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
26531                        *snippet_word_count >= word_count
26532                    })
26533                {
26534                    snippet_list_cutoff_index += 1;
26535                }
26536
26537                // Take only the candidates with at least `word_count` many words
26538                let snippet_candidates_at_word_len =
26539                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
26540
26541                let candidates = snippet_candidates_at_word_len
26542                    .iter()
26543                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
26544                    .enumerate() // index in `sorted_snippet_candidates`
26545                    // First char must match
26546                    .filter(|(_ix, prefix)| {
26547                        itertools::equal(
26548                            prefix
26549                                .chars()
26550                                .next()
26551                                .into_iter()
26552                                .flat_map(|c| c.to_lowercase()),
26553                            buffer_window
26554                                .chars()
26555                                .next()
26556                                .into_iter()
26557                                .flat_map(|c| c.to_lowercase()),
26558                        )
26559                    })
26560                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
26561                    .collect::<Vec<StringMatchCandidate>>();
26562
26563                matches.extend(
26564                    fuzzy::match_strings(
26565                        &candidates,
26566                        &buffer_window,
26567                        buffer_window.chars().any(|c| c.is_uppercase()),
26568                        true,
26569                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
26570                        &Default::default(),
26571                        executor.clone(),
26572                    )
26573                    .await
26574                    .into_iter()
26575                    .map(|string_match| (string_match, buffer_window.len())),
26576                );
26577
26578                if matches.len() >= MAX_RESULTS {
26579                    break;
26580                }
26581            }
26582
26583            let to_lsp = |point: &text::Anchor| {
26584                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
26585                point_to_lsp(end)
26586            };
26587            let lsp_end = to_lsp(&buffer_anchor);
26588
26589            if matches.len() >= MAX_RESULTS {
26590                is_incomplete = true;
26591            }
26592
26593            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
26594                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
26595                    sorted_snippet_candidates[string_match.candidate_id];
26596                let snippet = &snippets[snippet_index];
26597                let start = buffer_offset - buffer_window_len;
26598                let start = snapshot.anchor_before(start);
26599                let range = start..buffer_anchor;
26600                let lsp_start = to_lsp(&start);
26601                let lsp_range = lsp::Range {
26602                    start: lsp_start,
26603                    end: lsp_end,
26604                };
26605                Completion {
26606                    replace_range: range,
26607                    new_text: snippet.body.clone(),
26608                    source: CompletionSource::Lsp {
26609                        insert_range: None,
26610                        server_id: LanguageServerId(usize::MAX),
26611                        resolved: true,
26612                        lsp_completion: Box::new(lsp::CompletionItem {
26613                            label: snippet.prefix.first().unwrap().clone(),
26614                            kind: Some(CompletionItemKind::SNIPPET),
26615                            label_details: snippet.description.as_ref().map(|description| {
26616                                lsp::CompletionItemLabelDetails {
26617                                    detail: Some(description.clone()),
26618                                    description: None,
26619                                }
26620                            }),
26621                            insert_text_format: Some(InsertTextFormat::SNIPPET),
26622                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
26623                                lsp::InsertReplaceEdit {
26624                                    new_text: snippet.body.clone(),
26625                                    insert: lsp_range,
26626                                    replace: lsp_range,
26627                                },
26628                            )),
26629                            filter_text: Some(snippet.body.clone()),
26630                            sort_text: Some(char::MAX.to_string()),
26631                            ..lsp::CompletionItem::default()
26632                        }),
26633                        lsp_defaults: None,
26634                    },
26635                    label: CodeLabel {
26636                        text: matching_prefix.clone(),
26637                        runs: Vec::new(),
26638                        filter_range: 0..matching_prefix.len(),
26639                    },
26640                    icon_path: None,
26641                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
26642                        single_line: snippet.name.clone().into(),
26643                        plain_text: snippet
26644                            .description
26645                            .clone()
26646                            .map(|description| description.into()),
26647                    }),
26648                    insert_text_mode: None,
26649                    confirm: None,
26650                    match_start: Some(start),
26651                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
26652                }
26653            }));
26654        }
26655
26656        Ok(CompletionResponse {
26657            completions,
26658            display_options: CompletionDisplayOptions::default(),
26659            is_incomplete,
26660        })
26661    })
26662}
26663
26664impl CompletionProvider for Entity<Project> {
26665    fn completions(
26666        &self,
26667        _excerpt_id: ExcerptId,
26668        buffer: &Entity<Buffer>,
26669        buffer_position: text::Anchor,
26670        options: CompletionContext,
26671        _window: &mut Window,
26672        cx: &mut Context<Editor>,
26673    ) -> Task<Result<Vec<CompletionResponse>>> {
26674        self.update(cx, |project, cx| {
26675            let task = project.completions(buffer, buffer_position, options, cx);
26676            cx.background_spawn(task)
26677        })
26678    }
26679
26680    fn resolve_completions(
26681        &self,
26682        buffer: Entity<Buffer>,
26683        completion_indices: Vec<usize>,
26684        completions: Rc<RefCell<Box<[Completion]>>>,
26685        cx: &mut Context<Editor>,
26686    ) -> Task<Result<bool>> {
26687        self.update(cx, |project, cx| {
26688            project.lsp_store().update(cx, |lsp_store, cx| {
26689                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
26690            })
26691        })
26692    }
26693
26694    fn apply_additional_edits_for_completion(
26695        &self,
26696        buffer: Entity<Buffer>,
26697        completions: Rc<RefCell<Box<[Completion]>>>,
26698        completion_index: usize,
26699        push_to_history: bool,
26700        cx: &mut Context<Editor>,
26701    ) -> Task<Result<Option<language::Transaction>>> {
26702        self.update(cx, |project, cx| {
26703            project.lsp_store().update(cx, |lsp_store, cx| {
26704                lsp_store.apply_additional_edits_for_completion(
26705                    buffer,
26706                    completions,
26707                    completion_index,
26708                    push_to_history,
26709                    cx,
26710                )
26711            })
26712        })
26713    }
26714
26715    fn is_completion_trigger(
26716        &self,
26717        buffer: &Entity<Buffer>,
26718        position: language::Anchor,
26719        text: &str,
26720        trigger_in_words: bool,
26721        cx: &mut Context<Editor>,
26722    ) -> bool {
26723        let mut chars = text.chars();
26724        let char = if let Some(char) = chars.next() {
26725            char
26726        } else {
26727            return false;
26728        };
26729        if chars.next().is_some() {
26730            return false;
26731        }
26732
26733        let buffer = buffer.read(cx);
26734        let snapshot = buffer.snapshot();
26735        let classifier = snapshot
26736            .char_classifier_at(position)
26737            .scope_context(Some(CharScopeContext::Completion));
26738        if trigger_in_words && classifier.is_word(char) {
26739            return true;
26740        }
26741
26742        buffer.completion_triggers().contains(text)
26743    }
26744
26745    fn show_snippets(&self) -> bool {
26746        true
26747    }
26748}
26749
26750impl SemanticsProvider for Entity<Project> {
26751    fn hover(
26752        &self,
26753        buffer: &Entity<Buffer>,
26754        position: text::Anchor,
26755        cx: &mut App,
26756    ) -> Option<Task<Option<Vec<project::Hover>>>> {
26757        Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
26758    }
26759
26760    fn document_highlights(
26761        &self,
26762        buffer: &Entity<Buffer>,
26763        position: text::Anchor,
26764        cx: &mut App,
26765    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
26766        Some(self.update(cx, |project, cx| {
26767            project.document_highlights(buffer, position, cx)
26768        }))
26769    }
26770
26771    fn definitions(
26772        &self,
26773        buffer: &Entity<Buffer>,
26774        position: text::Anchor,
26775        kind: GotoDefinitionKind,
26776        cx: &mut App,
26777    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
26778        Some(self.update(cx, |project, cx| match kind {
26779            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
26780            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
26781            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
26782            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
26783        }))
26784    }
26785
26786    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
26787        self.update(cx, |project, cx| {
26788            if project
26789                .active_debug_session(cx)
26790                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
26791            {
26792                return true;
26793            }
26794
26795            buffer.update(cx, |buffer, cx| {
26796                project.any_language_server_supports_inlay_hints(buffer, cx)
26797            })
26798        })
26799    }
26800
26801    fn inline_values(
26802        &self,
26803        buffer_handle: Entity<Buffer>,
26804        range: Range<text::Anchor>,
26805        cx: &mut App,
26806    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
26807        self.update(cx, |project, cx| {
26808            let (session, active_stack_frame) = project.active_debug_session(cx)?;
26809
26810            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
26811        })
26812    }
26813
26814    fn applicable_inlay_chunks(
26815        &self,
26816        buffer: &Entity<Buffer>,
26817        ranges: &[Range<text::Anchor>],
26818        cx: &mut App,
26819    ) -> Vec<Range<BufferRow>> {
26820        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
26821            lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
26822        })
26823    }
26824
26825    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
26826        self.read(cx).lsp_store().update(cx, |lsp_store, _| {
26827            lsp_store.invalidate_inlay_hints(for_buffers)
26828        });
26829    }
26830
26831    fn inlay_hints(
26832        &self,
26833        invalidate: InvalidationStrategy,
26834        buffer: Entity<Buffer>,
26835        ranges: Vec<Range<text::Anchor>>,
26836        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26837        cx: &mut App,
26838    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
26839        Some(self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
26840            lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
26841        }))
26842    }
26843
26844    fn range_for_rename(
26845        &self,
26846        buffer: &Entity<Buffer>,
26847        position: text::Anchor,
26848        cx: &mut App,
26849    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
26850        Some(self.update(cx, |project, cx| {
26851            let buffer = buffer.clone();
26852            let task = project.prepare_rename(buffer.clone(), position, cx);
26853            cx.spawn(async move |_, cx| {
26854                Ok(match task.await? {
26855                    PrepareRenameResponse::Success(range) => Some(range),
26856                    PrepareRenameResponse::InvalidPosition => None,
26857                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
26858                        // Fallback on using TreeSitter info to determine identifier range
26859                        buffer.read_with(cx, |buffer, _| {
26860                            let snapshot = buffer.snapshot();
26861                            let (range, kind) = snapshot.surrounding_word(position, None);
26862                            if kind != Some(CharKind::Word) {
26863                                return None;
26864                            }
26865                            Some(
26866                                snapshot.anchor_before(range.start)
26867                                    ..snapshot.anchor_after(range.end),
26868                            )
26869                        })
26870                    }
26871                })
26872            })
26873        }))
26874    }
26875
26876    fn perform_rename(
26877        &self,
26878        buffer: &Entity<Buffer>,
26879        position: text::Anchor,
26880        new_name: String,
26881        cx: &mut App,
26882    ) -> Option<Task<Result<ProjectTransaction>>> {
26883        Some(self.update(cx, |project, cx| {
26884            project.perform_rename(buffer.clone(), position, new_name, cx)
26885        }))
26886    }
26887}
26888
26889fn consume_contiguous_rows(
26890    contiguous_row_selections: &mut Vec<Selection<Point>>,
26891    selection: &Selection<Point>,
26892    display_map: &DisplaySnapshot,
26893    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
26894) -> (MultiBufferRow, MultiBufferRow) {
26895    contiguous_row_selections.push(selection.clone());
26896    let start_row = starting_row(selection, display_map);
26897    let mut end_row = ending_row(selection, display_map);
26898
26899    while let Some(next_selection) = selections.peek() {
26900        if next_selection.start.row <= end_row.0 {
26901            end_row = ending_row(next_selection, display_map);
26902            contiguous_row_selections.push(selections.next().unwrap().clone());
26903        } else {
26904            break;
26905        }
26906    }
26907    (start_row, end_row)
26908}
26909
26910fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
26911    if selection.start.column > 0 {
26912        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
26913    } else {
26914        MultiBufferRow(selection.start.row)
26915    }
26916}
26917
26918fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
26919    if next_selection.end.column > 0 || next_selection.is_empty() {
26920        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
26921    } else {
26922        MultiBufferRow(next_selection.end.row)
26923    }
26924}
26925
26926impl EditorSnapshot {
26927    pub fn remote_selections_in_range<'a>(
26928        &'a self,
26929        range: &'a Range<Anchor>,
26930        collaboration_hub: &dyn CollaborationHub,
26931        cx: &'a App,
26932    ) -> impl 'a + Iterator<Item = RemoteSelection> {
26933        let participant_names = collaboration_hub.user_names(cx);
26934        let participant_indices = collaboration_hub.user_participant_indices(cx);
26935        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
26936        let collaborators_by_replica_id = collaborators_by_peer_id
26937            .values()
26938            .map(|collaborator| (collaborator.replica_id, collaborator))
26939            .collect::<HashMap<_, _>>();
26940        self.buffer_snapshot()
26941            .selections_in_range(range, false)
26942            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
26943                if replica_id == ReplicaId::AGENT {
26944                    Some(RemoteSelection {
26945                        replica_id,
26946                        selection,
26947                        cursor_shape,
26948                        line_mode,
26949                        collaborator_id: CollaboratorId::Agent,
26950                        user_name: Some("Agent".into()),
26951                        color: cx.theme().players().agent(),
26952                    })
26953                } else {
26954                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
26955                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
26956                    let user_name = participant_names.get(&collaborator.user_id).cloned();
26957                    Some(RemoteSelection {
26958                        replica_id,
26959                        selection,
26960                        cursor_shape,
26961                        line_mode,
26962                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
26963                        user_name,
26964                        color: if let Some(index) = participant_index {
26965                            cx.theme().players().color_for_participant(index.0)
26966                        } else {
26967                            cx.theme().players().absent()
26968                        },
26969                    })
26970                }
26971            })
26972    }
26973
26974    pub fn hunks_for_ranges(
26975        &self,
26976        ranges: impl IntoIterator<Item = Range<Point>>,
26977    ) -> Vec<MultiBufferDiffHunk> {
26978        let mut hunks = Vec::new();
26979        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
26980            HashMap::default();
26981        for query_range in ranges {
26982            let query_rows =
26983                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
26984            for hunk in self.buffer_snapshot().diff_hunks_in_range(
26985                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
26986            ) {
26987                // Include deleted hunks that are adjacent to the query range, because
26988                // otherwise they would be missed.
26989                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
26990                if hunk.status().is_deleted() {
26991                    intersects_range |= hunk.row_range.start == query_rows.end;
26992                    intersects_range |= hunk.row_range.end == query_rows.start;
26993                }
26994                if intersects_range {
26995                    if !processed_buffer_rows
26996                        .entry(hunk.buffer_id)
26997                        .or_default()
26998                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
26999                    {
27000                        continue;
27001                    }
27002                    hunks.push(hunk);
27003                }
27004            }
27005        }
27006
27007        hunks
27008    }
27009
27010    fn display_diff_hunks_for_rows<'a>(
27011        &'a self,
27012        display_rows: Range<DisplayRow>,
27013        folded_buffers: &'a HashSet<BufferId>,
27014    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27015        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27016        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27017
27018        self.buffer_snapshot()
27019            .diff_hunks_in_range(buffer_start..buffer_end)
27020            .filter_map(|hunk| {
27021                if folded_buffers.contains(&hunk.buffer_id) {
27022                    return None;
27023                }
27024
27025                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27026                let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
27027
27028                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27029                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27030
27031                let display_hunk = if hunk_display_start.column() != 0 {
27032                    DisplayDiffHunk::Folded {
27033                        display_row: hunk_display_start.row(),
27034                    }
27035                } else {
27036                    let mut end_row = hunk_display_end.row();
27037                    if hunk_display_end.column() > 0 {
27038                        end_row.0 += 1;
27039                    }
27040                    let is_created_file = hunk.is_created_file();
27041
27042                    DisplayDiffHunk::Unfolded {
27043                        status: hunk.status(),
27044                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27045                            ..hunk.diff_base_byte_range.end.0,
27046                        word_diffs: hunk.word_diffs,
27047                        display_row_range: hunk_display_start.row()..end_row,
27048                        multi_buffer_range: Anchor::range_in_buffer(
27049                            hunk.excerpt_id,
27050                            hunk.buffer_range,
27051                        ),
27052                        is_created_file,
27053                    }
27054                };
27055
27056                Some(display_hunk)
27057            })
27058    }
27059
27060    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27061        self.display_snapshot
27062            .buffer_snapshot()
27063            .language_at(position)
27064    }
27065
27066    pub fn is_focused(&self) -> bool {
27067        self.is_focused
27068    }
27069
27070    pub fn placeholder_text(&self) -> Option<String> {
27071        self.placeholder_display_snapshot
27072            .as_ref()
27073            .map(|display_map| display_map.text())
27074    }
27075
27076    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27077        self.scroll_anchor.scroll_position(&self.display_snapshot)
27078    }
27079
27080    pub fn gutter_dimensions(
27081        &self,
27082        font_id: FontId,
27083        font_size: Pixels,
27084        style: &EditorStyle,
27085        window: &mut Window,
27086        cx: &App,
27087    ) -> GutterDimensions {
27088        if self.show_gutter
27089            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27090            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27091        {
27092            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27093                matches!(
27094                    ProjectSettings::get_global(cx).git.git_gutter,
27095                    GitGutterSetting::TrackedFiles
27096                )
27097            });
27098            let gutter_settings = EditorSettings::get_global(cx).gutter;
27099            let show_line_numbers = self
27100                .show_line_numbers
27101                .unwrap_or(gutter_settings.line_numbers);
27102            let line_gutter_width = if show_line_numbers {
27103                // Avoid flicker-like gutter resizes when the line number gains another digit by
27104                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27105                let min_width_for_number_on_gutter =
27106                    ch_advance * gutter_settings.min_line_number_digits as f32;
27107                self.max_line_number_width(style, window)
27108                    .max(min_width_for_number_on_gutter)
27109            } else {
27110                0.0.into()
27111            };
27112
27113            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27114            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27115
27116            let git_blame_entries_width =
27117                self.git_blame_gutter_max_author_length
27118                    .map(|max_author_length| {
27119                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27120                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27121
27122                        /// The number of characters to dedicate to gaps and margins.
27123                        const SPACING_WIDTH: usize = 4;
27124
27125                        let max_char_count = max_author_length.min(renderer.max_author_length())
27126                            + ::git::SHORT_SHA_LENGTH
27127                            + MAX_RELATIVE_TIMESTAMP.len()
27128                            + SPACING_WIDTH;
27129
27130                        ch_advance * max_char_count
27131                    });
27132
27133            let is_singleton = self.buffer_snapshot().is_singleton();
27134
27135            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27136            left_padding += if !is_singleton {
27137                ch_width * 4.0
27138            } else if show_runnables || show_breakpoints {
27139                ch_width * 3.0
27140            } else if show_git_gutter && show_line_numbers {
27141                ch_width * 2.0
27142            } else if show_git_gutter || show_line_numbers {
27143                ch_width
27144            } else {
27145                px(0.)
27146            };
27147
27148            let shows_folds = is_singleton && gutter_settings.folds;
27149
27150            let right_padding = if shows_folds && show_line_numbers {
27151                ch_width * 4.0
27152            } else if shows_folds || (!is_singleton && show_line_numbers) {
27153                ch_width * 3.0
27154            } else if show_line_numbers {
27155                ch_width
27156            } else {
27157                px(0.)
27158            };
27159
27160            GutterDimensions {
27161                left_padding,
27162                right_padding,
27163                width: line_gutter_width + left_padding + right_padding,
27164                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27165                git_blame_entries_width,
27166            }
27167        } else if self.offset_content {
27168            GutterDimensions::default_with_margin(font_id, font_size, cx)
27169        } else {
27170            GutterDimensions::default()
27171        }
27172    }
27173
27174    pub fn render_crease_toggle(
27175        &self,
27176        buffer_row: MultiBufferRow,
27177        row_contains_cursor: bool,
27178        editor: Entity<Editor>,
27179        window: &mut Window,
27180        cx: &mut App,
27181    ) -> Option<AnyElement> {
27182        let folded = self.is_line_folded(buffer_row);
27183        let mut is_foldable = false;
27184
27185        if let Some(crease) = self
27186            .crease_snapshot
27187            .query_row(buffer_row, self.buffer_snapshot())
27188        {
27189            is_foldable = true;
27190            match crease {
27191                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27192                    if let Some(render_toggle) = render_toggle {
27193                        let toggle_callback =
27194                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27195                                if folded {
27196                                    editor.update(cx, |editor, cx| {
27197                                        editor.fold_at(buffer_row, window, cx)
27198                                    });
27199                                } else {
27200                                    editor.update(cx, |editor, cx| {
27201                                        editor.unfold_at(buffer_row, window, cx)
27202                                    });
27203                                }
27204                            });
27205                        return Some((render_toggle)(
27206                            buffer_row,
27207                            folded,
27208                            toggle_callback,
27209                            window,
27210                            cx,
27211                        ));
27212                    }
27213                }
27214            }
27215        }
27216
27217        is_foldable |= self.starts_indent(buffer_row);
27218
27219        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27220            Some(
27221                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27222                    .toggle_state(folded)
27223                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27224                        if folded {
27225                            this.unfold_at(buffer_row, window, cx);
27226                        } else {
27227                            this.fold_at(buffer_row, window, cx);
27228                        }
27229                    }))
27230                    .into_any_element(),
27231            )
27232        } else {
27233            None
27234        }
27235    }
27236
27237    pub fn render_crease_trailer(
27238        &self,
27239        buffer_row: MultiBufferRow,
27240        window: &mut Window,
27241        cx: &mut App,
27242    ) -> Option<AnyElement> {
27243        let folded = self.is_line_folded(buffer_row);
27244        if let Crease::Inline { render_trailer, .. } = self
27245            .crease_snapshot
27246            .query_row(buffer_row, self.buffer_snapshot())?
27247        {
27248            let render_trailer = render_trailer.as_ref()?;
27249            Some(render_trailer(buffer_row, folded, window, cx))
27250        } else {
27251            None
27252        }
27253    }
27254
27255    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27256        let digit_count = self.widest_line_number().ilog10() + 1;
27257        column_pixels(style, digit_count as usize, window)
27258    }
27259
27260    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27261    ///
27262    /// This is positive if `base` is before `line`.
27263    fn relative_line_delta(
27264        &self,
27265        current_selection_head: DisplayRow,
27266        first_visible_row: DisplayRow,
27267        consider_wrapped_lines: bool,
27268    ) -> i64 {
27269        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27270        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27271
27272        if consider_wrapped_lines {
27273            let wrap_snapshot = self.wrap_snapshot();
27274            let base_wrap_row = wrap_snapshot
27275                .make_wrap_point(current_selection_head, Bias::Left)
27276                .row();
27277            let wrap_row = wrap_snapshot
27278                .make_wrap_point(first_visible_row, Bias::Left)
27279                .row();
27280
27281            wrap_row.0 as i64 - base_wrap_row.0 as i64
27282        } else {
27283            let fold_snapshot = self.fold_snapshot();
27284            let base_fold_row = fold_snapshot
27285                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27286                .row();
27287            let fold_row = fold_snapshot
27288                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27289                .row();
27290
27291            fold_row as i64 - base_fold_row as i64
27292        }
27293    }
27294
27295    /// Returns the unsigned relative line number to display for each row in `rows`.
27296    ///
27297    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27298    pub fn calculate_relative_line_numbers(
27299        &self,
27300        rows: &Range<DisplayRow>,
27301        current_selection_head: DisplayRow,
27302        count_wrapped_lines: bool,
27303    ) -> HashMap<DisplayRow, u32> {
27304        let initial_offset =
27305            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27306        let current_selection_point = current_selection_head.as_display_point().to_point(self);
27307
27308        self.row_infos(rows.start)
27309            .take(rows.len())
27310            .enumerate()
27311            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27312            .filter(|(_row, row_info)| {
27313                row_info.buffer_row.is_some()
27314                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27315            })
27316            .enumerate()
27317            .filter(|(_, (row, row_info))| {
27318                // We want to check here that
27319                // - the row is not the current selection head to ensure the current
27320                // line has absolute numbering
27321                // - similarly, should the selection head live in a soft-wrapped line
27322                // and we are not counting those, that the parent line keeps its
27323                // absolute number
27324                // - lastly, if we are in a deleted line, it is fine to number this
27325                // relative with 0, as otherwise it would have no line number at all
27326                (*row != current_selection_head
27327                    && (count_wrapped_lines
27328                        || row_info.buffer_row != Some(current_selection_point.row)))
27329                    || row_info
27330                        .diff_status
27331                        .is_some_and(|status| status.is_deleted())
27332            })
27333            .map(|(i, (row, _))| (row, (initial_offset + i as i64).unsigned_abs() as u32))
27334            .collect()
27335    }
27336}
27337
27338pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27339    let font_size = style.text.font_size.to_pixels(window.rem_size());
27340    let layout = window.text_system().shape_line(
27341        SharedString::from(" ".repeat(column)),
27342        font_size,
27343        &[TextRun {
27344            len: column,
27345            font: style.text.font(),
27346            color: Hsla::default(),
27347            ..Default::default()
27348        }],
27349        None,
27350    );
27351
27352    layout.width
27353}
27354
27355impl Deref for EditorSnapshot {
27356    type Target = DisplaySnapshot;
27357
27358    fn deref(&self) -> &Self::Target {
27359        &self.display_snapshot
27360    }
27361}
27362
27363#[derive(Clone, Debug, PartialEq, Eq)]
27364pub enum EditorEvent {
27365    /// Emitted when the stored review comments change (added, removed, or updated).
27366    ReviewCommentsChanged {
27367        /// The new total count of review comments.
27368        total_count: usize,
27369    },
27370    InputIgnored {
27371        text: Arc<str>,
27372    },
27373    InputHandled {
27374        utf16_range_to_replace: Option<Range<isize>>,
27375        text: Arc<str>,
27376    },
27377    ExcerptsAdded {
27378        buffer: Entity<Buffer>,
27379        predecessor: ExcerptId,
27380        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27381    },
27382    ExcerptsRemoved {
27383        ids: Vec<ExcerptId>,
27384        removed_buffer_ids: Vec<BufferId>,
27385    },
27386    BufferFoldToggled {
27387        ids: Vec<ExcerptId>,
27388        folded: bool,
27389    },
27390    ExcerptsEdited {
27391        ids: Vec<ExcerptId>,
27392    },
27393    ExcerptsExpanded {
27394        ids: Vec<ExcerptId>,
27395    },
27396    ExpandExcerptsRequested {
27397        excerpt_ids: Vec<ExcerptId>,
27398        lines: u32,
27399        direction: ExpandExcerptDirection,
27400    },
27401    StageOrUnstageRequested {
27402        stage: bool,
27403        hunks: Vec<MultiBufferDiffHunk>,
27404    },
27405    RestoreRequested {
27406        hunks: Vec<MultiBufferDiffHunk>,
27407    },
27408    BufferEdited,
27409    Edited {
27410        transaction_id: clock::Lamport,
27411    },
27412    Reparsed(BufferId),
27413    Focused,
27414    FocusedIn,
27415    Blurred,
27416    DirtyChanged,
27417    Saved,
27418    TitleChanged,
27419    SelectionsChanged {
27420        local: bool,
27421    },
27422    ScrollPositionChanged {
27423        local: bool,
27424        autoscroll: bool,
27425    },
27426    TransactionUndone {
27427        transaction_id: clock::Lamport,
27428    },
27429    TransactionBegun {
27430        transaction_id: clock::Lamport,
27431    },
27432    CursorShapeChanged,
27433    BreadcrumbsChanged,
27434    PushedToNavHistory {
27435        anchor: Anchor,
27436        is_deactivate: bool,
27437    },
27438}
27439
27440impl EventEmitter<EditorEvent> for Editor {}
27441
27442impl Focusable for Editor {
27443    fn focus_handle(&self, _cx: &App) -> FocusHandle {
27444        self.focus_handle.clone()
27445    }
27446}
27447
27448impl Render for Editor {
27449    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27450        EditorElement::new(&cx.entity(), self.create_style(cx))
27451    }
27452}
27453
27454impl EntityInputHandler for Editor {
27455    fn text_for_range(
27456        &mut self,
27457        range_utf16: Range<usize>,
27458        adjusted_range: &mut Option<Range<usize>>,
27459        _: &mut Window,
27460        cx: &mut Context<Self>,
27461    ) -> Option<String> {
27462        let snapshot = self.buffer.read(cx).read(cx);
27463        let start = snapshot.clip_offset_utf16(
27464            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
27465            Bias::Left,
27466        );
27467        let end = snapshot.clip_offset_utf16(
27468            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
27469            Bias::Right,
27470        );
27471        if (start.0.0..end.0.0) != range_utf16 {
27472            adjusted_range.replace(start.0.0..end.0.0);
27473        }
27474        Some(snapshot.text_for_range(start..end).collect())
27475    }
27476
27477    fn selected_text_range(
27478        &mut self,
27479        ignore_disabled_input: bool,
27480        _: &mut Window,
27481        cx: &mut Context<Self>,
27482    ) -> Option<UTF16Selection> {
27483        // Prevent the IME menu from appearing when holding down an alphabetic key
27484        // while input is disabled.
27485        if !ignore_disabled_input && !self.input_enabled {
27486            return None;
27487        }
27488
27489        let selection = self
27490            .selections
27491            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
27492        let range = selection.range();
27493
27494        Some(UTF16Selection {
27495            range: range.start.0.0..range.end.0.0,
27496            reversed: selection.reversed,
27497        })
27498    }
27499
27500    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
27501        let snapshot = self.buffer.read(cx).read(cx);
27502        let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
27503        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
27504    }
27505
27506    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
27507        self.clear_highlights::<InputComposition>(cx);
27508        self.ime_transaction.take();
27509    }
27510
27511    fn replace_text_in_range(
27512        &mut self,
27513        range_utf16: Option<Range<usize>>,
27514        text: &str,
27515        window: &mut Window,
27516        cx: &mut Context<Self>,
27517    ) {
27518        if !self.input_enabled {
27519            cx.emit(EditorEvent::InputIgnored { text: text.into() });
27520            return;
27521        }
27522
27523        self.transact(window, cx, |this, window, cx| {
27524            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
27525                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27526                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27527                Some(this.selection_replacement_ranges(range_utf16, cx))
27528            } else {
27529                this.marked_text_ranges(cx)
27530            };
27531
27532            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
27533                let newest_selection_id = this.selections.newest_anchor().id;
27534                this.selections
27535                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27536                    .iter()
27537                    .zip(ranges_to_replace.iter())
27538                    .find_map(|(selection, range)| {
27539                        if selection.id == newest_selection_id {
27540                            Some(
27541                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27542                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27543                            )
27544                        } else {
27545                            None
27546                        }
27547                    })
27548            });
27549
27550            cx.emit(EditorEvent::InputHandled {
27551                utf16_range_to_replace: range_to_replace,
27552                text: text.into(),
27553            });
27554
27555            if let Some(new_selected_ranges) = new_selected_ranges {
27556                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27557                    selections.select_ranges(new_selected_ranges)
27558                });
27559                this.backspace(&Default::default(), window, cx);
27560            }
27561
27562            this.handle_input(text, window, cx);
27563        });
27564
27565        if let Some(transaction) = self.ime_transaction {
27566            self.buffer.update(cx, |buffer, cx| {
27567                buffer.group_until_transaction(transaction, cx);
27568            });
27569        }
27570
27571        self.unmark_text(window, cx);
27572    }
27573
27574    fn replace_and_mark_text_in_range(
27575        &mut self,
27576        range_utf16: Option<Range<usize>>,
27577        text: &str,
27578        new_selected_range_utf16: Option<Range<usize>>,
27579        window: &mut Window,
27580        cx: &mut Context<Self>,
27581    ) {
27582        if !self.input_enabled {
27583            return;
27584        }
27585
27586        let transaction = self.transact(window, cx, |this, window, cx| {
27587            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
27588                let snapshot = this.buffer.read(cx).read(cx);
27589                if let Some(relative_range_utf16) = range_utf16.as_ref() {
27590                    for marked_range in &mut marked_ranges {
27591                        marked_range.end = marked_range.start + relative_range_utf16.end;
27592                        marked_range.start += relative_range_utf16.start;
27593                        marked_range.start =
27594                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
27595                        marked_range.end =
27596                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
27597                    }
27598                }
27599                Some(marked_ranges)
27600            } else if let Some(range_utf16) = range_utf16 {
27601                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27602                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27603                Some(this.selection_replacement_ranges(range_utf16, cx))
27604            } else {
27605                None
27606            };
27607
27608            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
27609                let newest_selection_id = this.selections.newest_anchor().id;
27610                this.selections
27611                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27612                    .iter()
27613                    .zip(ranges_to_replace.iter())
27614                    .find_map(|(selection, range)| {
27615                        if selection.id == newest_selection_id {
27616                            Some(
27617                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27618                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27619                            )
27620                        } else {
27621                            None
27622                        }
27623                    })
27624            });
27625
27626            cx.emit(EditorEvent::InputHandled {
27627                utf16_range_to_replace: range_to_replace,
27628                text: text.into(),
27629            });
27630
27631            if let Some(ranges) = ranges_to_replace {
27632                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
27633                    s.select_ranges(ranges)
27634                });
27635            }
27636
27637            let marked_ranges = {
27638                let snapshot = this.buffer.read(cx).read(cx);
27639                this.selections
27640                    .disjoint_anchors_arc()
27641                    .iter()
27642                    .map(|selection| {
27643                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
27644                    })
27645                    .collect::<Vec<_>>()
27646            };
27647
27648            if text.is_empty() {
27649                this.unmark_text(window, cx);
27650            } else {
27651                this.highlight_text::<InputComposition>(
27652                    marked_ranges.clone(),
27653                    HighlightStyle {
27654                        underline: Some(UnderlineStyle {
27655                            thickness: px(1.),
27656                            color: None,
27657                            wavy: false,
27658                        }),
27659                        ..Default::default()
27660                    },
27661                    cx,
27662                );
27663            }
27664
27665            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
27666            let use_autoclose = this.use_autoclose;
27667            let use_auto_surround = this.use_auto_surround;
27668            this.set_use_autoclose(false);
27669            this.set_use_auto_surround(false);
27670            this.handle_input(text, window, cx);
27671            this.set_use_autoclose(use_autoclose);
27672            this.set_use_auto_surround(use_auto_surround);
27673
27674            if let Some(new_selected_range) = new_selected_range_utf16 {
27675                let snapshot = this.buffer.read(cx).read(cx);
27676                let new_selected_ranges = marked_ranges
27677                    .into_iter()
27678                    .map(|marked_range| {
27679                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
27680                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
27681                            insertion_start.0 + new_selected_range.start,
27682                        ));
27683                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
27684                            insertion_start.0 + new_selected_range.end,
27685                        ));
27686                        snapshot.clip_offset_utf16(new_start, Bias::Left)
27687                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
27688                    })
27689                    .collect::<Vec<_>>();
27690
27691                drop(snapshot);
27692                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27693                    selections.select_ranges(new_selected_ranges)
27694                });
27695            }
27696        });
27697
27698        self.ime_transaction = self.ime_transaction.or(transaction);
27699        if let Some(transaction) = self.ime_transaction {
27700            self.buffer.update(cx, |buffer, cx| {
27701                buffer.group_until_transaction(transaction, cx);
27702            });
27703        }
27704
27705        if self.text_highlights::<InputComposition>(cx).is_none() {
27706            self.ime_transaction.take();
27707        }
27708    }
27709
27710    fn bounds_for_range(
27711        &mut self,
27712        range_utf16: Range<usize>,
27713        element_bounds: gpui::Bounds<Pixels>,
27714        window: &mut Window,
27715        cx: &mut Context<Self>,
27716    ) -> Option<gpui::Bounds<Pixels>> {
27717        let text_layout_details = self.text_layout_details(window, cx);
27718        let CharacterDimensions {
27719            em_width,
27720            em_advance,
27721            line_height,
27722        } = self.character_dimensions(window, cx);
27723
27724        let snapshot = self.snapshot(window, cx);
27725        let scroll_position = snapshot.scroll_position();
27726        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
27727
27728        let start =
27729            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
27730        let x = Pixels::from(
27731            ScrollOffset::from(
27732                snapshot.x_for_display_point(start, &text_layout_details)
27733                    + self.gutter_dimensions.full_width(),
27734            ) - scroll_left,
27735        );
27736        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
27737
27738        Some(Bounds {
27739            origin: element_bounds.origin + point(x, y),
27740            size: size(em_width, line_height),
27741        })
27742    }
27743
27744    fn character_index_for_point(
27745        &mut self,
27746        point: gpui::Point<Pixels>,
27747        _window: &mut Window,
27748        _cx: &mut Context<Self>,
27749    ) -> Option<usize> {
27750        let position_map = self.last_position_map.as_ref()?;
27751        if !position_map.text_hitbox.contains(&point) {
27752            return None;
27753        }
27754        let display_point = position_map.point_for_position(point).previous_valid;
27755        let anchor = position_map
27756            .snapshot
27757            .display_point_to_anchor(display_point, Bias::Left);
27758        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
27759        Some(utf16_offset.0.0)
27760    }
27761
27762    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
27763        self.input_enabled
27764    }
27765}
27766
27767trait SelectionExt {
27768    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
27769    fn spanned_rows(
27770        &self,
27771        include_end_if_at_line_start: bool,
27772        map: &DisplaySnapshot,
27773    ) -> Range<MultiBufferRow>;
27774}
27775
27776impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
27777    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
27778        let start = self
27779            .start
27780            .to_point(map.buffer_snapshot())
27781            .to_display_point(map);
27782        let end = self
27783            .end
27784            .to_point(map.buffer_snapshot())
27785            .to_display_point(map);
27786        if self.reversed {
27787            end..start
27788        } else {
27789            start..end
27790        }
27791    }
27792
27793    fn spanned_rows(
27794        &self,
27795        include_end_if_at_line_start: bool,
27796        map: &DisplaySnapshot,
27797    ) -> Range<MultiBufferRow> {
27798        let start = self.start.to_point(map.buffer_snapshot());
27799        let mut end = self.end.to_point(map.buffer_snapshot());
27800        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
27801            end.row -= 1;
27802        }
27803
27804        let buffer_start = map.prev_line_boundary(start).0;
27805        let buffer_end = map.next_line_boundary(end).0;
27806        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
27807    }
27808}
27809
27810impl<T: InvalidationRegion> InvalidationStack<T> {
27811    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
27812    where
27813        S: Clone + ToOffset,
27814    {
27815        while let Some(region) = self.last() {
27816            let all_selections_inside_invalidation_ranges =
27817                if selections.len() == region.ranges().len() {
27818                    selections
27819                        .iter()
27820                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
27821                        .all(|(selection, invalidation_range)| {
27822                            let head = selection.head().to_offset(buffer);
27823                            invalidation_range.start <= head && invalidation_range.end >= head
27824                        })
27825                } else {
27826                    false
27827                };
27828
27829            if all_selections_inside_invalidation_ranges {
27830                break;
27831            } else {
27832                self.pop();
27833            }
27834        }
27835    }
27836}
27837
27838#[derive(Clone)]
27839struct ErasedEditorImpl(Entity<Editor>);
27840
27841impl ui_input::ErasedEditor for ErasedEditorImpl {
27842    fn text(&self, cx: &App) -> String {
27843        self.0.read(cx).text(cx)
27844    }
27845
27846    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
27847        self.0.update(cx, |this, cx| {
27848            this.set_text(text, window, cx);
27849        })
27850    }
27851
27852    fn clear(&self, window: &mut Window, cx: &mut App) {
27853        self.0.update(cx, |this, cx| this.clear(window, cx));
27854    }
27855
27856    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
27857        self.0.update(cx, |this, cx| {
27858            this.set_placeholder_text(text, window, cx);
27859        });
27860    }
27861
27862    fn focus_handle(&self, cx: &App) -> FocusHandle {
27863        self.0.read(cx).focus_handle(cx)
27864    }
27865
27866    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
27867        let settings = ThemeSettings::get_global(cx);
27868        let theme_color = cx.theme().colors();
27869
27870        let text_style = TextStyle {
27871            font_family: settings.ui_font.family.clone(),
27872            font_features: settings.ui_font.features.clone(),
27873            font_size: rems(0.875).into(),
27874            font_weight: settings.buffer_font.weight,
27875            font_style: FontStyle::Normal,
27876            line_height: relative(1.2),
27877            color: theme_color.text,
27878            ..Default::default()
27879        };
27880        let editor_style = EditorStyle {
27881            background: theme_color.ghost_element_background,
27882            local_player: cx.theme().players().local(),
27883            syntax: cx.theme().syntax().clone(),
27884            text: text_style,
27885            ..Default::default()
27886        };
27887        EditorElement::new(&self.0, editor_style).into_any()
27888    }
27889
27890    fn as_any(&self) -> &dyn Any {
27891        &self.0
27892    }
27893
27894    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
27895        self.0.update(cx, |editor, cx| {
27896            let editor_offset = editor.buffer().read(cx).len(cx);
27897            editor.change_selections(
27898                SelectionEffects::scroll(Autoscroll::Next),
27899                window,
27900                cx,
27901                |s| s.select_ranges(Some(editor_offset..editor_offset)),
27902            );
27903        });
27904    }
27905
27906    fn subscribe(
27907        &self,
27908        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
27909        window: &mut Window,
27910        cx: &mut App,
27911    ) -> Subscription {
27912        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
27913            let event = match event {
27914                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
27915                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
27916                _ => return,
27917            };
27918            (callback)(event, window, cx);
27919        })
27920    }
27921
27922    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
27923        self.0.update(cx, |editor, cx| {
27924            editor.set_masked(masked, cx);
27925        });
27926    }
27927}
27928impl<T> Default for InvalidationStack<T> {
27929    fn default() -> Self {
27930        Self(Default::default())
27931    }
27932}
27933
27934impl<T> Deref for InvalidationStack<T> {
27935    type Target = Vec<T>;
27936
27937    fn deref(&self) -> &Self::Target {
27938        &self.0
27939    }
27940}
27941
27942impl<T> DerefMut for InvalidationStack<T> {
27943    fn deref_mut(&mut self) -> &mut Self::Target {
27944        &mut self.0
27945    }
27946}
27947
27948impl InvalidationRegion for SnippetState {
27949    fn ranges(&self) -> &[Range<Anchor>] {
27950        &self.ranges[self.active_index]
27951    }
27952}
27953
27954fn edit_prediction_edit_text(
27955    current_snapshot: &BufferSnapshot,
27956    edits: &[(Range<Anchor>, impl AsRef<str>)],
27957    edit_preview: &EditPreview,
27958    include_deletions: bool,
27959    cx: &App,
27960) -> HighlightedText {
27961    let edits = edits
27962        .iter()
27963        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
27964        .collect::<Vec<_>>();
27965
27966    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
27967}
27968
27969fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
27970    // Fallback for providers that don't provide edit_preview (like Copilot/Supermaven)
27971    // Just show the raw edit text with basic styling
27972    let mut text = String::new();
27973    let mut highlights = Vec::new();
27974
27975    let insertion_highlight_style = HighlightStyle {
27976        color: Some(cx.theme().colors().text),
27977        ..Default::default()
27978    };
27979
27980    for (_, edit_text) in edits {
27981        let start_offset = text.len();
27982        text.push_str(edit_text);
27983        let end_offset = text.len();
27984
27985        if start_offset < end_offset {
27986            highlights.push((start_offset..end_offset, insertion_highlight_style));
27987        }
27988    }
27989
27990    HighlightedText {
27991        text: text.into(),
27992        highlights,
27993    }
27994}
27995
27996pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
27997    match severity {
27998        lsp::DiagnosticSeverity::ERROR => colors.error,
27999        lsp::DiagnosticSeverity::WARNING => colors.warning,
28000        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28001        lsp::DiagnosticSeverity::HINT => colors.info,
28002        _ => colors.ignored,
28003    }
28004}
28005
28006pub fn styled_runs_for_code_label<'a>(
28007    label: &'a CodeLabel,
28008    syntax_theme: &'a theme::SyntaxTheme,
28009    local_player: &'a theme::PlayerColor,
28010) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28011    let fade_out = HighlightStyle {
28012        fade_out: Some(0.35),
28013        ..Default::default()
28014    };
28015
28016    let mut prev_end = label.filter_range.end;
28017    label
28018        .runs
28019        .iter()
28020        .enumerate()
28021        .flat_map(move |(ix, (range, highlight_id))| {
28022            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28023                HighlightStyle {
28024                    color: Some(local_player.cursor),
28025                    ..Default::default()
28026                }
28027            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28028                HighlightStyle {
28029                    background_color: Some(local_player.selection),
28030                    ..Default::default()
28031                }
28032            } else if let Some(style) = highlight_id.style(syntax_theme) {
28033                style
28034            } else {
28035                return Default::default();
28036            };
28037            let muted_style = style.highlight(fade_out);
28038
28039            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28040            if range.start >= label.filter_range.end {
28041                if range.start > prev_end {
28042                    runs.push((prev_end..range.start, fade_out));
28043                }
28044                runs.push((range.clone(), muted_style));
28045            } else if range.end <= label.filter_range.end {
28046                runs.push((range.clone(), style));
28047            } else {
28048                runs.push((range.start..label.filter_range.end, style));
28049                runs.push((label.filter_range.end..range.end, muted_style));
28050            }
28051            prev_end = cmp::max(prev_end, range.end);
28052
28053            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28054                runs.push((prev_end..label.text.len(), fade_out));
28055            }
28056
28057            runs
28058        })
28059}
28060
28061pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28062    let mut prev_index = 0;
28063    let mut prev_codepoint: Option<char> = None;
28064    text.char_indices()
28065        .chain([(text.len(), '\0')])
28066        .filter_map(move |(index, codepoint)| {
28067            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28068            let is_boundary = index == text.len()
28069                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28070                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28071            if is_boundary {
28072                let chunk = &text[prev_index..index];
28073                prev_index = index;
28074                Some(chunk)
28075            } else {
28076                None
28077            }
28078        })
28079}
28080
28081/// Given a string of text immediately before the cursor, iterates over possible
28082/// strings a snippet could match to. More precisely: returns an iterator over
28083/// suffixes of `text` created by splitting at word boundaries (before & after
28084/// every non-word character).
28085///
28086/// Shorter suffixes are returned first.
28087pub(crate) fn snippet_candidate_suffixes(
28088    text: &str,
28089    is_word_char: impl Fn(char) -> bool,
28090) -> impl std::iter::Iterator<Item = &str> {
28091    let mut prev_index = text.len();
28092    let mut prev_codepoint = None;
28093    text.char_indices()
28094        .rev()
28095        .chain([(0, '\0')])
28096        .filter_map(move |(index, codepoint)| {
28097            let prev_index = std::mem::replace(&mut prev_index, index);
28098            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28099            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28100                None
28101            } else {
28102                let chunk = &text[prev_index..]; // go to end of string
28103                Some(chunk)
28104            }
28105        })
28106}
28107
28108pub trait RangeToAnchorExt: Sized {
28109    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28110
28111    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28112        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28113        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28114    }
28115}
28116
28117impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28118    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28119        let start_offset = self.start.to_offset(snapshot);
28120        let end_offset = self.end.to_offset(snapshot);
28121        if start_offset == end_offset {
28122            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28123        } else {
28124            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28125        }
28126    }
28127}
28128
28129pub trait RowExt {
28130    fn as_f64(&self) -> f64;
28131
28132    fn next_row(&self) -> Self;
28133
28134    fn previous_row(&self) -> Self;
28135
28136    fn minus(&self, other: Self) -> u32;
28137}
28138
28139impl RowExt for DisplayRow {
28140    fn as_f64(&self) -> f64 {
28141        self.0 as _
28142    }
28143
28144    fn next_row(&self) -> Self {
28145        Self(self.0 + 1)
28146    }
28147
28148    fn previous_row(&self) -> Self {
28149        Self(self.0.saturating_sub(1))
28150    }
28151
28152    fn minus(&self, other: Self) -> u32 {
28153        self.0 - other.0
28154    }
28155}
28156
28157impl RowExt for MultiBufferRow {
28158    fn as_f64(&self) -> f64 {
28159        self.0 as _
28160    }
28161
28162    fn next_row(&self) -> Self {
28163        Self(self.0 + 1)
28164    }
28165
28166    fn previous_row(&self) -> Self {
28167        Self(self.0.saturating_sub(1))
28168    }
28169
28170    fn minus(&self, other: Self) -> u32 {
28171        self.0 - other.0
28172    }
28173}
28174
28175trait RowRangeExt {
28176    type Row;
28177
28178    fn len(&self) -> usize;
28179
28180    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28181}
28182
28183impl RowRangeExt for Range<MultiBufferRow> {
28184    type Row = MultiBufferRow;
28185
28186    fn len(&self) -> usize {
28187        (self.end.0 - self.start.0) as usize
28188    }
28189
28190    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28191        (self.start.0..self.end.0).map(MultiBufferRow)
28192    }
28193}
28194
28195impl RowRangeExt for Range<DisplayRow> {
28196    type Row = DisplayRow;
28197
28198    fn len(&self) -> usize {
28199        (self.end.0 - self.start.0) as usize
28200    }
28201
28202    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28203        (self.start.0..self.end.0).map(DisplayRow)
28204    }
28205}
28206
28207/// If select range has more than one line, we
28208/// just point the cursor to range.start.
28209fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28210    if range.start.row == range.end.row {
28211        range
28212    } else {
28213        range.start..range.start
28214    }
28215}
28216pub struct KillRing(ClipboardItem);
28217impl Global for KillRing {}
28218
28219const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28220
28221enum BreakpointPromptEditAction {
28222    Log,
28223    Condition,
28224    HitCondition,
28225}
28226
28227struct BreakpointPromptEditor {
28228    pub(crate) prompt: Entity<Editor>,
28229    editor: WeakEntity<Editor>,
28230    breakpoint_anchor: Anchor,
28231    breakpoint: Breakpoint,
28232    edit_action: BreakpointPromptEditAction,
28233    block_ids: HashSet<CustomBlockId>,
28234    editor_margins: Arc<Mutex<EditorMargins>>,
28235    _subscriptions: Vec<Subscription>,
28236}
28237
28238impl BreakpointPromptEditor {
28239    const MAX_LINES: u8 = 4;
28240
28241    fn new(
28242        editor: WeakEntity<Editor>,
28243        breakpoint_anchor: Anchor,
28244        breakpoint: Breakpoint,
28245        edit_action: BreakpointPromptEditAction,
28246        window: &mut Window,
28247        cx: &mut Context<Self>,
28248    ) -> Self {
28249        let base_text = match edit_action {
28250            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28251            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28252            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28253        }
28254        .map(|msg| msg.to_string())
28255        .unwrap_or_default();
28256
28257        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28258        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28259
28260        let prompt = cx.new(|cx| {
28261            let mut prompt = Editor::new(
28262                EditorMode::AutoHeight {
28263                    min_lines: 1,
28264                    max_lines: Some(Self::MAX_LINES as usize),
28265                },
28266                buffer,
28267                None,
28268                window,
28269                cx,
28270            );
28271            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28272            prompt.set_show_cursor_when_unfocused(false, cx);
28273            prompt.set_placeholder_text(
28274                match edit_action {
28275                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28276                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28277                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28278                },
28279                window,
28280                cx,
28281            );
28282
28283            prompt
28284        });
28285
28286        Self {
28287            prompt,
28288            editor,
28289            breakpoint_anchor,
28290            breakpoint,
28291            edit_action,
28292            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28293            block_ids: Default::default(),
28294            _subscriptions: vec![],
28295        }
28296    }
28297
28298    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28299        self.block_ids.extend(block_ids)
28300    }
28301
28302    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28303        if let Some(editor) = self.editor.upgrade() {
28304            let message = self
28305                .prompt
28306                .read(cx)
28307                .buffer
28308                .read(cx)
28309                .as_singleton()
28310                .expect("A multi buffer in breakpoint prompt isn't possible")
28311                .read(cx)
28312                .as_rope()
28313                .to_string();
28314
28315            editor.update(cx, |editor, cx| {
28316                editor.edit_breakpoint_at_anchor(
28317                    self.breakpoint_anchor,
28318                    self.breakpoint.clone(),
28319                    match self.edit_action {
28320                        BreakpointPromptEditAction::Log => {
28321                            BreakpointEditAction::EditLogMessage(message.into())
28322                        }
28323                        BreakpointPromptEditAction::Condition => {
28324                            BreakpointEditAction::EditCondition(message.into())
28325                        }
28326                        BreakpointPromptEditAction::HitCondition => {
28327                            BreakpointEditAction::EditHitCondition(message.into())
28328                        }
28329                    },
28330                    cx,
28331                );
28332
28333                editor.remove_blocks(self.block_ids.clone(), None, cx);
28334                cx.focus_self(window);
28335            });
28336        }
28337    }
28338
28339    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28340        self.editor
28341            .update(cx, |editor, cx| {
28342                editor.remove_blocks(self.block_ids.clone(), None, cx);
28343                window.focus(&editor.focus_handle, cx);
28344            })
28345            .log_err();
28346    }
28347
28348    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28349        let settings = ThemeSettings::get_global(cx);
28350        let text_style = TextStyle {
28351            color: if self.prompt.read(cx).read_only(cx) {
28352                cx.theme().colors().text_disabled
28353            } else {
28354                cx.theme().colors().text
28355            },
28356            font_family: settings.buffer_font.family.clone(),
28357            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28358            font_size: settings.buffer_font_size(cx).into(),
28359            font_weight: settings.buffer_font.weight,
28360            line_height: relative(settings.buffer_line_height.value()),
28361            ..Default::default()
28362        };
28363        EditorElement::new(
28364            &self.prompt,
28365            EditorStyle {
28366                background: cx.theme().colors().editor_background,
28367                local_player: cx.theme().players().local(),
28368                text: text_style,
28369                ..Default::default()
28370            },
28371        )
28372    }
28373}
28374
28375impl Render for BreakpointPromptEditor {
28376    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28377        let editor_margins = *self.editor_margins.lock();
28378        let gutter_dimensions = editor_margins.gutter;
28379        h_flex()
28380            .key_context("Editor")
28381            .bg(cx.theme().colors().editor_background)
28382            .border_y_1()
28383            .border_color(cx.theme().status().info_border)
28384            .size_full()
28385            .py(window.line_height() / 2.5)
28386            .on_action(cx.listener(Self::confirm))
28387            .on_action(cx.listener(Self::cancel))
28388            .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
28389            .child(div().flex_1().child(self.render_prompt_editor(cx)))
28390    }
28391}
28392
28393impl Focusable for BreakpointPromptEditor {
28394    fn focus_handle(&self, cx: &App) -> FocusHandle {
28395        self.prompt.focus_handle(cx)
28396    }
28397}
28398
28399fn all_edits_insertions_or_deletions(
28400    edits: &Vec<(Range<Anchor>, Arc<str>)>,
28401    snapshot: &MultiBufferSnapshot,
28402) -> bool {
28403    let mut all_insertions = true;
28404    let mut all_deletions = true;
28405
28406    for (range, new_text) in edits.iter() {
28407        let range_is_empty = range.to_offset(snapshot).is_empty();
28408        let text_is_empty = new_text.is_empty();
28409
28410        if range_is_empty != text_is_empty {
28411            if range_is_empty {
28412                all_deletions = false;
28413            } else {
28414                all_insertions = false;
28415            }
28416        } else {
28417            return false;
28418        }
28419
28420        if !all_insertions && !all_deletions {
28421            return false;
28422        }
28423    }
28424    all_insertions || all_deletions
28425}
28426
28427struct MissingEditPredictionKeybindingTooltip;
28428
28429impl Render for MissingEditPredictionKeybindingTooltip {
28430    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28431        ui::tooltip_container(cx, |container, cx| {
28432            container
28433                .flex_shrink_0()
28434                .max_w_80()
28435                .min_h(rems_from_px(124.))
28436                .justify_between()
28437                .child(
28438                    v_flex()
28439                        .flex_1()
28440                        .text_ui_sm(cx)
28441                        .child(Label::new("Conflict with Accept Keybinding"))
28442                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
28443                )
28444                .child(
28445                    h_flex()
28446                        .pb_1()
28447                        .gap_1()
28448                        .items_end()
28449                        .w_full()
28450                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
28451                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
28452                        }))
28453                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
28454                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
28455                        })),
28456                )
28457        })
28458    }
28459}
28460
28461#[derive(Debug, Clone, Copy, PartialEq)]
28462pub struct LineHighlight {
28463    pub background: Background,
28464    pub border: Option<gpui::Hsla>,
28465    pub include_gutter: bool,
28466    pub type_id: Option<TypeId>,
28467}
28468
28469struct LineManipulationResult {
28470    pub new_text: String,
28471    pub line_count_before: usize,
28472    pub line_count_after: usize,
28473}
28474
28475fn render_diff_hunk_controls(
28476    row: u32,
28477    status: &DiffHunkStatus,
28478    hunk_range: Range<Anchor>,
28479    is_created_file: bool,
28480    line_height: Pixels,
28481    editor: &Entity<Editor>,
28482    _window: &mut Window,
28483    cx: &mut App,
28484) -> AnyElement {
28485    h_flex()
28486        .h(line_height)
28487        .mr_1()
28488        .gap_1()
28489        .px_0p5()
28490        .pb_1()
28491        .border_x_1()
28492        .border_b_1()
28493        .border_color(cx.theme().colors().border_variant)
28494        .rounded_b_lg()
28495        .bg(cx.theme().colors().editor_background)
28496        .gap_1()
28497        .block_mouse_except_scroll()
28498        .shadow_md()
28499        .child(if status.has_secondary_hunk() {
28500            Button::new(("stage", row as u64), "Stage")
28501                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28502                .tooltip({
28503                    let focus_handle = editor.focus_handle(cx);
28504                    move |_window, cx| {
28505                        Tooltip::for_action_in(
28506                            "Stage Hunk",
28507                            &::git::ToggleStaged,
28508                            &focus_handle,
28509                            cx,
28510                        )
28511                    }
28512                })
28513                .on_click({
28514                    let editor = editor.clone();
28515                    move |_event, _window, cx| {
28516                        editor.update(cx, |editor, cx| {
28517                            editor.stage_or_unstage_diff_hunks(
28518                                true,
28519                                vec![hunk_range.start..hunk_range.start],
28520                                cx,
28521                            );
28522                        });
28523                    }
28524                })
28525        } else {
28526            Button::new(("unstage", row as u64), "Unstage")
28527                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28528                .tooltip({
28529                    let focus_handle = editor.focus_handle(cx);
28530                    move |_window, cx| {
28531                        Tooltip::for_action_in(
28532                            "Unstage Hunk",
28533                            &::git::ToggleStaged,
28534                            &focus_handle,
28535                            cx,
28536                        )
28537                    }
28538                })
28539                .on_click({
28540                    let editor = editor.clone();
28541                    move |_event, _window, cx| {
28542                        editor.update(cx, |editor, cx| {
28543                            editor.stage_or_unstage_diff_hunks(
28544                                false,
28545                                vec![hunk_range.start..hunk_range.start],
28546                                cx,
28547                            );
28548                        });
28549                    }
28550                })
28551        })
28552        .child(
28553            Button::new(("restore", row as u64), "Restore")
28554                .tooltip({
28555                    let focus_handle = editor.focus_handle(cx);
28556                    move |_window, cx| {
28557                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
28558                    }
28559                })
28560                .on_click({
28561                    let editor = editor.clone();
28562                    move |_event, window, cx| {
28563                        editor.update(cx, |editor, cx| {
28564                            let snapshot = editor.snapshot(window, cx);
28565                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
28566                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
28567                        });
28568                    }
28569                })
28570                .disabled(is_created_file),
28571        )
28572        .when(
28573            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
28574            |el| {
28575                el.child(
28576                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
28577                        .shape(IconButtonShape::Square)
28578                        .icon_size(IconSize::Small)
28579                        // .disabled(!has_multiple_hunks)
28580                        .tooltip({
28581                            let focus_handle = editor.focus_handle(cx);
28582                            move |_window, cx| {
28583                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
28584                            }
28585                        })
28586                        .on_click({
28587                            let editor = editor.clone();
28588                            move |_event, window, cx| {
28589                                editor.update(cx, |editor, cx| {
28590                                    let snapshot = editor.snapshot(window, cx);
28591                                    let position =
28592                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
28593                                    editor.go_to_hunk_before_or_after_position(
28594                                        &snapshot,
28595                                        position,
28596                                        Direction::Next,
28597                                        window,
28598                                        cx,
28599                                    );
28600                                    editor.expand_selected_diff_hunks(cx);
28601                                });
28602                            }
28603                        }),
28604                )
28605                .child(
28606                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
28607                        .shape(IconButtonShape::Square)
28608                        .icon_size(IconSize::Small)
28609                        // .disabled(!has_multiple_hunks)
28610                        .tooltip({
28611                            let focus_handle = editor.focus_handle(cx);
28612                            move |_window, cx| {
28613                                Tooltip::for_action_in(
28614                                    "Previous Hunk",
28615                                    &GoToPreviousHunk,
28616                                    &focus_handle,
28617                                    cx,
28618                                )
28619                            }
28620                        })
28621                        .on_click({
28622                            let editor = editor.clone();
28623                            move |_event, window, cx| {
28624                                editor.update(cx, |editor, cx| {
28625                                    let snapshot = editor.snapshot(window, cx);
28626                                    let point =
28627                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
28628                                    editor.go_to_hunk_before_or_after_position(
28629                                        &snapshot,
28630                                        point,
28631                                        Direction::Prev,
28632                                        window,
28633                                        cx,
28634                                    );
28635                                    editor.expand_selected_diff_hunks(cx);
28636                                });
28637                            }
28638                        }),
28639                )
28640            },
28641        )
28642        .into_any_element()
28643}
28644
28645pub fn multibuffer_context_lines(cx: &App) -> u32 {
28646    EditorSettings::try_get(cx)
28647        .map(|settings| settings.excerpt_context_lines)
28648        .unwrap_or(2)
28649        .min(32)
28650}