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 document_colors;
   21mod document_symbols;
   22mod editor_settings;
   23mod element;
   24mod folding_ranges;
   25mod git;
   26mod highlight_matching_bracket;
   27mod hover_links;
   28pub mod hover_popover;
   29mod indent_guides;
   30mod inlays;
   31pub mod items;
   32mod jsx_tag_auto_close;
   33mod linked_editing_ranges;
   34mod lsp_ext;
   35mod mouse_context_menu;
   36pub mod movement;
   37mod persistence;
   38mod runnables;
   39mod rust_analyzer_ext;
   40pub mod scroll;
   41mod selections_collection;
   42pub mod semantic_tokens;
   43mod split;
   44pub mod split_editor_view;
   45
   46#[cfg(test)]
   47mod code_completion_tests;
   48#[cfg(test)]
   49mod edit_prediction_tests;
   50#[cfg(test)]
   51mod editor_tests;
   52mod signature_help;
   53#[cfg(any(test, feature = "test-support"))]
   54pub mod test;
   55
   56pub(crate) use actions::*;
   57pub use display_map::{
   58    ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder, HighlightKey,
   59    SemanticTokenHighlight,
   60};
   61pub use edit_prediction_types::Direction;
   62pub use editor_settings::{
   63    CompletionDetailAlignment, CurrentLineHighlight, DiffViewStyle, DocumentColorsRenderMode,
   64    EditorSettings, HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings,
   65    ShowMinimap,
   66};
   67pub use element::{
   68    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   69    render_breadcrumb_text,
   70};
   71pub use git::blame::BlameRenderer;
   72pub use hover_popover::hover_markdown_style;
   73pub use inlays::Inlay;
   74pub use items::MAX_TAB_TITLE_LEN;
   75pub use linked_editing_ranges::LinkedEdits;
   76pub use lsp::CompletionContext;
   77pub use lsp_ext::lsp_tasks;
   78pub use multi_buffer::{
   79    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   80    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   81    ToPoint,
   82};
   83pub use split::{SplittableEditor, ToggleSplitDiff};
   84pub use split_editor_view::SplitEditorView;
   85pub use text::Bias;
   86
   87use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   88use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   89use anyhow::{Context as _, Result, anyhow, bail};
   90use blink_manager::BlinkManager;
   91use buffer_diff::DiffHunkStatus;
   92use client::{Collaborator, ParticipantIndex, parse_zed_link};
   93use clock::ReplicaId;
   94use code_context_menus::{
   95    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   96    CompletionsMenu, ContextMenuOrigin,
   97};
   98use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   99use convert_case::{Case, Casing};
  100use dap::TelemetrySpawnLocation;
  101use display_map::*;
  102use document_colors::LspColorData;
  103use edit_prediction_types::{
  104    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
  105    EditPredictionGranularity, SuggestionDisplayType,
  106};
  107use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
  108use element::{LineWithInvisibles, PositionMap, layout_line};
  109use futures::{
  110    FutureExt,
  111    future::{self, Shared, join},
  112};
  113use fuzzy::{StringMatch, StringMatchCandidate};
  114use git::blame::{GitBlame, GlobalBlameRenderer};
  115use gpui::{
  116    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  117    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  118    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
  119    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla,
  120    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
  121    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
  122    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  123    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  124    pulsating_between, px, relative, size,
  125};
  126use hover_links::{HoverLink, HoveredLinkState, find_file};
  127use hover_popover::{HoverState, hide_hover};
  128use indent_guides::ActiveIndentGuidesState;
  129use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  130use itertools::{Either, Itertools};
  131use language::{
  132    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  133    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  134    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  135    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, LocalFile, OffsetRangeExt,
  136    OutlineItem, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
  137    WordsQuery,
  138    language_settings::{
  139        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  140        all_language_settings, language_settings,
  141    },
  142    point_from_lsp, point_to_lsp, text_diff_with_options,
  143};
  144use linked_editing_ranges::refresh_linked_ranges;
  145use lsp::{
  146    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  147    LanguageServerId,
  148};
  149use markdown::Markdown;
  150use mouse_context_menu::MouseContextMenu;
  151use movement::TextLayoutDetails;
  152use multi_buffer::{
  153    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  154};
  155use parking_lot::Mutex;
  156use persistence::EditorDb;
  157use project::{
  158    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  159    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  160    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  161    ProjectItem, ProjectPath, ProjectTransaction,
  162    debugger::{
  163        breakpoint_store::{
  164            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  165            BreakpointStore, BreakpointStoreEvent,
  166        },
  167        session::{Session, SessionEvent},
  168    },
  169    git_store::GitStoreEvent,
  170    lsp_store::{
  171        BufferSemanticTokens, CacheInlayHints, CompletionDocumentation, FormatTrigger,
  172        LspFormatTarget, OpenLspBufferHandle, RefreshForServer,
  173    },
  174    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  175};
  176use rand::seq::SliceRandom;
  177use regex::Regex;
  178use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  179use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  180use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  181use serde::{Deserialize, Serialize};
  182use settings::{
  183    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  184    update_settings_file,
  185};
  186use smallvec::{SmallVec, smallvec};
  187use snippet::Snippet;
  188use std::{
  189    any::{Any, TypeId},
  190    borrow::Cow,
  191    cell::{OnceCell, RefCell},
  192    cmp::{self, Ordering, Reverse},
  193    collections::hash_map,
  194    iter::{self, Peekable},
  195    mem,
  196    num::NonZeroU32,
  197    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  198    path::{Path, PathBuf},
  199    rc::Rc,
  200    sync::Arc,
  201    time::{Duration, Instant},
  202};
  203use task::TaskVariables;
  204use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
  205use theme::{
  206    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  207    ThemeSettings, observe_buffer_font_size_adjustment,
  208};
  209use ui::{
  210    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  211    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  212    utils::WithRemSize,
  213};
  214use ui_input::ErasedEditor;
  215use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  216use workspace::{
  217    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  218    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  219    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  220    item::{ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  221    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  222    searchable::SearchEvent,
  223};
  224pub use zed_actions::editor::RevealInFileManager;
  225use zed_actions::editor::{MoveDown, MoveUp};
  226
  227use crate::{
  228    code_context_menus::CompletionsMenuSource,
  229    editor_settings::MultiCursorModifier,
  230    hover_links::{find_url, find_url_from_range},
  231    inlays::{
  232        InlineValueCache,
  233        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  234    },
  235    runnables::{ResolvedTasks, RunnableData, RunnableTasks},
  236    scroll::{ScrollOffset, ScrollPixelOffset},
  237    selections_collection::resolve_selections_wrapping_blocks,
  238    semantic_tokens::SemanticTokenState,
  239    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  240};
  241
  242pub const FILE_HEADER_HEIGHT: u32 = 2;
  243pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  244pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  245const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  246const MAX_LINE_LEN: usize = 1024;
  247const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  248const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  249pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  250#[doc(hidden)]
  251pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  252pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  253
  254pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  255pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  256pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  257pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  258
  259pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  260pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  261
  262pub type RenderDiffHunkControlsFn = Arc<
  263    dyn Fn(
  264        u32,
  265        &DiffHunkStatus,
  266        Range<Anchor>,
  267        bool,
  268        Pixels,
  269        &Entity<Editor>,
  270        &mut Window,
  271        &mut App,
  272    ) -> AnyElement,
  273>;
  274
  275enum ReportEditorEvent {
  276    Saved { auto_saved: bool },
  277    EditorOpened,
  278    Closed,
  279}
  280
  281impl ReportEditorEvent {
  282    pub fn event_type(&self) -> &'static str {
  283        match self {
  284            Self::Saved { .. } => "Editor Saved",
  285            Self::EditorOpened => "Editor Opened",
  286            Self::Closed => "Editor Closed",
  287        }
  288    }
  289}
  290
  291pub enum ActiveDebugLine {}
  292pub enum DebugStackFrameLine {}
  293
  294pub enum ConflictsOuter {}
  295pub enum ConflictsOurs {}
  296pub enum ConflictsTheirs {}
  297pub enum ConflictsOursMarker {}
  298pub enum ConflictsTheirsMarker {}
  299
  300pub struct HunkAddedColor;
  301pub struct HunkRemovedColor;
  302
  303#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  304pub enum Navigated {
  305    Yes,
  306    No,
  307}
  308
  309impl Navigated {
  310    pub fn from_bool(yes: bool) -> Navigated {
  311        if yes { Navigated::Yes } else { Navigated::No }
  312    }
  313}
  314
  315#[derive(Debug, Clone, PartialEq, Eq)]
  316enum DisplayDiffHunk {
  317    Folded {
  318        display_row: DisplayRow,
  319    },
  320    Unfolded {
  321        is_created_file: bool,
  322        diff_base_byte_range: Range<usize>,
  323        display_row_range: Range<DisplayRow>,
  324        multi_buffer_range: Range<Anchor>,
  325        status: DiffHunkStatus,
  326        word_diffs: Vec<Range<MultiBufferOffset>>,
  327    },
  328}
  329
  330pub enum HideMouseCursorOrigin {
  331    TypingAction,
  332    MovementAction,
  333}
  334
  335pub fn init(cx: &mut App) {
  336    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  337    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  338
  339    workspace::register_project_item::<Editor>(cx);
  340    workspace::FollowableViewRegistry::register::<Editor>(cx);
  341    workspace::register_serializable_item::<Editor>(cx);
  342
  343    cx.observe_new(
  344        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  345            workspace.register_action(Editor::new_file);
  346            workspace.register_action(Editor::new_file_split);
  347            workspace.register_action(Editor::new_file_vertical);
  348            workspace.register_action(Editor::new_file_horizontal);
  349            workspace.register_action(Editor::cancel_language_server_work);
  350            workspace.register_action(Editor::toggle_focus);
  351        },
  352    )
  353    .detach();
  354
  355    cx.on_action(move |_: &workspace::NewFile, cx| {
  356        let app_state = workspace::AppState::global(cx);
  357        if let Some(app_state) = app_state.upgrade() {
  358            workspace::open_new(
  359                Default::default(),
  360                app_state,
  361                cx,
  362                |workspace, window, cx| {
  363                    Editor::new_file(workspace, &Default::default(), window, cx)
  364                },
  365            )
  366            .detach_and_log_err(cx);
  367        }
  368    })
  369    .on_action(move |_: &workspace::NewWindow, cx| {
  370        let app_state = workspace::AppState::global(cx);
  371        if let Some(app_state) = app_state.upgrade() {
  372            workspace::open_new(
  373                Default::default(),
  374                app_state,
  375                cx,
  376                |workspace, window, cx| {
  377                    cx.activate(true);
  378                    Editor::new_file(workspace, &Default::default(), window, cx)
  379                },
  380            )
  381            .detach_and_log_err(cx);
  382        }
  383    });
  384    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  385        Arc::new(ErasedEditorImpl(
  386            cx.new(|cx| Editor::single_line(window, cx)),
  387        )) as Arc<dyn ErasedEditor>
  388    });
  389    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  390}
  391
  392pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  393    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  394}
  395
  396pub trait DiagnosticRenderer {
  397    fn render_group(
  398        &self,
  399        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  400        buffer_id: BufferId,
  401        snapshot: EditorSnapshot,
  402        editor: WeakEntity<Editor>,
  403        language_registry: Option<Arc<LanguageRegistry>>,
  404        cx: &mut App,
  405    ) -> Vec<BlockProperties<Anchor>>;
  406
  407    fn render_hover(
  408        &self,
  409        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  410        range: Range<Point>,
  411        buffer_id: BufferId,
  412        language_registry: Option<Arc<LanguageRegistry>>,
  413        cx: &mut App,
  414    ) -> Option<Entity<markdown::Markdown>>;
  415
  416    fn open_link(
  417        &self,
  418        editor: &mut Editor,
  419        link: SharedString,
  420        window: &mut Window,
  421        cx: &mut Context<Editor>,
  422    );
  423}
  424
  425pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  426
  427impl GlobalDiagnosticRenderer {
  428    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  429        cx.try_global::<Self>().map(|g| g.0.clone())
  430    }
  431}
  432
  433impl gpui::Global for GlobalDiagnosticRenderer {}
  434pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  435    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  436}
  437
  438pub struct SearchWithinRange;
  439
  440trait InvalidationRegion {
  441    fn ranges(&self) -> &[Range<Anchor>];
  442}
  443
  444#[derive(Clone, Debug, PartialEq)]
  445pub enum SelectPhase {
  446    Begin {
  447        position: DisplayPoint,
  448        add: bool,
  449        click_count: usize,
  450    },
  451    BeginColumnar {
  452        position: DisplayPoint,
  453        reset: bool,
  454        mode: ColumnarMode,
  455        goal_column: u32,
  456    },
  457    Extend {
  458        position: DisplayPoint,
  459        click_count: usize,
  460    },
  461    Update {
  462        position: DisplayPoint,
  463        goal_column: u32,
  464        scroll_delta: gpui::Point<f32>,
  465    },
  466    End,
  467}
  468
  469#[derive(Clone, Debug, PartialEq)]
  470pub enum ColumnarMode {
  471    FromMouse,
  472    FromSelection,
  473}
  474
  475#[derive(Clone, Debug)]
  476pub enum SelectMode {
  477    Character,
  478    Word(Range<Anchor>),
  479    Line(Range<Anchor>),
  480    All,
  481}
  482
  483#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  484pub enum SizingBehavior {
  485    /// The editor will layout itself using `size_full` and will include the vertical
  486    /// scroll margin as requested by user settings.
  487    #[default]
  488    Default,
  489    /// The editor will layout itself using `size_full`, but will not have any
  490    /// vertical overscroll.
  491    ExcludeOverscrollMargin,
  492    /// The editor will request a vertical size according to its content and will be
  493    /// layouted without a vertical scroll margin.
  494    SizeByContent,
  495}
  496
  497#[derive(Clone, PartialEq, Eq, Debug)]
  498pub enum EditorMode {
  499    SingleLine,
  500    AutoHeight {
  501        min_lines: usize,
  502        max_lines: Option<usize>,
  503    },
  504    Full {
  505        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  506        scale_ui_elements_with_buffer_font_size: bool,
  507        /// When set to `true`, the editor will render a background for the active line.
  508        show_active_line_background: bool,
  509        /// Determines the sizing behavior for this editor
  510        sizing_behavior: SizingBehavior,
  511    },
  512    Minimap {
  513        parent: WeakEntity<Editor>,
  514    },
  515}
  516
  517impl EditorMode {
  518    pub fn full() -> Self {
  519        Self::Full {
  520            scale_ui_elements_with_buffer_font_size: true,
  521            show_active_line_background: true,
  522            sizing_behavior: SizingBehavior::Default,
  523        }
  524    }
  525
  526    #[inline]
  527    pub fn is_full(&self) -> bool {
  528        matches!(self, Self::Full { .. })
  529    }
  530
  531    #[inline]
  532    pub fn is_single_line(&self) -> bool {
  533        matches!(self, Self::SingleLine { .. })
  534    }
  535
  536    #[inline]
  537    fn is_minimap(&self) -> bool {
  538        matches!(self, Self::Minimap { .. })
  539    }
  540}
  541
  542#[derive(Copy, Clone, Debug)]
  543pub enum SoftWrap {
  544    /// Prefer not to wrap at all.
  545    ///
  546    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  547    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  548    GitDiff,
  549    /// Prefer a single line generally, unless an overly long line is encountered.
  550    None,
  551    /// Soft wrap lines that exceed the editor width.
  552    EditorWidth,
  553    /// Soft wrap lines at the preferred line length.
  554    Column(u32),
  555    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  556    Bounded(u32),
  557}
  558
  559#[derive(Clone)]
  560pub struct EditorStyle {
  561    pub background: Hsla,
  562    pub border: Hsla,
  563    pub local_player: PlayerColor,
  564    pub text: TextStyle,
  565    pub scrollbar_width: Pixels,
  566    pub syntax: Arc<SyntaxTheme>,
  567    pub status: StatusColors,
  568    pub inlay_hints_style: HighlightStyle,
  569    pub edit_prediction_styles: EditPredictionStyles,
  570    pub unnecessary_code_fade: f32,
  571    pub show_underlines: bool,
  572}
  573
  574impl Default for EditorStyle {
  575    fn default() -> Self {
  576        Self {
  577            background: Hsla::default(),
  578            border: Hsla::default(),
  579            local_player: PlayerColor::default(),
  580            text: TextStyle::default(),
  581            scrollbar_width: Pixels::default(),
  582            syntax: Default::default(),
  583            // HACK: Status colors don't have a real default.
  584            // We should look into removing the status colors from the editor
  585            // style and retrieve them directly from the theme.
  586            status: StatusColors::dark(),
  587            inlay_hints_style: HighlightStyle::default(),
  588            edit_prediction_styles: EditPredictionStyles {
  589                insertion: HighlightStyle::default(),
  590                whitespace: HighlightStyle::default(),
  591            },
  592            unnecessary_code_fade: Default::default(),
  593            show_underlines: true,
  594        }
  595    }
  596}
  597
  598pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  599    let show_background = language_settings::language_settings(None, None, cx)
  600        .inlay_hints
  601        .show_background;
  602
  603    let mut style = cx.theme().syntax().get("hint");
  604
  605    if style.color.is_none() {
  606        style.color = Some(cx.theme().status().hint);
  607    }
  608
  609    if !show_background {
  610        style.background_color = None;
  611        return style;
  612    }
  613
  614    if style.background_color.is_none() {
  615        style.background_color = Some(cx.theme().status().hint_background);
  616    }
  617
  618    style
  619}
  620
  621pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  622    EditPredictionStyles {
  623        insertion: HighlightStyle {
  624            color: Some(cx.theme().status().predictive),
  625            ..HighlightStyle::default()
  626        },
  627        whitespace: HighlightStyle {
  628            background_color: Some(cx.theme().status().created_background),
  629            ..HighlightStyle::default()
  630        },
  631    }
  632}
  633
  634type CompletionId = usize;
  635
  636pub(crate) enum EditDisplayMode {
  637    TabAccept,
  638    DiffPopover,
  639    Inline,
  640}
  641
  642enum EditPrediction {
  643    Edit {
  644        edits: Vec<(Range<Anchor>, Arc<str>)>,
  645        /// Predicted cursor position as (anchor, offset_from_anchor).
  646        /// The anchor is in multibuffer coordinates; after applying edits,
  647        /// resolve the anchor and add the offset to get the final cursor position.
  648        cursor_position: Option<(Anchor, usize)>,
  649        edit_preview: Option<EditPreview>,
  650        display_mode: EditDisplayMode,
  651        snapshot: BufferSnapshot,
  652    },
  653    /// Move to a specific location in the active editor
  654    MoveWithin {
  655        target: Anchor,
  656        snapshot: BufferSnapshot,
  657    },
  658    /// Move to a specific location in a different editor (not the active one)
  659    MoveOutside {
  660        target: language::Anchor,
  661        snapshot: BufferSnapshot,
  662    },
  663}
  664
  665struct EditPredictionState {
  666    inlay_ids: Vec<InlayId>,
  667    completion: EditPrediction,
  668    completion_id: Option<SharedString>,
  669    invalidation_range: Option<Range<Anchor>>,
  670}
  671
  672enum EditPredictionSettings {
  673    Disabled,
  674    Enabled {
  675        show_in_menu: bool,
  676        preview_requires_modifier: bool,
  677    },
  678}
  679
  680#[derive(Debug, Clone)]
  681struct InlineDiagnostic {
  682    message: SharedString,
  683    group_id: usize,
  684    is_primary: bool,
  685    start: Point,
  686    severity: lsp::DiagnosticSeverity,
  687}
  688
  689pub enum MenuEditPredictionsPolicy {
  690    Never,
  691    ByProvider,
  692}
  693
  694pub enum EditPredictionPreview {
  695    /// Modifier is not pressed
  696    Inactive { released_too_fast: bool },
  697    /// Modifier pressed
  698    Active {
  699        since: Instant,
  700        previous_scroll_position: Option<SharedScrollAnchor>,
  701    },
  702}
  703
  704#[derive(Copy, Clone, Eq, PartialEq)]
  705enum EditPredictionKeybindSurface {
  706    Inline,
  707    CursorPopoverCompact,
  708    CursorPopoverExpanded,
  709}
  710
  711#[derive(Copy, Clone, Eq, PartialEq, Debug)]
  712enum EditPredictionKeybindAction {
  713    Accept,
  714    Preview,
  715}
  716
  717struct EditPredictionKeybindDisplay {
  718    #[cfg(test)]
  719    accept_keystroke: Option<gpui::KeybindingKeystroke>,
  720    #[cfg(test)]
  721    preview_keystroke: Option<gpui::KeybindingKeystroke>,
  722    displayed_keystroke: Option<gpui::KeybindingKeystroke>,
  723    action: EditPredictionKeybindAction,
  724    missing_accept_keystroke: bool,
  725    show_hold_label: bool,
  726}
  727
  728impl EditPredictionPreview {
  729    pub fn released_too_fast(&self) -> bool {
  730        match self {
  731            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  732            EditPredictionPreview::Active { .. } => false,
  733        }
  734    }
  735
  736    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  737        if let EditPredictionPreview::Active {
  738            previous_scroll_position,
  739            ..
  740        } = self
  741        {
  742            *previous_scroll_position = scroll_position;
  743        }
  744    }
  745}
  746
  747pub struct ContextMenuOptions {
  748    pub min_entries_visible: usize,
  749    pub max_entries_visible: usize,
  750    pub placement: Option<ContextMenuPlacement>,
  751}
  752
  753#[derive(Debug, Clone, PartialEq, Eq)]
  754pub enum ContextMenuPlacement {
  755    Above,
  756    Below,
  757}
  758
  759#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  760struct EditorActionId(usize);
  761
  762impl EditorActionId {
  763    pub fn post_inc(&mut self) -> Self {
  764        let answer = self.0;
  765
  766        *self = Self(answer + 1);
  767
  768        Self(answer)
  769    }
  770}
  771
  772// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  773// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  774
  775type BackgroundHighlight = (
  776    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  777    Arc<[Range<Anchor>]>,
  778);
  779type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  780
  781#[derive(Default)]
  782struct ScrollbarMarkerState {
  783    scrollbar_size: Size<Pixels>,
  784    dirty: bool,
  785    markers: Arc<[PaintQuad]>,
  786    pending_refresh: Option<Task<Result<()>>>,
  787}
  788
  789impl ScrollbarMarkerState {
  790    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  791        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  792    }
  793}
  794
  795#[derive(Clone, Copy, PartialEq, Eq)]
  796pub enum MinimapVisibility {
  797    Disabled,
  798    Enabled {
  799        /// The configuration currently present in the users settings.
  800        setting_configuration: bool,
  801        /// Whether to override the currently set visibility from the users setting.
  802        toggle_override: bool,
  803    },
  804}
  805
  806impl MinimapVisibility {
  807    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  808        if mode.is_full() {
  809            Self::Enabled {
  810                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  811                toggle_override: false,
  812            }
  813        } else {
  814            Self::Disabled
  815        }
  816    }
  817
  818    fn hidden(&self) -> Self {
  819        match *self {
  820            Self::Enabled {
  821                setting_configuration,
  822                ..
  823            } => Self::Enabled {
  824                setting_configuration,
  825                toggle_override: setting_configuration,
  826            },
  827            Self::Disabled => Self::Disabled,
  828        }
  829    }
  830
  831    fn disabled(&self) -> bool {
  832        matches!(*self, Self::Disabled)
  833    }
  834
  835    fn settings_visibility(&self) -> bool {
  836        match *self {
  837            Self::Enabled {
  838                setting_configuration,
  839                ..
  840            } => setting_configuration,
  841            _ => false,
  842        }
  843    }
  844
  845    fn visible(&self) -> bool {
  846        match *self {
  847            Self::Enabled {
  848                setting_configuration,
  849                toggle_override,
  850            } => setting_configuration ^ toggle_override,
  851            _ => false,
  852        }
  853    }
  854
  855    fn toggle_visibility(&self) -> Self {
  856        match *self {
  857            Self::Enabled {
  858                toggle_override,
  859                setting_configuration,
  860            } => Self::Enabled {
  861                setting_configuration,
  862                toggle_override: !toggle_override,
  863            },
  864            Self::Disabled => Self::Disabled,
  865        }
  866    }
  867}
  868
  869#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  870pub enum BufferSerialization {
  871    All,
  872    NonDirtyBuffers,
  873}
  874
  875impl BufferSerialization {
  876    fn new(restore_unsaved_buffers: bool) -> Self {
  877        if restore_unsaved_buffers {
  878            Self::All
  879        } else {
  880            Self::NonDirtyBuffers
  881        }
  882    }
  883}
  884
  885/// Addons allow storing per-editor state in other crates (e.g. Vim)
  886pub trait Addon: 'static {
  887    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  888
  889    fn render_buffer_header_controls(
  890        &self,
  891        _: &ExcerptInfo,
  892        _: &Window,
  893        _: &App,
  894    ) -> Option<AnyElement> {
  895        None
  896    }
  897
  898    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  899        None
  900    }
  901
  902    fn to_any(&self) -> &dyn std::any::Any;
  903
  904    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  905        None
  906    }
  907}
  908
  909struct ChangeLocation {
  910    current: Option<Vec<Anchor>>,
  911    original: Vec<Anchor>,
  912}
  913impl ChangeLocation {
  914    fn locations(&self) -> &[Anchor] {
  915        self.current.as_ref().unwrap_or(&self.original)
  916    }
  917}
  918
  919/// A set of caret positions, registered when the editor was edited.
  920pub struct ChangeList {
  921    changes: Vec<ChangeLocation>,
  922    /// Currently "selected" change.
  923    position: Option<usize>,
  924}
  925
  926impl ChangeList {
  927    pub fn new() -> Self {
  928        Self {
  929            changes: Vec::new(),
  930            position: None,
  931        }
  932    }
  933
  934    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  935    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  936    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  937        if self.changes.is_empty() {
  938            return None;
  939        }
  940
  941        let prev = self.position.unwrap_or(self.changes.len());
  942        let next = if direction == Direction::Prev {
  943            prev.saturating_sub(count)
  944        } else {
  945            (prev + count).min(self.changes.len() - 1)
  946        };
  947        self.position = Some(next);
  948        self.changes.get(next).map(|change| change.locations())
  949    }
  950
  951    /// Adds a new change to the list, resetting the change list position.
  952    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  953        self.position.take();
  954        if let Some(last) = self.changes.last_mut()
  955            && group
  956        {
  957            last.current = Some(new_positions)
  958        } else {
  959            self.changes.push(ChangeLocation {
  960                original: new_positions,
  961                current: None,
  962            });
  963        }
  964    }
  965
  966    pub fn last(&self) -> Option<&[Anchor]> {
  967        self.changes.last().map(|change| change.locations())
  968    }
  969
  970    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  971        self.changes.last().map(|change| change.original.as_slice())
  972    }
  973
  974    pub fn invert_last_group(&mut self) {
  975        if let Some(last) = self.changes.last_mut()
  976            && let Some(current) = last.current.as_mut()
  977        {
  978            mem::swap(&mut last.original, current);
  979        }
  980    }
  981}
  982
  983#[derive(Clone)]
  984struct InlineBlamePopoverState {
  985    scroll_handle: ScrollHandle,
  986    commit_message: Option<ParsedCommitMessage>,
  987    markdown: Entity<Markdown>,
  988}
  989
  990struct InlineBlamePopover {
  991    position: gpui::Point<Pixels>,
  992    hide_task: Option<Task<()>>,
  993    popover_bounds: Option<Bounds<Pixels>>,
  994    popover_state: InlineBlamePopoverState,
  995    keyboard_grace: bool,
  996}
  997
  998enum SelectionDragState {
  999    /// State when no drag related activity is detected.
 1000    None,
 1001    /// State when the mouse is down on a selection that is about to be dragged.
 1002    ReadyToDrag {
 1003        selection: Selection<Anchor>,
 1004        click_position: gpui::Point<Pixels>,
 1005        mouse_down_time: Instant,
 1006    },
 1007    /// State when the mouse is dragging the selection in the editor.
 1008    Dragging {
 1009        selection: Selection<Anchor>,
 1010        drop_cursor: Selection<Anchor>,
 1011        hide_drop_cursor: bool,
 1012    },
 1013}
 1014
 1015enum ColumnarSelectionState {
 1016    FromMouse {
 1017        selection_tail: Anchor,
 1018        display_point: Option<DisplayPoint>,
 1019    },
 1020    FromSelection {
 1021        selection_tail: Anchor,
 1022    },
 1023}
 1024
 1025/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1026/// a breakpoint on them.
 1027#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1028struct PhantomBreakpointIndicator {
 1029    display_row: DisplayRow,
 1030    /// There's a small debounce between hovering over the line and showing the indicator.
 1031    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1032    is_active: bool,
 1033    collides_with_existing_breakpoint: bool,
 1034}
 1035
 1036/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1037/// in diff view mode.
 1038#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1039pub(crate) struct PhantomDiffReviewIndicator {
 1040    /// The starting anchor of the selection (or the only row if not dragging).
 1041    pub start: Anchor,
 1042    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1043    pub end: Anchor,
 1044    /// There's a small debounce between hovering over the line and showing the indicator.
 1045    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1046    pub is_active: bool,
 1047}
 1048
 1049#[derive(Clone, Debug)]
 1050pub(crate) struct DiffReviewDragState {
 1051    pub start_anchor: Anchor,
 1052    pub current_anchor: Anchor,
 1053}
 1054
 1055impl DiffReviewDragState {
 1056    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1057        let start = self.start_anchor.to_display_point(snapshot).row();
 1058        let current = self.current_anchor.to_display_point(snapshot).row();
 1059
 1060        (start..=current).sorted()
 1061    }
 1062}
 1063
 1064/// Identifies a specific hunk in the diff buffer.
 1065/// Used as a key to group comments by their location.
 1066#[derive(Clone, Debug)]
 1067pub struct DiffHunkKey {
 1068    /// The file path (relative to worktree) this hunk belongs to.
 1069    pub file_path: Arc<util::rel_path::RelPath>,
 1070    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1071    pub hunk_start_anchor: Anchor,
 1072}
 1073
 1074/// A review comment stored locally before being sent to the Agent panel.
 1075#[derive(Clone)]
 1076pub struct StoredReviewComment {
 1077    /// Unique identifier for this comment (for edit/delete operations).
 1078    pub id: usize,
 1079    /// The comment text entered by the user.
 1080    pub comment: String,
 1081    /// Anchors for the code range being reviewed.
 1082    pub range: Range<Anchor>,
 1083    /// Timestamp when the comment was created (for chronological ordering).
 1084    pub created_at: Instant,
 1085    /// Whether this comment is currently being edited inline.
 1086    pub is_editing: bool,
 1087}
 1088
 1089impl StoredReviewComment {
 1090    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1091        Self {
 1092            id,
 1093            comment,
 1094            range: anchor_range,
 1095            created_at: Instant::now(),
 1096            is_editing: false,
 1097        }
 1098    }
 1099}
 1100
 1101/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1102pub(crate) struct DiffReviewOverlay {
 1103    pub anchor_range: Range<Anchor>,
 1104    /// The block ID for the overlay.
 1105    pub block_id: CustomBlockId,
 1106    /// The editor entity for the review input.
 1107    pub prompt_editor: Entity<Editor>,
 1108    /// The hunk key this overlay belongs to.
 1109    pub hunk_key: DiffHunkKey,
 1110    /// Whether the comments section is expanded.
 1111    pub comments_expanded: bool,
 1112    /// Editors for comments currently being edited inline.
 1113    /// Key: comment ID, Value: Editor entity for inline editing.
 1114    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1115    /// Subscriptions for inline edit editors' action handlers.
 1116    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1117    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1118    /// The current user's avatar URI for display in comment rows.
 1119    pub user_avatar_uri: Option<SharedUri>,
 1120    /// Subscription to keep the action handler alive.
 1121    _subscription: Subscription,
 1122}
 1123
 1124/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1125///
 1126/// See the [module level documentation](self) for more information.
 1127pub struct Editor {
 1128    focus_handle: FocusHandle,
 1129    last_focused_descendant: Option<WeakFocusHandle>,
 1130    /// The text buffer being edited
 1131    buffer: Entity<MultiBuffer>,
 1132    /// Map of how text in the buffer should be displayed.
 1133    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1134    pub display_map: Entity<DisplayMap>,
 1135    placeholder_display_map: Option<Entity<DisplayMap>>,
 1136    pub selections: SelectionsCollection,
 1137    pub scroll_manager: ScrollManager,
 1138    /// When inline assist editors are linked, they all render cursors because
 1139    /// typing enters text into each of them, even the ones that aren't focused.
 1140    pub(crate) show_cursor_when_unfocused: bool,
 1141    columnar_selection_state: Option<ColumnarSelectionState>,
 1142    add_selections_state: Option<AddSelectionsState>,
 1143    select_next_state: Option<SelectNextState>,
 1144    select_prev_state: Option<SelectNextState>,
 1145    selection_history: SelectionHistory,
 1146    defer_selection_effects: bool,
 1147    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1148    autoclose_regions: Vec<AutocloseRegion>,
 1149    snippet_stack: InvalidationStack<SnippetState>,
 1150    select_syntax_node_history: SelectSyntaxNodeHistory,
 1151    ime_transaction: Option<TransactionId>,
 1152    pub diagnostics_max_severity: DiagnosticSeverity,
 1153    active_diagnostics: ActiveDiagnostic,
 1154    show_inline_diagnostics: bool,
 1155    inline_diagnostics_update: Task<()>,
 1156    inline_diagnostics_enabled: bool,
 1157    diagnostics_enabled: bool,
 1158    word_completions_enabled: bool,
 1159    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1160    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1161    hard_wrap: Option<usize>,
 1162    project: Option<Entity<Project>>,
 1163    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1164    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1165    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1166    blink_manager: Entity<BlinkManager>,
 1167    show_cursor_names: bool,
 1168    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1169    pub show_local_selections: bool,
 1170    mode: EditorMode,
 1171    show_breadcrumbs: bool,
 1172    show_gutter: bool,
 1173    show_scrollbars: ScrollbarAxes,
 1174    minimap_visibility: MinimapVisibility,
 1175    offset_content: bool,
 1176    disable_expand_excerpt_buttons: bool,
 1177    delegate_expand_excerpts: bool,
 1178    delegate_stage_and_restore: bool,
 1179    delegate_open_excerpts: bool,
 1180    enable_lsp_data: bool,
 1181    enable_runnables: bool,
 1182    show_line_numbers: Option<bool>,
 1183    use_relative_line_numbers: Option<bool>,
 1184    show_git_diff_gutter: Option<bool>,
 1185    show_code_actions: Option<bool>,
 1186    show_runnables: Option<bool>,
 1187    show_breakpoints: Option<bool>,
 1188    show_diff_review_button: bool,
 1189    show_wrap_guides: Option<bool>,
 1190    show_indent_guides: Option<bool>,
 1191    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1192    highlight_order: usize,
 1193    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1194    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1195    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1196    scrollbar_marker_state: ScrollbarMarkerState,
 1197    active_indent_guides_state: ActiveIndentGuidesState,
 1198    nav_history: Option<ItemNavHistory>,
 1199    context_menu: RefCell<Option<CodeContextMenu>>,
 1200    context_menu_options: Option<ContextMenuOptions>,
 1201    mouse_context_menu: Option<MouseContextMenu>,
 1202    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1203    inline_blame_popover: Option<InlineBlamePopover>,
 1204    inline_blame_popover_show_task: Option<Task<()>>,
 1205    signature_help_state: SignatureHelpState,
 1206    auto_signature_help: Option<bool>,
 1207    find_all_references_task_sources: Vec<Anchor>,
 1208    next_completion_id: CompletionId,
 1209    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1210    code_actions_task: Option<Task<Result<()>>>,
 1211    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1212    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1213    debounced_selection_highlight_complete: bool,
 1214    document_highlights_task: Option<Task<()>>,
 1215    linked_editing_range_task: Option<Task<Option<()>>>,
 1216    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1217    pending_rename: Option<RenameState>,
 1218    searchable: bool,
 1219    cursor_shape: CursorShape,
 1220    /// Whether the cursor is offset one character to the left when something is
 1221    /// selected (needed for vim visual mode)
 1222    cursor_offset_on_selection: bool,
 1223    current_line_highlight: Option<CurrentLineHighlight>,
 1224    /// Whether to collapse search match ranges to just their start position.
 1225    /// When true, navigating to a match positions the cursor at the match
 1226    /// without selecting the matched text.
 1227    collapse_matches: bool,
 1228    autoindent_mode: Option<AutoindentMode>,
 1229    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1230    input_enabled: bool,
 1231    expects_character_input: bool,
 1232    use_modal_editing: bool,
 1233    read_only: bool,
 1234    leader_id: Option<CollaboratorId>,
 1235    remote_id: Option<ViewId>,
 1236    pub hover_state: HoverState,
 1237    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1238    prev_pressure_stage: Option<PressureStage>,
 1239    gutter_hovered: bool,
 1240    hovered_link_state: Option<HoveredLinkState>,
 1241    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1242    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1243    active_edit_prediction: Option<EditPredictionState>,
 1244    /// Used to prevent flickering as the user types while the menu is open
 1245    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1246    edit_prediction_settings: EditPredictionSettings,
 1247    edit_predictions_hidden_for_vim_mode: bool,
 1248    show_edit_predictions_override: Option<bool>,
 1249    show_completions_on_input_override: Option<bool>,
 1250    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1251    edit_prediction_preview: EditPredictionPreview,
 1252    in_leading_whitespace: bool,
 1253    next_inlay_id: usize,
 1254    next_color_inlay_id: usize,
 1255    _subscriptions: Vec<Subscription>,
 1256    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1257    gutter_dimensions: GutterDimensions,
 1258    style: Option<EditorStyle>,
 1259    text_style_refinement: Option<TextStyleRefinement>,
 1260    next_editor_action_id: EditorActionId,
 1261    editor_actions: Rc<
 1262        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1263    >,
 1264    use_autoclose: bool,
 1265    use_auto_surround: bool,
 1266    auto_replace_emoji_shortcode: bool,
 1267    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1268    show_git_blame_gutter: bool,
 1269    show_git_blame_inline: bool,
 1270    show_git_blame_inline_delay_task: Option<Task<()>>,
 1271    git_blame_inline_enabled: bool,
 1272    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1273    buffer_serialization: Option<BufferSerialization>,
 1274    show_selection_menu: Option<bool>,
 1275    blame: Option<Entity<GitBlame>>,
 1276    blame_subscription: Option<Subscription>,
 1277    custom_context_menu: Option<
 1278        Box<
 1279            dyn 'static
 1280                + Fn(
 1281                    &mut Self,
 1282                    DisplayPoint,
 1283                    &mut Window,
 1284                    &mut Context<Self>,
 1285                ) -> Option<Entity<ui::ContextMenu>>,
 1286        >,
 1287    >,
 1288    last_bounds: Option<Bounds<Pixels>>,
 1289    last_position_map: Option<Rc<PositionMap>>,
 1290    expect_bounds_change: Option<Bounds<Pixels>>,
 1291    runnables: RunnableData,
 1292    breakpoint_store: Option<Entity<BreakpointStore>>,
 1293    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1294    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1295    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1296    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1297    /// when hunks have comments stored.
 1298    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1299    /// Stored review comments grouped by hunk.
 1300    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1301    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1302    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1303    /// Counter for generating unique comment IDs.
 1304    next_review_comment_id: usize,
 1305    hovered_diff_hunk_row: Option<DisplayRow>,
 1306    pull_diagnostics_task: Task<()>,
 1307    in_project_search: bool,
 1308    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1309    breadcrumb_header: Option<String>,
 1310    focused_block: Option<FocusedBlock>,
 1311    next_scroll_position: NextScrollCursorCenterTopBottom,
 1312    addons: HashMap<TypeId, Box<dyn Addon>>,
 1313    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1314    load_diff_task: Option<Shared<Task<()>>>,
 1315    /// Whether we are temporarily displaying a diff other than git's
 1316    temporary_diff_override: bool,
 1317    selection_mark_mode: bool,
 1318    toggle_fold_multiple_buffers: Task<()>,
 1319    _scroll_cursor_center_top_bottom_task: Task<()>,
 1320    serialize_selections: Task<()>,
 1321    serialize_folds: Task<()>,
 1322    mouse_cursor_hidden: bool,
 1323    minimap: Option<Entity<Self>>,
 1324    hide_mouse_mode: HideMouseMode,
 1325    pub change_list: ChangeList,
 1326    inline_value_cache: InlineValueCache,
 1327    number_deleted_lines: bool,
 1328
 1329    selection_drag_state: SelectionDragState,
 1330    colors: Option<LspColorData>,
 1331    post_scroll_update: Task<()>,
 1332    refresh_colors_task: Task<()>,
 1333    use_document_folding_ranges: bool,
 1334    refresh_folding_ranges_task: Task<()>,
 1335    inlay_hints: Option<LspInlayHintData>,
 1336    folding_newlines: Task<()>,
 1337    select_next_is_case_sensitive: Option<bool>,
 1338    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1339    on_local_selections_changed:
 1340        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1341    suppress_selection_callback: bool,
 1342    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1343    accent_data: Option<AccentData>,
 1344    bracket_fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1345    semantic_token_state: SemanticTokenState,
 1346    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1347    refresh_document_symbols_task: Shared<Task<()>>,
 1348    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1349    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1350    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1351    sticky_headers_task: Task<()>,
 1352    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1353    pub(crate) colorize_brackets_task: Task<()>,
 1354}
 1355
 1356#[derive(Debug, PartialEq)]
 1357struct AccentData {
 1358    colors: AccentColors,
 1359    overrides: Vec<SharedString>,
 1360}
 1361
 1362fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1363    if debounce_ms > 0 {
 1364        Some(Duration::from_millis(debounce_ms))
 1365    } else {
 1366        None
 1367    }
 1368}
 1369
 1370#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1371enum NextScrollCursorCenterTopBottom {
 1372    #[default]
 1373    Center,
 1374    Top,
 1375    Bottom,
 1376}
 1377
 1378impl NextScrollCursorCenterTopBottom {
 1379    fn next(&self) -> Self {
 1380        match self {
 1381            Self::Center => Self::Top,
 1382            Self::Top => Self::Bottom,
 1383            Self::Bottom => Self::Center,
 1384        }
 1385    }
 1386}
 1387
 1388#[derive(Clone)]
 1389pub struct EditorSnapshot {
 1390    pub mode: EditorMode,
 1391    show_gutter: bool,
 1392    offset_content: bool,
 1393    show_line_numbers: Option<bool>,
 1394    number_deleted_lines: bool,
 1395    show_git_diff_gutter: Option<bool>,
 1396    show_code_actions: Option<bool>,
 1397    show_runnables: Option<bool>,
 1398    show_breakpoints: Option<bool>,
 1399    git_blame_gutter_max_author_length: Option<usize>,
 1400    pub display_snapshot: DisplaySnapshot,
 1401    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1402    is_focused: bool,
 1403    scroll_anchor: SharedScrollAnchor,
 1404    ongoing_scroll: OngoingScroll,
 1405    current_line_highlight: CurrentLineHighlight,
 1406    gutter_hovered: bool,
 1407    semantic_tokens_enabled: bool,
 1408}
 1409
 1410#[derive(Default, Debug, Clone, Copy)]
 1411pub struct GutterDimensions {
 1412    pub left_padding: Pixels,
 1413    pub right_padding: Pixels,
 1414    pub width: Pixels,
 1415    pub margin: Pixels,
 1416    pub git_blame_entries_width: Option<Pixels>,
 1417}
 1418
 1419impl GutterDimensions {
 1420    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1421        Self {
 1422            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1423            ..Default::default()
 1424        }
 1425    }
 1426
 1427    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1428        -cx.text_system().descent(font_id, font_size)
 1429    }
 1430    /// The full width of the space taken up by the gutter.
 1431    pub fn full_width(&self) -> Pixels {
 1432        self.margin + self.width
 1433    }
 1434
 1435    /// The width of the space reserved for the fold indicators,
 1436    /// use alongside 'justify_end' and `gutter_width` to
 1437    /// right align content with the line numbers
 1438    pub fn fold_area_width(&self) -> Pixels {
 1439        self.margin + self.right_padding
 1440    }
 1441}
 1442
 1443struct CharacterDimensions {
 1444    em_width: Pixels,
 1445    em_advance: Pixels,
 1446    line_height: Pixels,
 1447}
 1448
 1449#[derive(Debug)]
 1450pub struct RemoteSelection {
 1451    pub replica_id: ReplicaId,
 1452    pub selection: Selection<Anchor>,
 1453    pub cursor_shape: CursorShape,
 1454    pub collaborator_id: CollaboratorId,
 1455    pub line_mode: bool,
 1456    pub user_name: Option<SharedString>,
 1457    pub color: PlayerColor,
 1458}
 1459
 1460#[derive(Clone, Debug)]
 1461struct SelectionHistoryEntry {
 1462    selections: Arc<[Selection<Anchor>]>,
 1463    select_next_state: Option<SelectNextState>,
 1464    select_prev_state: Option<SelectNextState>,
 1465    add_selections_state: Option<AddSelectionsState>,
 1466}
 1467
 1468#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1469enum SelectionHistoryMode {
 1470    #[default]
 1471    Normal,
 1472    Undoing,
 1473    Redoing,
 1474    Skipping,
 1475}
 1476
 1477#[derive(Clone, PartialEq, Eq, Hash)]
 1478struct HoveredCursor {
 1479    replica_id: ReplicaId,
 1480    selection_id: usize,
 1481}
 1482
 1483#[derive(Debug)]
 1484/// SelectionEffects controls the side-effects of updating the selection.
 1485///
 1486/// The default behaviour does "what you mostly want":
 1487/// - it pushes to the nav history if the cursor moved by >10 lines
 1488/// - it re-triggers completion requests
 1489/// - it scrolls to fit
 1490///
 1491/// You might want to modify these behaviours. For example when doing a "jump"
 1492/// like go to definition, we always want to add to nav history; but when scrolling
 1493/// in vim mode we never do.
 1494///
 1495/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1496/// move.
 1497#[derive(Clone)]
 1498pub struct SelectionEffects {
 1499    nav_history: Option<bool>,
 1500    completions: bool,
 1501    scroll: Option<Autoscroll>,
 1502}
 1503
 1504impl Default for SelectionEffects {
 1505    fn default() -> Self {
 1506        Self {
 1507            nav_history: None,
 1508            completions: true,
 1509            scroll: Some(Autoscroll::fit()),
 1510        }
 1511    }
 1512}
 1513impl SelectionEffects {
 1514    pub fn scroll(scroll: Autoscroll) -> Self {
 1515        Self {
 1516            scroll: Some(scroll),
 1517            ..Default::default()
 1518        }
 1519    }
 1520
 1521    pub fn no_scroll() -> Self {
 1522        Self {
 1523            scroll: None,
 1524            ..Default::default()
 1525        }
 1526    }
 1527
 1528    pub fn completions(self, completions: bool) -> Self {
 1529        Self {
 1530            completions,
 1531            ..self
 1532        }
 1533    }
 1534
 1535    pub fn nav_history(self, nav_history: bool) -> Self {
 1536        Self {
 1537            nav_history: Some(nav_history),
 1538            ..self
 1539        }
 1540    }
 1541}
 1542
 1543struct DeferredSelectionEffectsState {
 1544    changed: bool,
 1545    effects: SelectionEffects,
 1546    old_cursor_position: Anchor,
 1547    history_entry: SelectionHistoryEntry,
 1548}
 1549
 1550#[derive(Default)]
 1551struct SelectionHistory {
 1552    #[allow(clippy::type_complexity)]
 1553    selections_by_transaction:
 1554        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1555    mode: SelectionHistoryMode,
 1556    undo_stack: VecDeque<SelectionHistoryEntry>,
 1557    redo_stack: VecDeque<SelectionHistoryEntry>,
 1558}
 1559
 1560impl SelectionHistory {
 1561    #[track_caller]
 1562    fn insert_transaction(
 1563        &mut self,
 1564        transaction_id: TransactionId,
 1565        selections: Arc<[Selection<Anchor>]>,
 1566    ) {
 1567        if selections.is_empty() {
 1568            log::error!(
 1569                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1570                std::panic::Location::caller()
 1571            );
 1572            return;
 1573        }
 1574        self.selections_by_transaction
 1575            .insert(transaction_id, (selections, None));
 1576    }
 1577
 1578    #[allow(clippy::type_complexity)]
 1579    fn transaction(
 1580        &self,
 1581        transaction_id: TransactionId,
 1582    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1583        self.selections_by_transaction.get(&transaction_id)
 1584    }
 1585
 1586    #[allow(clippy::type_complexity)]
 1587    fn transaction_mut(
 1588        &mut self,
 1589        transaction_id: TransactionId,
 1590    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1591        self.selections_by_transaction.get_mut(&transaction_id)
 1592    }
 1593
 1594    fn push(&mut self, entry: SelectionHistoryEntry) {
 1595        if !entry.selections.is_empty() {
 1596            match self.mode {
 1597                SelectionHistoryMode::Normal => {
 1598                    self.push_undo(entry);
 1599                    self.redo_stack.clear();
 1600                }
 1601                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1602                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1603                SelectionHistoryMode::Skipping => {}
 1604            }
 1605        }
 1606    }
 1607
 1608    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1609        if self
 1610            .undo_stack
 1611            .back()
 1612            .is_none_or(|e| e.selections != entry.selections)
 1613        {
 1614            self.undo_stack.push_back(entry);
 1615            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1616                self.undo_stack.pop_front();
 1617            }
 1618        }
 1619    }
 1620
 1621    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1622        if self
 1623            .redo_stack
 1624            .back()
 1625            .is_none_or(|e| e.selections != entry.selections)
 1626        {
 1627            self.redo_stack.push_back(entry);
 1628            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1629                self.redo_stack.pop_front();
 1630            }
 1631        }
 1632    }
 1633}
 1634
 1635#[derive(Clone, Copy)]
 1636pub struct RowHighlightOptions {
 1637    pub autoscroll: bool,
 1638    pub include_gutter: bool,
 1639}
 1640
 1641impl Default for RowHighlightOptions {
 1642    fn default() -> Self {
 1643        Self {
 1644            autoscroll: Default::default(),
 1645            include_gutter: true,
 1646        }
 1647    }
 1648}
 1649
 1650struct RowHighlight {
 1651    index: usize,
 1652    range: Range<Anchor>,
 1653    color: Hsla,
 1654    options: RowHighlightOptions,
 1655    type_id: TypeId,
 1656}
 1657
 1658#[derive(Clone, Debug)]
 1659struct AddSelectionsState {
 1660    groups: Vec<AddSelectionsGroup>,
 1661}
 1662
 1663#[derive(Clone, Debug)]
 1664struct AddSelectionsGroup {
 1665    above: bool,
 1666    stack: Vec<usize>,
 1667}
 1668
 1669#[derive(Clone)]
 1670struct SelectNextState {
 1671    query: AhoCorasick,
 1672    wordwise: bool,
 1673    done: bool,
 1674}
 1675
 1676impl std::fmt::Debug for SelectNextState {
 1677    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1678        f.debug_struct(std::any::type_name::<Self>())
 1679            .field("wordwise", &self.wordwise)
 1680            .field("done", &self.done)
 1681            .finish()
 1682    }
 1683}
 1684
 1685#[derive(Debug)]
 1686struct AutocloseRegion {
 1687    selection_id: usize,
 1688    range: Range<Anchor>,
 1689    pair: BracketPair,
 1690}
 1691
 1692#[derive(Debug)]
 1693struct SnippetState {
 1694    ranges: Vec<Vec<Range<Anchor>>>,
 1695    active_index: usize,
 1696    choices: Vec<Option<Vec<String>>>,
 1697}
 1698
 1699#[doc(hidden)]
 1700pub struct RenameState {
 1701    pub range: Range<Anchor>,
 1702    pub old_name: Arc<str>,
 1703    pub editor: Entity<Editor>,
 1704    block_id: CustomBlockId,
 1705}
 1706
 1707struct InvalidationStack<T>(Vec<T>);
 1708
 1709struct RegisteredEditPredictionDelegate {
 1710    provider: Arc<dyn EditPredictionDelegateHandle>,
 1711    _subscription: Subscription,
 1712}
 1713
 1714#[derive(Debug, PartialEq, Eq)]
 1715pub struct ActiveDiagnosticGroup {
 1716    pub active_range: Range<Anchor>,
 1717    pub active_message: String,
 1718    pub group_id: usize,
 1719    pub blocks: HashSet<CustomBlockId>,
 1720}
 1721
 1722#[derive(Debug, PartialEq, Eq)]
 1723
 1724pub(crate) enum ActiveDiagnostic {
 1725    None,
 1726    All,
 1727    Group(ActiveDiagnosticGroup),
 1728}
 1729
 1730#[derive(Serialize, Deserialize, Clone, Debug)]
 1731pub struct ClipboardSelection {
 1732    /// The number of bytes in this selection.
 1733    pub len: usize,
 1734    /// Whether this was a full-line selection.
 1735    pub is_entire_line: bool,
 1736    /// The indentation of the first line when this content was originally copied.
 1737    pub first_line_indent: u32,
 1738    #[serde(default)]
 1739    pub file_path: Option<PathBuf>,
 1740    #[serde(default)]
 1741    pub line_range: Option<RangeInclusive<u32>>,
 1742}
 1743
 1744impl ClipboardSelection {
 1745    pub fn for_buffer(
 1746        len: usize,
 1747        is_entire_line: bool,
 1748        range: Range<Point>,
 1749        buffer: &MultiBufferSnapshot,
 1750        project: Option<&Entity<Project>>,
 1751        cx: &App,
 1752    ) -> Self {
 1753        let first_line_indent = buffer
 1754            .indent_size_for_line(MultiBufferRow(range.start.row))
 1755            .len;
 1756
 1757        let file_path = util::maybe!({
 1758            let project = project?.read(cx);
 1759            let file = buffer.file_at(range.start)?;
 1760            let project_path = ProjectPath {
 1761                worktree_id: file.worktree_id(cx),
 1762                path: file.path().clone(),
 1763            };
 1764            project.absolute_path(&project_path, cx)
 1765        });
 1766
 1767        let line_range = file_path.as_ref().and_then(|_| {
 1768            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1769            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1770            if start_excerpt_id == end_excerpt_id {
 1771                Some(start_point.row..=end_point.row)
 1772            } else {
 1773                None
 1774            }
 1775        });
 1776
 1777        Self {
 1778            len,
 1779            is_entire_line,
 1780            first_line_indent,
 1781            file_path,
 1782            line_range,
 1783        }
 1784    }
 1785}
 1786
 1787// selections, scroll behavior, was newest selection reversed
 1788type SelectSyntaxNodeHistoryState = (
 1789    Box<[Selection<Anchor>]>,
 1790    SelectSyntaxNodeScrollBehavior,
 1791    bool,
 1792);
 1793
 1794#[derive(Default)]
 1795struct SelectSyntaxNodeHistory {
 1796    stack: Vec<SelectSyntaxNodeHistoryState>,
 1797    // disable temporarily to allow changing selections without losing the stack
 1798    pub disable_clearing: bool,
 1799}
 1800
 1801impl SelectSyntaxNodeHistory {
 1802    pub fn try_clear(&mut self) {
 1803        if !self.disable_clearing {
 1804            self.stack.clear();
 1805        }
 1806    }
 1807
 1808    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1809        self.stack.push(selection);
 1810    }
 1811
 1812    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1813        self.stack.pop()
 1814    }
 1815}
 1816
 1817enum SelectSyntaxNodeScrollBehavior {
 1818    CursorTop,
 1819    FitSelection,
 1820    CursorBottom,
 1821}
 1822
 1823#[derive(Debug, Clone, Copy)]
 1824pub(crate) struct NavigationData {
 1825    cursor_anchor: Anchor,
 1826    cursor_position: Point,
 1827    scroll_anchor: ScrollAnchor,
 1828    scroll_top_row: u32,
 1829}
 1830
 1831#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1832pub enum GotoDefinitionKind {
 1833    Symbol,
 1834    Declaration,
 1835    Type,
 1836    Implementation,
 1837}
 1838
 1839pub enum FormatTarget {
 1840    Buffers(HashSet<Entity<Buffer>>),
 1841    Ranges(Vec<Range<MultiBufferPoint>>),
 1842}
 1843
 1844pub(crate) struct FocusedBlock {
 1845    id: BlockId,
 1846    focus_handle: WeakFocusHandle,
 1847}
 1848
 1849#[derive(Clone, Debug)]
 1850pub enum JumpData {
 1851    MultiBufferRow {
 1852        row: MultiBufferRow,
 1853        line_offset_from_top: u32,
 1854    },
 1855    MultiBufferPoint {
 1856        excerpt_id: ExcerptId,
 1857        position: Point,
 1858        anchor: text::Anchor,
 1859        line_offset_from_top: u32,
 1860    },
 1861}
 1862
 1863pub enum MultibufferSelectionMode {
 1864    First,
 1865    All,
 1866}
 1867
 1868#[derive(Clone, Copy, Debug, Default)]
 1869pub struct RewrapOptions {
 1870    pub override_language_settings: bool,
 1871    pub preserve_existing_whitespace: bool,
 1872}
 1873
 1874impl Editor {
 1875    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1876        let buffer = cx.new(|cx| Buffer::local("", cx));
 1877        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1878        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1879    }
 1880
 1881    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1882        let buffer = cx.new(|cx| Buffer::local("", cx));
 1883        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1884        Self::new(EditorMode::full(), buffer, None, window, cx)
 1885    }
 1886
 1887    pub fn auto_height(
 1888        min_lines: usize,
 1889        max_lines: usize,
 1890        window: &mut Window,
 1891        cx: &mut Context<Self>,
 1892    ) -> Self {
 1893        let buffer = cx.new(|cx| Buffer::local("", cx));
 1894        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1895        Self::new(
 1896            EditorMode::AutoHeight {
 1897                min_lines,
 1898                max_lines: Some(max_lines),
 1899            },
 1900            buffer,
 1901            None,
 1902            window,
 1903            cx,
 1904        )
 1905    }
 1906
 1907    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1908    /// The editor grows as tall as needed to fit its content.
 1909    pub fn auto_height_unbounded(
 1910        min_lines: usize,
 1911        window: &mut Window,
 1912        cx: &mut Context<Self>,
 1913    ) -> Self {
 1914        let buffer = cx.new(|cx| Buffer::local("", cx));
 1915        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1916        Self::new(
 1917            EditorMode::AutoHeight {
 1918                min_lines,
 1919                max_lines: None,
 1920            },
 1921            buffer,
 1922            None,
 1923            window,
 1924            cx,
 1925        )
 1926    }
 1927
 1928    pub fn for_buffer(
 1929        buffer: Entity<Buffer>,
 1930        project: Option<Entity<Project>>,
 1931        window: &mut Window,
 1932        cx: &mut Context<Self>,
 1933    ) -> Self {
 1934        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1935        Self::new(EditorMode::full(), buffer, project, window, cx)
 1936    }
 1937
 1938    pub fn for_multibuffer(
 1939        buffer: Entity<MultiBuffer>,
 1940        project: Option<Entity<Project>>,
 1941        window: &mut Window,
 1942        cx: &mut Context<Self>,
 1943    ) -> Self {
 1944        Self::new(EditorMode::full(), buffer, project, window, cx)
 1945    }
 1946
 1947    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1948        let mut clone = Self::new(
 1949            self.mode.clone(),
 1950            self.buffer.clone(),
 1951            self.project.clone(),
 1952            window,
 1953            cx,
 1954        );
 1955        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1956            let snapshot = display_map.snapshot(cx);
 1957            clone.display_map.update(cx, |display_map, cx| {
 1958                display_map.set_state(&snapshot, cx);
 1959            });
 1960            snapshot
 1961        });
 1962        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1963        clone.folds_did_change(cx);
 1964        clone.selections.clone_state(&self.selections);
 1965        clone
 1966            .scroll_manager
 1967            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1968        clone.searchable = self.searchable;
 1969        clone.read_only = self.read_only;
 1970        clone.buffers_with_disabled_indent_guides =
 1971            self.buffers_with_disabled_indent_guides.clone();
 1972        clone
 1973    }
 1974
 1975    pub fn new(
 1976        mode: EditorMode,
 1977        buffer: Entity<MultiBuffer>,
 1978        project: Option<Entity<Project>>,
 1979        window: &mut Window,
 1980        cx: &mut Context<Self>,
 1981    ) -> Self {
 1982        Editor::new_internal(mode, buffer, project, None, window, cx)
 1983    }
 1984
 1985    pub fn refresh_sticky_headers(
 1986        &mut self,
 1987        display_snapshot: &DisplaySnapshot,
 1988        cx: &mut Context<Editor>,
 1989    ) {
 1990        if !self.mode.is_full() {
 1991            return;
 1992        }
 1993        let multi_buffer = display_snapshot.buffer_snapshot();
 1994        let scroll_anchor = self
 1995            .scroll_manager
 1996            .native_anchor(display_snapshot, cx)
 1997            .anchor;
 1998        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 1999            return;
 2000        };
 2001        let buffer = buffer.clone();
 2002
 2003        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2004        let max_row = buffer.max_point().row;
 2005        let start_row = buffer_visible_start.row.min(max_row);
 2006        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2007
 2008        let syntax = self.style(cx).syntax.clone();
 2009        let background_task = cx.background_spawn(async move {
 2010            buffer
 2011                .outline_items_containing(
 2012                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2013                    true,
 2014                    Some(syntax.as_ref()),
 2015                )
 2016                .into_iter()
 2017                .map(|outline_item| OutlineItem {
 2018                    depth: outline_item.depth,
 2019                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2020                    source_range_for_text: Anchor::range_in_buffer(
 2021                        excerpt_id,
 2022                        outline_item.source_range_for_text,
 2023                    ),
 2024                    text: outline_item.text,
 2025                    highlight_ranges: outline_item.highlight_ranges,
 2026                    name_ranges: outline_item.name_ranges,
 2027                    body_range: outline_item
 2028                        .body_range
 2029                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2030                    annotation_range: outline_item
 2031                        .annotation_range
 2032                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2033                })
 2034                .collect()
 2035        });
 2036        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2037            let sticky_headers = background_task.await;
 2038            this.update(cx, |this, cx| {
 2039                this.sticky_headers = Some(sticky_headers);
 2040                cx.notify();
 2041            })
 2042            .ok();
 2043        });
 2044    }
 2045
 2046    fn new_internal(
 2047        mode: EditorMode,
 2048        multi_buffer: Entity<MultiBuffer>,
 2049        project: Option<Entity<Project>>,
 2050        display_map: Option<Entity<DisplayMap>>,
 2051        window: &mut Window,
 2052        cx: &mut Context<Self>,
 2053    ) -> Self {
 2054        debug_assert!(
 2055            display_map.is_none() || mode.is_minimap(),
 2056            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2057        );
 2058
 2059        let full_mode = mode.is_full();
 2060        let is_minimap = mode.is_minimap();
 2061        let diagnostics_max_severity = if full_mode {
 2062            EditorSettings::get_global(cx)
 2063                .diagnostics_max_severity
 2064                .unwrap_or(DiagnosticSeverity::Hint)
 2065        } else {
 2066            DiagnosticSeverity::Off
 2067        };
 2068        let style = window.text_style();
 2069        let font_size = style.font_size.to_pixels(window.rem_size());
 2070        let editor = cx.entity().downgrade();
 2071        let fold_placeholder = FoldPlaceholder {
 2072            constrain_width: false,
 2073            render: Arc::new(move |fold_id, fold_range, cx| {
 2074                let editor = editor.clone();
 2075                FoldPlaceholder::fold_element(fold_id, cx)
 2076                    .cursor_pointer()
 2077                    .child("")
 2078                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2079                    .on_click(move |_, _window, cx| {
 2080                        editor
 2081                            .update(cx, |editor, cx| {
 2082                                editor.unfold_ranges(
 2083                                    &[fold_range.start..fold_range.end],
 2084                                    true,
 2085                                    false,
 2086                                    cx,
 2087                                );
 2088                                cx.stop_propagation();
 2089                            })
 2090                            .ok();
 2091                    })
 2092                    .into_any()
 2093            }),
 2094            merge_adjacent: true,
 2095            ..FoldPlaceholder::default()
 2096        };
 2097        let display_map = display_map.unwrap_or_else(|| {
 2098            cx.new(|cx| {
 2099                DisplayMap::new(
 2100                    multi_buffer.clone(),
 2101                    style.font(),
 2102                    font_size,
 2103                    None,
 2104                    FILE_HEADER_HEIGHT,
 2105                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2106                    fold_placeholder,
 2107                    diagnostics_max_severity,
 2108                    cx,
 2109                )
 2110            })
 2111        });
 2112
 2113        let selections = SelectionsCollection::new();
 2114
 2115        let blink_manager = cx.new(|cx| {
 2116            let mut blink_manager = BlinkManager::new(
 2117                CURSOR_BLINK_INTERVAL,
 2118                |cx| EditorSettings::get_global(cx).cursor_blink,
 2119                cx,
 2120            );
 2121            if is_minimap {
 2122                blink_manager.disable(cx);
 2123            }
 2124            blink_manager
 2125        });
 2126
 2127        let soft_wrap_mode_override =
 2128            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2129
 2130        let mut project_subscriptions = Vec::new();
 2131        if full_mode && let Some(project) = project.as_ref() {
 2132            project_subscriptions.push(cx.subscribe_in(
 2133                project,
 2134                window,
 2135                |editor, _, event, window, cx| match event {
 2136                    project::Event::RefreshCodeLens => {
 2137                        // we always query lens with actions, without storing them, always refreshing them
 2138                    }
 2139                    project::Event::RefreshInlayHints {
 2140                        server_id,
 2141                        request_id,
 2142                    } => {
 2143                        editor.refresh_inlay_hints(
 2144                            InlayHintRefreshReason::RefreshRequested {
 2145                                server_id: *server_id,
 2146                                request_id: *request_id,
 2147                            },
 2148                            cx,
 2149                        );
 2150                    }
 2151                    project::Event::RefreshSemanticTokens {
 2152                        server_id,
 2153                        request_id,
 2154                    } => {
 2155                        editor.refresh_semantic_tokens(
 2156                            None,
 2157                            Some(RefreshForServer {
 2158                                server_id: *server_id,
 2159                                request_id: *request_id,
 2160                            }),
 2161                            cx,
 2162                        );
 2163                    }
 2164                    project::Event::LanguageServerRemoved(_) => {
 2165                        editor.registered_buffers.clear();
 2166                        editor.register_visible_buffers(cx);
 2167                        editor.invalidate_semantic_tokens(None);
 2168                        editor.refresh_runnables(None, window, cx);
 2169                        editor.update_lsp_data(None, window, cx);
 2170                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2171                    }
 2172                    project::Event::SnippetEdit(id, snippet_edits) => {
 2173                        // todo(lw): Non singletons
 2174                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2175                            let snapshot = buffer.read(cx).snapshot();
 2176                            let focus_handle = editor.focus_handle(cx);
 2177                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2178                                for (range, snippet) in snippet_edits {
 2179                                    let buffer_range =
 2180                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2181                                    editor
 2182                                        .insert_snippet(
 2183                                            &[MultiBufferOffset(buffer_range.start)
 2184                                                ..MultiBufferOffset(buffer_range.end)],
 2185                                            snippet.clone(),
 2186                                            window,
 2187                                            cx,
 2188                                        )
 2189                                        .ok();
 2190                                }
 2191                            }
 2192                        }
 2193                    }
 2194                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2195                        let buffer_id = *buffer_id;
 2196                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2197                            editor.register_buffer(buffer_id, cx);
 2198                            editor.refresh_runnables(Some(buffer_id), window, cx);
 2199                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2200                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2201                            refresh_linked_ranges(editor, window, cx);
 2202                            editor.refresh_code_actions(window, cx);
 2203                            editor.refresh_document_highlights(cx);
 2204                        }
 2205                    }
 2206
 2207                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2208                        let Some(workspace) = editor.workspace() else {
 2209                            return;
 2210                        };
 2211                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2212                        else {
 2213                            return;
 2214                        };
 2215
 2216                        if active_editor.entity_id() == cx.entity_id() {
 2217                            let entity_id = cx.entity_id();
 2218                            workspace.update(cx, |this, cx| {
 2219                                this.panes_mut()
 2220                                    .iter_mut()
 2221                                    .filter(|pane| pane.entity_id() != entity_id)
 2222                                    .for_each(|p| {
 2223                                        p.update(cx, |pane, _| {
 2224                                            pane.nav_history_mut().rename_item(
 2225                                                entity_id,
 2226                                                project_path.clone(),
 2227                                                abs_path.clone().into(),
 2228                                            );
 2229                                        })
 2230                                    });
 2231                            });
 2232
 2233                            Self::open_transaction_for_hidden_buffers(
 2234                                workspace,
 2235                                transaction.clone(),
 2236                                "Rename".to_string(),
 2237                                window,
 2238                                cx,
 2239                            );
 2240                        }
 2241                    }
 2242
 2243                    project::Event::WorkspaceEditApplied(transaction) => {
 2244                        let Some(workspace) = editor.workspace() else {
 2245                            return;
 2246                        };
 2247                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2248                        else {
 2249                            return;
 2250                        };
 2251
 2252                        if active_editor.entity_id() == cx.entity_id() {
 2253                            Self::open_transaction_for_hidden_buffers(
 2254                                workspace,
 2255                                transaction.clone(),
 2256                                "LSP Edit".to_string(),
 2257                                window,
 2258                                cx,
 2259                            );
 2260                        }
 2261                    }
 2262
 2263                    _ => {}
 2264                },
 2265            ));
 2266            if let Some(task_inventory) = project
 2267                .read(cx)
 2268                .task_store()
 2269                .read(cx)
 2270                .task_inventory()
 2271                .cloned()
 2272            {
 2273                project_subscriptions.push(cx.observe_in(
 2274                    &task_inventory,
 2275                    window,
 2276                    |editor, _, window, cx| {
 2277                        editor.refresh_runnables(None, window, cx);
 2278                    },
 2279                ));
 2280            };
 2281
 2282            project_subscriptions.push(cx.subscribe_in(
 2283                &project.read(cx).breakpoint_store(),
 2284                window,
 2285                |editor, _, event, window, cx| match event {
 2286                    BreakpointStoreEvent::ClearDebugLines => {
 2287                        editor.clear_row_highlights::<ActiveDebugLine>();
 2288                        editor.refresh_inline_values(cx);
 2289                    }
 2290                    BreakpointStoreEvent::SetDebugLine => {
 2291                        if editor.go_to_active_debug_line(window, cx) {
 2292                            cx.stop_propagation();
 2293                        }
 2294
 2295                        editor.refresh_inline_values(cx);
 2296                    }
 2297                    _ => {}
 2298                },
 2299            ));
 2300            let git_store = project.read(cx).git_store().clone();
 2301            let project = project.clone();
 2302            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2303                if let GitStoreEvent::RepositoryAdded = event {
 2304                    this.load_diff_task = Some(
 2305                        update_uncommitted_diff_for_buffer(
 2306                            cx.entity(),
 2307                            &project,
 2308                            this.buffer.read(cx).all_buffers(),
 2309                            this.buffer.clone(),
 2310                            cx,
 2311                        )
 2312                        .shared(),
 2313                    );
 2314                }
 2315            }));
 2316        }
 2317
 2318        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2319
 2320        let inlay_hint_settings =
 2321            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2322        let focus_handle = cx.focus_handle();
 2323        if !is_minimap {
 2324            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2325                .detach();
 2326            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2327                .detach();
 2328            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2329                .detach();
 2330            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2331                .detach();
 2332            cx.observe_pending_input(window, Self::observe_pending_input)
 2333                .detach();
 2334        }
 2335
 2336        let show_indent_guides =
 2337            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2338                Some(false)
 2339            } else {
 2340                None
 2341            };
 2342
 2343        let breakpoint_store = match (&mode, project.as_ref()) {
 2344            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2345            _ => None,
 2346        };
 2347
 2348        let mut code_action_providers = Vec::new();
 2349        let mut load_uncommitted_diff = None;
 2350        if let Some(project) = project.clone() {
 2351            load_uncommitted_diff = Some(
 2352                update_uncommitted_diff_for_buffer(
 2353                    cx.entity(),
 2354                    &project,
 2355                    multi_buffer.read(cx).all_buffers(),
 2356                    multi_buffer.clone(),
 2357                    cx,
 2358                )
 2359                .shared(),
 2360            );
 2361            code_action_providers.push(Rc::new(project) as Rc<_>);
 2362        }
 2363
 2364        let mut editor = Self {
 2365            focus_handle,
 2366            show_cursor_when_unfocused: false,
 2367            last_focused_descendant: None,
 2368            buffer: multi_buffer.clone(),
 2369            display_map: display_map.clone(),
 2370            placeholder_display_map: None,
 2371            selections,
 2372            scroll_manager: ScrollManager::new(cx),
 2373            columnar_selection_state: None,
 2374            add_selections_state: None,
 2375            select_next_state: None,
 2376            select_prev_state: None,
 2377            selection_history: SelectionHistory::default(),
 2378            defer_selection_effects: false,
 2379            deferred_selection_effects_state: None,
 2380            autoclose_regions: Vec::new(),
 2381            snippet_stack: InvalidationStack::default(),
 2382            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2383            ime_transaction: None,
 2384            active_diagnostics: ActiveDiagnostic::None,
 2385            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2386            inline_diagnostics_update: Task::ready(()),
 2387            inline_diagnostics: Vec::new(),
 2388            soft_wrap_mode_override,
 2389            diagnostics_max_severity,
 2390            hard_wrap: None,
 2391            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2392            semantics_provider: project
 2393                .as_ref()
 2394                .map(|project| Rc::new(project.downgrade()) as _),
 2395            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2396            project,
 2397            blink_manager: blink_manager.clone(),
 2398            show_local_selections: true,
 2399            show_scrollbars: ScrollbarAxes {
 2400                horizontal: full_mode,
 2401                vertical: full_mode,
 2402            },
 2403            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2404            offset_content: !matches!(mode, EditorMode::SingleLine),
 2405            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2406            show_gutter: full_mode,
 2407            show_line_numbers: (!full_mode).then_some(false),
 2408            use_relative_line_numbers: None,
 2409            disable_expand_excerpt_buttons: !full_mode,
 2410            delegate_expand_excerpts: false,
 2411            delegate_stage_and_restore: false,
 2412            delegate_open_excerpts: false,
 2413            enable_lsp_data: true,
 2414            enable_runnables: true,
 2415            show_git_diff_gutter: None,
 2416            show_code_actions: None,
 2417            show_runnables: None,
 2418            show_breakpoints: None,
 2419            show_diff_review_button: false,
 2420            show_wrap_guides: None,
 2421            show_indent_guides,
 2422            buffers_with_disabled_indent_guides: HashSet::default(),
 2423            highlight_order: 0,
 2424            highlighted_rows: HashMap::default(),
 2425            background_highlights: HashMap::default(),
 2426            gutter_highlights: HashMap::default(),
 2427            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2428            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2429            nav_history: None,
 2430            context_menu: RefCell::new(None),
 2431            context_menu_options: None,
 2432            mouse_context_menu: None,
 2433            completion_tasks: Vec::new(),
 2434            inline_blame_popover: None,
 2435            inline_blame_popover_show_task: None,
 2436            signature_help_state: SignatureHelpState::default(),
 2437            auto_signature_help: None,
 2438            find_all_references_task_sources: Vec::new(),
 2439            next_completion_id: 0,
 2440            next_inlay_id: 0,
 2441            code_action_providers,
 2442            available_code_actions: None,
 2443            code_actions_task: None,
 2444            quick_selection_highlight_task: None,
 2445            debounced_selection_highlight_task: None,
 2446            debounced_selection_highlight_complete: false,
 2447            document_highlights_task: None,
 2448            linked_editing_range_task: None,
 2449            pending_rename: None,
 2450            searchable: !is_minimap,
 2451            cursor_shape: EditorSettings::get_global(cx)
 2452                .cursor_shape
 2453                .unwrap_or_default(),
 2454            cursor_offset_on_selection: false,
 2455            current_line_highlight: None,
 2456            autoindent_mode: Some(AutoindentMode::EachLine),
 2457            collapse_matches: false,
 2458            workspace: None,
 2459            input_enabled: !is_minimap,
 2460            expects_character_input: !is_minimap,
 2461            use_modal_editing: full_mode,
 2462            read_only: is_minimap,
 2463            use_autoclose: true,
 2464            use_auto_surround: true,
 2465            auto_replace_emoji_shortcode: false,
 2466            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2467            leader_id: None,
 2468            remote_id: None,
 2469            hover_state: HoverState::default(),
 2470            pending_mouse_down: None,
 2471            prev_pressure_stage: None,
 2472            hovered_link_state: None,
 2473            edit_prediction_provider: None,
 2474            active_edit_prediction: None,
 2475            stale_edit_prediction_in_menu: None,
 2476            edit_prediction_preview: EditPredictionPreview::Inactive {
 2477                released_too_fast: false,
 2478            },
 2479            inline_diagnostics_enabled: full_mode,
 2480            diagnostics_enabled: full_mode,
 2481            word_completions_enabled: full_mode,
 2482            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2483            gutter_hovered: false,
 2484            pixel_position_of_newest_cursor: None,
 2485            last_bounds: None,
 2486            last_position_map: None,
 2487            expect_bounds_change: None,
 2488            gutter_dimensions: GutterDimensions::default(),
 2489            style: None,
 2490            show_cursor_names: false,
 2491            hovered_cursors: HashMap::default(),
 2492            next_editor_action_id: EditorActionId::default(),
 2493            editor_actions: Rc::default(),
 2494            edit_predictions_hidden_for_vim_mode: false,
 2495            show_edit_predictions_override: None,
 2496            show_completions_on_input_override: None,
 2497            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2498            edit_prediction_settings: EditPredictionSettings::Disabled,
 2499            in_leading_whitespace: false,
 2500            custom_context_menu: None,
 2501            show_git_blame_gutter: false,
 2502            show_git_blame_inline: false,
 2503            show_selection_menu: None,
 2504            show_git_blame_inline_delay_task: None,
 2505            git_blame_inline_enabled: full_mode
 2506                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2507            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2508            buffer_serialization: is_minimap.not().then(|| {
 2509                BufferSerialization::new(
 2510                    ProjectSettings::get_global(cx)
 2511                        .session
 2512                        .restore_unsaved_buffers,
 2513                )
 2514            }),
 2515            blame: None,
 2516            blame_subscription: None,
 2517
 2518            breakpoint_store,
 2519            gutter_breakpoint_indicator: (None, None),
 2520            gutter_diff_review_indicator: (None, None),
 2521            diff_review_drag_state: None,
 2522            diff_review_overlays: Vec::new(),
 2523            stored_review_comments: Vec::new(),
 2524            next_review_comment_id: 0,
 2525            hovered_diff_hunk_row: None,
 2526            _subscriptions: (!is_minimap)
 2527                .then(|| {
 2528                    vec![
 2529                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2530                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2531                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2532                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2533                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2534                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2535                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2536                        cx.observe_window_activation(window, |editor, window, cx| {
 2537                            let active = window.is_window_active();
 2538                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2539                                if active {
 2540                                    blink_manager.enable(cx);
 2541                                } else {
 2542                                    blink_manager.disable(cx);
 2543                                }
 2544                            });
 2545                            if active {
 2546                                editor.show_mouse_cursor(cx);
 2547                            }
 2548                        }),
 2549                    ]
 2550                })
 2551                .unwrap_or_default(),
 2552            runnables: RunnableData::new(),
 2553            pull_diagnostics_task: Task::ready(()),
 2554            colors: None,
 2555            refresh_colors_task: Task::ready(()),
 2556            use_document_folding_ranges: false,
 2557            refresh_folding_ranges_task: Task::ready(()),
 2558            inlay_hints: None,
 2559            next_color_inlay_id: 0,
 2560            post_scroll_update: Task::ready(()),
 2561            linked_edit_ranges: Default::default(),
 2562            in_project_search: false,
 2563            previous_search_ranges: None,
 2564            breadcrumb_header: None,
 2565            focused_block: None,
 2566            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2567            addons: HashMap::default(),
 2568            registered_buffers: HashMap::default(),
 2569            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2570            selection_mark_mode: false,
 2571            toggle_fold_multiple_buffers: Task::ready(()),
 2572            serialize_selections: Task::ready(()),
 2573            serialize_folds: Task::ready(()),
 2574            text_style_refinement: None,
 2575            load_diff_task: load_uncommitted_diff,
 2576            temporary_diff_override: false,
 2577            mouse_cursor_hidden: false,
 2578            minimap: None,
 2579            hide_mouse_mode: EditorSettings::get_global(cx)
 2580                .hide_mouse
 2581                .unwrap_or_default(),
 2582            change_list: ChangeList::new(),
 2583            mode,
 2584            selection_drag_state: SelectionDragState::None,
 2585            folding_newlines: Task::ready(()),
 2586            lookup_key: None,
 2587            select_next_is_case_sensitive: None,
 2588            on_local_selections_changed: None,
 2589            suppress_selection_callback: false,
 2590            applicable_language_settings: HashMap::default(),
 2591            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2592            accent_data: None,
 2593            bracket_fetched_tree_sitter_chunks: HashMap::default(),
 2594            number_deleted_lines: false,
 2595            refresh_matching_bracket_highlights_task: Task::ready(()),
 2596            refresh_document_symbols_task: Task::ready(()).shared(),
 2597            lsp_document_symbols: HashMap::default(),
 2598            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2599            outline_symbols_at_cursor: None,
 2600            sticky_headers_task: Task::ready(()),
 2601            sticky_headers: None,
 2602            colorize_brackets_task: Task::ready(()),
 2603        };
 2604
 2605        if is_minimap {
 2606            return editor;
 2607        }
 2608
 2609        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2610        editor.accent_data = editor.fetch_accent_data(cx);
 2611
 2612        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2613            editor
 2614                ._subscriptions
 2615                .push(cx.observe(breakpoints, |_, _, cx| {
 2616                    cx.notify();
 2617                }));
 2618        }
 2619        editor._subscriptions.extend(project_subscriptions);
 2620
 2621        editor._subscriptions.push(cx.subscribe_in(
 2622            &cx.entity(),
 2623            window,
 2624            |editor, _, e: &EditorEvent, window, cx| match e {
 2625                EditorEvent::ScrollPositionChanged { local, .. } => {
 2626                    if *local {
 2627                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2628                        editor.inline_blame_popover.take();
 2629                        let snapshot = editor.snapshot(window, cx);
 2630                        let new_anchor = editor
 2631                            .scroll_manager
 2632                            .native_anchor(&snapshot.display_snapshot, cx);
 2633                        editor.update_restoration_data(cx, move |data| {
 2634                            data.scroll_position = (
 2635                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2636                                new_anchor.offset,
 2637                            );
 2638                        });
 2639
 2640                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2641                            cx.background_executor()
 2642                                .timer(Duration::from_millis(50))
 2643                                .await;
 2644                            editor
 2645                                .update_in(cx, |editor, window, cx| {
 2646                                    editor.update_data_on_scroll(window, cx)
 2647                                })
 2648                                .ok();
 2649                        });
 2650                    }
 2651                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2652                }
 2653                EditorEvent::Edited { .. } => {
 2654                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2655                        .map(|vim_mode| vim_mode.0)
 2656                        .unwrap_or(false);
 2657                    if !vim_mode {
 2658                        let display_map = editor.display_snapshot(cx);
 2659                        let selections = editor.selections.all_adjusted_display(&display_map);
 2660                        let pop_state = editor
 2661                            .change_list
 2662                            .last()
 2663                            .map(|previous| {
 2664                                previous.len() == selections.len()
 2665                                    && previous.iter().enumerate().all(|(ix, p)| {
 2666                                        p.to_display_point(&display_map).row()
 2667                                            == selections[ix].head().row()
 2668                                    })
 2669                            })
 2670                            .unwrap_or(false);
 2671                        let new_positions = selections
 2672                            .into_iter()
 2673                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2674                            .collect();
 2675                        editor
 2676                            .change_list
 2677                            .push_to_change_list(pop_state, new_positions);
 2678                    }
 2679                }
 2680                _ => (),
 2681            },
 2682        ));
 2683
 2684        if let Some(dap_store) = editor
 2685            .project
 2686            .as_ref()
 2687            .map(|project| project.read(cx).dap_store())
 2688        {
 2689            let weak_editor = cx.weak_entity();
 2690
 2691            editor
 2692                ._subscriptions
 2693                .push(
 2694                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2695                        let session_entity = cx.entity();
 2696                        weak_editor
 2697                            .update(cx, |editor, cx| {
 2698                                editor._subscriptions.push(
 2699                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2700                                );
 2701                            })
 2702                            .ok();
 2703                    }),
 2704                );
 2705
 2706            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2707                editor
 2708                    ._subscriptions
 2709                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2710            }
 2711        }
 2712
 2713        // skip adding the initial selection to selection history
 2714        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2715        editor.end_selection(window, cx);
 2716        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2717
 2718        editor.scroll_manager.show_scrollbars(window, cx);
 2719        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2720
 2721        if full_mode {
 2722            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2723            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2724
 2725            if editor.git_blame_inline_enabled {
 2726                editor.start_git_blame_inline(false, window, cx);
 2727            }
 2728
 2729            editor.go_to_active_debug_line(window, cx);
 2730
 2731            editor.minimap =
 2732                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2733            editor.colors = Some(LspColorData::new(cx));
 2734            editor.use_document_folding_ranges = true;
 2735            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2736
 2737            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2738                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2739            }
 2740            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2741        }
 2742
 2743        editor
 2744    }
 2745
 2746    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2747        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2748    }
 2749
 2750    pub fn deploy_mouse_context_menu(
 2751        &mut self,
 2752        position: gpui::Point<Pixels>,
 2753        context_menu: Entity<ContextMenu>,
 2754        window: &mut Window,
 2755        cx: &mut Context<Self>,
 2756    ) {
 2757        self.mouse_context_menu = Some(MouseContextMenu::new(
 2758            self,
 2759            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2760            context_menu,
 2761            window,
 2762            cx,
 2763        ));
 2764    }
 2765
 2766    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2767        self.mouse_context_menu
 2768            .as_ref()
 2769            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2770    }
 2771
 2772    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2773        if self
 2774            .selections
 2775            .pending_anchor()
 2776            .is_some_and(|pending_selection| {
 2777                let snapshot = self.buffer().read(cx).snapshot(cx);
 2778                pending_selection.range().includes(range, &snapshot)
 2779            })
 2780        {
 2781            return true;
 2782        }
 2783
 2784        self.selections
 2785            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2786            .into_iter()
 2787            .any(|selection| {
 2788                // This is needed to cover a corner case, if we just check for an existing
 2789                // selection in the fold range, having a cursor at the start of the fold
 2790                // marks it as selected. Non-empty selections don't cause this.
 2791                let length = selection.end - selection.start;
 2792                length > 0
 2793            })
 2794    }
 2795
 2796    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2797        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2798    }
 2799
 2800    fn key_context_internal(
 2801        &self,
 2802        has_active_edit_prediction: bool,
 2803        window: &mut Window,
 2804        cx: &mut App,
 2805    ) -> KeyContext {
 2806        let mut key_context = KeyContext::new_with_defaults();
 2807        key_context.add("Editor");
 2808        let mode = match self.mode {
 2809            EditorMode::SingleLine => "single_line",
 2810            EditorMode::AutoHeight { .. } => "auto_height",
 2811            EditorMode::Minimap { .. } => "minimap",
 2812            EditorMode::Full { .. } => "full",
 2813        };
 2814
 2815        if EditorSettings::jupyter_enabled(cx) {
 2816            key_context.add("jupyter");
 2817        }
 2818
 2819        key_context.set("mode", mode);
 2820        if self.pending_rename.is_some() {
 2821            key_context.add("renaming");
 2822        }
 2823
 2824        if let Some(snippet_stack) = self.snippet_stack.last() {
 2825            key_context.add("in_snippet");
 2826
 2827            if snippet_stack.active_index > 0 {
 2828                key_context.add("has_previous_tabstop");
 2829            }
 2830
 2831            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2832                key_context.add("has_next_tabstop");
 2833            }
 2834        }
 2835
 2836        match self.context_menu.borrow().as_ref() {
 2837            Some(CodeContextMenu::Completions(menu)) => {
 2838                if menu.visible() {
 2839                    key_context.add("menu");
 2840                    key_context.add("showing_completions");
 2841                }
 2842            }
 2843            Some(CodeContextMenu::CodeActions(menu)) => {
 2844                if menu.visible() {
 2845                    key_context.add("menu");
 2846                    key_context.add("showing_code_actions")
 2847                }
 2848            }
 2849            None => {}
 2850        }
 2851
 2852        if self.signature_help_state.has_multiple_signatures() {
 2853            key_context.add("showing_signature_help");
 2854        }
 2855
 2856        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2857        if !self.focus_handle(cx).contains_focused(window, cx)
 2858            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2859        {
 2860            for addon in self.addons.values() {
 2861                addon.extend_key_context(&mut key_context, cx)
 2862            }
 2863        }
 2864
 2865        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2866            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2867                Some(
 2868                    file.full_path(cx)
 2869                        .extension()?
 2870                        .to_string_lossy()
 2871                        .to_lowercase(),
 2872                )
 2873            }) {
 2874                key_context.set("extension", extension);
 2875            }
 2876        } else {
 2877            key_context.add("multibuffer");
 2878        }
 2879
 2880        if has_active_edit_prediction {
 2881            key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2882            key_context.add("copilot_suggestion");
 2883        }
 2884
 2885        if self.in_leading_whitespace {
 2886            key_context.add("in_leading_whitespace");
 2887        }
 2888        if self.edit_prediction_requires_modifier() {
 2889            key_context.set("edit_prediction_mode", "subtle")
 2890        } else {
 2891            key_context.set("edit_prediction_mode", "eager");
 2892        }
 2893
 2894        if self.selection_mark_mode {
 2895            key_context.add("selection_mode");
 2896        }
 2897
 2898        let disjoint = self.selections.disjoint_anchors();
 2899        if matches!(
 2900            &self.mode,
 2901            EditorMode::SingleLine | EditorMode::AutoHeight { .. }
 2902        ) && let [selection] = disjoint
 2903            && selection.start == selection.end
 2904        {
 2905            let snapshot = self.snapshot(window, cx);
 2906            let snapshot = snapshot.buffer_snapshot();
 2907            let caret_offset = selection.end.to_offset(snapshot);
 2908
 2909            if caret_offset == MultiBufferOffset(0) {
 2910                key_context.add("start_of_input");
 2911            }
 2912
 2913            if caret_offset == snapshot.len() {
 2914                key_context.add("end_of_input");
 2915            }
 2916        }
 2917
 2918        if self.has_any_expanded_diff_hunks(cx) {
 2919            key_context.add("diffs_expanded");
 2920        }
 2921
 2922        key_context
 2923    }
 2924
 2925    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2926        self.last_bounds.as_ref()
 2927    }
 2928
 2929    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2930        if self.mouse_cursor_hidden {
 2931            self.mouse_cursor_hidden = false;
 2932            cx.notify();
 2933        }
 2934    }
 2935
 2936    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2937        let hide_mouse_cursor = match origin {
 2938            HideMouseCursorOrigin::TypingAction => {
 2939                matches!(
 2940                    self.hide_mouse_mode,
 2941                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2942                )
 2943            }
 2944            HideMouseCursorOrigin::MovementAction => {
 2945                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2946            }
 2947        };
 2948        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2949            self.mouse_cursor_hidden = hide_mouse_cursor;
 2950            cx.notify();
 2951        }
 2952    }
 2953
 2954    fn accept_edit_prediction_keystroke(
 2955        &self,
 2956        granularity: EditPredictionGranularity,
 2957        window: &mut Window,
 2958        cx: &mut App,
 2959    ) -> Option<gpui::KeybindingKeystroke> {
 2960        let key_context = self.key_context_internal(true, window, cx);
 2961
 2962        let bindings =
 2963            match granularity {
 2964                EditPredictionGranularity::Word => window
 2965                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2966                EditPredictionGranularity::Line => window
 2967                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2968                EditPredictionGranularity::Full => {
 2969                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2970                }
 2971            };
 2972
 2973        bindings
 2974            .into_iter()
 2975            .rev()
 2976            .find_map(|binding| match binding.keystrokes() {
 2977                [keystroke, ..] => Some(keystroke.clone()),
 2978                _ => None,
 2979            })
 2980    }
 2981
 2982    fn preview_edit_prediction_keystroke(
 2983        &self,
 2984        window: &mut Window,
 2985        cx: &mut App,
 2986    ) -> Option<gpui::KeybindingKeystroke> {
 2987        let key_context = self.key_context_internal(true, window, cx);
 2988        let bindings = window.bindings_for_action_in_context(&AcceptEditPrediction, key_context);
 2989        bindings
 2990            .into_iter()
 2991            .rev()
 2992            .find_map(|binding| match binding.keystrokes() {
 2993                [keystroke, ..] if keystroke.modifiers().modified() => Some(keystroke.clone()),
 2994                _ => None,
 2995            })
 2996    }
 2997
 2998    fn edit_prediction_preview_modifiers_held(
 2999        &self,
 3000        modifiers: &Modifiers,
 3001        window: &mut Window,
 3002        cx: &mut App,
 3003    ) -> bool {
 3004        let key_context = self.key_context_internal(true, window, cx);
 3005        let actions: [&dyn Action; 3] = [
 3006            &AcceptEditPrediction,
 3007            &AcceptNextWordEditPrediction,
 3008            &AcceptNextLineEditPrediction,
 3009        ];
 3010
 3011        actions.into_iter().any(|action| {
 3012            window
 3013                .bindings_for_action_in_context(action, key_context.clone())
 3014                .into_iter()
 3015                .rev()
 3016                .any(|binding| {
 3017                    binding.keystrokes().first().is_some_and(|keystroke| {
 3018                        keystroke.modifiers().modified() && keystroke.modifiers() == modifiers
 3019                    })
 3020                })
 3021        })
 3022    }
 3023
 3024    fn edit_prediction_cursor_popover_prefers_preview(
 3025        &self,
 3026        completion: &EditPredictionState,
 3027    ) -> bool {
 3028        match &completion.completion {
 3029            EditPrediction::Edit {
 3030                edits, snapshot, ..
 3031            } => {
 3032                let mut start_row: Option<u32> = None;
 3033                let mut end_row: Option<u32> = None;
 3034
 3035                for (range, text) in edits {
 3036                    let edit_start_row = range.start.text_anchor.to_point(snapshot).row;
 3037                    let old_end_row = range.end.text_anchor.to_point(snapshot).row;
 3038                    let inserted_newline_count = text
 3039                        .as_ref()
 3040                        .chars()
 3041                        .filter(|character| *character == '\n')
 3042                        .count() as u32;
 3043                    let deleted_newline_count = old_end_row - edit_start_row;
 3044                    let preview_end_row = edit_start_row + inserted_newline_count;
 3045
 3046                    start_row =
 3047                        Some(start_row.map_or(edit_start_row, |row| row.min(edit_start_row)));
 3048                    end_row = Some(end_row.map_or(preview_end_row, |row| row.max(preview_end_row)));
 3049
 3050                    if deleted_newline_count > 1 {
 3051                        end_row = Some(end_row.map_or(old_end_row, |row| row.max(old_end_row)));
 3052                    }
 3053                }
 3054
 3055                start_row
 3056                    .zip(end_row)
 3057                    .is_some_and(|(start_row, end_row)| end_row > start_row)
 3058            }
 3059            EditPrediction::MoveWithin { .. } | EditPrediction::MoveOutside { .. } => false,
 3060        }
 3061    }
 3062
 3063    fn edit_prediction_keybind_display(
 3064        &self,
 3065        surface: EditPredictionKeybindSurface,
 3066        window: &mut Window,
 3067        cx: &mut App,
 3068    ) -> EditPredictionKeybindDisplay {
 3069        let accept_keystroke =
 3070            self.accept_edit_prediction_keystroke(EditPredictionGranularity::Full, window, cx);
 3071        let preview_keystroke = self.preview_edit_prediction_keystroke(window, cx);
 3072
 3073        let action = match surface {
 3074            EditPredictionKeybindSurface::Inline
 3075            | EditPredictionKeybindSurface::CursorPopoverCompact => {
 3076                if self.edit_prediction_requires_modifier() {
 3077                    EditPredictionKeybindAction::Preview
 3078                } else {
 3079                    EditPredictionKeybindAction::Accept
 3080                }
 3081            }
 3082            EditPredictionKeybindSurface::CursorPopoverExpanded => self
 3083                .active_edit_prediction
 3084                .as_ref()
 3085                .filter(|completion| {
 3086                    self.edit_prediction_cursor_popover_prefers_preview(completion)
 3087                })
 3088                .map_or(EditPredictionKeybindAction::Accept, |_| {
 3089                    EditPredictionKeybindAction::Preview
 3090                }),
 3091        };
 3092        #[cfg(test)]
 3093        let preview_copy = preview_keystroke.clone();
 3094        #[cfg(test)]
 3095        let accept_copy = accept_keystroke.clone();
 3096
 3097        let displayed_keystroke = match surface {
 3098            EditPredictionKeybindSurface::Inline => match action {
 3099                EditPredictionKeybindAction::Accept => accept_keystroke,
 3100                EditPredictionKeybindAction::Preview => preview_keystroke,
 3101            },
 3102            EditPredictionKeybindSurface::CursorPopoverCompact
 3103            | EditPredictionKeybindSurface::CursorPopoverExpanded => match action {
 3104                EditPredictionKeybindAction::Accept => accept_keystroke,
 3105                EditPredictionKeybindAction::Preview => {
 3106                    preview_keystroke.or_else(|| accept_keystroke.clone())
 3107                }
 3108            },
 3109        };
 3110
 3111        let missing_accept_keystroke = displayed_keystroke.is_none();
 3112
 3113        EditPredictionKeybindDisplay {
 3114            #[cfg(test)]
 3115            accept_keystroke: accept_copy,
 3116            #[cfg(test)]
 3117            preview_keystroke: preview_copy,
 3118            displayed_keystroke,
 3119            action,
 3120            missing_accept_keystroke,
 3121            show_hold_label: matches!(surface, EditPredictionKeybindSurface::CursorPopoverCompact)
 3122                && self.edit_prediction_preview.released_too_fast(),
 3123        }
 3124    }
 3125
 3126    pub fn new_file(
 3127        workspace: &mut Workspace,
 3128        _: &workspace::NewFile,
 3129        window: &mut Window,
 3130        cx: &mut Context<Workspace>,
 3131    ) {
 3132        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3133            "Failed to create buffer",
 3134            window,
 3135            cx,
 3136            |e, _, _| match e.error_code() {
 3137                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3138                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3139                e.error_tag("required").unwrap_or("the latest version")
 3140            )),
 3141                _ => None,
 3142            },
 3143        );
 3144    }
 3145
 3146    pub fn new_in_workspace(
 3147        workspace: &mut Workspace,
 3148        window: &mut Window,
 3149        cx: &mut Context<Workspace>,
 3150    ) -> Task<Result<Entity<Editor>>> {
 3151        let project = workspace.project().clone();
 3152        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3153
 3154        cx.spawn_in(window, async move |workspace, cx| {
 3155            let buffer = create.await?;
 3156            workspace.update_in(cx, |workspace, window, cx| {
 3157                let editor =
 3158                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3159                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3160                editor
 3161            })
 3162        })
 3163    }
 3164
 3165    fn new_file_vertical(
 3166        workspace: &mut Workspace,
 3167        _: &workspace::NewFileSplitVertical,
 3168        window: &mut Window,
 3169        cx: &mut Context<Workspace>,
 3170    ) {
 3171        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3172    }
 3173
 3174    fn new_file_horizontal(
 3175        workspace: &mut Workspace,
 3176        _: &workspace::NewFileSplitHorizontal,
 3177        window: &mut Window,
 3178        cx: &mut Context<Workspace>,
 3179    ) {
 3180        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3181    }
 3182
 3183    fn new_file_split(
 3184        workspace: &mut Workspace,
 3185        action: &workspace::NewFileSplit,
 3186        window: &mut Window,
 3187        cx: &mut Context<Workspace>,
 3188    ) {
 3189        Self::new_file_in_direction(workspace, action.0, window, cx)
 3190    }
 3191
 3192    fn new_file_in_direction(
 3193        workspace: &mut Workspace,
 3194        direction: SplitDirection,
 3195        window: &mut Window,
 3196        cx: &mut Context<Workspace>,
 3197    ) {
 3198        let project = workspace.project().clone();
 3199        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3200
 3201        cx.spawn_in(window, async move |workspace, cx| {
 3202            let buffer = create.await?;
 3203            workspace.update_in(cx, move |workspace, window, cx| {
 3204                workspace.split_item(
 3205                    direction,
 3206                    Box::new(
 3207                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3208                    ),
 3209                    window,
 3210                    cx,
 3211                )
 3212            })?;
 3213            anyhow::Ok(())
 3214        })
 3215        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3216            match e.error_code() {
 3217                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3218                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3219                e.error_tag("required").unwrap_or("the latest version")
 3220            )),
 3221                _ => None,
 3222            }
 3223        });
 3224    }
 3225
 3226    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3227        self.leader_id
 3228    }
 3229
 3230    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3231        &self.buffer
 3232    }
 3233
 3234    pub fn project(&self) -> Option<&Entity<Project>> {
 3235        self.project.as_ref()
 3236    }
 3237
 3238    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3239        self.workspace.as_ref()?.0.upgrade()
 3240    }
 3241
 3242    /// Detaches a task and shows an error notification in the workspace if available,
 3243    /// otherwise just logs the error.
 3244    pub fn detach_and_notify_err<R, E>(
 3245        &self,
 3246        task: Task<Result<R, E>>,
 3247        window: &mut Window,
 3248        cx: &mut App,
 3249    ) where
 3250        E: std::fmt::Debug + std::fmt::Display + 'static,
 3251        R: 'static,
 3252    {
 3253        if let Some(workspace) = self.workspace() {
 3254            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3255        } else {
 3256            task.detach_and_log_err(cx);
 3257        }
 3258    }
 3259
 3260    /// Returns the workspace serialization ID if this editor should be serialized.
 3261    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3262        self.workspace
 3263            .as_ref()
 3264            .filter(|_| self.should_serialize_buffer())
 3265            .and_then(|workspace| workspace.1)
 3266    }
 3267
 3268    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3269        self.buffer().read(cx).title(cx)
 3270    }
 3271
 3272    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3273        let git_blame_gutter_max_author_length = self
 3274            .render_git_blame_gutter(cx)
 3275            .then(|| {
 3276                if let Some(blame) = self.blame.as_ref() {
 3277                    let max_author_length =
 3278                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3279                    Some(max_author_length)
 3280                } else {
 3281                    None
 3282                }
 3283            })
 3284            .flatten();
 3285
 3286        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3287
 3288        EditorSnapshot {
 3289            mode: self.mode.clone(),
 3290            show_gutter: self.show_gutter,
 3291            offset_content: self.offset_content,
 3292            show_line_numbers: self.show_line_numbers,
 3293            number_deleted_lines: self.number_deleted_lines,
 3294            show_git_diff_gutter: self.show_git_diff_gutter,
 3295            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3296            show_code_actions: self.show_code_actions,
 3297            show_runnables: self.show_runnables,
 3298            show_breakpoints: self.show_breakpoints,
 3299            git_blame_gutter_max_author_length,
 3300            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3301            display_snapshot,
 3302            placeholder_display_snapshot: self
 3303                .placeholder_display_map
 3304                .as_ref()
 3305                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3306            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3307            is_focused: self.focus_handle.is_focused(window),
 3308            current_line_highlight: self
 3309                .current_line_highlight
 3310                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3311            gutter_hovered: self.gutter_hovered,
 3312        }
 3313    }
 3314
 3315    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3316        self.buffer.read(cx).language_at(point, cx)
 3317    }
 3318
 3319    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3320        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3321    }
 3322
 3323    pub fn active_excerpt(
 3324        &self,
 3325        cx: &App,
 3326    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3327        self.buffer
 3328            .read(cx)
 3329            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3330    }
 3331
 3332    pub fn mode(&self) -> &EditorMode {
 3333        &self.mode
 3334    }
 3335
 3336    pub fn set_mode(&mut self, mode: EditorMode) {
 3337        self.mode = mode;
 3338    }
 3339
 3340    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3341        self.collaboration_hub.as_deref()
 3342    }
 3343
 3344    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3345        self.collaboration_hub = Some(hub);
 3346    }
 3347
 3348    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3349        self.in_project_search = in_project_search;
 3350    }
 3351
 3352    pub fn set_custom_context_menu(
 3353        &mut self,
 3354        f: impl 'static
 3355        + Fn(
 3356            &mut Self,
 3357            DisplayPoint,
 3358            &mut Window,
 3359            &mut Context<Self>,
 3360        ) -> Option<Entity<ui::ContextMenu>>,
 3361    ) {
 3362        self.custom_context_menu = Some(Box::new(f))
 3363    }
 3364
 3365    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3366        self.completion_provider = provider;
 3367    }
 3368
 3369    #[cfg(any(test, feature = "test-support"))]
 3370    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3371        self.completion_provider.clone()
 3372    }
 3373
 3374    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3375        self.semantics_provider.clone()
 3376    }
 3377
 3378    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3379        self.semantics_provider = provider;
 3380    }
 3381
 3382    pub fn set_edit_prediction_provider<T>(
 3383        &mut self,
 3384        provider: Option<Entity<T>>,
 3385        window: &mut Window,
 3386        cx: &mut Context<Self>,
 3387    ) where
 3388        T: EditPredictionDelegate,
 3389    {
 3390        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3391            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3392                if this.focus_handle.is_focused(window) {
 3393                    this.update_visible_edit_prediction(window, cx);
 3394                }
 3395            }),
 3396            provider: Arc::new(provider),
 3397        });
 3398        self.update_edit_prediction_settings(cx);
 3399        self.refresh_edit_prediction(false, false, window, cx);
 3400    }
 3401
 3402    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3403        self.placeholder_display_map
 3404            .as_ref()
 3405            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3406    }
 3407
 3408    pub fn set_placeholder_text(
 3409        &mut self,
 3410        placeholder_text: &str,
 3411        window: &mut Window,
 3412        cx: &mut Context<Self>,
 3413    ) {
 3414        let multibuffer = cx
 3415            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3416
 3417        let style = window.text_style();
 3418
 3419        self.placeholder_display_map = Some(cx.new(|cx| {
 3420            DisplayMap::new(
 3421                multibuffer,
 3422                style.font(),
 3423                style.font_size.to_pixels(window.rem_size()),
 3424                None,
 3425                FILE_HEADER_HEIGHT,
 3426                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3427                Default::default(),
 3428                DiagnosticSeverity::Off,
 3429                cx,
 3430            )
 3431        }));
 3432        cx.notify();
 3433    }
 3434
 3435    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3436        self.cursor_shape = cursor_shape;
 3437
 3438        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3439        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3440
 3441        cx.notify();
 3442    }
 3443
 3444    pub fn cursor_shape(&self) -> CursorShape {
 3445        self.cursor_shape
 3446    }
 3447
 3448    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3449        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3450    }
 3451
 3452    pub fn set_current_line_highlight(
 3453        &mut self,
 3454        current_line_highlight: Option<CurrentLineHighlight>,
 3455    ) {
 3456        self.current_line_highlight = current_line_highlight;
 3457    }
 3458
 3459    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3460        self.collapse_matches = collapse_matches;
 3461    }
 3462
 3463    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3464        if self.collapse_matches {
 3465            return range.start..range.start;
 3466        }
 3467        range.clone()
 3468    }
 3469
 3470    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3471        self.display_map.read(cx).clip_at_line_ends
 3472    }
 3473
 3474    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3475        if self.display_map.read(cx).clip_at_line_ends != clip {
 3476            self.display_map
 3477                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3478        }
 3479    }
 3480
 3481    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3482        self.input_enabled = input_enabled;
 3483    }
 3484
 3485    pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
 3486        self.expects_character_input = expects_character_input;
 3487    }
 3488
 3489    pub fn set_edit_predictions_hidden_for_vim_mode(
 3490        &mut self,
 3491        hidden: bool,
 3492        window: &mut Window,
 3493        cx: &mut Context<Self>,
 3494    ) {
 3495        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3496            self.edit_predictions_hidden_for_vim_mode = hidden;
 3497            if hidden {
 3498                self.update_visible_edit_prediction(window, cx);
 3499            } else {
 3500                self.refresh_edit_prediction(true, false, window, cx);
 3501            }
 3502        }
 3503    }
 3504
 3505    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3506        self.menu_edit_predictions_policy = value;
 3507    }
 3508
 3509    pub fn set_autoindent(&mut self, autoindent: bool) {
 3510        if autoindent {
 3511            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3512        } else {
 3513            self.autoindent_mode = None;
 3514        }
 3515    }
 3516
 3517    pub fn capability(&self, cx: &App) -> Capability {
 3518        if self.read_only {
 3519            Capability::ReadOnly
 3520        } else {
 3521            self.buffer.read(cx).capability()
 3522        }
 3523    }
 3524
 3525    pub fn read_only(&self, cx: &App) -> bool {
 3526        self.read_only || self.buffer.read(cx).read_only()
 3527    }
 3528
 3529    pub fn set_read_only(&mut self, read_only: bool) {
 3530        self.read_only = read_only;
 3531    }
 3532
 3533    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3534        self.use_autoclose = autoclose;
 3535    }
 3536
 3537    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3538        self.use_auto_surround = auto_surround;
 3539    }
 3540
 3541    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3542        self.auto_replace_emoji_shortcode = auto_replace;
 3543    }
 3544
 3545    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3546        self.buffer_serialization = should_serialize.then(|| {
 3547            BufferSerialization::new(
 3548                ProjectSettings::get_global(cx)
 3549                    .session
 3550                    .restore_unsaved_buffers,
 3551            )
 3552        })
 3553    }
 3554
 3555    fn should_serialize_buffer(&self) -> bool {
 3556        self.buffer_serialization.is_some()
 3557    }
 3558
 3559    pub fn toggle_edit_predictions(
 3560        &mut self,
 3561        _: &ToggleEditPrediction,
 3562        window: &mut Window,
 3563        cx: &mut Context<Self>,
 3564    ) {
 3565        if self.show_edit_predictions_override.is_some() {
 3566            self.set_show_edit_predictions(None, window, cx);
 3567        } else {
 3568            let show_edit_predictions = !self.edit_predictions_enabled();
 3569            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3570        }
 3571    }
 3572
 3573    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3574        self.show_completions_on_input_override = show_completions_on_input;
 3575    }
 3576
 3577    pub fn set_show_edit_predictions(
 3578        &mut self,
 3579        show_edit_predictions: Option<bool>,
 3580        window: &mut Window,
 3581        cx: &mut Context<Self>,
 3582    ) {
 3583        self.show_edit_predictions_override = show_edit_predictions;
 3584        self.update_edit_prediction_settings(cx);
 3585
 3586        if let Some(false) = show_edit_predictions {
 3587            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3588        } else {
 3589            self.refresh_edit_prediction(false, true, window, cx);
 3590        }
 3591    }
 3592
 3593    fn edit_predictions_disabled_in_scope(
 3594        &self,
 3595        buffer: &Entity<Buffer>,
 3596        buffer_position: language::Anchor,
 3597        cx: &App,
 3598    ) -> bool {
 3599        let snapshot = buffer.read(cx).snapshot();
 3600        let settings = snapshot.settings_at(buffer_position, cx);
 3601
 3602        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3603            return false;
 3604        };
 3605
 3606        scope.override_name().is_some_and(|scope_name| {
 3607            settings
 3608                .edit_predictions_disabled_in
 3609                .iter()
 3610                .any(|s| s == scope_name)
 3611        })
 3612    }
 3613
 3614    pub fn set_use_modal_editing(&mut self, to: bool) {
 3615        self.use_modal_editing = to;
 3616    }
 3617
 3618    pub fn use_modal_editing(&self) -> bool {
 3619        self.use_modal_editing
 3620    }
 3621
 3622    fn selections_did_change(
 3623        &mut self,
 3624        local: bool,
 3625        old_cursor_position: &Anchor,
 3626        effects: SelectionEffects,
 3627        window: &mut Window,
 3628        cx: &mut Context<Self>,
 3629    ) {
 3630        window.invalidate_character_coordinates();
 3631
 3632        // Copy selections to primary selection buffer
 3633        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3634        if local {
 3635            let selections = self
 3636                .selections
 3637                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3638            let buffer_handle = self.buffer.read(cx).read(cx);
 3639
 3640            let mut text = String::new();
 3641            for (index, selection) in selections.iter().enumerate() {
 3642                let text_for_selection = buffer_handle
 3643                    .text_for_range(selection.start..selection.end)
 3644                    .collect::<String>();
 3645
 3646                text.push_str(&text_for_selection);
 3647                if index != selections.len() - 1 {
 3648                    text.push('\n');
 3649                }
 3650            }
 3651
 3652            if !text.is_empty() {
 3653                cx.write_to_primary(ClipboardItem::new_string(text));
 3654            }
 3655        }
 3656
 3657        let selection_anchors = self.selections.disjoint_anchors_arc();
 3658
 3659        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3660            self.buffer.update(cx, |buffer, cx| {
 3661                buffer.set_active_selections(
 3662                    &selection_anchors,
 3663                    self.selections.line_mode(),
 3664                    self.cursor_shape,
 3665                    cx,
 3666                )
 3667            });
 3668        }
 3669        let display_map = self
 3670            .display_map
 3671            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3672        let buffer = display_map.buffer_snapshot();
 3673        if self.selections.count() == 1 {
 3674            self.add_selections_state = None;
 3675        }
 3676        self.select_next_state = None;
 3677        self.select_prev_state = None;
 3678        self.select_syntax_node_history.try_clear();
 3679        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3680        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3681        self.take_rename(false, window, cx);
 3682
 3683        let newest_selection = self.selections.newest_anchor();
 3684        let new_cursor_position = newest_selection.head();
 3685        let selection_start = newest_selection.start;
 3686
 3687        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3688            self.push_to_nav_history(
 3689                *old_cursor_position,
 3690                Some(new_cursor_position.to_point(buffer)),
 3691                false,
 3692                effects.nav_history == Some(true),
 3693                cx,
 3694            );
 3695        }
 3696
 3697        if local {
 3698            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3699                self.register_buffer(buffer_id, cx);
 3700            }
 3701
 3702            let mut context_menu = self.context_menu.borrow_mut();
 3703            let completion_menu = match context_menu.as_ref() {
 3704                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3705                Some(CodeContextMenu::CodeActions(_)) => {
 3706                    *context_menu = None;
 3707                    None
 3708                }
 3709                None => None,
 3710            };
 3711            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3712            drop(context_menu);
 3713
 3714            if effects.completions
 3715                && let Some(completion_position) = completion_position
 3716            {
 3717                let start_offset = selection_start.to_offset(buffer);
 3718                let position_matches = start_offset == completion_position.to_offset(buffer);
 3719                let continue_showing = if let Some((snap, ..)) =
 3720                    buffer.point_to_buffer_offset(completion_position)
 3721                    && !snap.capability.editable()
 3722                {
 3723                    false
 3724                } else if position_matches {
 3725                    if self.snippet_stack.is_empty() {
 3726                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3727                            == Some(CharKind::Word)
 3728                    } else {
 3729                        // Snippet choices can be shown even when the cursor is in whitespace.
 3730                        // Dismissing the menu with actions like backspace is handled by
 3731                        // invalidation regions.
 3732                        true
 3733                    }
 3734                } else {
 3735                    false
 3736                };
 3737
 3738                if continue_showing {
 3739                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3740                } else {
 3741                    self.hide_context_menu(window, cx);
 3742                }
 3743            }
 3744
 3745            hide_hover(self, cx);
 3746
 3747            if old_cursor_position.to_display_point(&display_map).row()
 3748                != new_cursor_position.to_display_point(&display_map).row()
 3749            {
 3750                self.available_code_actions.take();
 3751            }
 3752            self.refresh_code_actions(window, cx);
 3753            self.refresh_document_highlights(cx);
 3754            refresh_linked_ranges(self, window, cx);
 3755
 3756            self.refresh_selected_text_highlights(&display_map, false, window, cx);
 3757            self.refresh_matching_bracket_highlights(&display_map, cx);
 3758            self.refresh_outline_symbols_at_cursor(cx);
 3759            self.update_visible_edit_prediction(window, cx);
 3760            self.inline_blame_popover.take();
 3761            if self.git_blame_inline_enabled {
 3762                self.start_inline_blame_timer(window, cx);
 3763            }
 3764        }
 3765
 3766        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3767
 3768        if local && !self.suppress_selection_callback {
 3769            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3770                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3771                callback(cursor_position, window, cx);
 3772            }
 3773        }
 3774
 3775        cx.emit(EditorEvent::SelectionsChanged { local });
 3776
 3777        let selections = &self.selections.disjoint_anchors_arc();
 3778        if selections.len() == 1 {
 3779            cx.emit(SearchEvent::ActiveMatchChanged)
 3780        }
 3781        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3782            let inmemory_selections = selections
 3783                .iter()
 3784                .map(|s| {
 3785                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3786                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3787                })
 3788                .collect();
 3789            self.update_restoration_data(cx, |data| {
 3790                data.selections = inmemory_selections;
 3791            });
 3792
 3793            if WorkspaceSettings::get(None, cx).restore_on_startup
 3794                != RestoreOnStartupBehavior::EmptyTab
 3795                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3796            {
 3797                let snapshot = self.buffer().read(cx).snapshot(cx);
 3798                let selections = selections.clone();
 3799                let background_executor = cx.background_executor().clone();
 3800                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3801                let db = EditorDb::global(cx);
 3802                self.serialize_selections = cx.background_spawn(async move {
 3803                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3804                    let db_selections = selections
 3805                        .iter()
 3806                        .map(|selection| {
 3807                            (
 3808                                selection.start.to_offset(&snapshot).0,
 3809                                selection.end.to_offset(&snapshot).0,
 3810                            )
 3811                        })
 3812                        .collect();
 3813
 3814                    db.save_editor_selections(editor_id, workspace_id, db_selections)
 3815                        .await
 3816                        .with_context(|| {
 3817                            format!(
 3818                                "persisting editor selections for editor {editor_id}, \
 3819                                workspace {workspace_id:?}"
 3820                            )
 3821                        })
 3822                        .log_err();
 3823                });
 3824            }
 3825        }
 3826
 3827        cx.notify();
 3828    }
 3829
 3830    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3831        use text::ToOffset as _;
 3832        use text::ToPoint as _;
 3833
 3834        if self.mode.is_minimap()
 3835            || WorkspaceSettings::get(None, cx).restore_on_startup
 3836                == RestoreOnStartupBehavior::EmptyTab
 3837        {
 3838            return;
 3839        }
 3840
 3841        if !self.buffer().read(cx).is_singleton() {
 3842            return;
 3843        }
 3844
 3845        let display_snapshot = self
 3846            .display_map
 3847            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3848        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3849            return;
 3850        };
 3851        let inmemory_folds = display_snapshot
 3852            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3853            .map(|fold| {
 3854                fold.range.start.text_anchor.to_point(&snapshot)
 3855                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3856            })
 3857            .collect();
 3858        self.update_restoration_data(cx, |data| {
 3859            data.folds = inmemory_folds;
 3860        });
 3861
 3862        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3863            return;
 3864        };
 3865
 3866        // Get file path for path-based fold storage (survives tab close)
 3867        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3868            project::File::from_dyn(buffer.read(cx).file())
 3869                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3870        }) else {
 3871            return;
 3872        };
 3873
 3874        let background_executor = cx.background_executor().clone();
 3875        const FINGERPRINT_LEN: usize = 32;
 3876        let db_folds = display_snapshot
 3877            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3878            .map(|fold| {
 3879                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3880                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3881
 3882                // Extract fingerprints - content at fold boundaries for validation on restore
 3883                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3884                // content that might change independently.
 3885                // start_fp: first min(32, fold_len) bytes of fold content
 3886                // end_fp: last min(32, fold_len) bytes of fold content
 3887                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3888                let fold_len = end - start;
 3889                let start_fp_end = snapshot
 3890                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3891                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3892                let end_fp_start = snapshot
 3893                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3894                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3895
 3896                (start, end, start_fp, end_fp)
 3897            })
 3898            .collect::<Vec<_>>();
 3899        let db = EditorDb::global(cx);
 3900        self.serialize_folds = cx.background_spawn(async move {
 3901            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3902            if db_folds.is_empty() {
 3903                // No folds - delete any persisted folds for this file
 3904                db.delete_file_folds(workspace_id, file_path)
 3905                    .await
 3906                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3907                    .log_err();
 3908            } else {
 3909                db.save_file_folds(workspace_id, file_path, db_folds)
 3910                    .await
 3911                    .with_context(|| {
 3912                        format!("persisting file folds for workspace {workspace_id:?}")
 3913                    })
 3914                    .log_err();
 3915            }
 3916        });
 3917    }
 3918
 3919    pub fn sync_selections(
 3920        &mut self,
 3921        other: Entity<Editor>,
 3922        cx: &mut Context<Self>,
 3923    ) -> gpui::Subscription {
 3924        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3925        if !other_selections.is_empty() {
 3926            self.selections
 3927                .change_with(&self.display_snapshot(cx), |selections| {
 3928                    selections.select_anchors(other_selections);
 3929                });
 3930        }
 3931
 3932        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3933            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3934                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3935                if other_selections.is_empty() {
 3936                    return;
 3937                }
 3938                let snapshot = this.display_snapshot(cx);
 3939                this.selections.change_with(&snapshot, |selections| {
 3940                    selections.select_anchors(other_selections);
 3941                });
 3942            }
 3943        });
 3944
 3945        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3946            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3947                let these_selections = this.selections.disjoint_anchors().to_vec();
 3948                if these_selections.is_empty() {
 3949                    return;
 3950                }
 3951                other.update(cx, |other_editor, cx| {
 3952                    let snapshot = other_editor.display_snapshot(cx);
 3953                    other_editor
 3954                        .selections
 3955                        .change_with(&snapshot, |selections| {
 3956                            selections.select_anchors(these_selections);
 3957                        })
 3958                });
 3959            }
 3960        });
 3961
 3962        Subscription::join(other_subscription, this_subscription)
 3963    }
 3964
 3965    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3966        if self.buffer().read(cx).is_singleton() {
 3967            return;
 3968        }
 3969        let snapshot = self.buffer.read(cx).snapshot(cx);
 3970        let buffer_ids: HashSet<BufferId> = self
 3971            .selections
 3972            .disjoint_anchor_ranges()
 3973            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3974            .collect();
 3975        for buffer_id in buffer_ids {
 3976            self.unfold_buffer(buffer_id, cx);
 3977        }
 3978    }
 3979
 3980    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3981    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3982    /// effects of selection change occur at the end of the transaction.
 3983    pub fn change_selections<R>(
 3984        &mut self,
 3985        effects: SelectionEffects,
 3986        window: &mut Window,
 3987        cx: &mut Context<Self>,
 3988        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3989    ) -> R {
 3990        let snapshot = self.display_snapshot(cx);
 3991        if let Some(state) = &mut self.deferred_selection_effects_state {
 3992            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3993            state.effects.completions = effects.completions;
 3994            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3995            let (changed, result) = self.selections.change_with(&snapshot, change);
 3996            state.changed |= changed;
 3997            return result;
 3998        }
 3999        let mut state = DeferredSelectionEffectsState {
 4000            changed: false,
 4001            effects,
 4002            old_cursor_position: self.selections.newest_anchor().head(),
 4003            history_entry: SelectionHistoryEntry {
 4004                selections: self.selections.disjoint_anchors_arc(),
 4005                select_next_state: self.select_next_state.clone(),
 4006                select_prev_state: self.select_prev_state.clone(),
 4007                add_selections_state: self.add_selections_state.clone(),
 4008            },
 4009        };
 4010        let (changed, result) = self.selections.change_with(&snapshot, change);
 4011        state.changed = state.changed || changed;
 4012        if self.defer_selection_effects {
 4013            self.deferred_selection_effects_state = Some(state);
 4014        } else {
 4015            self.apply_selection_effects(state, window, cx);
 4016        }
 4017        result
 4018    }
 4019
 4020    /// Defers the effects of selection change, so that the effects of multiple calls to
 4021    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 4022    /// to selection history and the state of popovers based on selection position aren't
 4023    /// erroneously updated.
 4024    pub fn with_selection_effects_deferred<R>(
 4025        &mut self,
 4026        window: &mut Window,
 4027        cx: &mut Context<Self>,
 4028        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 4029    ) -> R {
 4030        let already_deferred = self.defer_selection_effects;
 4031        self.defer_selection_effects = true;
 4032        let result = update(self, window, cx);
 4033        if !already_deferred {
 4034            self.defer_selection_effects = false;
 4035            if let Some(state) = self.deferred_selection_effects_state.take() {
 4036                self.apply_selection_effects(state, window, cx);
 4037            }
 4038        }
 4039        result
 4040    }
 4041
 4042    fn apply_selection_effects(
 4043        &mut self,
 4044        state: DeferredSelectionEffectsState,
 4045        window: &mut Window,
 4046        cx: &mut Context<Self>,
 4047    ) {
 4048        if state.changed {
 4049            self.selection_history.push(state.history_entry);
 4050
 4051            if let Some(autoscroll) = state.effects.scroll {
 4052                self.request_autoscroll(autoscroll, cx);
 4053            }
 4054
 4055            let old_cursor_position = &state.old_cursor_position;
 4056
 4057            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 4058
 4059            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 4060                self.show_signature_help_auto(window, cx);
 4061            }
 4062        }
 4063    }
 4064
 4065    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 4066    where
 4067        I: IntoIterator<Item = (Range<S>, T)>,
 4068        S: ToOffset,
 4069        T: Into<Arc<str>>,
 4070    {
 4071        if self.read_only(cx) {
 4072            return;
 4073        }
 4074
 4075        self.buffer
 4076            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 4077    }
 4078
 4079    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 4080    where
 4081        I: IntoIterator<Item = (Range<S>, T)>,
 4082        S: ToOffset,
 4083        T: Into<Arc<str>>,
 4084    {
 4085        if self.read_only(cx) {
 4086            return;
 4087        }
 4088
 4089        self.buffer.update(cx, |buffer, cx| {
 4090            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 4091        });
 4092    }
 4093
 4094    pub fn edit_with_block_indent<I, S, T>(
 4095        &mut self,
 4096        edits: I,
 4097        original_indent_columns: Vec<Option<u32>>,
 4098        cx: &mut Context<Self>,
 4099    ) where
 4100        I: IntoIterator<Item = (Range<S>, T)>,
 4101        S: ToOffset,
 4102        T: Into<Arc<str>>,
 4103    {
 4104        if self.read_only(cx) {
 4105            return;
 4106        }
 4107
 4108        self.buffer.update(cx, |buffer, cx| {
 4109            buffer.edit(
 4110                edits,
 4111                Some(AutoindentMode::Block {
 4112                    original_indent_columns,
 4113                }),
 4114                cx,
 4115            )
 4116        });
 4117    }
 4118
 4119    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 4120        self.hide_context_menu(window, cx);
 4121
 4122        match phase {
 4123            SelectPhase::Begin {
 4124                position,
 4125                add,
 4126                click_count,
 4127            } => self.begin_selection(position, add, click_count, window, cx),
 4128            SelectPhase::BeginColumnar {
 4129                position,
 4130                goal_column,
 4131                reset,
 4132                mode,
 4133            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 4134            SelectPhase::Extend {
 4135                position,
 4136                click_count,
 4137            } => self.extend_selection(position, click_count, window, cx),
 4138            SelectPhase::Update {
 4139                position,
 4140                goal_column,
 4141                scroll_delta,
 4142            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4143            SelectPhase::End => self.end_selection(window, cx),
 4144        }
 4145    }
 4146
 4147    fn extend_selection(
 4148        &mut self,
 4149        position: DisplayPoint,
 4150        click_count: usize,
 4151        window: &mut Window,
 4152        cx: &mut Context<Self>,
 4153    ) {
 4154        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4155        let tail = self
 4156            .selections
 4157            .newest::<MultiBufferOffset>(&display_map)
 4158            .tail();
 4159        let click_count = click_count.max(match self.selections.select_mode() {
 4160            SelectMode::Character => 1,
 4161            SelectMode::Word(_) => 2,
 4162            SelectMode::Line(_) => 3,
 4163            SelectMode::All => 4,
 4164        });
 4165        self.begin_selection(position, false, click_count, window, cx);
 4166
 4167        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4168
 4169        let current_selection = match self.selections.select_mode() {
 4170            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4171            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4172        };
 4173
 4174        let mut pending_selection = self
 4175            .selections
 4176            .pending_anchor()
 4177            .cloned()
 4178            .expect("extend_selection not called with pending selection");
 4179
 4180        if pending_selection
 4181            .start
 4182            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4183            == Ordering::Greater
 4184        {
 4185            pending_selection.start = current_selection.start;
 4186        }
 4187        if pending_selection
 4188            .end
 4189            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4190            == Ordering::Less
 4191        {
 4192            pending_selection.end = current_selection.end;
 4193            pending_selection.reversed = true;
 4194        }
 4195
 4196        let mut pending_mode = self.selections.pending_mode().unwrap();
 4197        match &mut pending_mode {
 4198            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4199            _ => {}
 4200        }
 4201
 4202        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4203            SelectionEffects::scroll(Autoscroll::fit())
 4204        } else {
 4205            SelectionEffects::no_scroll()
 4206        };
 4207
 4208        self.change_selections(effects, window, cx, |s| {
 4209            s.set_pending(pending_selection.clone(), pending_mode);
 4210            s.set_is_extending(true);
 4211        });
 4212    }
 4213
 4214    fn begin_selection(
 4215        &mut self,
 4216        position: DisplayPoint,
 4217        add: bool,
 4218        click_count: usize,
 4219        window: &mut Window,
 4220        cx: &mut Context<Self>,
 4221    ) {
 4222        if !self.focus_handle.is_focused(window) {
 4223            self.last_focused_descendant = None;
 4224            window.focus(&self.focus_handle, cx);
 4225        }
 4226
 4227        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4228        let buffer = display_map.buffer_snapshot();
 4229        let position = display_map.clip_point(position, Bias::Left);
 4230
 4231        let start;
 4232        let end;
 4233        let mode;
 4234        let mut auto_scroll;
 4235        match click_count {
 4236            1 => {
 4237                start = buffer.anchor_before(position.to_point(&display_map));
 4238                end = start;
 4239                mode = SelectMode::Character;
 4240                auto_scroll = true;
 4241            }
 4242            2 => {
 4243                let position = display_map
 4244                    .clip_point(position, Bias::Left)
 4245                    .to_offset(&display_map, Bias::Left);
 4246                let (range, _) = buffer.surrounding_word(position, None);
 4247                start = buffer.anchor_before(range.start);
 4248                end = buffer.anchor_before(range.end);
 4249                mode = SelectMode::Word(start..end);
 4250                auto_scroll = true;
 4251            }
 4252            3 => {
 4253                let position = display_map
 4254                    .clip_point(position, Bias::Left)
 4255                    .to_point(&display_map);
 4256                let line_start = display_map.prev_line_boundary(position).0;
 4257                let next_line_start = buffer.clip_point(
 4258                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4259                    Bias::Left,
 4260                );
 4261                start = buffer.anchor_before(line_start);
 4262                end = buffer.anchor_before(next_line_start);
 4263                mode = SelectMode::Line(start..end);
 4264                auto_scroll = true;
 4265            }
 4266            _ => {
 4267                start = buffer.anchor_before(MultiBufferOffset(0));
 4268                end = buffer.anchor_before(buffer.len());
 4269                mode = SelectMode::All;
 4270                auto_scroll = false;
 4271            }
 4272        }
 4273        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4274
 4275        let point_to_delete: Option<usize> = {
 4276            let selected_points: Vec<Selection<Point>> =
 4277                self.selections.disjoint_in_range(start..end, &display_map);
 4278
 4279            if !add || click_count > 1 {
 4280                None
 4281            } else if !selected_points.is_empty() {
 4282                Some(selected_points[0].id)
 4283            } else {
 4284                let clicked_point_already_selected =
 4285                    self.selections.disjoint_anchors().iter().find(|selection| {
 4286                        selection.start.to_point(buffer) == start.to_point(buffer)
 4287                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4288                    });
 4289
 4290                clicked_point_already_selected.map(|selection| selection.id)
 4291            }
 4292        };
 4293
 4294        let selections_count = self.selections.count();
 4295        let effects = if auto_scroll {
 4296            SelectionEffects::default()
 4297        } else {
 4298            SelectionEffects::no_scroll()
 4299        };
 4300
 4301        self.change_selections(effects, window, cx, |s| {
 4302            if let Some(point_to_delete) = point_to_delete {
 4303                s.delete(point_to_delete);
 4304
 4305                if selections_count == 1 {
 4306                    s.set_pending_anchor_range(start..end, mode);
 4307                }
 4308            } else {
 4309                if !add {
 4310                    s.clear_disjoint();
 4311                }
 4312
 4313                s.set_pending_anchor_range(start..end, mode);
 4314            }
 4315        });
 4316    }
 4317
 4318    fn begin_columnar_selection(
 4319        &mut self,
 4320        position: DisplayPoint,
 4321        goal_column: u32,
 4322        reset: bool,
 4323        mode: ColumnarMode,
 4324        window: &mut Window,
 4325        cx: &mut Context<Self>,
 4326    ) {
 4327        if !self.focus_handle.is_focused(window) {
 4328            self.last_focused_descendant = None;
 4329            window.focus(&self.focus_handle, cx);
 4330        }
 4331
 4332        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4333
 4334        if reset {
 4335            let pointer_position = display_map
 4336                .buffer_snapshot()
 4337                .anchor_before(position.to_point(&display_map));
 4338
 4339            self.change_selections(
 4340                SelectionEffects::scroll(Autoscroll::newest()),
 4341                window,
 4342                cx,
 4343                |s| {
 4344                    s.clear_disjoint();
 4345                    s.set_pending_anchor_range(
 4346                        pointer_position..pointer_position,
 4347                        SelectMode::Character,
 4348                    );
 4349                },
 4350            );
 4351        };
 4352
 4353        let tail = self.selections.newest::<Point>(&display_map).tail();
 4354        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4355        self.columnar_selection_state = match mode {
 4356            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4357                selection_tail: selection_anchor,
 4358                display_point: if reset {
 4359                    if position.column() != goal_column {
 4360                        Some(DisplayPoint::new(position.row(), goal_column))
 4361                    } else {
 4362                        None
 4363                    }
 4364                } else {
 4365                    None
 4366                },
 4367            }),
 4368            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4369                selection_tail: selection_anchor,
 4370            }),
 4371        };
 4372
 4373        if !reset {
 4374            self.select_columns(position, goal_column, &display_map, window, cx);
 4375        }
 4376    }
 4377
 4378    fn update_selection(
 4379        &mut self,
 4380        position: DisplayPoint,
 4381        goal_column: u32,
 4382        scroll_delta: gpui::Point<f32>,
 4383        window: &mut Window,
 4384        cx: &mut Context<Self>,
 4385    ) {
 4386        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4387
 4388        if self.columnar_selection_state.is_some() {
 4389            self.select_columns(position, goal_column, &display_map, window, cx);
 4390        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4391            let buffer = display_map.buffer_snapshot();
 4392            let head;
 4393            let tail;
 4394            let mode = self.selections.pending_mode().unwrap();
 4395            match &mode {
 4396                SelectMode::Character => {
 4397                    head = position.to_point(&display_map);
 4398                    tail = pending.tail().to_point(buffer);
 4399                }
 4400                SelectMode::Word(original_range) => {
 4401                    let offset = display_map
 4402                        .clip_point(position, Bias::Left)
 4403                        .to_offset(&display_map, Bias::Left);
 4404                    let original_range = original_range.to_offset(buffer);
 4405
 4406                    let head_offset = if buffer.is_inside_word(offset, None)
 4407                        || original_range.contains(&offset)
 4408                    {
 4409                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4410                        if word_range.start < original_range.start {
 4411                            word_range.start
 4412                        } else {
 4413                            word_range.end
 4414                        }
 4415                    } else {
 4416                        offset
 4417                    };
 4418
 4419                    head = head_offset.to_point(buffer);
 4420                    if head_offset <= original_range.start {
 4421                        tail = original_range.end.to_point(buffer);
 4422                    } else {
 4423                        tail = original_range.start.to_point(buffer);
 4424                    }
 4425                }
 4426                SelectMode::Line(original_range) => {
 4427                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4428
 4429                    let position = display_map
 4430                        .clip_point(position, Bias::Left)
 4431                        .to_point(&display_map);
 4432                    let line_start = display_map.prev_line_boundary(position).0;
 4433                    let next_line_start = buffer.clip_point(
 4434                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4435                        Bias::Left,
 4436                    );
 4437
 4438                    if line_start < original_range.start {
 4439                        head = line_start
 4440                    } else {
 4441                        head = next_line_start
 4442                    }
 4443
 4444                    if head <= original_range.start {
 4445                        tail = original_range.end;
 4446                    } else {
 4447                        tail = original_range.start;
 4448                    }
 4449                }
 4450                SelectMode::All => {
 4451                    return;
 4452                }
 4453            };
 4454
 4455            if head < tail {
 4456                pending.start = buffer.anchor_before(head);
 4457                pending.end = buffer.anchor_before(tail);
 4458                pending.reversed = true;
 4459            } else {
 4460                pending.start = buffer.anchor_before(tail);
 4461                pending.end = buffer.anchor_before(head);
 4462                pending.reversed = false;
 4463            }
 4464
 4465            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4466                s.set_pending(pending.clone(), mode);
 4467            });
 4468        } else {
 4469            log::error!("update_selection dispatched with no pending selection");
 4470            return;
 4471        }
 4472
 4473        self.apply_scroll_delta(scroll_delta, window, cx);
 4474        cx.notify();
 4475    }
 4476
 4477    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4478        self.columnar_selection_state.take();
 4479        if let Some(pending_mode) = self.selections.pending_mode() {
 4480            let selections = self
 4481                .selections
 4482                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4483            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4484                s.select(selections);
 4485                s.clear_pending();
 4486                if s.is_extending() {
 4487                    s.set_is_extending(false);
 4488                } else {
 4489                    s.set_select_mode(pending_mode);
 4490                }
 4491            });
 4492        }
 4493    }
 4494
 4495    fn select_columns(
 4496        &mut self,
 4497        head: DisplayPoint,
 4498        goal_column: u32,
 4499        display_map: &DisplaySnapshot,
 4500        window: &mut Window,
 4501        cx: &mut Context<Self>,
 4502    ) {
 4503        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4504            return;
 4505        };
 4506
 4507        let tail = match columnar_state {
 4508            ColumnarSelectionState::FromMouse {
 4509                selection_tail,
 4510                display_point,
 4511            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4512            ColumnarSelectionState::FromSelection { selection_tail } => {
 4513                selection_tail.to_display_point(display_map)
 4514            }
 4515        };
 4516
 4517        let start_row = cmp::min(tail.row(), head.row());
 4518        let end_row = cmp::max(tail.row(), head.row());
 4519        let start_column = cmp::min(tail.column(), goal_column);
 4520        let end_column = cmp::max(tail.column(), goal_column);
 4521        let reversed = start_column < tail.column();
 4522
 4523        let selection_ranges = (start_row.0..=end_row.0)
 4524            .map(DisplayRow)
 4525            .filter_map(|row| {
 4526                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4527                    || start_column <= display_map.line_len(row))
 4528                    && !display_map.is_block_line(row)
 4529                {
 4530                    let start = display_map
 4531                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4532                        .to_point(display_map);
 4533                    let end = display_map
 4534                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4535                        .to_point(display_map);
 4536                    if reversed {
 4537                        Some(end..start)
 4538                    } else {
 4539                        Some(start..end)
 4540                    }
 4541                } else {
 4542                    None
 4543                }
 4544            })
 4545            .collect::<Vec<_>>();
 4546        if selection_ranges.is_empty() {
 4547            return;
 4548        }
 4549
 4550        let ranges = match columnar_state {
 4551            ColumnarSelectionState::FromMouse { .. } => {
 4552                let mut non_empty_ranges = selection_ranges
 4553                    .iter()
 4554                    .filter(|selection_range| selection_range.start != selection_range.end)
 4555                    .peekable();
 4556                if non_empty_ranges.peek().is_some() {
 4557                    non_empty_ranges.cloned().collect()
 4558                } else {
 4559                    selection_ranges
 4560                }
 4561            }
 4562            _ => selection_ranges,
 4563        };
 4564
 4565        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4566            s.select_ranges(ranges);
 4567        });
 4568        cx.notify();
 4569    }
 4570
 4571    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4572        self.selections
 4573            .all_adjusted(snapshot)
 4574            .iter()
 4575            .any(|selection| !selection.is_empty())
 4576    }
 4577
 4578    pub fn has_pending_nonempty_selection(&self) -> bool {
 4579        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4580            Some(Selection { start, end, .. }) => start != end,
 4581            None => false,
 4582        };
 4583
 4584        pending_nonempty_selection
 4585            || (self.columnar_selection_state.is_some()
 4586                && self.selections.disjoint_anchors().len() > 1)
 4587    }
 4588
 4589    pub fn has_pending_selection(&self) -> bool {
 4590        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4591    }
 4592
 4593    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4594        self.selection_mark_mode = false;
 4595        self.selection_drag_state = SelectionDragState::None;
 4596
 4597        if self.dismiss_menus_and_popups(true, window, cx) {
 4598            cx.notify();
 4599            return;
 4600        }
 4601        if self.clear_expanded_diff_hunks(cx) {
 4602            cx.notify();
 4603            return;
 4604        }
 4605        if self.show_git_blame_gutter {
 4606            self.show_git_blame_gutter = false;
 4607            cx.notify();
 4608            return;
 4609        }
 4610
 4611        if self.mode.is_full()
 4612            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4613        {
 4614            cx.notify();
 4615            return;
 4616        }
 4617
 4618        cx.propagate();
 4619    }
 4620
 4621    pub fn dismiss_menus_and_popups(
 4622        &mut self,
 4623        is_user_requested: bool,
 4624        window: &mut Window,
 4625        cx: &mut Context<Self>,
 4626    ) -> bool {
 4627        let mut dismissed = false;
 4628
 4629        dismissed |= self.take_rename(false, window, cx).is_some();
 4630        dismissed |= self.hide_blame_popover(true, cx);
 4631        dismissed |= hide_hover(self, cx);
 4632        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4633        dismissed |= self.hide_context_menu(window, cx).is_some();
 4634        dismissed |= self.mouse_context_menu.take().is_some();
 4635        dismissed |= is_user_requested
 4636            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4637        dismissed |= self.snippet_stack.pop().is_some();
 4638        if self.diff_review_drag_state.is_some() {
 4639            self.cancel_diff_review_drag(cx);
 4640            dismissed = true;
 4641        }
 4642        if !self.diff_review_overlays.is_empty() {
 4643            self.dismiss_all_diff_review_overlays(cx);
 4644            dismissed = true;
 4645        }
 4646
 4647        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4648            self.dismiss_diagnostics(cx);
 4649            dismissed = true;
 4650        }
 4651
 4652        dismissed
 4653    }
 4654
 4655    fn linked_editing_ranges_for(
 4656        &self,
 4657        selection: Range<text::Anchor>,
 4658        cx: &App,
 4659    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4660        if self.linked_edit_ranges.is_empty() {
 4661            return None;
 4662        }
 4663        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4664            selection.end.buffer_id.and_then(|end_buffer_id| {
 4665                if selection.start.buffer_id != Some(end_buffer_id) {
 4666                    return None;
 4667                }
 4668                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4669                let snapshot = buffer.read(cx).snapshot();
 4670                self.linked_edit_ranges
 4671                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4672                    .map(|ranges| (ranges, snapshot, buffer))
 4673            })?;
 4674        use text::ToOffset as TO;
 4675        // find offset from the start of current range to current cursor position
 4676        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4677
 4678        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4679        let start_difference = start_offset - start_byte_offset;
 4680        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4681        let end_difference = end_offset - start_byte_offset;
 4682
 4683        // Current range has associated linked ranges.
 4684        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4685        for range in linked_ranges.iter() {
 4686            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4687            let end_offset = start_offset + end_difference;
 4688            let start_offset = start_offset + start_difference;
 4689            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4690                continue;
 4691            }
 4692            if self.selections.disjoint_anchor_ranges().any(|s| {
 4693                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4694                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4695                {
 4696                    return false;
 4697                }
 4698                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4699                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4700            }) {
 4701                continue;
 4702            }
 4703            let start = buffer_snapshot.anchor_after(start_offset);
 4704            let end = buffer_snapshot.anchor_after(end_offset);
 4705            linked_edits
 4706                .entry(buffer.clone())
 4707                .or_default()
 4708                .push(start..end);
 4709        }
 4710        Some(linked_edits)
 4711    }
 4712
 4713    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4714        let text: Arc<str> = text.into();
 4715
 4716        if self.read_only(cx) {
 4717            return;
 4718        }
 4719
 4720        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4721
 4722        self.unfold_buffers_with_selections(cx);
 4723
 4724        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4725        let mut bracket_inserted = false;
 4726        let mut edits = Vec::new();
 4727        let mut linked_edits = LinkedEdits::new();
 4728        let mut new_selections = Vec::with_capacity(selections.len());
 4729        let mut new_autoclose_regions = Vec::new();
 4730        let snapshot = self.buffer.read(cx).read(cx);
 4731        let mut clear_linked_edit_ranges = false;
 4732        let mut all_selections_read_only = true;
 4733        let mut has_adjacent_edits = false;
 4734        let mut in_adjacent_group = false;
 4735
 4736        let mut regions = self
 4737            .selections_with_autoclose_regions(selections, &snapshot)
 4738            .peekable();
 4739
 4740        while let Some((selection, autoclose_region)) = regions.next() {
 4741            if snapshot
 4742                .point_to_buffer_point(selection.head())
 4743                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4744            {
 4745                continue;
 4746            }
 4747            if snapshot
 4748                .point_to_buffer_point(selection.tail())
 4749                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4750            {
 4751                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4752                continue;
 4753            }
 4754            all_selections_read_only = false;
 4755
 4756            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4757                // Determine if the inserted text matches the opening or closing
 4758                // bracket of any of this language's bracket pairs.
 4759                let mut bracket_pair = None;
 4760                let mut is_bracket_pair_start = false;
 4761                let mut is_bracket_pair_end = false;
 4762                if !text.is_empty() {
 4763                    let mut bracket_pair_matching_end = None;
 4764                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4765                    //  and they are removing the character that triggered IME popup.
 4766                    for (pair, enabled) in scope.brackets() {
 4767                        if !pair.close && !pair.surround {
 4768                            continue;
 4769                        }
 4770
 4771                        if enabled && pair.start.ends_with(text.as_ref()) {
 4772                            let prefix_len = pair.start.len() - text.len();
 4773                            let preceding_text_matches_prefix = prefix_len == 0
 4774                                || (selection.start.column >= (prefix_len as u32)
 4775                                    && snapshot.contains_str_at(
 4776                                        Point::new(
 4777                                            selection.start.row,
 4778                                            selection.start.column - (prefix_len as u32),
 4779                                        ),
 4780                                        &pair.start[..prefix_len],
 4781                                    ));
 4782                            if preceding_text_matches_prefix {
 4783                                bracket_pair = Some(pair.clone());
 4784                                is_bracket_pair_start = true;
 4785                                break;
 4786                            }
 4787                        }
 4788                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4789                        {
 4790                            // take first bracket pair matching end, but don't break in case a later bracket
 4791                            // pair matches start
 4792                            bracket_pair_matching_end = Some(pair.clone());
 4793                        }
 4794                    }
 4795                    if let Some(end) = bracket_pair_matching_end
 4796                        && bracket_pair.is_none()
 4797                    {
 4798                        bracket_pair = Some(end);
 4799                        is_bracket_pair_end = true;
 4800                    }
 4801                }
 4802
 4803                if let Some(bracket_pair) = bracket_pair {
 4804                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4805                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4806                    let auto_surround =
 4807                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4808                    if selection.is_empty() {
 4809                        if is_bracket_pair_start {
 4810                            // If the inserted text is a suffix of an opening bracket and the
 4811                            // selection is preceded by the rest of the opening bracket, then
 4812                            // insert the closing bracket.
 4813                            let following_text_allows_autoclose = snapshot
 4814                                .chars_at(selection.start)
 4815                                .next()
 4816                                .is_none_or(|c| scope.should_autoclose_before(c));
 4817
 4818                            let preceding_text_allows_autoclose = selection.start.column == 0
 4819                                || snapshot
 4820                                    .reversed_chars_at(selection.start)
 4821                                    .next()
 4822                                    .is_none_or(|c| {
 4823                                        bracket_pair.start != bracket_pair.end
 4824                                            || !snapshot
 4825                                                .char_classifier_at(selection.start)
 4826                                                .is_word(c)
 4827                                    });
 4828
 4829                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4830                                && bracket_pair.start.len() == 1
 4831                            {
 4832                                let target = bracket_pair.start.chars().next().unwrap();
 4833                                let mut byte_offset = 0u32;
 4834                                let current_line_count = snapshot
 4835                                    .reversed_chars_at(selection.start)
 4836                                    .take_while(|&c| c != '\n')
 4837                                    .filter(|c| {
 4838                                        byte_offset += c.len_utf8() as u32;
 4839                                        if *c != target {
 4840                                            return false;
 4841                                        }
 4842
 4843                                        let point = Point::new(
 4844                                            selection.start.row,
 4845                                            selection.start.column.saturating_sub(byte_offset),
 4846                                        );
 4847
 4848                                        let is_enabled = snapshot
 4849                                            .language_scope_at(point)
 4850                                            .and_then(|scope| {
 4851                                                scope
 4852                                                    .brackets()
 4853                                                    .find(|(pair, _)| {
 4854                                                        pair.start == bracket_pair.start
 4855                                                    })
 4856                                                    .map(|(_, enabled)| enabled)
 4857                                            })
 4858                                            .unwrap_or(true);
 4859
 4860                                        let is_delimiter = snapshot
 4861                                            .language_scope_at(Point::new(
 4862                                                point.row,
 4863                                                point.column + 1,
 4864                                            ))
 4865                                            .and_then(|scope| {
 4866                                                scope
 4867                                                    .brackets()
 4868                                                    .find(|(pair, _)| {
 4869                                                        pair.start == bracket_pair.start
 4870                                                    })
 4871                                                    .map(|(_, enabled)| !enabled)
 4872                                            })
 4873                                            .unwrap_or(false);
 4874
 4875                                        is_enabled && !is_delimiter
 4876                                    })
 4877                                    .count();
 4878                                current_line_count % 2 == 1
 4879                            } else {
 4880                                false
 4881                            };
 4882
 4883                            if autoclose
 4884                                && bracket_pair.close
 4885                                && following_text_allows_autoclose
 4886                                && preceding_text_allows_autoclose
 4887                                && !is_closing_quote
 4888                            {
 4889                                let anchor = snapshot.anchor_before(selection.end);
 4890                                new_selections.push((selection.map(|_| anchor), text.len()));
 4891                                new_autoclose_regions.push((
 4892                                    anchor,
 4893                                    text.len(),
 4894                                    selection.id,
 4895                                    bracket_pair.clone(),
 4896                                ));
 4897                                edits.push((
 4898                                    selection.range(),
 4899                                    format!("{}{}", text, bracket_pair.end).into(),
 4900                                ));
 4901                                bracket_inserted = true;
 4902                                continue;
 4903                            }
 4904                        }
 4905
 4906                        if let Some(region) = autoclose_region {
 4907                            // If the selection is followed by an auto-inserted closing bracket,
 4908                            // then don't insert that closing bracket again; just move the selection
 4909                            // past the closing bracket.
 4910                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4911                                && text.as_ref() == region.pair.end.as_str()
 4912                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4913                            if should_skip {
 4914                                let anchor = snapshot.anchor_after(selection.end);
 4915                                new_selections
 4916                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4917                                continue;
 4918                            }
 4919                        }
 4920
 4921                        let always_treat_brackets_as_autoclosed = snapshot
 4922                            .language_settings_at(selection.start, cx)
 4923                            .always_treat_brackets_as_autoclosed;
 4924                        if always_treat_brackets_as_autoclosed
 4925                            && is_bracket_pair_end
 4926                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4927                        {
 4928                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4929                            // and the inserted text is a closing bracket and the selection is followed
 4930                            // by the closing bracket then move the selection past the closing bracket.
 4931                            let anchor = snapshot.anchor_after(selection.end);
 4932                            new_selections.push((selection.map(|_| anchor), text.len()));
 4933                            continue;
 4934                        }
 4935                    }
 4936                    // If an opening bracket is 1 character long and is typed while
 4937                    // text is selected, then surround that text with the bracket pair.
 4938                    else if auto_surround
 4939                        && bracket_pair.surround
 4940                        && is_bracket_pair_start
 4941                        && bracket_pair.start.chars().count() == 1
 4942                    {
 4943                        edits.push((selection.start..selection.start, text.clone()));
 4944                        edits.push((
 4945                            selection.end..selection.end,
 4946                            bracket_pair.end.as_str().into(),
 4947                        ));
 4948                        bracket_inserted = true;
 4949                        new_selections.push((
 4950                            Selection {
 4951                                id: selection.id,
 4952                                start: snapshot.anchor_after(selection.start),
 4953                                end: snapshot.anchor_before(selection.end),
 4954                                reversed: selection.reversed,
 4955                                goal: selection.goal,
 4956                            },
 4957                            0,
 4958                        ));
 4959                        continue;
 4960                    }
 4961                }
 4962            }
 4963
 4964            if self.auto_replace_emoji_shortcode
 4965                && selection.is_empty()
 4966                && text.as_ref().ends_with(':')
 4967                && let Some(possible_emoji_short_code) =
 4968                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4969                && !possible_emoji_short_code.is_empty()
 4970                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4971            {
 4972                let emoji_shortcode_start = Point::new(
 4973                    selection.start.row,
 4974                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4975                );
 4976
 4977                // Remove shortcode from buffer
 4978                edits.push((
 4979                    emoji_shortcode_start..selection.start,
 4980                    "".to_string().into(),
 4981                ));
 4982                new_selections.push((
 4983                    Selection {
 4984                        id: selection.id,
 4985                        start: snapshot.anchor_after(emoji_shortcode_start),
 4986                        end: snapshot.anchor_before(selection.start),
 4987                        reversed: selection.reversed,
 4988                        goal: selection.goal,
 4989                    },
 4990                    0,
 4991                ));
 4992
 4993                // Insert emoji
 4994                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4995                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4996                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4997
 4998                continue;
 4999            }
 5000
 5001            let next_is_adjacent = regions
 5002                .peek()
 5003                .is_some_and(|(next, _)| selection.end == next.start);
 5004
 5005            // If not handling any auto-close operation, then just replace the selected
 5006            // text with the given input and move the selection to the end of the
 5007            // newly inserted text.
 5008            let anchor = if in_adjacent_group || next_is_adjacent {
 5009                // After edits the right bias would shift those anchor to the next visible fragment
 5010                // but we want to resolve to the previous one
 5011                snapshot.anchor_before(selection.end)
 5012            } else {
 5013                snapshot.anchor_after(selection.end)
 5014            };
 5015
 5016            if !self.linked_edit_ranges.is_empty() {
 5017                let start_anchor = snapshot.anchor_before(selection.start);
 5018
 5019                let is_word_char = text.chars().next().is_none_or(|char| {
 5020                    let classifier = snapshot
 5021                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 5022                        .scope_context(Some(CharScopeContext::LinkedEdit));
 5023                    classifier.is_word(char)
 5024                });
 5025                let is_dot = text.as_ref() == ".";
 5026                let should_apply_linked_edit = is_word_char || is_dot;
 5027
 5028                if should_apply_linked_edit {
 5029                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 5030                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 5031                } else {
 5032                    clear_linked_edit_ranges = true;
 5033                }
 5034            }
 5035
 5036            new_selections.push((selection.map(|_| anchor), 0));
 5037            edits.push((selection.start..selection.end, text.clone()));
 5038
 5039            has_adjacent_edits |= next_is_adjacent;
 5040            in_adjacent_group = next_is_adjacent;
 5041        }
 5042
 5043        if all_selections_read_only {
 5044            return;
 5045        }
 5046
 5047        drop(regions);
 5048        drop(snapshot);
 5049
 5050        self.transact(window, cx, |this, window, cx| {
 5051            if clear_linked_edit_ranges {
 5052                this.linked_edit_ranges.clear();
 5053            }
 5054            let initial_buffer_versions =
 5055                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 5056
 5057            this.buffer.update(cx, |buffer, cx| {
 5058                if has_adjacent_edits {
 5059                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 5060                } else {
 5061                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 5062                }
 5063            });
 5064            linked_edits.apply(cx);
 5065            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 5066            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 5067            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 5068            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 5069                new_anchor_selections,
 5070                &map,
 5071            )
 5072            .zip(new_selection_deltas)
 5073            .map(|(selection, delta)| Selection {
 5074                id: selection.id,
 5075                start: selection.start + delta,
 5076                end: selection.end + delta,
 5077                reversed: selection.reversed,
 5078                goal: SelectionGoal::None,
 5079            })
 5080            .collect::<Vec<_>>();
 5081
 5082            let mut i = 0;
 5083            for (position, delta, selection_id, pair) in new_autoclose_regions {
 5084                let position = position.to_offset(map.buffer_snapshot()) + delta;
 5085                let start = map.buffer_snapshot().anchor_before(position);
 5086                let end = map.buffer_snapshot().anchor_after(position);
 5087                while let Some(existing_state) = this.autoclose_regions.get(i) {
 5088                    match existing_state
 5089                        .range
 5090                        .start
 5091                        .cmp(&start, map.buffer_snapshot())
 5092                    {
 5093                        Ordering::Less => i += 1,
 5094                        Ordering::Greater => break,
 5095                        Ordering::Equal => {
 5096                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 5097                                Ordering::Less => i += 1,
 5098                                Ordering::Equal => break,
 5099                                Ordering::Greater => break,
 5100                            }
 5101                        }
 5102                    }
 5103                }
 5104                this.autoclose_regions.insert(
 5105                    i,
 5106                    AutocloseRegion {
 5107                        selection_id,
 5108                        range: start..end,
 5109                        pair,
 5110                    },
 5111                );
 5112            }
 5113
 5114            let had_active_edit_prediction = this.has_active_edit_prediction();
 5115            this.change_selections(
 5116                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 5117                window,
 5118                cx,
 5119                |s| s.select(new_selections),
 5120            );
 5121
 5122            if !bracket_inserted
 5123                && let Some(on_type_format_task) =
 5124                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 5125            {
 5126                on_type_format_task.detach_and_log_err(cx);
 5127            }
 5128
 5129            let editor_settings = EditorSettings::get_global(cx);
 5130            if bracket_inserted
 5131                && (editor_settings.auto_signature_help
 5132                    || editor_settings.show_signature_help_after_edits)
 5133            {
 5134                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5135            }
 5136
 5137            let trigger_in_words =
 5138                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5139            if this.hard_wrap.is_some() {
 5140                let latest: Range<Point> = this.selections.newest(&map).range();
 5141                if latest.is_empty()
 5142                    && this
 5143                        .buffer()
 5144                        .read(cx)
 5145                        .snapshot(cx)
 5146                        .line_len(MultiBufferRow(latest.start.row))
 5147                        == latest.start.column
 5148                {
 5149                    this.rewrap_impl(
 5150                        RewrapOptions {
 5151                            override_language_settings: true,
 5152                            preserve_existing_whitespace: true,
 5153                        },
 5154                        cx,
 5155                    )
 5156                }
 5157            }
 5158            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5159            refresh_linked_ranges(this, window, cx);
 5160            this.refresh_edit_prediction(true, false, window, cx);
 5161            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5162        });
 5163    }
 5164
 5165    fn find_possible_emoji_shortcode_at_position(
 5166        snapshot: &MultiBufferSnapshot,
 5167        position: Point,
 5168    ) -> Option<String> {
 5169        let mut chars = Vec::new();
 5170        let mut found_colon = false;
 5171        for char in snapshot.reversed_chars_at(position).take(100) {
 5172            // Found a possible emoji shortcode in the middle of the buffer
 5173            if found_colon {
 5174                if char.is_whitespace() {
 5175                    chars.reverse();
 5176                    return Some(chars.iter().collect());
 5177                }
 5178                // If the previous character is not a whitespace, we are in the middle of a word
 5179                // and we only want to complete the shortcode if the word is made up of other emojis
 5180                let mut containing_word = String::new();
 5181                for ch in snapshot
 5182                    .reversed_chars_at(position)
 5183                    .skip(chars.len() + 1)
 5184                    .take(100)
 5185                {
 5186                    if ch.is_whitespace() {
 5187                        break;
 5188                    }
 5189                    containing_word.push(ch);
 5190                }
 5191                let containing_word = containing_word.chars().rev().collect::<String>();
 5192                if util::word_consists_of_emojis(containing_word.as_str()) {
 5193                    chars.reverse();
 5194                    return Some(chars.iter().collect());
 5195                }
 5196            }
 5197
 5198            if char.is_whitespace() || !char.is_ascii() {
 5199                return None;
 5200            }
 5201            if char == ':' {
 5202                found_colon = true;
 5203            } else {
 5204                chars.push(char);
 5205            }
 5206        }
 5207        // Found a possible emoji shortcode at the beginning of the buffer
 5208        chars.reverse();
 5209        Some(chars.iter().collect())
 5210    }
 5211
 5212    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5213        if self.read_only(cx) {
 5214            return;
 5215        }
 5216
 5217        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5218        self.transact(window, cx, |this, window, cx| {
 5219            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5220                let selections = this
 5221                    .selections
 5222                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5223                let multi_buffer = this.buffer.read(cx);
 5224                let buffer = multi_buffer.snapshot(cx);
 5225                selections
 5226                    .iter()
 5227                    .map(|selection| {
 5228                        let start_point = selection.start.to_point(&buffer);
 5229                        let mut existing_indent =
 5230                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5231                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5232                        let start = selection.start;
 5233                        let end = selection.end;
 5234                        let selection_is_empty = start == end;
 5235                        let language_scope = buffer.language_scope_at(start);
 5236                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5237                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5238                                &buffer,
 5239                                start..end,
 5240                                language,
 5241                            )
 5242                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5243                                    &buffer,
 5244                                    start..end,
 5245                                );
 5246
 5247                            let mut newline_config = NewlineConfig::Newline {
 5248                                additional_indent: IndentSize::spaces(0),
 5249                                extra_line_additional_indent: if needs_extra_newline {
 5250                                    Some(IndentSize::spaces(0))
 5251                                } else {
 5252                                    None
 5253                                },
 5254                                prevent_auto_indent: false,
 5255                            };
 5256
 5257                            let comment_delimiter = maybe!({
 5258                                if !selection_is_empty {
 5259                                    return None;
 5260                                }
 5261
 5262                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5263                                    return None;
 5264                                }
 5265
 5266                                return comment_delimiter_for_newline(
 5267                                    &start_point,
 5268                                    &buffer,
 5269                                    language,
 5270                                );
 5271                            });
 5272
 5273                            let doc_delimiter = maybe!({
 5274                                if !selection_is_empty {
 5275                                    return None;
 5276                                }
 5277
 5278                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5279                                    return None;
 5280                                }
 5281
 5282                                return documentation_delimiter_for_newline(
 5283                                    &start_point,
 5284                                    &buffer,
 5285                                    language,
 5286                                    &mut newline_config,
 5287                                );
 5288                            });
 5289
 5290                            let list_delimiter = maybe!({
 5291                                if !selection_is_empty {
 5292                                    return None;
 5293                                }
 5294
 5295                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5296                                    return None;
 5297                                }
 5298
 5299                                return list_delimiter_for_newline(
 5300                                    &start_point,
 5301                                    &buffer,
 5302                                    language,
 5303                                    &mut newline_config,
 5304                                );
 5305                            });
 5306
 5307                            (
 5308                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5309                                newline_config,
 5310                            )
 5311                        } else {
 5312                            (
 5313                                None,
 5314                                NewlineConfig::Newline {
 5315                                    additional_indent: IndentSize::spaces(0),
 5316                                    extra_line_additional_indent: None,
 5317                                    prevent_auto_indent: false,
 5318                                },
 5319                            )
 5320                        };
 5321
 5322                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5323                            NewlineConfig::ClearCurrentLine => {
 5324                                let row_start =
 5325                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5326                                (row_start, String::new(), false)
 5327                            }
 5328                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5329                                let row_start =
 5330                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5331                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5332                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5333                                let reduced_indent =
 5334                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5335                                let mut new_text = String::new();
 5336                                new_text.extend(reduced_indent.chars());
 5337                                new_text.push_str(continuation);
 5338                                (row_start, new_text, true)
 5339                            }
 5340                            NewlineConfig::Newline {
 5341                                additional_indent,
 5342                                extra_line_additional_indent,
 5343                                prevent_auto_indent,
 5344                            } => {
 5345                                let auto_indent_mode =
 5346                                    buffer.language_settings_at(start, cx).auto_indent;
 5347                                let preserve_indent =
 5348                                    auto_indent_mode != language::AutoIndentMode::None;
 5349                                let apply_syntax_indent =
 5350                                    auto_indent_mode == language::AutoIndentMode::SyntaxAware;
 5351                                let capacity_for_delimiter =
 5352                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5353                                let existing_indent_len = if preserve_indent {
 5354                                    existing_indent.len as usize
 5355                                } else {
 5356                                    0
 5357                                };
 5358                                let extra_line_len = extra_line_additional_indent
 5359                                    .map(|i| 1 + existing_indent_len + i.len as usize)
 5360                                    .unwrap_or(0);
 5361                                let mut new_text = String::with_capacity(
 5362                                    1 + capacity_for_delimiter
 5363                                        + existing_indent_len
 5364                                        + additional_indent.len as usize
 5365                                        + extra_line_len,
 5366                                );
 5367                                new_text.push('\n');
 5368                                if preserve_indent {
 5369                                    new_text.extend(existing_indent.chars());
 5370                                }
 5371                                new_text.extend(additional_indent.chars());
 5372                                if let Some(delimiter) = &delimiter {
 5373                                    new_text.push_str(delimiter);
 5374                                }
 5375                                if let Some(extra_indent) = extra_line_additional_indent {
 5376                                    new_text.push('\n');
 5377                                    if preserve_indent {
 5378                                        new_text.extend(existing_indent.chars());
 5379                                    }
 5380                                    new_text.extend(extra_indent.chars());
 5381                                }
 5382                                (
 5383                                    start,
 5384                                    new_text,
 5385                                    *prevent_auto_indent || !apply_syntax_indent,
 5386                                )
 5387                            }
 5388                        };
 5389
 5390                        let anchor = buffer.anchor_after(end);
 5391                        let new_selection = selection.map(|_| anchor);
 5392                        (
 5393                            ((edit_start..end, new_text), prevent_auto_indent),
 5394                            (newline_config.has_extra_line(), new_selection),
 5395                        )
 5396                    })
 5397                    .unzip()
 5398            };
 5399
 5400            let mut auto_indent_edits = Vec::new();
 5401            let mut edits = Vec::new();
 5402            for (edit, prevent_auto_indent) in edits_with_flags {
 5403                if prevent_auto_indent {
 5404                    edits.push(edit);
 5405                } else {
 5406                    auto_indent_edits.push(edit);
 5407                }
 5408            }
 5409            if !edits.is_empty() {
 5410                this.edit(edits, cx);
 5411            }
 5412            if !auto_indent_edits.is_empty() {
 5413                this.edit_with_autoindent(auto_indent_edits, cx);
 5414            }
 5415
 5416            let buffer = this.buffer.read(cx).snapshot(cx);
 5417            let new_selections = selection_info
 5418                .into_iter()
 5419                .map(|(extra_newline_inserted, new_selection)| {
 5420                    let mut cursor = new_selection.end.to_point(&buffer);
 5421                    if extra_newline_inserted {
 5422                        cursor.row -= 1;
 5423                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5424                    }
 5425                    new_selection.map(|_| cursor)
 5426                })
 5427                .collect();
 5428
 5429            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5430            this.refresh_edit_prediction(true, false, window, cx);
 5431            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5432                task.detach_and_log_err(cx);
 5433            }
 5434        });
 5435    }
 5436
 5437    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5438        if self.read_only(cx) {
 5439            return;
 5440        }
 5441
 5442        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5443
 5444        let buffer = self.buffer.read(cx);
 5445        let snapshot = buffer.snapshot(cx);
 5446
 5447        let mut edits = Vec::new();
 5448        let mut rows = Vec::new();
 5449
 5450        for (rows_inserted, selection) in self
 5451            .selections
 5452            .all_adjusted(&self.display_snapshot(cx))
 5453            .into_iter()
 5454            .enumerate()
 5455        {
 5456            let cursor = selection.head();
 5457            let row = cursor.row;
 5458
 5459            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5460
 5461            let newline = "\n".to_string();
 5462            edits.push((start_of_line..start_of_line, newline));
 5463
 5464            rows.push(row + rows_inserted as u32);
 5465        }
 5466
 5467        self.transact(window, cx, |editor, window, cx| {
 5468            editor.edit(edits, cx);
 5469
 5470            editor.change_selections(Default::default(), window, cx, |s| {
 5471                let mut index = 0;
 5472                s.move_cursors_with(&mut |map, _, _| {
 5473                    let row = rows[index];
 5474                    index += 1;
 5475
 5476                    let point = Point::new(row, 0);
 5477                    let boundary = map.next_line_boundary(point).1;
 5478                    let clipped = map.clip_point(boundary, Bias::Left);
 5479
 5480                    (clipped, SelectionGoal::None)
 5481                });
 5482            });
 5483
 5484            let mut indent_edits = Vec::new();
 5485            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5486            for row in rows {
 5487                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5488                for (row, indent) in indents {
 5489                    if indent.len == 0 {
 5490                        continue;
 5491                    }
 5492
 5493                    let text = match indent.kind {
 5494                        IndentKind::Space => " ".repeat(indent.len as usize),
 5495                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5496                    };
 5497                    let point = Point::new(row.0, 0);
 5498                    indent_edits.push((point..point, text));
 5499                }
 5500            }
 5501            editor.edit(indent_edits, cx);
 5502            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5503                format.detach_and_log_err(cx);
 5504            }
 5505        });
 5506    }
 5507
 5508    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5509        if self.read_only(cx) {
 5510            return;
 5511        }
 5512
 5513        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5514
 5515        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5516        let mut rows = Vec::new();
 5517        let mut rows_inserted = 0;
 5518
 5519        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5520            let cursor = selection.head();
 5521            let row = cursor.row;
 5522
 5523            let point = Point::new(row, 0);
 5524            let Some((buffer_handle, buffer_point, _)) =
 5525                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5526            else {
 5527                continue;
 5528            };
 5529
 5530            buffer_edits
 5531                .entry(buffer_handle.entity_id())
 5532                .or_insert_with(|| (buffer_handle, Vec::new()))
 5533                .1
 5534                .push(buffer_point);
 5535
 5536            rows_inserted += 1;
 5537            rows.push(row + rows_inserted);
 5538        }
 5539
 5540        self.transact(window, cx, |editor, window, cx| {
 5541            for (_, (buffer_handle, points)) in &buffer_edits {
 5542                buffer_handle.update(cx, |buffer, cx| {
 5543                    let edits: Vec<_> = points
 5544                        .iter()
 5545                        .map(|point| {
 5546                            let target = Point::new(point.row + 1, 0);
 5547                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5548                            (start_of_line..start_of_line, "\n")
 5549                        })
 5550                        .collect();
 5551                    buffer.edit(edits, None, cx);
 5552                });
 5553            }
 5554
 5555            editor.change_selections(Default::default(), window, cx, |s| {
 5556                let mut index = 0;
 5557                s.move_cursors_with(&mut |map, _, _| {
 5558                    let row = rows[index];
 5559                    index += 1;
 5560
 5561                    let point = Point::new(row, 0);
 5562                    let boundary = map.next_line_boundary(point).1;
 5563                    let clipped = map.clip_point(boundary, Bias::Left);
 5564
 5565                    (clipped, SelectionGoal::None)
 5566                });
 5567            });
 5568
 5569            let mut indent_edits = Vec::new();
 5570            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5571            for row in rows {
 5572                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5573                for (row, indent) in indents {
 5574                    if indent.len == 0 {
 5575                        continue;
 5576                    }
 5577
 5578                    let text = match indent.kind {
 5579                        IndentKind::Space => " ".repeat(indent.len as usize),
 5580                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5581                    };
 5582                    let point = Point::new(row.0, 0);
 5583                    indent_edits.push((point..point, text));
 5584                }
 5585            }
 5586            editor.edit(indent_edits, cx);
 5587            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5588                format.detach_and_log_err(cx);
 5589            }
 5590        });
 5591    }
 5592
 5593    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5594        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5595            original_indent_columns: Vec::new(),
 5596        });
 5597        self.replace_selections(text, autoindent, window, cx, false);
 5598    }
 5599
 5600    /// Replaces the editor's selections with the provided `text`, applying the
 5601    /// given `autoindent_mode` (`None` will skip autoindentation).
 5602    ///
 5603    /// Early returns if the editor is in read-only mode, without applying any
 5604    /// edits.
 5605    fn replace_selections(
 5606        &mut self,
 5607        text: &str,
 5608        autoindent_mode: Option<AutoindentMode>,
 5609        window: &mut Window,
 5610        cx: &mut Context<Self>,
 5611        apply_linked_edits: bool,
 5612    ) {
 5613        if self.read_only(cx) {
 5614            return;
 5615        }
 5616
 5617        let text: Arc<str> = text.into();
 5618        self.transact(window, cx, |this, window, cx| {
 5619            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5620            let linked_edits = if apply_linked_edits {
 5621                this.linked_edits_for_selections(text.clone(), cx)
 5622            } else {
 5623                LinkedEdits::new()
 5624            };
 5625
 5626            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5627                let anchors = {
 5628                    let snapshot = buffer.read(cx);
 5629                    old_selections
 5630                        .iter()
 5631                        .map(|s| {
 5632                            let anchor = snapshot.anchor_after(s.head());
 5633                            s.map(|_| anchor)
 5634                        })
 5635                        .collect::<Vec<_>>()
 5636                };
 5637                buffer.edit(
 5638                    old_selections
 5639                        .iter()
 5640                        .map(|s| (s.start..s.end, text.clone())),
 5641                    autoindent_mode,
 5642                    cx,
 5643                );
 5644                anchors
 5645            });
 5646
 5647            linked_edits.apply(cx);
 5648
 5649            this.change_selections(Default::default(), window, cx, |s| {
 5650                s.select_anchors(selection_anchors);
 5651            });
 5652
 5653            if apply_linked_edits {
 5654                refresh_linked_ranges(this, window, cx);
 5655            }
 5656
 5657            cx.notify();
 5658        });
 5659    }
 5660
 5661    /// Collects linked edits for the current selections, pairing each linked
 5662    /// range with `text`.
 5663    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5664        let mut linked_edits = LinkedEdits::new();
 5665        if !self.linked_edit_ranges.is_empty() {
 5666            for selection in self.selections.disjoint_anchors() {
 5667                let start = selection.start.text_anchor;
 5668                let end = selection.end.text_anchor;
 5669                linked_edits.push(self, start..end, text.clone(), cx);
 5670            }
 5671        }
 5672        linked_edits
 5673    }
 5674
 5675    /// Deletes the content covered by the current selections and applies
 5676    /// linked edits.
 5677    pub fn delete_selections_with_linked_edits(
 5678        &mut self,
 5679        window: &mut Window,
 5680        cx: &mut Context<Self>,
 5681    ) {
 5682        self.replace_selections("", None, window, cx, true);
 5683    }
 5684
 5685    #[cfg(any(test, feature = "test-support"))]
 5686    pub fn set_linked_edit_ranges_for_testing(
 5687        &mut self,
 5688        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5689        cx: &mut Context<Self>,
 5690    ) -> Option<()> {
 5691        let Some((buffer, _)) = self
 5692            .buffer
 5693            .read(cx)
 5694            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5695        else {
 5696            return None;
 5697        };
 5698        let buffer = buffer.read(cx);
 5699        let buffer_id = buffer.remote_id();
 5700        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5701        for (base_range, linked_ranges_points) in ranges {
 5702            let base_anchor =
 5703                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5704            let linked_anchors = linked_ranges_points
 5705                .into_iter()
 5706                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5707                .collect();
 5708            linked_ranges.push((base_anchor, linked_anchors));
 5709        }
 5710        let mut map = HashMap::default();
 5711        map.insert(buffer_id, linked_ranges);
 5712        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5713        Some(())
 5714    }
 5715
 5716    fn trigger_completion_on_input(
 5717        &mut self,
 5718        text: &str,
 5719        trigger_in_words: bool,
 5720        window: &mut Window,
 5721        cx: &mut Context<Self>,
 5722    ) {
 5723        let completions_source = self
 5724            .context_menu
 5725            .borrow()
 5726            .as_ref()
 5727            .and_then(|menu| match menu {
 5728                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5729                CodeContextMenu::CodeActions(_) => None,
 5730            });
 5731
 5732        match completions_source {
 5733            Some(CompletionsMenuSource::Words { .. }) => {
 5734                self.open_or_update_completions_menu(
 5735                    Some(CompletionsMenuSource::Words {
 5736                        ignore_threshold: false,
 5737                    }),
 5738                    None,
 5739                    trigger_in_words,
 5740                    window,
 5741                    cx,
 5742                );
 5743            }
 5744            _ => self.open_or_update_completions_menu(
 5745                None,
 5746                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5747                true,
 5748                window,
 5749                cx,
 5750            ),
 5751        }
 5752    }
 5753
 5754    /// If any empty selections is touching the start of its innermost containing autoclose
 5755    /// region, expand it to select the brackets.
 5756    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5757        let selections = self
 5758            .selections
 5759            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5760        let buffer = self.buffer.read(cx).read(cx);
 5761        let new_selections = self
 5762            .selections_with_autoclose_regions(selections, &buffer)
 5763            .map(|(mut selection, region)| {
 5764                if !selection.is_empty() {
 5765                    return selection;
 5766                }
 5767
 5768                if let Some(region) = region {
 5769                    let mut range = region.range.to_offset(&buffer);
 5770                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5771                        range.start -= region.pair.start.len();
 5772                        if buffer.contains_str_at(range.start, &region.pair.start)
 5773                            && buffer.contains_str_at(range.end, &region.pair.end)
 5774                        {
 5775                            range.end += region.pair.end.len();
 5776                            selection.start = range.start;
 5777                            selection.end = range.end;
 5778
 5779                            return selection;
 5780                        }
 5781                    }
 5782                }
 5783
 5784                let always_treat_brackets_as_autoclosed = buffer
 5785                    .language_settings_at(selection.start, cx)
 5786                    .always_treat_brackets_as_autoclosed;
 5787
 5788                if !always_treat_brackets_as_autoclosed {
 5789                    return selection;
 5790                }
 5791
 5792                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5793                    for (pair, enabled) in scope.brackets() {
 5794                        if !enabled || !pair.close {
 5795                            continue;
 5796                        }
 5797
 5798                        if buffer.contains_str_at(selection.start, &pair.end) {
 5799                            let pair_start_len = pair.start.len();
 5800                            if buffer.contains_str_at(
 5801                                selection.start.saturating_sub_usize(pair_start_len),
 5802                                &pair.start,
 5803                            ) {
 5804                                selection.start -= pair_start_len;
 5805                                selection.end += pair.end.len();
 5806
 5807                                return selection;
 5808                            }
 5809                        }
 5810                    }
 5811                }
 5812
 5813                selection
 5814            })
 5815            .collect();
 5816
 5817        drop(buffer);
 5818        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5819            selections.select(new_selections)
 5820        });
 5821    }
 5822
 5823    /// Iterate the given selections, and for each one, find the smallest surrounding
 5824    /// autoclose region. This uses the ordering of the selections and the autoclose
 5825    /// regions to avoid repeated comparisons.
 5826    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5827        &'a self,
 5828        selections: impl IntoIterator<Item = Selection<D>>,
 5829        buffer: &'a MultiBufferSnapshot,
 5830    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5831        let mut i = 0;
 5832        let mut regions = self.autoclose_regions.as_slice();
 5833        selections.into_iter().map(move |selection| {
 5834            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5835
 5836            let mut enclosing = None;
 5837            while let Some(pair_state) = regions.get(i) {
 5838                if pair_state.range.end.to_offset(buffer) < range.start {
 5839                    regions = &regions[i + 1..];
 5840                    i = 0;
 5841                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5842                    break;
 5843                } else {
 5844                    if pair_state.selection_id == selection.id {
 5845                        enclosing = Some(pair_state);
 5846                    }
 5847                    i += 1;
 5848                }
 5849            }
 5850
 5851            (selection, enclosing)
 5852        })
 5853    }
 5854
 5855    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5856    fn invalidate_autoclose_regions(
 5857        &mut self,
 5858        mut selections: &[Selection<Anchor>],
 5859        buffer: &MultiBufferSnapshot,
 5860    ) {
 5861        self.autoclose_regions.retain(|state| {
 5862            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5863                return false;
 5864            }
 5865
 5866            let mut i = 0;
 5867            while let Some(selection) = selections.get(i) {
 5868                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5869                    selections = &selections[1..];
 5870                    continue;
 5871                }
 5872                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5873                    break;
 5874                }
 5875                if selection.id == state.selection_id {
 5876                    return true;
 5877                } else {
 5878                    i += 1;
 5879                }
 5880            }
 5881            false
 5882        });
 5883    }
 5884
 5885    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5886        let offset = position.to_offset(buffer);
 5887        let (word_range, kind) =
 5888            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5889        if offset > word_range.start && kind == Some(CharKind::Word) {
 5890            Some(
 5891                buffer
 5892                    .text_for_range(word_range.start..offset)
 5893                    .collect::<String>(),
 5894            )
 5895        } else {
 5896            None
 5897        }
 5898    }
 5899
 5900    pub fn visible_excerpts(
 5901        &self,
 5902        lsp_related_only: bool,
 5903        cx: &mut Context<Editor>,
 5904    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5905        let project = self.project().cloned();
 5906        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5907        let multi_buffer = self.buffer().read(cx);
 5908        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5909        multi_buffer_snapshot
 5910            .range_to_buffer_ranges(
 5911                self.multi_buffer_visible_range(&display_snapshot, cx)
 5912                    .to_inclusive(),
 5913            )
 5914            .into_iter()
 5915            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5916            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5917                if !lsp_related_only {
 5918                    return Some((
 5919                        excerpt_id,
 5920                        (
 5921                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5922                            buffer.version().clone(),
 5923                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5924                        ),
 5925                    ));
 5926                }
 5927
 5928                let project = project.as_ref()?.read(cx);
 5929                let buffer_file = project::File::from_dyn(buffer.file())?;
 5930                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5931                let worktree_entry = buffer_worktree
 5932                    .read(cx)
 5933                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5934                if worktree_entry.is_ignored {
 5935                    None
 5936                } else {
 5937                    Some((
 5938                        excerpt_id,
 5939                        (
 5940                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5941                            buffer.version().clone(),
 5942                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5943                        ),
 5944                    ))
 5945                }
 5946            })
 5947            .collect()
 5948    }
 5949
 5950    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5951        TextLayoutDetails {
 5952            text_system: window.text_system().clone(),
 5953            editor_style: self.style.clone().unwrap(),
 5954            rem_size: window.rem_size(),
 5955            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5956            visible_rows: self.visible_line_count(),
 5957            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5958        }
 5959    }
 5960
 5961    fn trigger_on_type_formatting(
 5962        &self,
 5963        input: String,
 5964        window: &mut Window,
 5965        cx: &mut Context<Self>,
 5966    ) -> Option<Task<Result<()>>> {
 5967        if input.chars().count() != 1 {
 5968            return None;
 5969        }
 5970
 5971        let project = self.project()?;
 5972        let position = self.selections.newest_anchor().head();
 5973        let (buffer, buffer_position) = self
 5974            .buffer
 5975            .read(cx)
 5976            .text_anchor_for_position(position, cx)?;
 5977
 5978        let settings = language_settings::language_settings(
 5979            buffer
 5980                .read(cx)
 5981                .language_at(buffer_position)
 5982                .map(|l| l.name()),
 5983            buffer.read(cx).file(),
 5984            cx,
 5985        );
 5986        if !settings.use_on_type_format {
 5987            return None;
 5988        }
 5989
 5990        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5991        // hence we do LSP request & edit on host side only — add formats to host's history.
 5992        let push_to_lsp_host_history = true;
 5993        // If this is not the host, append its history with new edits.
 5994        let push_to_client_history = project.read(cx).is_via_collab();
 5995
 5996        let on_type_formatting = project.update(cx, |project, cx| {
 5997            project.on_type_format(
 5998                buffer.clone(),
 5999                buffer_position,
 6000                input,
 6001                push_to_lsp_host_history,
 6002                cx,
 6003            )
 6004        });
 6005        Some(cx.spawn_in(window, async move |editor, cx| {
 6006            if let Some(transaction) = on_type_formatting.await? {
 6007                if push_to_client_history {
 6008                    buffer.update(cx, |buffer, _| {
 6009                        buffer.push_transaction(transaction, Instant::now());
 6010                        buffer.finalize_last_transaction();
 6011                    });
 6012                }
 6013                editor.update(cx, |editor, cx| {
 6014                    editor.refresh_document_highlights(cx);
 6015                })?;
 6016            }
 6017            Ok(())
 6018        }))
 6019    }
 6020
 6021    pub fn show_word_completions(
 6022        &mut self,
 6023        _: &ShowWordCompletions,
 6024        window: &mut Window,
 6025        cx: &mut Context<Self>,
 6026    ) {
 6027        self.open_or_update_completions_menu(
 6028            Some(CompletionsMenuSource::Words {
 6029                ignore_threshold: true,
 6030            }),
 6031            None,
 6032            false,
 6033            window,
 6034            cx,
 6035        );
 6036    }
 6037
 6038    pub fn show_completions(
 6039        &mut self,
 6040        _: &ShowCompletions,
 6041        window: &mut Window,
 6042        cx: &mut Context<Self>,
 6043    ) {
 6044        self.open_or_update_completions_menu(None, None, false, window, cx);
 6045    }
 6046
 6047    fn open_or_update_completions_menu(
 6048        &mut self,
 6049        requested_source: Option<CompletionsMenuSource>,
 6050        trigger: Option<String>,
 6051        trigger_in_words: bool,
 6052        window: &mut Window,
 6053        cx: &mut Context<Self>,
 6054    ) {
 6055        if self.pending_rename.is_some() {
 6056            return;
 6057        }
 6058
 6059        let completions_source = self
 6060            .context_menu
 6061            .borrow()
 6062            .as_ref()
 6063            .and_then(|menu| match menu {
 6064                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 6065                CodeContextMenu::CodeActions(_) => None,
 6066            });
 6067
 6068        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 6069
 6070        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 6071        // inserted and selected. To handle that case, the start of the selection is used so that
 6072        // the menu starts with all choices.
 6073        let position = self
 6074            .selections
 6075            .newest_anchor()
 6076            .start
 6077            .bias_right(&multibuffer_snapshot);
 6078        if position.diff_base_anchor.is_some() {
 6079            return;
 6080        }
 6081        let buffer_position = multibuffer_snapshot.anchor_before(position);
 6082        let Some(buffer) = buffer_position
 6083            .text_anchor
 6084            .buffer_id
 6085            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 6086        else {
 6087            return;
 6088        };
 6089        let buffer_snapshot = buffer.read(cx).snapshot();
 6090
 6091        let menu_is_open = matches!(
 6092            self.context_menu.borrow().as_ref(),
 6093            Some(CodeContextMenu::Completions(_))
 6094        );
 6095
 6096        let language = buffer_snapshot
 6097            .language_at(buffer_position.text_anchor)
 6098            .map(|language| language.name());
 6099
 6100        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 6101        let completion_settings = language_settings.completions.clone();
 6102
 6103        let show_completions_on_input = self
 6104            .show_completions_on_input_override
 6105            .unwrap_or(language_settings.show_completions_on_input);
 6106        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 6107            return;
 6108        }
 6109
 6110        let query: Option<Arc<String>> =
 6111            Self::completion_query(&multibuffer_snapshot, buffer_position)
 6112                .map(|query| query.into());
 6113
 6114        drop(multibuffer_snapshot);
 6115
 6116        // Hide the current completions menu when query is empty. Without this, cached
 6117        // completions from before the trigger char may be reused (#32774).
 6118        if query.is_none() && menu_is_open {
 6119            self.hide_context_menu(window, cx);
 6120        }
 6121
 6122        let mut ignore_word_threshold = false;
 6123        let provider = match requested_source {
 6124            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 6125            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 6126                ignore_word_threshold = ignore_threshold;
 6127                None
 6128            }
 6129            Some(CompletionsMenuSource::SnippetChoices)
 6130            | Some(CompletionsMenuSource::SnippetsOnly) => {
 6131                log::error!("bug: SnippetChoices requested_source is not handled");
 6132                None
 6133            }
 6134        };
 6135
 6136        let sort_completions = provider
 6137            .as_ref()
 6138            .is_some_and(|provider| provider.sort_completions());
 6139
 6140        let filter_completions = provider
 6141            .as_ref()
 6142            .is_none_or(|provider| provider.filter_completions());
 6143
 6144        let was_snippets_only = matches!(
 6145            completions_source,
 6146            Some(CompletionsMenuSource::SnippetsOnly)
 6147        );
 6148
 6149        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 6150            if filter_completions {
 6151                menu.filter(
 6152                    query.clone().unwrap_or_default(),
 6153                    buffer_position.text_anchor,
 6154                    &buffer,
 6155                    provider.clone(),
 6156                    window,
 6157                    cx,
 6158                );
 6159            }
 6160            // When `is_incomplete` is false, no need to re-query completions when the current query
 6161            // is a suffix of the initial query.
 6162            let was_complete = !menu.is_incomplete;
 6163            if was_complete && !was_snippets_only {
 6164                // If the new query is a suffix of the old query (typing more characters) and
 6165                // the previous result was complete, the existing completions can be filtered.
 6166                //
 6167                // Note that snippet completions are always complete.
 6168                let query_matches = match (&menu.initial_query, &query) {
 6169                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6170                    (None, _) => true,
 6171                    _ => false,
 6172                };
 6173                if query_matches {
 6174                    let position_matches = if menu.initial_position == position {
 6175                        true
 6176                    } else {
 6177                        let snapshot = self.buffer.read(cx).read(cx);
 6178                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6179                    };
 6180                    if position_matches {
 6181                        return;
 6182                    }
 6183                }
 6184            }
 6185        };
 6186
 6187        let Anchor {
 6188            excerpt_id: buffer_excerpt_id,
 6189            text_anchor: buffer_position,
 6190            ..
 6191        } = buffer_position;
 6192
 6193        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6194            buffer_snapshot.surrounding_word(buffer_position, None)
 6195        {
 6196            let word_to_exclude = buffer_snapshot
 6197                .text_for_range(word_range.clone())
 6198                .collect::<String>();
 6199            (
 6200                buffer_snapshot.anchor_before(word_range.start)
 6201                    ..buffer_snapshot.anchor_after(buffer_position),
 6202                Some(word_to_exclude),
 6203            )
 6204        } else {
 6205            (buffer_position..buffer_position, None)
 6206        };
 6207
 6208        let show_completion_documentation = buffer_snapshot
 6209            .settings_at(buffer_position, cx)
 6210            .show_completion_documentation;
 6211
 6212        // The document can be large, so stay in reasonable bounds when searching for words,
 6213        // otherwise completion pop-up might be slow to appear.
 6214        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6215        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6216        let min_word_search = buffer_snapshot.clip_point(
 6217            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6218            Bias::Left,
 6219        );
 6220        let max_word_search = buffer_snapshot.clip_point(
 6221            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6222            Bias::Right,
 6223        );
 6224        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6225            ..buffer_snapshot.point_to_offset(max_word_search);
 6226
 6227        let skip_digits = query
 6228            .as_ref()
 6229            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6230
 6231        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6232            trigger.as_ref().is_none_or(|trigger| {
 6233                provider.is_completion_trigger(
 6234                    &buffer,
 6235                    position.text_anchor,
 6236                    trigger,
 6237                    trigger_in_words,
 6238                    cx,
 6239                )
 6240            })
 6241        });
 6242
 6243        let provider_responses = if let Some(provider) = &provider
 6244            && load_provider_completions
 6245        {
 6246            let trigger_character =
 6247                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6248            let completion_context = CompletionContext {
 6249                trigger_kind: match &trigger_character {
 6250                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6251                    None => CompletionTriggerKind::INVOKED,
 6252                },
 6253                trigger_character,
 6254            };
 6255
 6256            provider.completions(
 6257                buffer_excerpt_id,
 6258                &buffer,
 6259                buffer_position,
 6260                completion_context,
 6261                window,
 6262                cx,
 6263            )
 6264        } else {
 6265            Task::ready(Ok(Vec::new()))
 6266        };
 6267
 6268        let load_word_completions = if !self.word_completions_enabled {
 6269            false
 6270        } else if requested_source
 6271            == Some(CompletionsMenuSource::Words {
 6272                ignore_threshold: true,
 6273            })
 6274        {
 6275            true
 6276        } else {
 6277            load_provider_completions
 6278                && completion_settings.words != WordsCompletionMode::Disabled
 6279                && (ignore_word_threshold || {
 6280                    let words_min_length = completion_settings.words_min_length;
 6281                    // check whether word has at least `words_min_length` characters
 6282                    let query_chars = query.iter().flat_map(|q| q.chars());
 6283                    query_chars.take(words_min_length).count() == words_min_length
 6284                })
 6285        };
 6286
 6287        let mut words = if load_word_completions {
 6288            cx.background_spawn({
 6289                let buffer_snapshot = buffer_snapshot.clone();
 6290                async move {
 6291                    buffer_snapshot.words_in_range(WordsQuery {
 6292                        fuzzy_contents: None,
 6293                        range: word_search_range,
 6294                        skip_digits,
 6295                    })
 6296                }
 6297            })
 6298        } else {
 6299            Task::ready(BTreeMap::default())
 6300        };
 6301
 6302        let snippets = if let Some(provider) = &provider
 6303            && provider.show_snippets()
 6304            && let Some(project) = self.project()
 6305        {
 6306            let char_classifier = buffer_snapshot
 6307                .char_classifier_at(buffer_position)
 6308                .scope_context(Some(CharScopeContext::Completion));
 6309            project.update(cx, |project, cx| {
 6310                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6311            })
 6312        } else {
 6313            Task::ready(Ok(CompletionResponse {
 6314                completions: Vec::new(),
 6315                display_options: Default::default(),
 6316                is_incomplete: false,
 6317            }))
 6318        };
 6319
 6320        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6321
 6322        let id = post_inc(&mut self.next_completion_id);
 6323        let task = cx.spawn_in(window, async move |editor, cx| {
 6324            let Ok(()) = editor.update(cx, |this, _| {
 6325                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6326            }) else {
 6327                return;
 6328            };
 6329
 6330            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6331            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6332            let mut completions = Vec::new();
 6333            let mut is_incomplete = false;
 6334            let mut display_options: Option<CompletionDisplayOptions> = None;
 6335            if let Some(provider_responses) = provider_responses.await.log_err()
 6336                && !provider_responses.is_empty()
 6337            {
 6338                for response in provider_responses {
 6339                    completions.extend(response.completions);
 6340                    is_incomplete = is_incomplete || response.is_incomplete;
 6341                    match display_options.as_mut() {
 6342                        None => {
 6343                            display_options = Some(response.display_options);
 6344                        }
 6345                        Some(options) => options.merge(&response.display_options),
 6346                    }
 6347                }
 6348                if completion_settings.words == WordsCompletionMode::Fallback {
 6349                    words = Task::ready(BTreeMap::default());
 6350                }
 6351            }
 6352            let display_options = display_options.unwrap_or_default();
 6353
 6354            let mut words = words.await;
 6355            if let Some(word_to_exclude) = &word_to_exclude {
 6356                words.remove(word_to_exclude);
 6357            }
 6358            for lsp_completion in &completions {
 6359                words.remove(&lsp_completion.new_text);
 6360            }
 6361            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6362                replace_range: word_replace_range.clone(),
 6363                new_text: word.clone(),
 6364                label: CodeLabel::plain(word, None),
 6365                match_start: None,
 6366                snippet_deduplication_key: None,
 6367                icon_path: None,
 6368                documentation: None,
 6369                source: CompletionSource::BufferWord {
 6370                    word_range,
 6371                    resolved: false,
 6372                },
 6373                insert_text_mode: Some(InsertTextMode::AS_IS),
 6374                confirm: None,
 6375            }));
 6376
 6377            completions.extend(
 6378                snippets
 6379                    .await
 6380                    .into_iter()
 6381                    .flat_map(|response| response.completions),
 6382            );
 6383
 6384            let menu = if completions.is_empty() {
 6385                None
 6386            } else {
 6387                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6388                    let languages = editor
 6389                        .workspace
 6390                        .as_ref()
 6391                        .and_then(|(workspace, _)| workspace.upgrade())
 6392                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6393                    let menu = CompletionsMenu::new(
 6394                        id,
 6395                        requested_source.unwrap_or(if load_provider_completions {
 6396                            CompletionsMenuSource::Normal
 6397                        } else {
 6398                            CompletionsMenuSource::SnippetsOnly
 6399                        }),
 6400                        sort_completions,
 6401                        show_completion_documentation,
 6402                        position,
 6403                        query.clone(),
 6404                        is_incomplete,
 6405                        buffer.clone(),
 6406                        completions.into(),
 6407                        editor
 6408                            .context_menu()
 6409                            .borrow_mut()
 6410                            .as_ref()
 6411                            .map(|menu| menu.primary_scroll_handle()),
 6412                        display_options,
 6413                        snippet_sort_order,
 6414                        languages,
 6415                        language,
 6416                        cx,
 6417                    );
 6418
 6419                    let query = if filter_completions { query } else { None };
 6420                    let matches_task = menu.do_async_filtering(
 6421                        query.unwrap_or_default(),
 6422                        buffer_position,
 6423                        &buffer,
 6424                        cx,
 6425                    );
 6426                    (menu, matches_task)
 6427                }) else {
 6428                    return;
 6429                };
 6430
 6431                let matches = matches_task.await;
 6432
 6433                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6434                    // Newer menu already set, so exit.
 6435                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6436                        editor.context_menu.borrow().as_ref()
 6437                        && prev_menu.id > id
 6438                    {
 6439                        return;
 6440                    };
 6441
 6442                    // Only valid to take prev_menu because either the new menu is immediately set
 6443                    // below, or the menu is hidden.
 6444                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6445                        editor.context_menu.borrow_mut().take()
 6446                    {
 6447                        let position_matches =
 6448                            if prev_menu.initial_position == menu.initial_position {
 6449                                true
 6450                            } else {
 6451                                let snapshot = editor.buffer.read(cx).read(cx);
 6452                                prev_menu.initial_position.to_offset(&snapshot)
 6453                                    == menu.initial_position.to_offset(&snapshot)
 6454                            };
 6455                        if position_matches {
 6456                            // Preserve markdown cache before `set_filter_results` because it will
 6457                            // try to populate the documentation cache.
 6458                            menu.preserve_markdown_cache(prev_menu);
 6459                        }
 6460                    };
 6461
 6462                    menu.set_filter_results(matches, provider, window, cx);
 6463                }) else {
 6464                    return;
 6465                };
 6466
 6467                menu.visible().then_some(menu)
 6468            };
 6469
 6470            editor
 6471                .update_in(cx, |editor, window, cx| {
 6472                    if editor.focus_handle.is_focused(window)
 6473                        && let Some(menu) = menu
 6474                    {
 6475                        *editor.context_menu.borrow_mut() =
 6476                            Some(CodeContextMenu::Completions(menu));
 6477
 6478                        crate::hover_popover::hide_hover(editor, cx);
 6479                        if editor.show_edit_predictions_in_menu() {
 6480                            editor.update_visible_edit_prediction(window, cx);
 6481                        } else {
 6482                            editor
 6483                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6484                        }
 6485
 6486                        cx.notify();
 6487                        return;
 6488                    }
 6489
 6490                    if editor.completion_tasks.len() <= 1 {
 6491                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6492                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6493                        // If it was already hidden and we don't show edit predictions in the menu,
 6494                        // we should also show the edit prediction when available.
 6495                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6496                            editor.update_visible_edit_prediction(window, cx);
 6497                        }
 6498                    }
 6499                })
 6500                .ok();
 6501        });
 6502
 6503        self.completion_tasks.push((id, task));
 6504    }
 6505
 6506    #[cfg(any(test, feature = "test-support"))]
 6507    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6508        let menu = self.context_menu.borrow();
 6509        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6510            let completions = menu.completions.borrow();
 6511            Some(completions.to_vec())
 6512        } else {
 6513            None
 6514        }
 6515    }
 6516
 6517    pub fn with_completions_menu_matching_id<R>(
 6518        &self,
 6519        id: CompletionId,
 6520        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6521    ) -> R {
 6522        let mut context_menu = self.context_menu.borrow_mut();
 6523        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6524            return f(None);
 6525        };
 6526        if completions_menu.id != id {
 6527            return f(None);
 6528        }
 6529        f(Some(completions_menu))
 6530    }
 6531
 6532    pub fn confirm_completion(
 6533        &mut self,
 6534        action: &ConfirmCompletion,
 6535        window: &mut Window,
 6536        cx: &mut Context<Self>,
 6537    ) -> Option<Task<Result<()>>> {
 6538        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6539        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6540    }
 6541
 6542    pub fn confirm_completion_insert(
 6543        &mut self,
 6544        _: &ConfirmCompletionInsert,
 6545        window: &mut Window,
 6546        cx: &mut Context<Self>,
 6547    ) -> Option<Task<Result<()>>> {
 6548        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6549        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6550    }
 6551
 6552    pub fn confirm_completion_replace(
 6553        &mut self,
 6554        _: &ConfirmCompletionReplace,
 6555        window: &mut Window,
 6556        cx: &mut Context<Self>,
 6557    ) -> Option<Task<Result<()>>> {
 6558        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6559        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6560    }
 6561
 6562    pub fn compose_completion(
 6563        &mut self,
 6564        action: &ComposeCompletion,
 6565        window: &mut Window,
 6566        cx: &mut Context<Self>,
 6567    ) -> Option<Task<Result<()>>> {
 6568        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6569        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6570    }
 6571
 6572    fn do_completion(
 6573        &mut self,
 6574        item_ix: Option<usize>,
 6575        intent: CompletionIntent,
 6576        window: &mut Window,
 6577        cx: &mut Context<Editor>,
 6578    ) -> Option<Task<Result<()>>> {
 6579        use language::ToOffset as _;
 6580
 6581        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6582        else {
 6583            return None;
 6584        };
 6585
 6586        let candidate_id = {
 6587            let entries = completions_menu.entries.borrow();
 6588            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6589            if self.show_edit_predictions_in_menu() {
 6590                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6591            }
 6592            mat.candidate_id
 6593        };
 6594
 6595        let completion = completions_menu
 6596            .completions
 6597            .borrow()
 6598            .get(candidate_id)?
 6599            .clone();
 6600        cx.stop_propagation();
 6601
 6602        let buffer_handle = completions_menu.buffer.clone();
 6603
 6604        let CompletionEdit {
 6605            new_text,
 6606            snippet,
 6607            replace_range,
 6608        } = process_completion_for_edit(
 6609            &completion,
 6610            intent,
 6611            &buffer_handle,
 6612            &completions_menu.initial_position.text_anchor,
 6613            cx,
 6614        );
 6615
 6616        let buffer = buffer_handle.read(cx);
 6617        let snapshot = self.buffer.read(cx).snapshot(cx);
 6618        let newest_anchor = self.selections.newest_anchor();
 6619        let replace_range_multibuffer = {
 6620            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6621            excerpt.map_range_from_buffer(replace_range.clone())
 6622        };
 6623        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6624            return None;
 6625        }
 6626
 6627        let old_text = buffer
 6628            .text_for_range(replace_range.clone())
 6629            .collect::<String>();
 6630        let lookbehind = newest_anchor
 6631            .start
 6632            .text_anchor
 6633            .to_offset(buffer)
 6634            .saturating_sub(replace_range.start.0);
 6635        let lookahead = replace_range
 6636            .end
 6637            .0
 6638            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6639        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6640        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6641
 6642        let selections = self
 6643            .selections
 6644            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6645        let mut ranges = Vec::new();
 6646        let mut all_commit_ranges = Vec::new();
 6647        let mut linked_edits = LinkedEdits::new();
 6648
 6649        let text: Arc<str> = new_text.clone().into();
 6650        for selection in &selections {
 6651            let range = if selection.id == newest_anchor.id {
 6652                replace_range_multibuffer.clone()
 6653            } else {
 6654                let mut range = selection.range();
 6655
 6656                // if prefix is present, don't duplicate it
 6657                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6658                    range.start = range.start.saturating_sub_usize(lookbehind);
 6659
 6660                    // if suffix is also present, mimic the newest cursor and replace it
 6661                    if selection.id != newest_anchor.id
 6662                        && snapshot.contains_str_at(range.end, suffix)
 6663                    {
 6664                        range.end += lookahead;
 6665                    }
 6666                }
 6667                range
 6668            };
 6669
 6670            ranges.push(range.clone());
 6671
 6672            let start_anchor = snapshot.anchor_before(range.start);
 6673            let end_anchor = snapshot.anchor_after(range.end);
 6674            let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6675            all_commit_ranges.push(anchor_range.clone());
 6676
 6677            if !self.linked_edit_ranges.is_empty() {
 6678                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6679            }
 6680        }
 6681
 6682        let common_prefix_len = old_text
 6683            .chars()
 6684            .zip(new_text.chars())
 6685            .take_while(|(a, b)| a == b)
 6686            .map(|(a, _)| a.len_utf8())
 6687            .sum::<usize>();
 6688
 6689        cx.emit(EditorEvent::InputHandled {
 6690            utf16_range_to_replace: None,
 6691            text: new_text[common_prefix_len..].into(),
 6692        });
 6693
 6694        self.transact(window, cx, |editor, window, cx| {
 6695            if let Some(mut snippet) = snippet {
 6696                snippet.text = new_text.to_string();
 6697                editor
 6698                    .insert_snippet(&ranges, snippet, window, cx)
 6699                    .log_err();
 6700            } else {
 6701                editor.buffer.update(cx, |multi_buffer, cx| {
 6702                    let auto_indent = match completion.insert_text_mode {
 6703                        Some(InsertTextMode::AS_IS) => None,
 6704                        _ => editor.autoindent_mode.clone(),
 6705                    };
 6706                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6707                    multi_buffer.edit(edits, auto_indent, cx);
 6708                });
 6709            }
 6710            linked_edits.apply(cx);
 6711            editor.refresh_edit_prediction(true, false, window, cx);
 6712        });
 6713        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6714
 6715        let show_new_completions_on_confirm = completion
 6716            .confirm
 6717            .as_ref()
 6718            .is_some_and(|confirm| confirm(intent, window, cx));
 6719        if show_new_completions_on_confirm {
 6720            self.open_or_update_completions_menu(None, None, false, window, cx);
 6721        }
 6722
 6723        let provider = self.completion_provider.as_ref()?;
 6724
 6725        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6726        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6727            let CompletionSource::Lsp {
 6728                lsp_completion,
 6729                server_id,
 6730                ..
 6731            } = &completion.source
 6732            else {
 6733                return None;
 6734            };
 6735            let lsp_command = lsp_completion.command.as_ref()?;
 6736            let available_commands = lsp_store
 6737                .read(cx)
 6738                .lsp_server_capabilities
 6739                .get(server_id)
 6740                .and_then(|server_capabilities| {
 6741                    server_capabilities
 6742                        .execute_command_provider
 6743                        .as_ref()
 6744                        .map(|options| options.commands.as_slice())
 6745                })?;
 6746            if available_commands.contains(&lsp_command.command) {
 6747                Some(CodeAction {
 6748                    server_id: *server_id,
 6749                    range: language::Anchor::MIN..language::Anchor::MIN,
 6750                    lsp_action: LspAction::Command(lsp_command.clone()),
 6751                    resolved: false,
 6752                })
 6753            } else {
 6754                None
 6755            }
 6756        });
 6757
 6758        drop(completion);
 6759        let apply_edits = provider.apply_additional_edits_for_completion(
 6760            buffer_handle.clone(),
 6761            completions_menu.completions.clone(),
 6762            candidate_id,
 6763            true,
 6764            all_commit_ranges,
 6765            cx,
 6766        );
 6767
 6768        let editor_settings = EditorSettings::get_global(cx);
 6769        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6770            // After the code completion is finished, users often want to know what signatures are needed.
 6771            // so we should automatically call signature_help
 6772            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6773        }
 6774
 6775        Some(cx.spawn_in(window, async move |editor, cx| {
 6776            apply_edits.await?;
 6777
 6778            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6779                let title = command.lsp_action.title().to_owned();
 6780                let project_transaction = lsp_store
 6781                    .update(cx, |lsp_store, cx| {
 6782                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6783                    })
 6784                    .await
 6785                    .context("applying post-completion command")?;
 6786                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6787                    Self::open_project_transaction(
 6788                        &editor,
 6789                        workspace.downgrade(),
 6790                        project_transaction,
 6791                        title,
 6792                        cx,
 6793                    )
 6794                    .await?;
 6795                }
 6796            }
 6797
 6798            Ok(())
 6799        }))
 6800    }
 6801
 6802    pub fn toggle_code_actions(
 6803        &mut self,
 6804        action: &ToggleCodeActions,
 6805        window: &mut Window,
 6806        cx: &mut Context<Self>,
 6807    ) {
 6808        let quick_launch = action.quick_launch;
 6809        let mut context_menu = self.context_menu.borrow_mut();
 6810        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6811            if code_actions.deployed_from == action.deployed_from {
 6812                // Toggle if we're selecting the same one
 6813                *context_menu = None;
 6814                cx.notify();
 6815                return;
 6816            } else {
 6817                // Otherwise, clear it and start a new one
 6818                *context_menu = None;
 6819                cx.notify();
 6820            }
 6821        }
 6822        drop(context_menu);
 6823        let snapshot = self.snapshot(window, cx);
 6824        let deployed_from = action.deployed_from.clone();
 6825        let action = action.clone();
 6826        self.completion_tasks.clear();
 6827        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6828
 6829        let multibuffer_point = match &action.deployed_from {
 6830            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6831                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6832            }
 6833            _ => self
 6834                .selections
 6835                .newest::<Point>(&snapshot.display_snapshot)
 6836                .head(),
 6837        };
 6838        let Some((buffer, buffer_row)) = snapshot
 6839            .buffer_snapshot()
 6840            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6841            .and_then(|(buffer_snapshot, range)| {
 6842                self.buffer()
 6843                    .read(cx)
 6844                    .buffer(buffer_snapshot.remote_id())
 6845                    .map(|buffer| (buffer, range.start.row))
 6846            })
 6847        else {
 6848            return;
 6849        };
 6850        let buffer_id = buffer.read(cx).remote_id();
 6851        let tasks = self
 6852            .runnables
 6853            .runnables((buffer_id, buffer_row))
 6854            .map(|t| Arc::new(t.to_owned()));
 6855
 6856        if !self.focus_handle.is_focused(window) {
 6857            return;
 6858        }
 6859        let project = self.project.clone();
 6860
 6861        let code_actions_task = match deployed_from {
 6862            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6863            _ => self.code_actions(buffer_row, window, cx),
 6864        };
 6865
 6866        let runnable_task = match deployed_from {
 6867            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6868            _ => {
 6869                let mut task_context_task = Task::ready(None);
 6870                if let Some(tasks) = &tasks
 6871                    && let Some(project) = project
 6872                {
 6873                    task_context_task =
 6874                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6875                }
 6876
 6877                cx.spawn_in(window, {
 6878                    let buffer = buffer.clone();
 6879                    async move |editor, cx| {
 6880                        let task_context = task_context_task.await;
 6881
 6882                        let resolved_tasks =
 6883                            tasks
 6884                                .zip(task_context.clone())
 6885                                .map(|(tasks, task_context)| ResolvedTasks {
 6886                                    templates: tasks.resolve(&task_context).collect(),
 6887                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6888                                        multibuffer_point.row,
 6889                                        tasks.column,
 6890                                    )),
 6891                                });
 6892                        let debug_scenarios = editor
 6893                            .update(cx, |editor, cx| {
 6894                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6895                            })?
 6896                            .await;
 6897                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6898                    }
 6899                })
 6900            }
 6901        };
 6902
 6903        cx.spawn_in(window, async move |editor, cx| {
 6904            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6905            let code_actions = code_actions_task.await;
 6906            let spawn_straight_away = quick_launch
 6907                && resolved_tasks
 6908                    .as_ref()
 6909                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6910                && code_actions
 6911                    .as_ref()
 6912                    .is_none_or(|actions| actions.is_empty())
 6913                && debug_scenarios.is_empty();
 6914
 6915            editor.update_in(cx, |editor, window, cx| {
 6916                crate::hover_popover::hide_hover(editor, cx);
 6917                let actions = CodeActionContents::new(
 6918                    resolved_tasks,
 6919                    code_actions,
 6920                    debug_scenarios,
 6921                    task_context.unwrap_or_default(),
 6922                );
 6923
 6924                // Don't show the menu if there are no actions available
 6925                if actions.is_empty() {
 6926                    cx.notify();
 6927                    return Task::ready(Ok(()));
 6928                }
 6929
 6930                *editor.context_menu.borrow_mut() =
 6931                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6932                        buffer,
 6933                        actions,
 6934                        selected_item: Default::default(),
 6935                        scroll_handle: UniformListScrollHandle::default(),
 6936                        deployed_from,
 6937                    }));
 6938                cx.notify();
 6939                if spawn_straight_away
 6940                    && let Some(task) = editor.confirm_code_action(
 6941                        &ConfirmCodeAction { item_ix: Some(0) },
 6942                        window,
 6943                        cx,
 6944                    )
 6945                {
 6946                    return task;
 6947                }
 6948
 6949                Task::ready(Ok(()))
 6950            })
 6951        })
 6952        .detach_and_log_err(cx);
 6953    }
 6954
 6955    fn debug_scenarios(
 6956        &mut self,
 6957        resolved_tasks: &Option<ResolvedTasks>,
 6958        buffer: &Entity<Buffer>,
 6959        cx: &mut App,
 6960    ) -> Task<Vec<task::DebugScenario>> {
 6961        maybe!({
 6962            let project = self.project()?;
 6963            let dap_store = project.read(cx).dap_store();
 6964            let mut scenarios = vec![];
 6965            let resolved_tasks = resolved_tasks.as_ref()?;
 6966            let buffer = buffer.read(cx);
 6967            let language = buffer.language()?;
 6968            let file = buffer.file();
 6969            let debug_adapter = language_settings(language.name().into(), file, cx)
 6970                .debuggers
 6971                .first()
 6972                .map(SharedString::from)
 6973                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6974
 6975            dap_store.update(cx, |dap_store, cx| {
 6976                for (_, task) in &resolved_tasks.templates {
 6977                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6978                        task.original_task().clone(),
 6979                        debug_adapter.clone().into(),
 6980                        task.display_label().to_owned().into(),
 6981                        cx,
 6982                    );
 6983                    scenarios.push(maybe_scenario);
 6984                }
 6985            });
 6986            Some(cx.background_spawn(async move {
 6987                futures::future::join_all(scenarios)
 6988                    .await
 6989                    .into_iter()
 6990                    .flatten()
 6991                    .collect::<Vec<_>>()
 6992            }))
 6993        })
 6994        .unwrap_or_else(|| Task::ready(vec![]))
 6995    }
 6996
 6997    fn code_actions(
 6998        &mut self,
 6999        buffer_row: u32,
 7000        window: &mut Window,
 7001        cx: &mut Context<Self>,
 7002    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 7003        let mut task = self.code_actions_task.take();
 7004        cx.spawn_in(window, async move |editor, cx| {
 7005            while let Some(prev_task) = task {
 7006                prev_task.await.log_err();
 7007                task = editor
 7008                    .update(cx, |this, _| this.code_actions_task.take())
 7009                    .ok()?;
 7010            }
 7011
 7012            editor
 7013                .update(cx, |editor, cx| {
 7014                    editor
 7015                        .available_code_actions
 7016                        .clone()
 7017                        .and_then(|(location, code_actions)| {
 7018                            let snapshot = location.buffer.read(cx).snapshot();
 7019                            let point_range = location.range.to_point(&snapshot);
 7020                            let point_range = point_range.start.row..=point_range.end.row;
 7021                            if point_range.contains(&buffer_row) {
 7022                                Some(code_actions)
 7023                            } else {
 7024                                None
 7025                            }
 7026                        })
 7027                })
 7028                .ok()
 7029                .flatten()
 7030        })
 7031    }
 7032
 7033    pub fn confirm_code_action(
 7034        &mut self,
 7035        action: &ConfirmCodeAction,
 7036        window: &mut Window,
 7037        cx: &mut Context<Self>,
 7038    ) -> Option<Task<Result<()>>> {
 7039        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 7040
 7041        let actions_menu =
 7042            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 7043                menu
 7044            } else {
 7045                return None;
 7046            };
 7047
 7048        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 7049        let action = actions_menu.actions.get(action_ix)?;
 7050        let title = action.label();
 7051        let buffer = actions_menu.buffer;
 7052        let workspace = self.workspace()?;
 7053
 7054        match action {
 7055            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 7056                workspace.update(cx, |workspace, cx| {
 7057                    workspace.schedule_resolved_task(
 7058                        task_source_kind,
 7059                        resolved_task,
 7060                        false,
 7061                        window,
 7062                        cx,
 7063                    );
 7064
 7065                    Some(Task::ready(Ok(())))
 7066                })
 7067            }
 7068            CodeActionsItem::CodeAction {
 7069                excerpt_id,
 7070                action,
 7071                provider,
 7072            } => {
 7073                let apply_code_action =
 7074                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 7075                let workspace = workspace.downgrade();
 7076                Some(cx.spawn_in(window, async move |editor, cx| {
 7077                    let project_transaction = apply_code_action.await?;
 7078                    Self::open_project_transaction(
 7079                        &editor,
 7080                        workspace,
 7081                        project_transaction,
 7082                        title,
 7083                        cx,
 7084                    )
 7085                    .await
 7086                }))
 7087            }
 7088            CodeActionsItem::DebugScenario(scenario) => {
 7089                let context = actions_menu.actions.context.into();
 7090
 7091                workspace.update(cx, |workspace, cx| {
 7092                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 7093                    workspace.start_debug_session(
 7094                        scenario,
 7095                        context,
 7096                        Some(buffer),
 7097                        None,
 7098                        window,
 7099                        cx,
 7100                    );
 7101                });
 7102                Some(Task::ready(Ok(())))
 7103            }
 7104        }
 7105    }
 7106
 7107    fn open_transaction_for_hidden_buffers(
 7108        workspace: Entity<Workspace>,
 7109        transaction: ProjectTransaction,
 7110        title: String,
 7111        window: &mut Window,
 7112        cx: &mut Context<Self>,
 7113    ) {
 7114        if transaction.0.is_empty() {
 7115            return;
 7116        }
 7117
 7118        let edited_buffers_already_open = {
 7119            let other_editors: Vec<Entity<Editor>> = workspace
 7120                .read(cx)
 7121                .panes()
 7122                .iter()
 7123                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 7124                .filter(|editor| editor.entity_id() != cx.entity_id())
 7125                .collect();
 7126
 7127            transaction.0.keys().all(|buffer| {
 7128                other_editors.iter().any(|editor| {
 7129                    let multi_buffer = editor.read(cx).buffer();
 7130                    multi_buffer.read(cx).is_singleton()
 7131                        && multi_buffer
 7132                            .read(cx)
 7133                            .as_singleton()
 7134                            .map_or(false, |singleton| {
 7135                                singleton.entity_id() == buffer.entity_id()
 7136                            })
 7137                })
 7138            })
 7139        };
 7140        if !edited_buffers_already_open {
 7141            let workspace = workspace.downgrade();
 7142            cx.defer_in(window, move |_, window, cx| {
 7143                cx.spawn_in(window, async move |editor, cx| {
 7144                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 7145                        .await
 7146                        .ok()
 7147                })
 7148                .detach();
 7149            });
 7150        }
 7151    }
 7152
 7153    pub async fn open_project_transaction(
 7154        editor: &WeakEntity<Editor>,
 7155        workspace: WeakEntity<Workspace>,
 7156        transaction: ProjectTransaction,
 7157        title: String,
 7158        cx: &mut AsyncWindowContext,
 7159    ) -> Result<()> {
 7160        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7161        cx.update(|_, cx| {
 7162            entries.sort_unstable_by_key(|(buffer, _)| {
 7163                buffer.read(cx).file().map(|f| f.path().clone())
 7164            });
 7165        })?;
 7166        if entries.is_empty() {
 7167            return Ok(());
 7168        }
 7169
 7170        // If the project transaction's edits are all contained within this editor, then
 7171        // avoid opening a new editor to display them.
 7172
 7173        if let [(buffer, transaction)] = &*entries {
 7174            let excerpt = editor.update(cx, |editor, cx| {
 7175                editor
 7176                    .buffer()
 7177                    .read(cx)
 7178                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7179            })?;
 7180            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7181                && excerpted_buffer == *buffer
 7182            {
 7183                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7184                    let excerpt_range = excerpt_range.to_offset(buffer);
 7185                    buffer
 7186                        .edited_ranges_for_transaction::<usize>(transaction)
 7187                        .all(|range| {
 7188                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7189                        })
 7190                });
 7191
 7192                if all_edits_within_excerpt {
 7193                    return Ok(());
 7194                }
 7195            }
 7196        }
 7197
 7198        let mut ranges_to_highlight = Vec::new();
 7199        let excerpt_buffer = cx.new(|cx| {
 7200            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7201            for (buffer_handle, transaction) in &entries {
 7202                let edited_ranges = buffer_handle
 7203                    .read(cx)
 7204                    .edited_ranges_for_transaction::<Point>(transaction)
 7205                    .collect::<Vec<_>>();
 7206                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7207                    PathKey::for_buffer(buffer_handle, cx),
 7208                    buffer_handle.clone(),
 7209                    edited_ranges,
 7210                    multibuffer_context_lines(cx),
 7211                    cx,
 7212                );
 7213
 7214                ranges_to_highlight.extend(ranges);
 7215            }
 7216            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7217            multibuffer
 7218        });
 7219
 7220        workspace.update_in(cx, |workspace, window, cx| {
 7221            let project = workspace.project().clone();
 7222            let editor =
 7223                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7224            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7225            editor.update(cx, |editor, cx| {
 7226                editor.highlight_background(
 7227                    HighlightKey::Editor,
 7228                    &ranges_to_highlight,
 7229                    |_, theme| theme.colors().editor_highlighted_line_background,
 7230                    cx,
 7231                );
 7232            });
 7233        })?;
 7234
 7235        Ok(())
 7236    }
 7237
 7238    pub fn clear_code_action_providers(&mut self) {
 7239        self.code_action_providers.clear();
 7240        self.available_code_actions.take();
 7241    }
 7242
 7243    pub fn add_code_action_provider(
 7244        &mut self,
 7245        provider: Rc<dyn CodeActionProvider>,
 7246        window: &mut Window,
 7247        cx: &mut Context<Self>,
 7248    ) {
 7249        if self
 7250            .code_action_providers
 7251            .iter()
 7252            .any(|existing_provider| existing_provider.id() == provider.id())
 7253        {
 7254            return;
 7255        }
 7256
 7257        self.code_action_providers.push(provider);
 7258        self.refresh_code_actions(window, cx);
 7259    }
 7260
 7261    pub fn remove_code_action_provider(
 7262        &mut self,
 7263        id: Arc<str>,
 7264        window: &mut Window,
 7265        cx: &mut Context<Self>,
 7266    ) {
 7267        self.code_action_providers
 7268            .retain(|provider| provider.id() != id);
 7269        self.refresh_code_actions(window, cx);
 7270    }
 7271
 7272    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7273        !self.code_action_providers.is_empty()
 7274            && EditorSettings::get_global(cx).toolbar.code_actions
 7275    }
 7276
 7277    pub fn has_available_code_actions(&self) -> bool {
 7278        self.available_code_actions
 7279            .as_ref()
 7280            .is_some_and(|(_, actions)| !actions.is_empty())
 7281    }
 7282
 7283    fn render_inline_code_actions(
 7284        &self,
 7285        icon_size: ui::IconSize,
 7286        display_row: DisplayRow,
 7287        is_active: bool,
 7288        cx: &mut Context<Self>,
 7289    ) -> AnyElement {
 7290        let show_tooltip = !self.context_menu_visible();
 7291        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7292            .icon_size(icon_size)
 7293            .shape(ui::IconButtonShape::Square)
 7294            .icon_color(ui::Color::Hidden)
 7295            .toggle_state(is_active)
 7296            .when(show_tooltip, |this| {
 7297                this.tooltip({
 7298                    let focus_handle = self.focus_handle.clone();
 7299                    move |_window, cx| {
 7300                        Tooltip::for_action_in(
 7301                            "Toggle Code Actions",
 7302                            &ToggleCodeActions {
 7303                                deployed_from: None,
 7304                                quick_launch: false,
 7305                            },
 7306                            &focus_handle,
 7307                            cx,
 7308                        )
 7309                    }
 7310                })
 7311            })
 7312            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7313                window.focus(&editor.focus_handle(cx), cx);
 7314                editor.toggle_code_actions(
 7315                    &crate::actions::ToggleCodeActions {
 7316                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7317                            display_row,
 7318                        )),
 7319                        quick_launch: false,
 7320                    },
 7321                    window,
 7322                    cx,
 7323                );
 7324            }))
 7325            .into_any_element()
 7326    }
 7327
 7328    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7329        &self.context_menu
 7330    }
 7331
 7332    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7333        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7334            cx.background_executor()
 7335                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7336                .await;
 7337
 7338            let (start_buffer, start, _, end, newest_selection) = this
 7339                .update(cx, |this, cx| {
 7340                    let newest_selection = this.selections.newest_anchor().clone();
 7341                    if newest_selection.head().diff_base_anchor.is_some() {
 7342                        return None;
 7343                    }
 7344                    let display_snapshot = this.display_snapshot(cx);
 7345                    let newest_selection_adjusted =
 7346                        this.selections.newest_adjusted(&display_snapshot);
 7347                    let buffer = this.buffer.read(cx);
 7348
 7349                    let (start_buffer, start) =
 7350                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7351                    let (end_buffer, end) =
 7352                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7353
 7354                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7355                })?
 7356                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7357                .context(
 7358                    "Expected selection to lie in a single buffer when refreshing code actions",
 7359                )?;
 7360            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7361                let providers = this.code_action_providers.clone();
 7362                let tasks = this
 7363                    .code_action_providers
 7364                    .iter()
 7365                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7366                    .collect::<Vec<_>>();
 7367                (providers, tasks)
 7368            })?;
 7369
 7370            let mut actions = Vec::new();
 7371            for (provider, provider_actions) in
 7372                providers.into_iter().zip(future::join_all(tasks).await)
 7373            {
 7374                if let Some(provider_actions) = provider_actions.log_err() {
 7375                    actions.extend(provider_actions.into_iter().map(|action| {
 7376                        AvailableCodeAction {
 7377                            excerpt_id: newest_selection.start.excerpt_id,
 7378                            action,
 7379                            provider: provider.clone(),
 7380                        }
 7381                    }));
 7382                }
 7383            }
 7384
 7385            this.update(cx, |this, cx| {
 7386                this.available_code_actions = if actions.is_empty() {
 7387                    None
 7388                } else {
 7389                    Some((
 7390                        Location {
 7391                            buffer: start_buffer,
 7392                            range: start..end,
 7393                        },
 7394                        actions.into(),
 7395                    ))
 7396                };
 7397                cx.notify();
 7398            })
 7399        }));
 7400    }
 7401
 7402    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7403        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7404            self.show_git_blame_inline = false;
 7405
 7406            self.show_git_blame_inline_delay_task =
 7407                Some(cx.spawn_in(window, async move |this, cx| {
 7408                    cx.background_executor().timer(delay).await;
 7409
 7410                    this.update(cx, |this, cx| {
 7411                        this.show_git_blame_inline = true;
 7412                        cx.notify();
 7413                    })
 7414                    .log_err();
 7415                }));
 7416        }
 7417    }
 7418
 7419    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7420        let snapshot = self.snapshot(window, cx);
 7421        let cursor = self
 7422            .selections
 7423            .newest::<Point>(&snapshot.display_snapshot)
 7424            .head();
 7425        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7426        else {
 7427            return;
 7428        };
 7429
 7430        if self.blame.is_none() {
 7431            self.start_git_blame(true, window, cx);
 7432        }
 7433        let Some(blame) = self.blame.as_ref() else {
 7434            return;
 7435        };
 7436
 7437        let row_info = RowInfo {
 7438            buffer_id: Some(buffer.remote_id()),
 7439            buffer_row: Some(point.row),
 7440            ..Default::default()
 7441        };
 7442        let Some((buffer, blame_entry)) = blame
 7443            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7444            .flatten()
 7445        else {
 7446            return;
 7447        };
 7448
 7449        let anchor = self.selections.newest_anchor().head();
 7450        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7451        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7452            self.show_blame_popover(
 7453                buffer,
 7454                &blame_entry,
 7455                position + last_bounds.origin,
 7456                true,
 7457                cx,
 7458            );
 7459        };
 7460    }
 7461
 7462    fn show_blame_popover(
 7463        &mut self,
 7464        buffer: BufferId,
 7465        blame_entry: &BlameEntry,
 7466        position: gpui::Point<Pixels>,
 7467        ignore_timeout: bool,
 7468        cx: &mut Context<Self>,
 7469    ) {
 7470        if let Some(state) = &mut self.inline_blame_popover {
 7471            state.hide_task.take();
 7472        } else {
 7473            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7474            let blame_entry = blame_entry.clone();
 7475            let show_task = cx.spawn(async move |editor, cx| {
 7476                if !ignore_timeout {
 7477                    cx.background_executor()
 7478                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7479                        .await;
 7480                }
 7481                editor
 7482                    .update(cx, |editor, cx| {
 7483                        editor.inline_blame_popover_show_task.take();
 7484                        let Some(blame) = editor.blame.as_ref() else {
 7485                            return;
 7486                        };
 7487                        let blame = blame.read(cx);
 7488                        let details = blame.details_for_entry(buffer, &blame_entry);
 7489                        let markdown = cx.new(|cx| {
 7490                            Markdown::new(
 7491                                details
 7492                                    .as_ref()
 7493                                    .map(|message| message.message.clone())
 7494                                    .unwrap_or_default(),
 7495                                None,
 7496                                None,
 7497                                cx,
 7498                            )
 7499                        });
 7500                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7501                            position,
 7502                            hide_task: None,
 7503                            popover_bounds: None,
 7504                            popover_state: InlineBlamePopoverState {
 7505                                scroll_handle: ScrollHandle::new(),
 7506                                commit_message: details,
 7507                                markdown,
 7508                            },
 7509                            keyboard_grace: ignore_timeout,
 7510                        });
 7511                        cx.notify();
 7512                    })
 7513                    .ok();
 7514            });
 7515            self.inline_blame_popover_show_task = Some(show_task);
 7516        }
 7517    }
 7518
 7519    pub fn has_mouse_context_menu(&self) -> bool {
 7520        self.mouse_context_menu.is_some()
 7521    }
 7522
 7523    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7524        self.inline_blame_popover_show_task.take();
 7525        if let Some(state) = &mut self.inline_blame_popover {
 7526            let hide_task = cx.spawn(async move |editor, cx| {
 7527                if !ignore_timeout {
 7528                    cx.background_executor()
 7529                        .timer(std::time::Duration::from_millis(100))
 7530                        .await;
 7531                }
 7532                editor
 7533                    .update(cx, |editor, cx| {
 7534                        editor.inline_blame_popover.take();
 7535                        cx.notify();
 7536                    })
 7537                    .ok();
 7538            });
 7539            state.hide_task = Some(hide_task);
 7540            true
 7541        } else {
 7542            false
 7543        }
 7544    }
 7545
 7546    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7547        if self.pending_rename.is_some() {
 7548            return None;
 7549        }
 7550
 7551        let provider = self.semantics_provider.clone()?;
 7552        let buffer = self.buffer.read(cx);
 7553        let newest_selection = self.selections.newest_anchor().clone();
 7554        let cursor_position = newest_selection.head();
 7555        let (cursor_buffer, cursor_buffer_position) =
 7556            buffer.text_anchor_for_position(cursor_position, cx)?;
 7557        let (tail_buffer, tail_buffer_position) =
 7558            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7559        if cursor_buffer != tail_buffer {
 7560            return None;
 7561        }
 7562
 7563        let snapshot = cursor_buffer.read(cx).snapshot();
 7564        let word_ranges = cx.background_spawn(async move {
 7565            // this might look odd to put on the background thread, but
 7566            // `surrounding_word` can be quite expensive as it calls into
 7567            // tree-sitter language scopes
 7568            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7569            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7570            (start_word_range, end_word_range)
 7571        });
 7572
 7573        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7574        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7575            let (start_word_range, end_word_range) = word_ranges.await;
 7576            if start_word_range != end_word_range {
 7577                this.update(cx, |this, cx| {
 7578                    this.document_highlights_task.take();
 7579                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7580                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7581                })
 7582                .ok();
 7583                return;
 7584            }
 7585            cx.background_executor()
 7586                .timer(Duration::from_millis(debounce))
 7587                .await;
 7588
 7589            let highlights = if let Some(highlights) = cx.update(|cx| {
 7590                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7591            }) {
 7592                highlights.await.log_err()
 7593            } else {
 7594                None
 7595            };
 7596
 7597            if let Some(highlights) = highlights {
 7598                this.update(cx, |this, cx| {
 7599                    if this.pending_rename.is_some() {
 7600                        return;
 7601                    }
 7602
 7603                    let buffer = this.buffer.read(cx);
 7604                    if buffer
 7605                        .text_anchor_for_position(cursor_position, cx)
 7606                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7607                    {
 7608                        return;
 7609                    }
 7610
 7611                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7612                    let mut write_ranges = Vec::new();
 7613                    let mut read_ranges = Vec::new();
 7614                    for highlight in highlights {
 7615                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7616                        for (excerpt_id, _, excerpt_range) in
 7617                            buffer.excerpts_for_buffer(buffer_id, cx)
 7618                        {
 7619                            let start = highlight
 7620                                .range
 7621                                .start
 7622                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7623                            let end = highlight
 7624                                .range
 7625                                .end
 7626                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7627                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7628                                continue;
 7629                            }
 7630
 7631                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7632                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7633                                write_ranges.push(range);
 7634                            } else {
 7635                                read_ranges.push(range);
 7636                            }
 7637                        }
 7638                    }
 7639
 7640                    this.highlight_background(
 7641                        HighlightKey::DocumentHighlightRead,
 7642                        &read_ranges,
 7643                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7644                        cx,
 7645                    );
 7646                    this.highlight_background(
 7647                        HighlightKey::DocumentHighlightWrite,
 7648                        &write_ranges,
 7649                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7650                        cx,
 7651                    );
 7652                    cx.notify();
 7653                })
 7654                .log_err();
 7655            }
 7656        }));
 7657        None
 7658    }
 7659
 7660    fn prepare_highlight_query_from_selection(
 7661        &mut self,
 7662        snapshot: &DisplaySnapshot,
 7663        cx: &mut Context<Editor>,
 7664    ) -> Option<(String, Range<Anchor>)> {
 7665        if matches!(self.mode, EditorMode::SingleLine) {
 7666            return None;
 7667        }
 7668        if !EditorSettings::get_global(cx).selection_highlight {
 7669            return None;
 7670        }
 7671        if self.selections.count() != 1 || self.selections.line_mode() {
 7672            return None;
 7673        }
 7674        let selection = self.selections.newest::<Point>(&snapshot);
 7675        // If the selection spans multiple rows OR it is empty
 7676        if selection.start.row != selection.end.row
 7677            || selection.start.column == selection.end.column
 7678        {
 7679            return None;
 7680        }
 7681        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7682        let query = snapshot
 7683            .buffer_snapshot()
 7684            .text_for_range(selection_anchor_range.clone())
 7685            .collect::<String>();
 7686        if query.trim().is_empty() {
 7687            return None;
 7688        }
 7689        Some((query, selection_anchor_range))
 7690    }
 7691
 7692    #[ztracing::instrument(skip_all)]
 7693    fn update_selection_occurrence_highlights(
 7694        &mut self,
 7695        multi_buffer_snapshot: MultiBufferSnapshot,
 7696        query_text: String,
 7697        query_range: Range<Anchor>,
 7698        multi_buffer_range_to_query: Range<Point>,
 7699        use_debounce: bool,
 7700        window: &mut Window,
 7701        cx: &mut Context<Editor>,
 7702    ) -> Task<()> {
 7703        cx.spawn_in(window, async move |editor, cx| {
 7704            if use_debounce {
 7705                cx.background_executor()
 7706                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7707                    .await;
 7708            }
 7709            let match_task = cx.background_spawn(async move {
 7710                let buffer_ranges = multi_buffer_snapshot
 7711                    .range_to_buffer_ranges(
 7712                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7713                    )
 7714                    .into_iter()
 7715                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7716                let mut match_ranges = Vec::new();
 7717                let Ok(regex) = project::search::SearchQuery::text(
 7718                    query_text,
 7719                    false,
 7720                    false,
 7721                    false,
 7722                    Default::default(),
 7723                    Default::default(),
 7724                    false,
 7725                    None,
 7726                ) else {
 7727                    return Vec::default();
 7728                };
 7729                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7730                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7731                    match_ranges.extend(
 7732                        regex
 7733                            .search(
 7734                                buffer_snapshot,
 7735                                Some(search_range.start.0..search_range.end.0),
 7736                            )
 7737                            .await
 7738                            .into_iter()
 7739                            .filter_map(|match_range| {
 7740                                let match_start = buffer_snapshot
 7741                                    .anchor_after(search_range.start + match_range.start);
 7742                                let match_end = buffer_snapshot
 7743                                    .anchor_before(search_range.start + match_range.end);
 7744                                let match_anchor_range =
 7745                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7746                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7747                            }),
 7748                    );
 7749                }
 7750                match_ranges
 7751            });
 7752            let match_ranges = match_task.await;
 7753            editor
 7754                .update_in(cx, |editor, _, cx| {
 7755                    if use_debounce {
 7756                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7757                        editor.debounced_selection_highlight_complete = true;
 7758                    } else if editor.debounced_selection_highlight_complete {
 7759                        return;
 7760                    }
 7761                    if !match_ranges.is_empty() {
 7762                        editor.highlight_background(
 7763                            HighlightKey::SelectedTextHighlight,
 7764                            &match_ranges,
 7765                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7766                            cx,
 7767                        )
 7768                    }
 7769                })
 7770                .log_err();
 7771        })
 7772    }
 7773
 7774    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7775        struct NewlineFold;
 7776        let type_id = std::any::TypeId::of::<NewlineFold>();
 7777        if !self.mode.is_single_line() {
 7778            return;
 7779        }
 7780        let snapshot = self.snapshot(window, cx);
 7781        if snapshot.buffer_snapshot().max_point().row == 0 {
 7782            return;
 7783        }
 7784        let task = cx.background_spawn(async move {
 7785            let new_newlines = snapshot
 7786                .buffer_chars_at(MultiBufferOffset(0))
 7787                .filter_map(|(c, i)| {
 7788                    if c == '\n' {
 7789                        Some(
 7790                            snapshot.buffer_snapshot().anchor_after(i)
 7791                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7792                        )
 7793                    } else {
 7794                        None
 7795                    }
 7796                })
 7797                .collect::<Vec<_>>();
 7798            let existing_newlines = snapshot
 7799                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7800                .filter_map(|fold| {
 7801                    if fold.placeholder.type_tag == Some(type_id) {
 7802                        Some(fold.range.start..fold.range.end)
 7803                    } else {
 7804                        None
 7805                    }
 7806                })
 7807                .collect::<Vec<_>>();
 7808
 7809            (new_newlines, existing_newlines)
 7810        });
 7811        self.folding_newlines = cx.spawn(async move |this, cx| {
 7812            let (new_newlines, existing_newlines) = task.await;
 7813            if new_newlines == existing_newlines {
 7814                return;
 7815            }
 7816            let placeholder = FoldPlaceholder {
 7817                render: Arc::new(move |_, _, cx| {
 7818                    div()
 7819                        .bg(cx.theme().status().hint_background)
 7820                        .border_b_1()
 7821                        .size_full()
 7822                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7823                        .border_color(cx.theme().status().hint)
 7824                        .child("\\n")
 7825                        .into_any()
 7826                }),
 7827                constrain_width: false,
 7828                merge_adjacent: false,
 7829                type_tag: Some(type_id),
 7830                collapsed_text: None,
 7831            };
 7832            let creases = new_newlines
 7833                .into_iter()
 7834                .map(|range| Crease::simple(range, placeholder.clone()))
 7835                .collect();
 7836            this.update(cx, |this, cx| {
 7837                this.display_map.update(cx, |display_map, cx| {
 7838                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7839                    display_map.fold(creases, cx);
 7840                });
 7841            })
 7842            .ok();
 7843        });
 7844    }
 7845
 7846    #[ztracing::instrument(skip_all)]
 7847    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7848        if !self.lsp_data_enabled() {
 7849            return;
 7850        }
 7851        let cursor = self.selections.newest_anchor().head();
 7852        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7853
 7854        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7855            self.outline_symbols_at_cursor =
 7856                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7857            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7858            cx.notify();
 7859        } else {
 7860            let syntax = cx.theme().syntax().clone();
 7861            let background_task = cx.background_spawn(async move {
 7862                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7863            });
 7864            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7865                cx.spawn(async move |this, cx| {
 7866                    let symbols = background_task.await;
 7867                    this.update(cx, |this, cx| {
 7868                        this.outline_symbols_at_cursor = symbols;
 7869                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7870                        cx.notify();
 7871                    })
 7872                    .ok();
 7873                });
 7874        }
 7875    }
 7876
 7877    #[ztracing::instrument(skip_all)]
 7878    fn refresh_selected_text_highlights(
 7879        &mut self,
 7880        snapshot: &DisplaySnapshot,
 7881        on_buffer_edit: bool,
 7882        window: &mut Window,
 7883        cx: &mut Context<Editor>,
 7884    ) {
 7885        let Some((query_text, query_range)) =
 7886            self.prepare_highlight_query_from_selection(snapshot, cx)
 7887        else {
 7888            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7889            self.quick_selection_highlight_task.take();
 7890            self.debounced_selection_highlight_task.take();
 7891            self.debounced_selection_highlight_complete = false;
 7892            return;
 7893        };
 7894        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7895        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7896        let query_changed = self
 7897            .quick_selection_highlight_task
 7898            .as_ref()
 7899            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7900        if query_changed {
 7901            self.debounced_selection_highlight_complete = false;
 7902        }
 7903        if on_buffer_edit || query_changed {
 7904            self.quick_selection_highlight_task = Some((
 7905                query_range.clone(),
 7906                self.update_selection_occurrence_highlights(
 7907                    snapshot.buffer.clone(),
 7908                    query_text.clone(),
 7909                    query_range.clone(),
 7910                    self.multi_buffer_visible_range(&display_snapshot, cx),
 7911                    false,
 7912                    window,
 7913                    cx,
 7914                ),
 7915            ));
 7916        }
 7917        if on_buffer_edit
 7918            || self
 7919                .debounced_selection_highlight_task
 7920                .as_ref()
 7921                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7922        {
 7923            let multi_buffer_start = multi_buffer_snapshot
 7924                .anchor_before(MultiBufferOffset(0))
 7925                .to_point(&multi_buffer_snapshot);
 7926            let multi_buffer_end = multi_buffer_snapshot
 7927                .anchor_after(multi_buffer_snapshot.len())
 7928                .to_point(&multi_buffer_snapshot);
 7929            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7930            self.debounced_selection_highlight_task = Some((
 7931                query_range.clone(),
 7932                self.update_selection_occurrence_highlights(
 7933                    snapshot.buffer.clone(),
 7934                    query_text,
 7935                    query_range,
 7936                    multi_buffer_full_range,
 7937                    true,
 7938                    window,
 7939                    cx,
 7940                ),
 7941            ));
 7942        }
 7943    }
 7944
 7945    pub fn multi_buffer_visible_range(
 7946        &self,
 7947        display_snapshot: &DisplaySnapshot,
 7948        cx: &App,
 7949    ) -> Range<Point> {
 7950        let visible_start = self
 7951            .scroll_manager
 7952            .native_anchor(display_snapshot, cx)
 7953            .anchor
 7954            .to_point(display_snapshot.buffer_snapshot())
 7955            .to_display_point(display_snapshot);
 7956
 7957        let mut target_end = visible_start;
 7958        *target_end.row_mut() += self.visible_line_count().unwrap_or(0.).ceil() as u32;
 7959
 7960        visible_start.to_point(display_snapshot)
 7961            ..display_snapshot
 7962                .clip_point(target_end, Bias::Right)
 7963                .to_point(display_snapshot)
 7964    }
 7965
 7966    pub fn refresh_edit_prediction(
 7967        &mut self,
 7968        debounce: bool,
 7969        user_requested: bool,
 7970        window: &mut Window,
 7971        cx: &mut Context<Self>,
 7972    ) -> Option<()> {
 7973        if self.leader_id.is_some() {
 7974            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7975            return None;
 7976        }
 7977
 7978        let cursor = self.selections.newest_anchor().head();
 7979        let (buffer, cursor_buffer_position) =
 7980            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7981
 7982        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7983            return None;
 7984        }
 7985
 7986        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7987            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7988            return None;
 7989        }
 7990
 7991        self.update_visible_edit_prediction(window, cx);
 7992
 7993        if !user_requested
 7994            && (!self.should_show_edit_predictions()
 7995                || !self.is_focused(window)
 7996                || buffer.read(cx).is_empty())
 7997        {
 7998            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7999            return None;
 8000        }
 8001
 8002        self.edit_prediction_provider()?
 8003            .refresh(buffer, cursor_buffer_position, debounce, cx);
 8004        Some(())
 8005    }
 8006
 8007    fn show_edit_predictions_in_menu(&self) -> bool {
 8008        match self.edit_prediction_settings {
 8009            EditPredictionSettings::Disabled => false,
 8010            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 8011        }
 8012    }
 8013
 8014    pub fn edit_predictions_enabled(&self) -> bool {
 8015        match self.edit_prediction_settings {
 8016            EditPredictionSettings::Disabled => false,
 8017            EditPredictionSettings::Enabled { .. } => true,
 8018        }
 8019    }
 8020
 8021    fn edit_prediction_requires_modifier(&self) -> bool {
 8022        match self.edit_prediction_settings {
 8023            EditPredictionSettings::Disabled => false,
 8024            EditPredictionSettings::Enabled {
 8025                preview_requires_modifier,
 8026                ..
 8027            } => preview_requires_modifier,
 8028        }
 8029    }
 8030
 8031    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 8032        if self.edit_prediction_provider.is_none() {
 8033            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8034            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8035            return;
 8036        }
 8037
 8038        let selection = self.selections.newest_anchor();
 8039        let cursor = selection.head();
 8040
 8041        if let Some((buffer, cursor_buffer_position)) =
 8042            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8043        {
 8044            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8045                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8046                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8047                return;
 8048            }
 8049            self.edit_prediction_settings =
 8050                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8051        }
 8052    }
 8053
 8054    fn edit_prediction_settings_at_position(
 8055        &self,
 8056        buffer: &Entity<Buffer>,
 8057        buffer_position: language::Anchor,
 8058        cx: &App,
 8059    ) -> EditPredictionSettings {
 8060        if !self.mode.is_full()
 8061            || !self.show_edit_predictions_override.unwrap_or(true)
 8062            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 8063        {
 8064            return EditPredictionSettings::Disabled;
 8065        }
 8066
 8067        let buffer = buffer.read(cx);
 8068
 8069        let file = buffer.file();
 8070
 8071        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 8072            return EditPredictionSettings::Disabled;
 8073        };
 8074
 8075        let by_provider = matches!(
 8076            self.menu_edit_predictions_policy,
 8077            MenuEditPredictionsPolicy::ByProvider
 8078        );
 8079
 8080        let show_in_menu = by_provider
 8081            && self
 8082                .edit_prediction_provider
 8083                .as_ref()
 8084                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 8085
 8086        let preview_requires_modifier =
 8087            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 8088
 8089        EditPredictionSettings::Enabled {
 8090            show_in_menu,
 8091            preview_requires_modifier,
 8092        }
 8093    }
 8094
 8095    fn should_show_edit_predictions(&self) -> bool {
 8096        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 8097    }
 8098
 8099    pub fn edit_prediction_preview_is_active(&self) -> bool {
 8100        matches!(
 8101            self.edit_prediction_preview,
 8102            EditPredictionPreview::Active { .. }
 8103        )
 8104    }
 8105
 8106    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 8107        let cursor = self.selections.newest_anchor().head();
 8108        if let Some((buffer, cursor_position)) =
 8109            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8110        {
 8111            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 8112        } else {
 8113            false
 8114        }
 8115    }
 8116
 8117    pub fn supports_minimap(&self, cx: &App) -> bool {
 8118        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 8119    }
 8120
 8121    fn edit_predictions_enabled_in_buffer(
 8122        &self,
 8123        buffer: &Entity<Buffer>,
 8124        buffer_position: language::Anchor,
 8125        cx: &App,
 8126    ) -> bool {
 8127        maybe!({
 8128            if self.read_only(cx) || self.leader_id.is_some() {
 8129                return Some(false);
 8130            }
 8131            let provider = self.edit_prediction_provider()?;
 8132            if !provider.is_enabled(buffer, buffer_position, cx) {
 8133                return Some(false);
 8134            }
 8135            let buffer = buffer.read(cx);
 8136            let Some(file) = buffer.file() else {
 8137                return Some(true);
 8138            };
 8139            let settings = all_language_settings(Some(file), cx);
 8140            Some(settings.edit_predictions_enabled_for_file(file, cx))
 8141        })
 8142        .unwrap_or(false)
 8143    }
 8144
 8145    pub fn show_edit_prediction(
 8146        &mut self,
 8147        _: &ShowEditPrediction,
 8148        window: &mut Window,
 8149        cx: &mut Context<Self>,
 8150    ) {
 8151        if !self.has_active_edit_prediction() {
 8152            self.refresh_edit_prediction(false, true, window, cx);
 8153            return;
 8154        }
 8155
 8156        self.update_visible_edit_prediction(window, cx);
 8157    }
 8158
 8159    pub fn display_cursor_names(
 8160        &mut self,
 8161        _: &DisplayCursorNames,
 8162        window: &mut Window,
 8163        cx: &mut Context<Self>,
 8164    ) {
 8165        self.show_cursor_names(window, cx);
 8166    }
 8167
 8168    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8169        self.show_cursor_names = true;
 8170        cx.notify();
 8171        cx.spawn_in(window, async move |this, cx| {
 8172            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8173            this.update(cx, |this, cx| {
 8174                this.show_cursor_names = false;
 8175                cx.notify()
 8176            })
 8177            .ok()
 8178        })
 8179        .detach();
 8180    }
 8181
 8182    pub fn accept_partial_edit_prediction(
 8183        &mut self,
 8184        granularity: EditPredictionGranularity,
 8185        window: &mut Window,
 8186        cx: &mut Context<Self>,
 8187    ) {
 8188        if self.show_edit_predictions_in_menu() {
 8189            self.hide_context_menu(window, cx);
 8190        }
 8191
 8192        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8193            return;
 8194        };
 8195
 8196        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8197            return;
 8198        }
 8199
 8200        match &active_edit_prediction.completion {
 8201            EditPrediction::MoveWithin { target, .. } => {
 8202                let target = *target;
 8203
 8204                if matches!(granularity, EditPredictionGranularity::Full) {
 8205                    if let Some(position_map) = &self.last_position_map {
 8206                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8207                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8208
 8209                        if is_visible || !self.edit_prediction_requires_modifier() {
 8210                            self.unfold_ranges(&[target..target], true, false, cx);
 8211                            self.change_selections(
 8212                                SelectionEffects::scroll(Autoscroll::newest()),
 8213                                window,
 8214                                cx,
 8215                                |selections| {
 8216                                    selections.select_anchor_ranges([target..target]);
 8217                                },
 8218                            );
 8219                            self.clear_row_highlights::<EditPredictionPreview>();
 8220                            self.edit_prediction_preview
 8221                                .set_previous_scroll_position(None);
 8222                        } else {
 8223                            // Highlight and request scroll
 8224                            self.edit_prediction_preview
 8225                                .set_previous_scroll_position(Some(
 8226                                    position_map.snapshot.scroll_anchor,
 8227                                ));
 8228                            self.highlight_rows::<EditPredictionPreview>(
 8229                                target..target,
 8230                                cx.theme().colors().editor_highlighted_line_background,
 8231                                RowHighlightOptions {
 8232                                    autoscroll: true,
 8233                                    ..Default::default()
 8234                                },
 8235                                cx,
 8236                            );
 8237                            self.request_autoscroll(Autoscroll::fit(), cx);
 8238                        }
 8239                    }
 8240                } else {
 8241                    self.change_selections(
 8242                        SelectionEffects::scroll(Autoscroll::newest()),
 8243                        window,
 8244                        cx,
 8245                        |selections| {
 8246                            selections.select_anchor_ranges([target..target]);
 8247                        },
 8248                    );
 8249                }
 8250            }
 8251            EditPrediction::MoveOutside { snapshot, target } => {
 8252                if let Some(workspace) = self.workspace() {
 8253                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8254                        .detach_and_log_err(cx);
 8255                }
 8256            }
 8257            EditPrediction::Edit {
 8258                edits,
 8259                cursor_position,
 8260                ..
 8261            } => {
 8262                self.report_edit_prediction_event(
 8263                    active_edit_prediction.completion_id.clone(),
 8264                    true,
 8265                    cx,
 8266                );
 8267
 8268                match granularity {
 8269                    EditPredictionGranularity::Full => {
 8270                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8271
 8272                        // Compute fallback cursor position BEFORE applying the edit,
 8273                        // so the anchor tracks through the edit correctly
 8274                        let fallback_cursor_target = {
 8275                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8276                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8277                        };
 8278
 8279                        self.buffer.update(cx, |buffer, cx| {
 8280                            buffer.edit(edits.iter().cloned(), None, cx)
 8281                        });
 8282
 8283                        if let Some(provider) = self.edit_prediction_provider() {
 8284                            provider.accept(cx);
 8285                        }
 8286
 8287                        // Resolve cursor position after the edit is applied
 8288                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8289                            // The anchor tracks through the edit, then we add the offset
 8290                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8291                            let base_offset = anchor.to_offset(&snapshot).0;
 8292                            let target_offset =
 8293                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8294                            snapshot.anchor_after(target_offset)
 8295                        } else {
 8296                            fallback_cursor_target
 8297                        };
 8298
 8299                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8300                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8301                        });
 8302
 8303                        let selections = self.selections.disjoint_anchors_arc();
 8304                        if let Some(transaction_id_now) =
 8305                            self.buffer.read(cx).last_transaction_id(cx)
 8306                        {
 8307                            if transaction_id_prev != Some(transaction_id_now) {
 8308                                self.selection_history
 8309                                    .insert_transaction(transaction_id_now, selections);
 8310                            }
 8311                        }
 8312
 8313                        self.update_visible_edit_prediction(window, cx);
 8314                        if self.active_edit_prediction.is_none() {
 8315                            self.refresh_edit_prediction(true, true, window, cx);
 8316                        }
 8317                        cx.notify();
 8318                    }
 8319                    _ => {
 8320                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8321                        let cursor_offset = self
 8322                            .selections
 8323                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8324                            .head();
 8325
 8326                        let insertion = edits.iter().find_map(|(range, text)| {
 8327                            let range = range.to_offset(&snapshot);
 8328                            if range.is_empty() && range.start == cursor_offset {
 8329                                Some(text)
 8330                            } else {
 8331                                None
 8332                            }
 8333                        });
 8334
 8335                        if let Some(text) = insertion {
 8336                            let text_to_insert = match granularity {
 8337                                EditPredictionGranularity::Word => {
 8338                                    let mut partial = text
 8339                                        .chars()
 8340                                        .by_ref()
 8341                                        .take_while(|c| c.is_alphabetic())
 8342                                        .collect::<String>();
 8343                                    if partial.is_empty() {
 8344                                        partial = text
 8345                                            .chars()
 8346                                            .by_ref()
 8347                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8348                                            .collect::<String>();
 8349                                    }
 8350                                    partial
 8351                                }
 8352                                EditPredictionGranularity::Line => {
 8353                                    if let Some(line) = text.split_inclusive('\n').next() {
 8354                                        line.to_string()
 8355                                    } else {
 8356                                        text.to_string()
 8357                                    }
 8358                                }
 8359                                EditPredictionGranularity::Full => unreachable!(),
 8360                            };
 8361
 8362                            cx.emit(EditorEvent::InputHandled {
 8363                                utf16_range_to_replace: None,
 8364                                text: text_to_insert.clone().into(),
 8365                            });
 8366
 8367                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8368                            self.refresh_edit_prediction(true, true, window, cx);
 8369                            cx.notify();
 8370                        } else {
 8371                            self.accept_partial_edit_prediction(
 8372                                EditPredictionGranularity::Full,
 8373                                window,
 8374                                cx,
 8375                            );
 8376                        }
 8377                    }
 8378                }
 8379            }
 8380        }
 8381    }
 8382
 8383    pub fn accept_next_word_edit_prediction(
 8384        &mut self,
 8385        _: &AcceptNextWordEditPrediction,
 8386        window: &mut Window,
 8387        cx: &mut Context<Self>,
 8388    ) {
 8389        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8390    }
 8391
 8392    pub fn accept_next_line_edit_prediction(
 8393        &mut self,
 8394        _: &AcceptNextLineEditPrediction,
 8395        window: &mut Window,
 8396        cx: &mut Context<Self>,
 8397    ) {
 8398        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8399    }
 8400
 8401    pub fn accept_edit_prediction(
 8402        &mut self,
 8403        _: &AcceptEditPrediction,
 8404        window: &mut Window,
 8405        cx: &mut Context<Self>,
 8406    ) {
 8407        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8408    }
 8409
 8410    fn discard_edit_prediction(
 8411        &mut self,
 8412        reason: EditPredictionDiscardReason,
 8413        cx: &mut Context<Self>,
 8414    ) -> bool {
 8415        if reason == EditPredictionDiscardReason::Rejected {
 8416            let completion_id = self
 8417                .active_edit_prediction
 8418                .as_ref()
 8419                .and_then(|active_completion| active_completion.completion_id.clone());
 8420
 8421            self.report_edit_prediction_event(completion_id, false, cx);
 8422        }
 8423
 8424        if let Some(provider) = self.edit_prediction_provider() {
 8425            provider.discard(reason, cx);
 8426        }
 8427
 8428        self.take_active_edit_prediction(cx)
 8429    }
 8430
 8431    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8432        let Some(provider) = self.edit_prediction_provider() else {
 8433            return;
 8434        };
 8435
 8436        let Some((_, buffer, _)) = self
 8437            .buffer
 8438            .read(cx)
 8439            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8440        else {
 8441            return;
 8442        };
 8443
 8444        let extension = buffer
 8445            .read(cx)
 8446            .file()
 8447            .and_then(|file| Some(file.path().extension()?.to_string()));
 8448
 8449        let event_type = match accepted {
 8450            true => "Edit Prediction Accepted",
 8451            false => "Edit Prediction Discarded",
 8452        };
 8453        telemetry::event!(
 8454            event_type,
 8455            provider = provider.name(),
 8456            prediction_id = id,
 8457            suggestion_accepted = accepted,
 8458            file_extension = extension,
 8459        );
 8460    }
 8461
 8462    fn open_editor_at_anchor(
 8463        snapshot: &language::BufferSnapshot,
 8464        target: language::Anchor,
 8465        workspace: &Entity<Workspace>,
 8466        window: &mut Window,
 8467        cx: &mut App,
 8468    ) -> Task<Result<()>> {
 8469        workspace.update(cx, |workspace, cx| {
 8470            let path = snapshot.file().map(|file| file.full_path(cx));
 8471            let Some(path) =
 8472                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8473            else {
 8474                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8475            };
 8476            let target = text::ToPoint::to_point(&target, snapshot);
 8477            let item = workspace.open_path(path, None, true, window, cx);
 8478            window.spawn(cx, async move |cx| {
 8479                let Some(editor) = item.await?.downcast::<Editor>() else {
 8480                    return Ok(());
 8481                };
 8482                editor
 8483                    .update_in(cx, |editor, window, cx| {
 8484                        editor.go_to_singleton_buffer_point(target, window, cx);
 8485                    })
 8486                    .ok();
 8487                anyhow::Ok(())
 8488            })
 8489        })
 8490    }
 8491
 8492    pub fn has_active_edit_prediction(&self) -> bool {
 8493        self.active_edit_prediction.is_some()
 8494    }
 8495
 8496    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8497        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8498            return false;
 8499        };
 8500
 8501        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8502        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8503        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8504        true
 8505    }
 8506
 8507    /// Returns true when we're displaying the edit prediction popover below the cursor
 8508    /// like we are not previewing and the LSP autocomplete menu is visible
 8509    /// or we are in `when_holding_modifier` mode.
 8510    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8511        if self.edit_prediction_preview_is_active()
 8512            || !self.show_edit_predictions_in_menu()
 8513            || !self.edit_predictions_enabled()
 8514        {
 8515            return false;
 8516        }
 8517
 8518        if self.has_visible_completions_menu() {
 8519            return true;
 8520        }
 8521
 8522        has_completion && self.edit_prediction_requires_modifier()
 8523    }
 8524
 8525    fn handle_modifiers_changed(
 8526        &mut self,
 8527        modifiers: Modifiers,
 8528        position_map: &PositionMap,
 8529        window: &mut Window,
 8530        cx: &mut Context<Self>,
 8531    ) {
 8532        self.update_edit_prediction_settings(cx);
 8533
 8534        // Ensure that the edit prediction preview is updated, even when not
 8535        // enabled, if there's an active edit prediction preview.
 8536        if self.show_edit_predictions_in_menu()
 8537            || self.edit_prediction_requires_modifier()
 8538            || matches!(
 8539                self.edit_prediction_preview,
 8540                EditPredictionPreview::Active { .. }
 8541            )
 8542        {
 8543            self.update_edit_prediction_preview(&modifiers, window, cx);
 8544        }
 8545
 8546        self.update_selection_mode(&modifiers, position_map, window, cx);
 8547
 8548        let mouse_position = window.mouse_position();
 8549        if !position_map.text_hitbox.is_hovered(window) {
 8550            return;
 8551        }
 8552
 8553        self.update_hovered_link(
 8554            position_map.point_for_position(mouse_position),
 8555            Some(mouse_position),
 8556            &position_map.snapshot,
 8557            modifiers,
 8558            window,
 8559            cx,
 8560        )
 8561    }
 8562
 8563    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8564        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8565            MultiCursorModifier::Alt => modifiers.secondary(),
 8566            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8567        }
 8568    }
 8569
 8570    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8571        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8572            MultiCursorModifier::Alt => modifiers.alt,
 8573            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8574        }
 8575    }
 8576
 8577    fn columnar_selection_mode(
 8578        modifiers: &Modifiers,
 8579        cx: &mut Context<Self>,
 8580    ) -> Option<ColumnarMode> {
 8581        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8582            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8583                Some(ColumnarMode::FromMouse)
 8584            } else if Self::is_alt_pressed(modifiers, cx) {
 8585                Some(ColumnarMode::FromSelection)
 8586            } else {
 8587                None
 8588            }
 8589        } else {
 8590            None
 8591        }
 8592    }
 8593
 8594    fn update_selection_mode(
 8595        &mut self,
 8596        modifiers: &Modifiers,
 8597        position_map: &PositionMap,
 8598        window: &mut Window,
 8599        cx: &mut Context<Self>,
 8600    ) {
 8601        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8602            return;
 8603        };
 8604        if self.selections.pending_anchor().is_none() {
 8605            return;
 8606        }
 8607
 8608        let mouse_position = window.mouse_position();
 8609        let point_for_position = position_map.point_for_position(mouse_position);
 8610        let position = point_for_position.previous_valid;
 8611
 8612        self.select(
 8613            SelectPhase::BeginColumnar {
 8614                position,
 8615                reset: false,
 8616                mode,
 8617                goal_column: point_for_position.exact_unclipped.column(),
 8618            },
 8619            window,
 8620            cx,
 8621        );
 8622    }
 8623
 8624    fn update_edit_prediction_preview(
 8625        &mut self,
 8626        modifiers: &Modifiers,
 8627        window: &mut Window,
 8628        cx: &mut Context<Self>,
 8629    ) {
 8630        let modifiers_held = self.edit_prediction_preview_modifiers_held(modifiers, window, cx);
 8631
 8632        if modifiers_held {
 8633            if matches!(
 8634                self.edit_prediction_preview,
 8635                EditPredictionPreview::Inactive { .. }
 8636            ) {
 8637                self.edit_prediction_preview = EditPredictionPreview::Active {
 8638                    previous_scroll_position: None,
 8639                    since: Instant::now(),
 8640                };
 8641
 8642                self.update_visible_edit_prediction(window, cx);
 8643                cx.notify();
 8644            }
 8645        } else if let EditPredictionPreview::Active {
 8646            previous_scroll_position,
 8647            since,
 8648        } = self.edit_prediction_preview
 8649        {
 8650            if let (Some(previous_scroll_position), Some(position_map)) =
 8651                (previous_scroll_position, self.last_position_map.as_ref())
 8652            {
 8653                self.set_scroll_position(
 8654                    previous_scroll_position
 8655                        .scroll_position(&position_map.snapshot.display_snapshot),
 8656                    window,
 8657                    cx,
 8658                );
 8659            }
 8660
 8661            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8662                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8663            };
 8664            self.clear_row_highlights::<EditPredictionPreview>();
 8665            self.update_visible_edit_prediction(window, cx);
 8666            cx.notify();
 8667        }
 8668    }
 8669
 8670    fn update_visible_edit_prediction(
 8671        &mut self,
 8672        _window: &mut Window,
 8673        cx: &mut Context<Self>,
 8674    ) -> Option<()> {
 8675        if self.ime_transaction.is_some() {
 8676            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8677            return None;
 8678        }
 8679
 8680        let selection = self.selections.newest_anchor();
 8681        let cursor = selection.head();
 8682        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8683
 8684        // Check project-level disable_ai setting for the current buffer
 8685        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8686            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8687                return None;
 8688            }
 8689        }
 8690        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8691        let excerpt_id = cursor.excerpt_id;
 8692
 8693        let show_in_menu = self.show_edit_predictions_in_menu();
 8694        let completions_menu_has_precedence = !show_in_menu
 8695            && (self.context_menu.borrow().is_some()
 8696                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8697
 8698        if completions_menu_has_precedence
 8699            || !offset_selection.is_empty()
 8700            || self
 8701                .active_edit_prediction
 8702                .as_ref()
 8703                .is_some_and(|completion| {
 8704                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8705                        return false;
 8706                    };
 8707                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8708                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8709                    !invalidation_range.contains(&offset_selection.head())
 8710                })
 8711        {
 8712            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8713            return None;
 8714        }
 8715
 8716        self.take_active_edit_prediction(cx);
 8717        let Some(provider) = self.edit_prediction_provider() else {
 8718            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8719            return None;
 8720        };
 8721
 8722        let (buffer, cursor_buffer_position) =
 8723            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8724
 8725        self.edit_prediction_settings =
 8726            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8727
 8728        self.in_leading_whitespace = multibuffer.is_line_whitespace_upto(cursor);
 8729
 8730        if self.in_leading_whitespace {
 8731            let cursor_point = cursor.to_point(&multibuffer);
 8732            let mut suggested_indent = None;
 8733            multibuffer.suggested_indents_callback(
 8734                cursor_point.row..cursor_point.row + 1,
 8735                &mut |_, indent| {
 8736                    suggested_indent = Some(indent);
 8737                    ControlFlow::Break(())
 8738                },
 8739                cx,
 8740            );
 8741
 8742            if let Some(indent) = suggested_indent
 8743                && indent.len == cursor_point.column
 8744            {
 8745                self.in_leading_whitespace = false;
 8746            }
 8747        }
 8748
 8749        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8750
 8751        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8752        {
 8753            edit_prediction_types::EditPrediction::Local {
 8754                id,
 8755                edits,
 8756                cursor_position,
 8757                edit_preview,
 8758            } => (id, edits, cursor_position, edit_preview),
 8759            edit_prediction_types::EditPrediction::Jump {
 8760                id,
 8761                snapshot,
 8762                target,
 8763            } => {
 8764                if let Some(provider) = &self.edit_prediction_provider {
 8765                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8766                }
 8767                self.stale_edit_prediction_in_menu = None;
 8768                self.active_edit_prediction = Some(EditPredictionState {
 8769                    inlay_ids: vec![],
 8770                    completion: EditPrediction::MoveOutside { snapshot, target },
 8771                    completion_id: id,
 8772                    invalidation_range: None,
 8773                });
 8774                cx.notify();
 8775                return Some(());
 8776            }
 8777        };
 8778
 8779        let edits = edits
 8780            .into_iter()
 8781            .flat_map(|(range, new_text)| {
 8782                Some((
 8783                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8784                    new_text,
 8785                ))
 8786            })
 8787            .collect::<Vec<_>>();
 8788        if edits.is_empty() {
 8789            return None;
 8790        }
 8791
 8792        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8793            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8794            Some((anchor, predicted.offset))
 8795        });
 8796
 8797        let first_edit_start = edits.first().unwrap().0.start;
 8798        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8799        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8800
 8801        let last_edit_end = edits.last().unwrap().0.end;
 8802        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8803        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8804
 8805        let cursor_row = cursor.to_point(&multibuffer).row;
 8806
 8807        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8808
 8809        let mut inlay_ids = Vec::new();
 8810        let invalidation_row_range;
 8811        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8812            Some(cursor_row..edit_end_row)
 8813        } else if cursor_row > edit_end_row {
 8814            Some(edit_start_row..cursor_row)
 8815        } else {
 8816            None
 8817        };
 8818        let supports_jump = self
 8819            .edit_prediction_provider
 8820            .as_ref()
 8821            .map(|provider| provider.provider.supports_jump_to_edit())
 8822            .unwrap_or(true);
 8823
 8824        let is_move = supports_jump
 8825            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8826        let completion = if is_move {
 8827            if let Some(provider) = &self.edit_prediction_provider {
 8828                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8829            }
 8830            invalidation_row_range =
 8831                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8832            let target = first_edit_start;
 8833            EditPrediction::MoveWithin { target, snapshot }
 8834        } else {
 8835            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8836                && !self.edit_predictions_hidden_for_vim_mode;
 8837
 8838            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8839                if provider.show_tab_accept_marker() {
 8840                    EditDisplayMode::TabAccept
 8841                } else {
 8842                    EditDisplayMode::Inline
 8843                }
 8844            } else {
 8845                EditDisplayMode::DiffPopover
 8846            };
 8847
 8848            if show_completions_in_buffer {
 8849                if let Some(provider) = &self.edit_prediction_provider {
 8850                    let suggestion_display_type = match display_mode {
 8851                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8852                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8853                            SuggestionDisplayType::GhostText
 8854                        }
 8855                    };
 8856                    provider.provider.did_show(suggestion_display_type, cx);
 8857                }
 8858                if edits
 8859                    .iter()
 8860                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8861                {
 8862                    let mut inlays = Vec::new();
 8863                    for (range, new_text) in &edits {
 8864                        let inlay = Inlay::edit_prediction(
 8865                            post_inc(&mut self.next_inlay_id),
 8866                            range.start,
 8867                            new_text.as_ref(),
 8868                        );
 8869                        inlay_ids.push(inlay.id);
 8870                        inlays.push(inlay);
 8871                    }
 8872
 8873                    self.splice_inlays(&[], inlays, cx);
 8874                } else {
 8875                    let background_color = cx.theme().status().deleted_background;
 8876                    self.highlight_text(
 8877                        HighlightKey::EditPredictionHighlight,
 8878                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8879                        HighlightStyle {
 8880                            background_color: Some(background_color),
 8881                            ..Default::default()
 8882                        },
 8883                        cx,
 8884                    );
 8885                }
 8886            }
 8887
 8888            invalidation_row_range = edit_start_row..edit_end_row;
 8889
 8890            EditPrediction::Edit {
 8891                edits,
 8892                cursor_position,
 8893                edit_preview,
 8894                display_mode,
 8895                snapshot,
 8896            }
 8897        };
 8898
 8899        let invalidation_range = multibuffer
 8900            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8901            ..multibuffer.anchor_after(Point::new(
 8902                invalidation_row_range.end,
 8903                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8904            ));
 8905
 8906        self.stale_edit_prediction_in_menu = None;
 8907        self.active_edit_prediction = Some(EditPredictionState {
 8908            inlay_ids,
 8909            completion,
 8910            completion_id,
 8911            invalidation_range: Some(invalidation_range),
 8912        });
 8913
 8914        cx.notify();
 8915
 8916        Some(())
 8917    }
 8918
 8919    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8920        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8921    }
 8922
 8923    /// Get all display points of breakpoints that will be rendered within editor
 8924    ///
 8925    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8926    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8927    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8928    fn active_breakpoints(
 8929        &self,
 8930        range: Range<DisplayRow>,
 8931        window: &mut Window,
 8932        cx: &mut Context<Self>,
 8933    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8934        let mut breakpoint_display_points = HashMap::default();
 8935
 8936        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8937            return breakpoint_display_points;
 8938        };
 8939
 8940        let snapshot = self.snapshot(window, cx);
 8941
 8942        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8943        let Some(project) = self.project() else {
 8944            return breakpoint_display_points;
 8945        };
 8946
 8947        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8948            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8949
 8950        for (buffer_snapshot, range, excerpt_id) in
 8951            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8952        {
 8953            let Some(buffer) = project
 8954                .read(cx)
 8955                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8956            else {
 8957                continue;
 8958            };
 8959            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8960                &buffer,
 8961                Some(
 8962                    buffer_snapshot.anchor_before(range.start)
 8963                        ..buffer_snapshot.anchor_after(range.end),
 8964                ),
 8965                buffer_snapshot,
 8966                cx,
 8967            );
 8968            for (breakpoint, state) in breakpoints {
 8969                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8970                let position = multi_buffer_anchor
 8971                    .to_point(&multi_buffer_snapshot)
 8972                    .to_display_point(&snapshot);
 8973
 8974                breakpoint_display_points.insert(
 8975                    position.row(),
 8976                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8977                );
 8978            }
 8979        }
 8980
 8981        breakpoint_display_points
 8982    }
 8983
 8984    fn breakpoint_context_menu(
 8985        &self,
 8986        anchor: Anchor,
 8987        window: &mut Window,
 8988        cx: &mut Context<Self>,
 8989    ) -> Entity<ui::ContextMenu> {
 8990        let weak_editor = cx.weak_entity();
 8991        let focus_handle = self.focus_handle(cx);
 8992
 8993        let row = self
 8994            .buffer
 8995            .read(cx)
 8996            .snapshot(cx)
 8997            .summary_for_anchor::<Point>(&anchor)
 8998            .row;
 8999
 9000        let breakpoint = self
 9001            .breakpoint_at_row(row, window, cx)
 9002            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 9003
 9004        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 9005            "Edit Log Breakpoint"
 9006        } else {
 9007            "Set Log Breakpoint"
 9008        };
 9009
 9010        let condition_breakpoint_msg = if breakpoint
 9011            .as_ref()
 9012            .is_some_and(|bp| bp.1.condition.is_some())
 9013        {
 9014            "Edit Condition Breakpoint"
 9015        } else {
 9016            "Set Condition Breakpoint"
 9017        };
 9018
 9019        let hit_condition_breakpoint_msg = if breakpoint
 9020            .as_ref()
 9021            .is_some_and(|bp| bp.1.hit_condition.is_some())
 9022        {
 9023            "Edit Hit Condition Breakpoint"
 9024        } else {
 9025            "Set Hit Condition Breakpoint"
 9026        };
 9027
 9028        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 9029            "Unset Breakpoint"
 9030        } else {
 9031            "Set Breakpoint"
 9032        };
 9033
 9034        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 9035
 9036        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 9037            BreakpointState::Enabled => Some("Disable"),
 9038            BreakpointState::Disabled => Some("Enable"),
 9039        });
 9040
 9041        let (anchor, breakpoint) =
 9042            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 9043
 9044        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 9045            menu.on_blur_subscription(Subscription::new(|| {}))
 9046                .context(focus_handle)
 9047                .when(run_to_cursor, |this| {
 9048                    let weak_editor = weak_editor.clone();
 9049                    this.entry("Run to Cursor", None, move |window, cx| {
 9050                        weak_editor
 9051                            .update(cx, |editor, cx| {
 9052                                editor.change_selections(
 9053                                    SelectionEffects::no_scroll(),
 9054                                    window,
 9055                                    cx,
 9056                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 9057                                );
 9058                            })
 9059                            .ok();
 9060
 9061                        window.dispatch_action(Box::new(RunToCursor), cx);
 9062                    })
 9063                    .separator()
 9064                })
 9065                .when_some(toggle_state_msg, |this, msg| {
 9066                    this.entry(msg, None, {
 9067                        let weak_editor = weak_editor.clone();
 9068                        let breakpoint = breakpoint.clone();
 9069                        move |_window, cx| {
 9070                            weak_editor
 9071                                .update(cx, |this, cx| {
 9072                                    this.edit_breakpoint_at_anchor(
 9073                                        anchor,
 9074                                        breakpoint.as_ref().clone(),
 9075                                        BreakpointEditAction::InvertState,
 9076                                        cx,
 9077                                    );
 9078                                })
 9079                                .log_err();
 9080                        }
 9081                    })
 9082                })
 9083                .entry(set_breakpoint_msg, None, {
 9084                    let weak_editor = weak_editor.clone();
 9085                    let breakpoint = breakpoint.clone();
 9086                    move |_window, cx| {
 9087                        weak_editor
 9088                            .update(cx, |this, cx| {
 9089                                this.edit_breakpoint_at_anchor(
 9090                                    anchor,
 9091                                    breakpoint.as_ref().clone(),
 9092                                    BreakpointEditAction::Toggle,
 9093                                    cx,
 9094                                );
 9095                            })
 9096                            .log_err();
 9097                    }
 9098                })
 9099                .entry(log_breakpoint_msg, None, {
 9100                    let breakpoint = breakpoint.clone();
 9101                    let weak_editor = weak_editor.clone();
 9102                    move |window, cx| {
 9103                        weak_editor
 9104                            .update(cx, |this, cx| {
 9105                                this.add_edit_breakpoint_block(
 9106                                    anchor,
 9107                                    breakpoint.as_ref(),
 9108                                    BreakpointPromptEditAction::Log,
 9109                                    window,
 9110                                    cx,
 9111                                );
 9112                            })
 9113                            .log_err();
 9114                    }
 9115                })
 9116                .entry(condition_breakpoint_msg, None, {
 9117                    let breakpoint = breakpoint.clone();
 9118                    let weak_editor = weak_editor.clone();
 9119                    move |window, cx| {
 9120                        weak_editor
 9121                            .update(cx, |this, cx| {
 9122                                this.add_edit_breakpoint_block(
 9123                                    anchor,
 9124                                    breakpoint.as_ref(),
 9125                                    BreakpointPromptEditAction::Condition,
 9126                                    window,
 9127                                    cx,
 9128                                );
 9129                            })
 9130                            .log_err();
 9131                    }
 9132                })
 9133                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 9134                    weak_editor
 9135                        .update(cx, |this, cx| {
 9136                            this.add_edit_breakpoint_block(
 9137                                anchor,
 9138                                breakpoint.as_ref(),
 9139                                BreakpointPromptEditAction::HitCondition,
 9140                                window,
 9141                                cx,
 9142                            );
 9143                        })
 9144                        .log_err();
 9145                })
 9146        })
 9147    }
 9148
 9149    fn render_breakpoint(
 9150        &self,
 9151        position: Anchor,
 9152        row: DisplayRow,
 9153        breakpoint: &Breakpoint,
 9154        state: Option<BreakpointSessionState>,
 9155        cx: &mut Context<Self>,
 9156    ) -> IconButton {
 9157        let is_rejected = state.is_some_and(|s| !s.verified);
 9158        // Is it a breakpoint that shows up when hovering over gutter?
 9159        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9160            (false, false),
 9161            |PhantomBreakpointIndicator {
 9162                 is_active,
 9163                 display_row,
 9164                 collides_with_existing_breakpoint,
 9165             }| {
 9166                (
 9167                    is_active && display_row == row,
 9168                    collides_with_existing_breakpoint,
 9169                )
 9170            },
 9171        );
 9172
 9173        let (color, icon) = {
 9174            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9175                (false, false) => ui::IconName::DebugBreakpoint,
 9176                (true, false) => ui::IconName::DebugLogBreakpoint,
 9177                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9178                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9179            };
 9180
 9181            let theme_colors = cx.theme().colors();
 9182
 9183            let color = if is_phantom {
 9184                if collides_with_existing {
 9185                    Color::Custom(
 9186                        theme_colors
 9187                            .debugger_accent
 9188                            .blend(theme_colors.text.opacity(0.6)),
 9189                    )
 9190                } else {
 9191                    Color::Hint
 9192                }
 9193            } else if is_rejected {
 9194                Color::Disabled
 9195            } else {
 9196                Color::Debugger
 9197            };
 9198
 9199            (color, icon)
 9200        };
 9201
 9202        let breakpoint = Arc::from(breakpoint.clone());
 9203
 9204        let alt_as_text = gpui::Keystroke {
 9205            modifiers: Modifiers::secondary_key(),
 9206            ..Default::default()
 9207        };
 9208        let primary_action_text = if breakpoint.is_disabled() {
 9209            "Enable breakpoint"
 9210        } else if is_phantom && !collides_with_existing {
 9211            "Set breakpoint"
 9212        } else {
 9213            "Unset breakpoint"
 9214        };
 9215        let focus_handle = self.focus_handle.clone();
 9216
 9217        let meta = if is_rejected {
 9218            SharedString::from("No executable code is associated with this line.")
 9219        } else if collides_with_existing && !breakpoint.is_disabled() {
 9220            SharedString::from(format!(
 9221                "{alt_as_text}-click to disable,\nright-click for more options."
 9222            ))
 9223        } else {
 9224            SharedString::from("Right-click for more options.")
 9225        };
 9226        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9227            .icon_size(IconSize::XSmall)
 9228            .size(ui::ButtonSize::None)
 9229            .when(is_rejected, |this| {
 9230                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9231            })
 9232            .icon_color(color)
 9233            .style(ButtonStyle::Transparent)
 9234            .on_click(cx.listener({
 9235                move |editor, event: &ClickEvent, window, cx| {
 9236                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9237                        BreakpointEditAction::InvertState
 9238                    } else {
 9239                        BreakpointEditAction::Toggle
 9240                    };
 9241
 9242                    window.focus(&editor.focus_handle(cx), cx);
 9243                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9244                    editor.edit_breakpoint_at_anchor(
 9245                        position,
 9246                        breakpoint.as_ref().clone(),
 9247                        edit_action,
 9248                        cx,
 9249                    );
 9250                }
 9251            }))
 9252            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9253                editor.set_breakpoint_context_menu(
 9254                    row,
 9255                    Some(position),
 9256                    event.position(),
 9257                    window,
 9258                    cx,
 9259                );
 9260            }))
 9261            .tooltip(move |_window, cx| {
 9262                Tooltip::with_meta_in(
 9263                    primary_action_text,
 9264                    Some(&ToggleBreakpoint),
 9265                    meta.clone(),
 9266                    &focus_handle,
 9267                    cx,
 9268                )
 9269            })
 9270    }
 9271
 9272    fn build_tasks_context(
 9273        project: &Entity<Project>,
 9274        buffer: &Entity<Buffer>,
 9275        buffer_row: u32,
 9276        tasks: &Arc<RunnableTasks>,
 9277        cx: &mut Context<Self>,
 9278    ) -> Task<Option<task::TaskContext>> {
 9279        let position = Point::new(buffer_row, tasks.column);
 9280        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9281        let location = Location {
 9282            buffer: buffer.clone(),
 9283            range: range_start..range_start,
 9284        };
 9285        // Fill in the environmental variables from the tree-sitter captures
 9286        let mut captured_task_variables = TaskVariables::default();
 9287        for (capture_name, value) in tasks.extra_variables.clone() {
 9288            captured_task_variables.insert(
 9289                task::VariableName::Custom(capture_name.into()),
 9290                value.clone(),
 9291            );
 9292        }
 9293        project.update(cx, |project, cx| {
 9294            project.task_store().update(cx, |task_store, cx| {
 9295                task_store.task_context_for_location(captured_task_variables, location, cx)
 9296            })
 9297        })
 9298    }
 9299
 9300    pub fn context_menu_visible(&self) -> bool {
 9301        !self.edit_prediction_preview_is_active()
 9302            && self
 9303                .context_menu
 9304                .borrow()
 9305                .as_ref()
 9306                .is_some_and(|menu| menu.visible())
 9307    }
 9308
 9309    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9310        self.context_menu
 9311            .borrow()
 9312            .as_ref()
 9313            .map(|menu| menu.origin())
 9314    }
 9315
 9316    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9317        self.context_menu_options = Some(options);
 9318    }
 9319
 9320    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9321    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9322
 9323    fn render_edit_prediction_popover(
 9324        &mut self,
 9325        text_bounds: &Bounds<Pixels>,
 9326        content_origin: gpui::Point<Pixels>,
 9327        right_margin: Pixels,
 9328        editor_snapshot: &EditorSnapshot,
 9329        visible_row_range: Range<DisplayRow>,
 9330        scroll_top: ScrollOffset,
 9331        scroll_bottom: ScrollOffset,
 9332        line_layouts: &[LineWithInvisibles],
 9333        line_height: Pixels,
 9334        scroll_position: gpui::Point<ScrollOffset>,
 9335        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9336        newest_selection_head: Option<DisplayPoint>,
 9337        editor_width: Pixels,
 9338        style: &EditorStyle,
 9339        window: &mut Window,
 9340        cx: &mut App,
 9341    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9342        if self.mode().is_minimap() {
 9343            return None;
 9344        }
 9345        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9346
 9347        if self.edit_prediction_visible_in_cursor_popover(true) {
 9348            return None;
 9349        }
 9350
 9351        match &active_edit_prediction.completion {
 9352            EditPrediction::MoveWithin { target, .. } => {
 9353                let target_display_point = target.to_display_point(editor_snapshot);
 9354
 9355                if self.edit_prediction_requires_modifier() {
 9356                    if !self.edit_prediction_preview_is_active() {
 9357                        return None;
 9358                    }
 9359
 9360                    self.render_edit_prediction_modifier_jump_popover(
 9361                        text_bounds,
 9362                        content_origin,
 9363                        visible_row_range,
 9364                        line_layouts,
 9365                        line_height,
 9366                        scroll_pixel_position,
 9367                        newest_selection_head,
 9368                        target_display_point,
 9369                        window,
 9370                        cx,
 9371                    )
 9372                } else {
 9373                    self.render_edit_prediction_eager_jump_popover(
 9374                        text_bounds,
 9375                        content_origin,
 9376                        editor_snapshot,
 9377                        visible_row_range,
 9378                        scroll_top,
 9379                        scroll_bottom,
 9380                        line_height,
 9381                        scroll_pixel_position,
 9382                        target_display_point,
 9383                        editor_width,
 9384                        window,
 9385                        cx,
 9386                    )
 9387                }
 9388            }
 9389            EditPrediction::Edit {
 9390                display_mode: EditDisplayMode::Inline,
 9391                ..
 9392            } => None,
 9393            EditPrediction::Edit {
 9394                display_mode: EditDisplayMode::TabAccept,
 9395                edits,
 9396                ..
 9397            } => {
 9398                let range = &edits.first()?.0;
 9399                let target_display_point = range.end.to_display_point(editor_snapshot);
 9400
 9401                self.render_edit_prediction_end_of_line_popover(
 9402                    "Accept",
 9403                    editor_snapshot,
 9404                    visible_row_range,
 9405                    target_display_point,
 9406                    line_height,
 9407                    scroll_pixel_position,
 9408                    content_origin,
 9409                    editor_width,
 9410                    window,
 9411                    cx,
 9412                )
 9413            }
 9414            EditPrediction::Edit {
 9415                edits,
 9416                edit_preview,
 9417                display_mode: EditDisplayMode::DiffPopover,
 9418                snapshot,
 9419                ..
 9420            } => self.render_edit_prediction_diff_popover(
 9421                text_bounds,
 9422                content_origin,
 9423                right_margin,
 9424                editor_snapshot,
 9425                visible_row_range,
 9426                line_layouts,
 9427                line_height,
 9428                scroll_position,
 9429                scroll_pixel_position,
 9430                newest_selection_head,
 9431                editor_width,
 9432                style,
 9433                edits,
 9434                edit_preview,
 9435                snapshot,
 9436                window,
 9437                cx,
 9438            ),
 9439            EditPrediction::MoveOutside { snapshot, .. } => {
 9440                let mut element = self
 9441                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9442                    .into_any();
 9443
 9444                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9445                let origin_x = text_bounds.size.width - size.width - px(30.);
 9446                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9447                element.prepaint_at(origin, window, cx);
 9448
 9449                Some((element, origin))
 9450            }
 9451        }
 9452    }
 9453
 9454    fn render_edit_prediction_modifier_jump_popover(
 9455        &mut self,
 9456        text_bounds: &Bounds<Pixels>,
 9457        content_origin: gpui::Point<Pixels>,
 9458        visible_row_range: Range<DisplayRow>,
 9459        line_layouts: &[LineWithInvisibles],
 9460        line_height: Pixels,
 9461        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9462        newest_selection_head: Option<DisplayPoint>,
 9463        target_display_point: DisplayPoint,
 9464        window: &mut Window,
 9465        cx: &mut App,
 9466    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9467        let scrolled_content_origin =
 9468            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9469
 9470        const SCROLL_PADDING_Y: Pixels = px(12.);
 9471
 9472        if target_display_point.row() < visible_row_range.start {
 9473            return self.render_edit_prediction_scroll_popover(
 9474                &|_| SCROLL_PADDING_Y,
 9475                IconName::ArrowUp,
 9476                visible_row_range,
 9477                line_layouts,
 9478                newest_selection_head,
 9479                scrolled_content_origin,
 9480                window,
 9481                cx,
 9482            );
 9483        } else if target_display_point.row() >= visible_row_range.end {
 9484            return self.render_edit_prediction_scroll_popover(
 9485                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9486                IconName::ArrowDown,
 9487                visible_row_range,
 9488                line_layouts,
 9489                newest_selection_head,
 9490                scrolled_content_origin,
 9491                window,
 9492                cx,
 9493            );
 9494        }
 9495
 9496        const POLE_WIDTH: Pixels = px(2.);
 9497
 9498        let line_layout =
 9499            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9500        let target_column = target_display_point.column() as usize;
 9501
 9502        let target_x = line_layout.x_for_index(target_column);
 9503        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9504            - scroll_pixel_position.y;
 9505
 9506        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9507
 9508        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9509        border_color.l += 0.001;
 9510
 9511        let mut element = v_flex()
 9512            .items_end()
 9513            .when(flag_on_right, |el| el.items_start())
 9514            .child(if flag_on_right {
 9515                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9516                    .rounded_bl(px(0.))
 9517                    .rounded_tl(px(0.))
 9518                    .border_l_2()
 9519                    .border_color(border_color)
 9520            } else {
 9521                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9522                    .rounded_br(px(0.))
 9523                    .rounded_tr(px(0.))
 9524                    .border_r_2()
 9525                    .border_color(border_color)
 9526            })
 9527            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9528            .into_any();
 9529
 9530        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9531
 9532        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9533            - point(
 9534                if flag_on_right {
 9535                    POLE_WIDTH
 9536                } else {
 9537                    size.width - POLE_WIDTH
 9538                },
 9539                size.height - line_height,
 9540            );
 9541
 9542        origin.x = origin.x.max(content_origin.x);
 9543
 9544        element.prepaint_at(origin, window, cx);
 9545
 9546        Some((element, origin))
 9547    }
 9548
 9549    fn render_edit_prediction_scroll_popover(
 9550        &mut self,
 9551        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9552        scroll_icon: IconName,
 9553        visible_row_range: Range<DisplayRow>,
 9554        line_layouts: &[LineWithInvisibles],
 9555        newest_selection_head: Option<DisplayPoint>,
 9556        scrolled_content_origin: gpui::Point<Pixels>,
 9557        window: &mut Window,
 9558        cx: &mut App,
 9559    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9560        let mut element = self
 9561            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9562            .into_any();
 9563
 9564        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9565
 9566        let cursor = newest_selection_head?;
 9567        let cursor_row_layout =
 9568            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9569        let cursor_column = cursor.column() as usize;
 9570
 9571        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9572
 9573        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9574
 9575        element.prepaint_at(origin, window, cx);
 9576        Some((element, origin))
 9577    }
 9578
 9579    fn render_edit_prediction_eager_jump_popover(
 9580        &mut self,
 9581        text_bounds: &Bounds<Pixels>,
 9582        content_origin: gpui::Point<Pixels>,
 9583        editor_snapshot: &EditorSnapshot,
 9584        visible_row_range: Range<DisplayRow>,
 9585        scroll_top: ScrollOffset,
 9586        scroll_bottom: ScrollOffset,
 9587        line_height: Pixels,
 9588        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9589        target_display_point: DisplayPoint,
 9590        editor_width: Pixels,
 9591        window: &mut Window,
 9592        cx: &mut App,
 9593    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9594        if target_display_point.row().as_f64() < scroll_top {
 9595            let mut element = self
 9596                .render_edit_prediction_line_popover(
 9597                    "Jump to Edit",
 9598                    Some(IconName::ArrowUp),
 9599                    window,
 9600                    cx,
 9601                )
 9602                .into_any();
 9603
 9604            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9605            let offset = point(
 9606                (text_bounds.size.width - size.width) / 2.,
 9607                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9608            );
 9609
 9610            let origin = text_bounds.origin + offset;
 9611            element.prepaint_at(origin, window, cx);
 9612            Some((element, origin))
 9613        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9614            let mut element = self
 9615                .render_edit_prediction_line_popover(
 9616                    "Jump to Edit",
 9617                    Some(IconName::ArrowDown),
 9618                    window,
 9619                    cx,
 9620                )
 9621                .into_any();
 9622
 9623            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9624            let offset = point(
 9625                (text_bounds.size.width - size.width) / 2.,
 9626                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9627            );
 9628
 9629            let origin = text_bounds.origin + offset;
 9630            element.prepaint_at(origin, window, cx);
 9631            Some((element, origin))
 9632        } else {
 9633            self.render_edit_prediction_end_of_line_popover(
 9634                "Jump to Edit",
 9635                editor_snapshot,
 9636                visible_row_range,
 9637                target_display_point,
 9638                line_height,
 9639                scroll_pixel_position,
 9640                content_origin,
 9641                editor_width,
 9642                window,
 9643                cx,
 9644            )
 9645        }
 9646    }
 9647
 9648    fn render_edit_prediction_end_of_line_popover(
 9649        self: &mut Editor,
 9650        label: &'static str,
 9651        editor_snapshot: &EditorSnapshot,
 9652        visible_row_range: Range<DisplayRow>,
 9653        target_display_point: DisplayPoint,
 9654        line_height: Pixels,
 9655        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9656        content_origin: gpui::Point<Pixels>,
 9657        editor_width: Pixels,
 9658        window: &mut Window,
 9659        cx: &mut App,
 9660    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9661        let target_line_end = DisplayPoint::new(
 9662            target_display_point.row(),
 9663            editor_snapshot.line_len(target_display_point.row()),
 9664        );
 9665
 9666        let mut element = self
 9667            .render_edit_prediction_line_popover(label, None, window, cx)
 9668            .into_any();
 9669
 9670        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9671
 9672        let line_origin =
 9673            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9674
 9675        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9676        let mut origin = start_point
 9677            + line_origin
 9678            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9679        origin.x = origin.x.max(content_origin.x);
 9680
 9681        let max_x = content_origin.x + editor_width - size.width;
 9682
 9683        if origin.x > max_x {
 9684            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9685
 9686            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9687                origin.y += offset;
 9688                IconName::ArrowUp
 9689            } else {
 9690                origin.y -= offset;
 9691                IconName::ArrowDown
 9692            };
 9693
 9694            element = self
 9695                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9696                .into_any();
 9697
 9698            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9699
 9700            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9701        }
 9702
 9703        element.prepaint_at(origin, window, cx);
 9704        Some((element, origin))
 9705    }
 9706
 9707    fn render_edit_prediction_diff_popover(
 9708        self: &Editor,
 9709        text_bounds: &Bounds<Pixels>,
 9710        content_origin: gpui::Point<Pixels>,
 9711        right_margin: Pixels,
 9712        editor_snapshot: &EditorSnapshot,
 9713        visible_row_range: Range<DisplayRow>,
 9714        line_layouts: &[LineWithInvisibles],
 9715        line_height: Pixels,
 9716        scroll_position: gpui::Point<ScrollOffset>,
 9717        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9718        newest_selection_head: Option<DisplayPoint>,
 9719        editor_width: Pixels,
 9720        style: &EditorStyle,
 9721        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9722        edit_preview: &Option<language::EditPreview>,
 9723        snapshot: &language::BufferSnapshot,
 9724        window: &mut Window,
 9725        cx: &mut App,
 9726    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9727        let edit_start = edits
 9728            .first()
 9729            .unwrap()
 9730            .0
 9731            .start
 9732            .to_display_point(editor_snapshot);
 9733        let edit_end = edits
 9734            .last()
 9735            .unwrap()
 9736            .0
 9737            .end
 9738            .to_display_point(editor_snapshot);
 9739
 9740        let is_visible = visible_row_range.contains(&edit_start.row())
 9741            || visible_row_range.contains(&edit_end.row());
 9742        if !is_visible {
 9743            return None;
 9744        }
 9745
 9746        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9747            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9748        } else {
 9749            // Fallback for providers without edit_preview
 9750            crate::edit_prediction_fallback_text(edits, cx)
 9751        };
 9752
 9753        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9754        let line_count = highlighted_edits.text.lines().count();
 9755
 9756        const BORDER_WIDTH: Pixels = px(1.);
 9757
 9758        let keybind = self.render_edit_prediction_keybind(window, cx);
 9759        let has_keybind = keybind.is_some();
 9760
 9761        let mut element = h_flex()
 9762            .items_start()
 9763            .child(
 9764                h_flex()
 9765                    .bg(cx.theme().colors().editor_background)
 9766                    .border(BORDER_WIDTH)
 9767                    .shadow_xs()
 9768                    .border_color(cx.theme().colors().border)
 9769                    .rounded_l_lg()
 9770                    .when(line_count > 1, |el| el.rounded_br_lg())
 9771                    .pr_1()
 9772                    .child(styled_text),
 9773            )
 9774            .child(
 9775                h_flex()
 9776                    .h(line_height + BORDER_WIDTH * 2.)
 9777                    .px_1p5()
 9778                    .gap_1()
 9779                    // Workaround: For some reason, there's a gap if we don't do this
 9780                    .ml(-BORDER_WIDTH)
 9781                    .shadow(vec![gpui::BoxShadow {
 9782                        color: gpui::black().opacity(0.05),
 9783                        offset: point(px(1.), px(1.)),
 9784                        blur_radius: px(2.),
 9785                        spread_radius: px(0.),
 9786                    }])
 9787                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9788                    .border(BORDER_WIDTH)
 9789                    .border_color(cx.theme().colors().border)
 9790                    .rounded_r_lg()
 9791                    .id("edit_prediction_diff_popover_keybind")
 9792                    .when(!has_keybind, |el| {
 9793                        let status_colors = cx.theme().status();
 9794
 9795                        el.bg(status_colors.error_background)
 9796                            .border_color(status_colors.error.opacity(0.6))
 9797                            .child(Icon::new(IconName::Info).color(Color::Error))
 9798                            .cursor_default()
 9799                            .hoverable_tooltip(move |_window, cx| {
 9800                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9801                            })
 9802                    })
 9803                    .children(keybind),
 9804            )
 9805            .into_any();
 9806
 9807        let longest_row =
 9808            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9809        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9810            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9811        } else {
 9812            layout_line(
 9813                longest_row,
 9814                editor_snapshot,
 9815                style,
 9816                editor_width,
 9817                |_| false,
 9818                window,
 9819                cx,
 9820            )
 9821            .width
 9822        };
 9823
 9824        let viewport_bounds =
 9825            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9826                right: -right_margin,
 9827                ..Default::default()
 9828            });
 9829
 9830        let x_after_longest = Pixels::from(
 9831            ScrollPixelOffset::from(
 9832                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9833            ) - scroll_pixel_position.x,
 9834        );
 9835
 9836        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9837
 9838        // Fully visible if it can be displayed within the window (allow overlapping other
 9839        // panes). However, this is only allowed if the popover starts within text_bounds.
 9840        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9841            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9842
 9843        let mut origin = if can_position_to_the_right {
 9844            point(
 9845                x_after_longest,
 9846                text_bounds.origin.y
 9847                    + Pixels::from(
 9848                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9849                            - scroll_pixel_position.y,
 9850                    ),
 9851            )
 9852        } else {
 9853            let cursor_row = newest_selection_head.map(|head| head.row());
 9854            let above_edit = edit_start
 9855                .row()
 9856                .0
 9857                .checked_sub(line_count as u32)
 9858                .map(DisplayRow);
 9859            let below_edit = Some(edit_end.row() + 1);
 9860            let above_cursor =
 9861                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9862            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9863
 9864            // Place the edit popover adjacent to the edit if there is a location
 9865            // available that is onscreen and does not obscure the cursor. Otherwise,
 9866            // place it adjacent to the cursor.
 9867            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9868                .into_iter()
 9869                .flatten()
 9870                .find(|&start_row| {
 9871                    let end_row = start_row + line_count as u32;
 9872                    visible_row_range.contains(&start_row)
 9873                        && visible_row_range.contains(&end_row)
 9874                        && cursor_row
 9875                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9876                })?;
 9877
 9878            content_origin
 9879                + point(
 9880                    Pixels::from(-scroll_pixel_position.x),
 9881                    Pixels::from(
 9882                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9883                    ),
 9884                )
 9885        };
 9886
 9887        origin.x -= BORDER_WIDTH;
 9888
 9889        window.with_content_mask(
 9890            Some(gpui::ContentMask {
 9891                bounds: *text_bounds,
 9892            }),
 9893            |window| {
 9894                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9895            },
 9896        );
 9897
 9898        // Do not return an element, since it will already be drawn due to defer_draw.
 9899        None
 9900    }
 9901
 9902    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9903        px(30.)
 9904    }
 9905
 9906    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9907        if self.read_only(cx) {
 9908            cx.theme().players().read_only()
 9909        } else {
 9910            self.style.as_ref().unwrap().local_player
 9911        }
 9912    }
 9913
 9914    fn render_edit_prediction_inline_keystroke(
 9915        &self,
 9916        keystroke: &gpui::KeybindingKeystroke,
 9917        modifiers_color: Color,
 9918        cx: &App,
 9919    ) -> AnyElement {
 9920        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9921
 9922        h_flex()
 9923            .px_0p5()
 9924            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9925            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9926            .text_size(TextSize::XSmall.rems(cx))
 9927            .child(h_flex().children(ui::render_modifiers(
 9928                keystroke.modifiers(),
 9929                PlatformStyle::platform(),
 9930                Some(modifiers_color),
 9931                Some(IconSize::XSmall.rems().into()),
 9932                true,
 9933            )))
 9934            .when(is_platform_style_mac, |parent| {
 9935                parent.child(keystroke.key().to_string())
 9936            })
 9937            .when(!is_platform_style_mac, |parent| {
 9938                parent.child(
 9939                    Key::new(util::capitalize(keystroke.key()), Some(Color::Default))
 9940                        .size(Some(IconSize::XSmall.rems().into())),
 9941                )
 9942            })
 9943            .into_any()
 9944    }
 9945
 9946    fn render_edit_prediction_popover_keystroke(
 9947        &self,
 9948        keystroke: &gpui::KeybindingKeystroke,
 9949        color: Color,
 9950        cx: &App,
 9951    ) -> AnyElement {
 9952        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9953
 9954        if keystroke.modifiers().modified() {
 9955            h_flex()
 9956                .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9957                .when(is_platform_style_mac, |parent| parent.gap_1())
 9958                .child(h_flex().children(ui::render_modifiers(
 9959                    keystroke.modifiers(),
 9960                    PlatformStyle::platform(),
 9961                    Some(color),
 9962                    None,
 9963                    false,
 9964                )))
 9965                .into_any()
 9966        } else {
 9967            Key::new(util::capitalize(keystroke.key()), Some(color))
 9968                .size(Some(IconSize::XSmall.rems().into()))
 9969                .into_any_element()
 9970        }
 9971    }
 9972
 9973    fn render_edit_prediction_keybind(
 9974        &self,
 9975        window: &mut Window,
 9976        cx: &mut App,
 9977    ) -> Option<AnyElement> {
 9978        let keybind_display =
 9979            self.edit_prediction_keybind_display(EditPredictionKeybindSurface::Inline, window, cx);
 9980        let keystroke = keybind_display.displayed_keystroke.as_ref()?;
 9981
 9982        let modifiers_color = if *keystroke.modifiers() == window.modifiers() {
 9983            Color::Accent
 9984        } else {
 9985            Color::Muted
 9986        };
 9987
 9988        Some(self.render_edit_prediction_inline_keystroke(keystroke, modifiers_color, cx))
 9989    }
 9990
 9991    fn render_edit_prediction_line_popover(
 9992        &self,
 9993        label: impl Into<SharedString>,
 9994        icon: Option<IconName>,
 9995        window: &mut Window,
 9996        cx: &mut App,
 9997    ) -> Stateful<Div> {
 9998        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9999
10000        let keybind = self.render_edit_prediction_keybind(window, cx);
10001        let has_keybind = keybind.is_some();
10002        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10003
10004        h_flex()
10005            .id("ep-line-popover")
10006            .py_0p5()
10007            .pl_1()
10008            .pr(padding_right)
10009            .gap_1()
10010            .rounded_md()
10011            .border_1()
10012            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10013            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10014            .shadow_xs()
10015            .when(!has_keybind, |el| {
10016                let status_colors = cx.theme().status();
10017
10018                el.bg(status_colors.error_background)
10019                    .border_color(status_colors.error.opacity(0.6))
10020                    .pl_2()
10021                    .child(Icon::new(icons.error).color(Color::Error))
10022                    .cursor_default()
10023                    .hoverable_tooltip(move |_window, cx| {
10024                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10025                    })
10026            })
10027            .children(keybind)
10028            .child(
10029                Label::new(label)
10030                    .size(LabelSize::Small)
10031                    .when(!has_keybind, |el| {
10032                        el.color(cx.theme().status().error.into()).strikethrough()
10033                    }),
10034            )
10035            .when(!has_keybind, |el| {
10036                el.child(
10037                    h_flex().ml_1().child(
10038                        Icon::new(IconName::Info)
10039                            .size(IconSize::Small)
10040                            .color(cx.theme().status().error.into()),
10041                    ),
10042                )
10043            })
10044            .when_some(icon, |element, icon| {
10045                element.child(
10046                    div()
10047                        .mt(px(1.5))
10048                        .child(Icon::new(icon).size(IconSize::Small)),
10049                )
10050            })
10051    }
10052
10053    fn render_edit_prediction_jump_outside_popover(
10054        &self,
10055        snapshot: &BufferSnapshot,
10056        window: &mut Window,
10057        cx: &mut App,
10058    ) -> Stateful<Div> {
10059        let keybind = self.render_edit_prediction_keybind(window, cx);
10060        let has_keybind = keybind.is_some();
10061        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10062
10063        let file_name = snapshot
10064            .file()
10065            .map(|file| SharedString::new(file.file_name(cx)))
10066            .unwrap_or(SharedString::new_static("untitled"));
10067
10068        h_flex()
10069            .id("ep-jump-outside-popover")
10070            .py_1()
10071            .px_2()
10072            .gap_1()
10073            .rounded_md()
10074            .border_1()
10075            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10076            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10077            .shadow_xs()
10078            .when(!has_keybind, |el| {
10079                let status_colors = cx.theme().status();
10080
10081                el.bg(status_colors.error_background)
10082                    .border_color(status_colors.error.opacity(0.6))
10083                    .pl_2()
10084                    .child(Icon::new(icons.error).color(Color::Error))
10085                    .cursor_default()
10086                    .hoverable_tooltip(move |_window, cx| {
10087                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10088                    })
10089            })
10090            .children(keybind)
10091            .child(
10092                Label::new(file_name)
10093                    .size(LabelSize::Small)
10094                    .buffer_font(cx)
10095                    .when(!has_keybind, |el| {
10096                        el.color(cx.theme().status().error.into()).strikethrough()
10097                    }),
10098            )
10099            .when(!has_keybind, |el| {
10100                el.child(
10101                    h_flex().ml_1().child(
10102                        Icon::new(IconName::Info)
10103                            .size(IconSize::Small)
10104                            .color(cx.theme().status().error.into()),
10105                    ),
10106                )
10107            })
10108            .child(
10109                div()
10110                    .mt(px(1.5))
10111                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10112            )
10113    }
10114
10115    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10116        let accent_color = cx.theme().colors().text_accent;
10117        let editor_bg_color = cx.theme().colors().editor_background;
10118        editor_bg_color.blend(accent_color.opacity(0.1))
10119    }
10120
10121    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10122        let accent_color = cx.theme().colors().text_accent;
10123        let editor_bg_color = cx.theme().colors().editor_background;
10124        editor_bg_color.blend(accent_color.opacity(0.6))
10125    }
10126    fn get_prediction_provider_icons(
10127        provider: &Option<RegisteredEditPredictionDelegate>,
10128        cx: &App,
10129    ) -> edit_prediction_types::EditPredictionIconSet {
10130        match provider {
10131            Some(provider) => provider.provider.icons(cx),
10132            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10133        }
10134    }
10135
10136    fn render_edit_prediction_cursor_popover(
10137        &self,
10138        min_width: Pixels,
10139        max_width: Pixels,
10140        cursor_point: Point,
10141        style: &EditorStyle,
10142        window: &mut Window,
10143        cx: &mut Context<Editor>,
10144    ) -> Option<AnyElement> {
10145        let provider = self.edit_prediction_provider.as_ref()?;
10146        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10147
10148        let is_refreshing = provider.provider.is_refreshing(cx);
10149
10150        fn pending_completion_container(icon: IconName) -> Div {
10151            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10152        }
10153
10154        let completion = match &self.active_edit_prediction {
10155            Some(prediction) => {
10156                if !self.has_visible_completions_menu() {
10157                    const RADIUS: Pixels = px(6.);
10158                    const BORDER_WIDTH: Pixels = px(1.);
10159                    let keybind_display = self.edit_prediction_keybind_display(
10160                        EditPredictionKeybindSurface::CursorPopoverCompact,
10161                        window,
10162                        cx,
10163                    );
10164
10165                    return Some(
10166                        h_flex()
10167                            .elevation_2(cx)
10168                            .border(BORDER_WIDTH)
10169                            .border_color(cx.theme().colors().border)
10170                            .when(keybind_display.missing_accept_keystroke, |el| {
10171                                el.border_color(cx.theme().status().error)
10172                            })
10173                            .rounded(RADIUS)
10174                            .rounded_tl(px(0.))
10175                            .overflow_hidden()
10176                            .child(div().px_1p5().child(match &prediction.completion {
10177                                EditPrediction::MoveWithin { target, snapshot } => {
10178                                    use text::ToPoint as _;
10179                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10180                                    {
10181                                        Icon::new(icons.down)
10182                                    } else {
10183                                        Icon::new(icons.up)
10184                                    }
10185                                }
10186                                EditPrediction::MoveOutside { .. } => {
10187                                    // TODO [zeta2] custom icon for external jump?
10188                                    Icon::new(icons.base)
10189                                }
10190                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10191                            }))
10192                            .child(
10193                                h_flex()
10194                                    .gap_1()
10195                                    .py_1()
10196                                    .px_2()
10197                                    .rounded_r(RADIUS - BORDER_WIDTH)
10198                                    .border_l_1()
10199                                    .border_color(cx.theme().colors().border)
10200                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10201                                    .when(keybind_display.show_hold_label, |el| {
10202                                        el.child(
10203                                            Label::new("Hold")
10204                                                .size(LabelSize::Small)
10205                                                .when(
10206                                                    keybind_display.missing_accept_keystroke,
10207                                                    |el| el.strikethrough(),
10208                                                )
10209                                                .line_height_style(LineHeightStyle::UiLabel),
10210                                        )
10211                                    })
10212                                    .id("edit_prediction_cursor_popover_keybind")
10213                                    .when(keybind_display.missing_accept_keystroke, |el| {
10214                                        let status_colors = cx.theme().status();
10215
10216                                        el.bg(status_colors.error_background)
10217                                            .border_color(status_colors.error.opacity(0.6))
10218                                            .child(Icon::new(IconName::Info).color(Color::Error))
10219                                            .cursor_default()
10220                                            .hoverable_tooltip(move |_window, cx| {
10221                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10222                                                    .into()
10223                                            })
10224                                    })
10225                                    .when_some(
10226                                        keybind_display.displayed_keystroke.as_ref(),
10227                                        |el, compact_keystroke| {
10228                                            el.child(self.render_edit_prediction_popover_keystroke(
10229                                                compact_keystroke,
10230                                                Color::Default,
10231                                                cx,
10232                                            ))
10233                                        },
10234                                    ),
10235                            )
10236                            .into_any(),
10237                    );
10238                }
10239
10240                self.render_edit_prediction_cursor_popover_preview(
10241                    prediction,
10242                    cursor_point,
10243                    style,
10244                    cx,
10245                )?
10246            }
10247
10248            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10249                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10250                    stale_completion,
10251                    cursor_point,
10252                    style,
10253                    cx,
10254                )?,
10255
10256                None => pending_completion_container(icons.base)
10257                    .child(Label::new("...").size(LabelSize::Small)),
10258            },
10259
10260            None => pending_completion_container(icons.base)
10261                .child(Label::new("...").size(LabelSize::Small)),
10262        };
10263
10264        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10265            completion
10266                .with_animation(
10267                    "loading-completion",
10268                    Animation::new(Duration::from_secs(2))
10269                        .repeat()
10270                        .with_easing(pulsating_between(0.4, 0.8)),
10271                    |label, delta| label.opacity(delta),
10272                )
10273                .into_any_element()
10274        } else {
10275            completion.into_any_element()
10276        };
10277
10278        let has_completion = self.active_edit_prediction.is_some();
10279        let keybind_display = self.edit_prediction_keybind_display(
10280            EditPredictionKeybindSurface::CursorPopoverExpanded,
10281            window,
10282            cx,
10283        );
10284
10285        Some(
10286            h_flex()
10287                .min_w(min_width)
10288                .max_w(max_width)
10289                .flex_1()
10290                .elevation_2(cx)
10291                .border_color(cx.theme().colors().border)
10292                .child(
10293                    div()
10294                        .flex_1()
10295                        .py_1()
10296                        .px_2()
10297                        .overflow_hidden()
10298                        .child(completion),
10299                )
10300                .when_some(
10301                    keybind_display.displayed_keystroke.as_ref(),
10302                    |el, keystroke| {
10303                        let key_color = if !has_completion {
10304                            Color::Muted
10305                        } else {
10306                            Color::Default
10307                        };
10308
10309                        if keybind_display.action == EditPredictionKeybindAction::Preview {
10310                            el.child(
10311                                h_flex()
10312                                    .h_full()
10313                                    .border_l_1()
10314                                    .rounded_r_lg()
10315                                    .border_color(cx.theme().colors().border)
10316                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10317                                    .gap_1()
10318                                    .py_1()
10319                                    .px_2()
10320                                    .child(self.render_edit_prediction_popover_keystroke(
10321                                        keystroke, key_color, cx,
10322                                    ))
10323                                    .child(Label::new("Preview").into_any_element())
10324                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10325                            )
10326                        } else {
10327                            el.child(
10328                                h_flex()
10329                                    .h_full()
10330                                    .border_l_1()
10331                                    .rounded_r_lg()
10332                                    .border_color(cx.theme().colors().border)
10333                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10334                                    .gap_1()
10335                                    .py_1()
10336                                    .px_2()
10337                                    .child(self.render_edit_prediction_popover_keystroke(
10338                                        keystroke, key_color, cx,
10339                                    ))
10340                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10341                            )
10342                        }
10343                    },
10344                )
10345                .into_any(),
10346        )
10347    }
10348
10349    fn render_edit_prediction_cursor_popover_preview(
10350        &self,
10351        completion: &EditPredictionState,
10352        cursor_point: Point,
10353        style: &EditorStyle,
10354        cx: &mut Context<Editor>,
10355    ) -> Option<Div> {
10356        use text::ToPoint as _;
10357
10358        fn render_relative_row_jump(
10359            prefix: impl Into<String>,
10360            current_row: u32,
10361            target_row: u32,
10362        ) -> Div {
10363            let (row_diff, arrow) = if target_row < current_row {
10364                (current_row - target_row, IconName::ArrowUp)
10365            } else {
10366                (target_row - current_row, IconName::ArrowDown)
10367            };
10368
10369            h_flex()
10370                .child(
10371                    Label::new(format!("{}{}", prefix.into(), row_diff))
10372                        .color(Color::Muted)
10373                        .size(LabelSize::Small),
10374                )
10375                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10376        }
10377
10378        let supports_jump = self
10379            .edit_prediction_provider
10380            .as_ref()
10381            .map(|provider| provider.provider.supports_jump_to_edit())
10382            .unwrap_or(true);
10383
10384        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10385
10386        match &completion.completion {
10387            EditPrediction::MoveWithin {
10388                target, snapshot, ..
10389            } => {
10390                if !supports_jump {
10391                    return None;
10392                }
10393
10394                Some(
10395                    h_flex()
10396                        .px_2()
10397                        .gap_2()
10398                        .flex_1()
10399                        .child(
10400                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10401                                Icon::new(icons.down)
10402                            } else {
10403                                Icon::new(icons.up)
10404                            },
10405                        )
10406                        .child(Label::new("Jump to Edit")),
10407                )
10408            }
10409            EditPrediction::MoveOutside { snapshot, .. } => {
10410                let file_name = snapshot
10411                    .file()
10412                    .map(|file| file.file_name(cx))
10413                    .unwrap_or("untitled");
10414                Some(
10415                    h_flex()
10416                        .px_2()
10417                        .gap_2()
10418                        .flex_1()
10419                        .child(Icon::new(icons.base))
10420                        .child(Label::new(format!("Jump to {file_name}"))),
10421                )
10422            }
10423            EditPrediction::Edit {
10424                edits,
10425                edit_preview,
10426                snapshot,
10427                ..
10428            } => {
10429                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10430
10431                let (highlighted_edits, has_more_lines) =
10432                    if let Some(edit_preview) = edit_preview.as_ref() {
10433                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10434                            .first_line_preview()
10435                    } else {
10436                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10437                    };
10438
10439                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10440                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10441
10442                let preview = h_flex()
10443                    .gap_1()
10444                    .min_w_16()
10445                    .child(styled_text)
10446                    .when(has_more_lines, |parent| parent.child(""));
10447
10448                let left = if supports_jump && first_edit_row != cursor_point.row {
10449                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10450                        .into_any_element()
10451                } else {
10452                    Icon::new(icons.base).into_any_element()
10453                };
10454
10455                Some(
10456                    h_flex()
10457                        .h_full()
10458                        .flex_1()
10459                        .gap_2()
10460                        .pr_1()
10461                        .overflow_x_hidden()
10462                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10463                        .child(left)
10464                        .child(preview),
10465                )
10466            }
10467        }
10468    }
10469
10470    pub fn render_context_menu(
10471        &mut self,
10472        max_height_in_lines: u32,
10473        window: &mut Window,
10474        cx: &mut Context<Editor>,
10475    ) -> Option<AnyElement> {
10476        let menu = self.context_menu.borrow();
10477        let menu = menu.as_ref()?;
10478        if !menu.visible() {
10479            return None;
10480        };
10481        self.style
10482            .as_ref()
10483            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10484    }
10485
10486    fn render_context_menu_aside(
10487        &mut self,
10488        max_size: Size<Pixels>,
10489        window: &mut Window,
10490        cx: &mut Context<Editor>,
10491    ) -> Option<AnyElement> {
10492        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10493            if menu.visible() {
10494                menu.render_aside(max_size, window, cx)
10495            } else {
10496                None
10497            }
10498        })
10499    }
10500
10501    fn hide_context_menu(
10502        &mut self,
10503        window: &mut Window,
10504        cx: &mut Context<Self>,
10505    ) -> Option<CodeContextMenu> {
10506        cx.notify();
10507        self.completion_tasks.clear();
10508        let context_menu = self.context_menu.borrow_mut().take();
10509        self.stale_edit_prediction_in_menu.take();
10510        self.update_visible_edit_prediction(window, cx);
10511        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10512            && let Some(completion_provider) = &self.completion_provider
10513        {
10514            completion_provider.selection_changed(None, window, cx);
10515        }
10516        context_menu
10517    }
10518
10519    fn show_snippet_choices(
10520        &mut self,
10521        choices: &Vec<String>,
10522        selection: Range<Anchor>,
10523        cx: &mut Context<Self>,
10524    ) {
10525        let Some((_, buffer, _)) = self
10526            .buffer()
10527            .read(cx)
10528            .excerpt_containing(selection.start, cx)
10529        else {
10530            return;
10531        };
10532        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10533        else {
10534            return;
10535        };
10536        if buffer != end_buffer {
10537            log::error!("expected anchor range to have matching buffer IDs");
10538            return;
10539        }
10540
10541        let id = post_inc(&mut self.next_completion_id);
10542        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10543        let mut context_menu = self.context_menu.borrow_mut();
10544        let old_menu = context_menu.take();
10545        *context_menu = Some(CodeContextMenu::Completions(
10546            CompletionsMenu::new_snippet_choices(
10547                id,
10548                true,
10549                choices,
10550                selection,
10551                buffer,
10552                old_menu.map(|menu| menu.primary_scroll_handle()),
10553                snippet_sort_order,
10554            ),
10555        ));
10556    }
10557
10558    pub fn insert_snippet(
10559        &mut self,
10560        insertion_ranges: &[Range<MultiBufferOffset>],
10561        snippet: Snippet,
10562        window: &mut Window,
10563        cx: &mut Context<Self>,
10564    ) -> Result<()> {
10565        struct Tabstop<T> {
10566            is_end_tabstop: bool,
10567            ranges: Vec<Range<T>>,
10568            choices: Option<Vec<String>>,
10569        }
10570
10571        let tabstops = self.buffer.update(cx, |buffer, cx| {
10572            let snippet_text: Arc<str> = snippet.text.clone().into();
10573            let edits = insertion_ranges
10574                .iter()
10575                .cloned()
10576                .map(|range| (range, snippet_text.clone()));
10577            let autoindent_mode = AutoindentMode::Block {
10578                original_indent_columns: Vec::new(),
10579            };
10580            buffer.edit(edits, Some(autoindent_mode), cx);
10581
10582            let snapshot = &*buffer.read(cx);
10583            let snippet = &snippet;
10584            snippet
10585                .tabstops
10586                .iter()
10587                .map(|tabstop| {
10588                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10589                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10590                    });
10591                    let mut tabstop_ranges = tabstop
10592                        .ranges
10593                        .iter()
10594                        .flat_map(|tabstop_range| {
10595                            let mut delta = 0_isize;
10596                            insertion_ranges.iter().map(move |insertion_range| {
10597                                let insertion_start = insertion_range.start + delta;
10598                                delta += snippet.text.len() as isize
10599                                    - (insertion_range.end - insertion_range.start) as isize;
10600
10601                                let start =
10602                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10603                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10604                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10605                            })
10606                        })
10607                        .collect::<Vec<_>>();
10608                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10609
10610                    Tabstop {
10611                        is_end_tabstop,
10612                        ranges: tabstop_ranges,
10613                        choices: tabstop.choices.clone(),
10614                    }
10615                })
10616                .collect::<Vec<_>>()
10617        });
10618        if let Some(tabstop) = tabstops.first() {
10619            self.change_selections(Default::default(), window, cx, |s| {
10620                // Reverse order so that the first range is the newest created selection.
10621                // Completions will use it and autoscroll will prioritize it.
10622                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10623            });
10624
10625            if let Some(choices) = &tabstop.choices
10626                && let Some(selection) = tabstop.ranges.first()
10627            {
10628                self.show_snippet_choices(choices, selection.clone(), cx)
10629            }
10630
10631            // If we're already at the last tabstop and it's at the end of the snippet,
10632            // we're done, we don't need to keep the state around.
10633            if !tabstop.is_end_tabstop {
10634                let choices = tabstops
10635                    .iter()
10636                    .map(|tabstop| tabstop.choices.clone())
10637                    .collect();
10638
10639                let ranges = tabstops
10640                    .into_iter()
10641                    .map(|tabstop| tabstop.ranges)
10642                    .collect::<Vec<_>>();
10643
10644                self.snippet_stack.push(SnippetState {
10645                    active_index: 0,
10646                    ranges,
10647                    choices,
10648                });
10649            }
10650
10651            // Check whether the just-entered snippet ends with an auto-closable bracket.
10652            if self.autoclose_regions.is_empty() {
10653                let snapshot = self.buffer.read(cx).snapshot(cx);
10654                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10655                    let selection_head = selection.head();
10656                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10657                        continue;
10658                    };
10659
10660                    let mut bracket_pair = None;
10661                    let max_lookup_length = scope
10662                        .brackets()
10663                        .map(|(pair, _)| {
10664                            pair.start
10665                                .as_str()
10666                                .chars()
10667                                .count()
10668                                .max(pair.end.as_str().chars().count())
10669                        })
10670                        .max();
10671                    if let Some(max_lookup_length) = max_lookup_length {
10672                        let next_text = snapshot
10673                            .chars_at(selection_head)
10674                            .take(max_lookup_length)
10675                            .collect::<String>();
10676                        let prev_text = snapshot
10677                            .reversed_chars_at(selection_head)
10678                            .take(max_lookup_length)
10679                            .collect::<String>();
10680
10681                        for (pair, enabled) in scope.brackets() {
10682                            if enabled
10683                                && pair.close
10684                                && prev_text.starts_with(pair.start.as_str())
10685                                && next_text.starts_with(pair.end.as_str())
10686                            {
10687                                bracket_pair = Some(pair.clone());
10688                                break;
10689                            }
10690                        }
10691                    }
10692
10693                    if let Some(pair) = bracket_pair {
10694                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10695                        let autoclose_enabled =
10696                            self.use_autoclose && snapshot_settings.use_autoclose;
10697                        if autoclose_enabled {
10698                            let start = snapshot.anchor_after(selection_head);
10699                            let end = snapshot.anchor_after(selection_head);
10700                            self.autoclose_regions.push(AutocloseRegion {
10701                                selection_id: selection.id,
10702                                range: start..end,
10703                                pair,
10704                            });
10705                        }
10706                    }
10707                }
10708            }
10709        }
10710        Ok(())
10711    }
10712
10713    pub fn move_to_next_snippet_tabstop(
10714        &mut self,
10715        window: &mut Window,
10716        cx: &mut Context<Self>,
10717    ) -> bool {
10718        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10719    }
10720
10721    pub fn move_to_prev_snippet_tabstop(
10722        &mut self,
10723        window: &mut Window,
10724        cx: &mut Context<Self>,
10725    ) -> bool {
10726        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10727    }
10728
10729    pub fn move_to_snippet_tabstop(
10730        &mut self,
10731        bias: Bias,
10732        window: &mut Window,
10733        cx: &mut Context<Self>,
10734    ) -> bool {
10735        if let Some(mut snippet) = self.snippet_stack.pop() {
10736            match bias {
10737                Bias::Left => {
10738                    if snippet.active_index > 0 {
10739                        snippet.active_index -= 1;
10740                    } else {
10741                        self.snippet_stack.push(snippet);
10742                        return false;
10743                    }
10744                }
10745                Bias::Right => {
10746                    if snippet.active_index + 1 < snippet.ranges.len() {
10747                        snippet.active_index += 1;
10748                    } else {
10749                        self.snippet_stack.push(snippet);
10750                        return false;
10751                    }
10752                }
10753            }
10754            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10755                self.change_selections(Default::default(), window, cx, |s| {
10756                    // Reverse order so that the first range is the newest created selection.
10757                    // Completions will use it and autoscroll will prioritize it.
10758                    s.select_ranges(current_ranges.iter().rev().cloned())
10759                });
10760
10761                if let Some(choices) = &snippet.choices[snippet.active_index]
10762                    && let Some(selection) = current_ranges.first()
10763                {
10764                    self.show_snippet_choices(choices, selection.clone(), cx);
10765                }
10766
10767                // If snippet state is not at the last tabstop, push it back on the stack
10768                if snippet.active_index + 1 < snippet.ranges.len() {
10769                    self.snippet_stack.push(snippet);
10770                }
10771                return true;
10772            }
10773        }
10774
10775        false
10776    }
10777
10778    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10779        self.transact(window, cx, |this, window, cx| {
10780            this.select_all(&SelectAll, window, cx);
10781            this.insert("", window, cx);
10782        });
10783    }
10784
10785    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10786        if self.read_only(cx) {
10787            return;
10788        }
10789        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10790        self.transact(window, cx, |this, window, cx| {
10791            this.select_autoclose_pair(window, cx);
10792
10793            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10794
10795            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10796            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10797            for selection in &mut selections {
10798                if selection.is_empty() {
10799                    let old_head = selection.head();
10800                    let mut new_head =
10801                        movement::left(&display_map, old_head.to_display_point(&display_map))
10802                            .to_point(&display_map);
10803                    if let Some((buffer, line_buffer_range)) = display_map
10804                        .buffer_snapshot()
10805                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10806                    {
10807                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10808                        let indent_len = match indent_size.kind {
10809                            IndentKind::Space => {
10810                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10811                            }
10812                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10813                        };
10814                        if old_head.column <= indent_size.len && old_head.column > 0 {
10815                            let indent_len = indent_len.get();
10816                            new_head = cmp::min(
10817                                new_head,
10818                                MultiBufferPoint::new(
10819                                    old_head.row,
10820                                    ((old_head.column - 1) / indent_len) * indent_len,
10821                                ),
10822                            );
10823                        }
10824                    }
10825
10826                    selection.set_head(new_head, SelectionGoal::None);
10827                }
10828            }
10829
10830            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10831            this.insert("", window, cx);
10832            linked_edits.apply_with_left_expansion(cx);
10833            this.refresh_edit_prediction(true, false, window, cx);
10834            refresh_linked_ranges(this, window, cx);
10835        });
10836    }
10837
10838    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10839        if self.read_only(cx) {
10840            return;
10841        }
10842        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10843        self.transact(window, cx, |this, window, cx| {
10844            this.change_selections(Default::default(), window, cx, |s| {
10845                s.move_with(&mut |map, selection| {
10846                    if selection.is_empty() {
10847                        let cursor = movement::right(map, selection.head());
10848                        selection.end = cursor;
10849                        selection.reversed = true;
10850                        selection.goal = SelectionGoal::None;
10851                    }
10852                })
10853            });
10854            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10855            this.insert("", window, cx);
10856            linked_edits.apply(cx);
10857            this.refresh_edit_prediction(true, false, window, cx);
10858            refresh_linked_ranges(this, window, cx);
10859        });
10860    }
10861
10862    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10863        if self.mode.is_single_line() {
10864            cx.propagate();
10865            return;
10866        }
10867
10868        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10869        if self.move_to_prev_snippet_tabstop(window, cx) {
10870            return;
10871        }
10872        self.outdent(&Outdent, window, cx);
10873    }
10874
10875    pub fn next_snippet_tabstop(
10876        &mut self,
10877        _: &NextSnippetTabstop,
10878        window: &mut Window,
10879        cx: &mut Context<Self>,
10880    ) {
10881        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10882            cx.propagate();
10883            return;
10884        }
10885
10886        if self.move_to_next_snippet_tabstop(window, cx) {
10887            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10888            return;
10889        }
10890        cx.propagate();
10891    }
10892
10893    pub fn previous_snippet_tabstop(
10894        &mut self,
10895        _: &PreviousSnippetTabstop,
10896        window: &mut Window,
10897        cx: &mut Context<Self>,
10898    ) {
10899        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10900            cx.propagate();
10901            return;
10902        }
10903
10904        if self.move_to_prev_snippet_tabstop(window, cx) {
10905            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10906            return;
10907        }
10908        cx.propagate();
10909    }
10910
10911    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10912        if self.mode.is_single_line() {
10913            cx.propagate();
10914            return;
10915        }
10916
10917        if self.move_to_next_snippet_tabstop(window, cx) {
10918            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10919            return;
10920        }
10921        if self.read_only(cx) {
10922            return;
10923        }
10924        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10925        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10926        let buffer = self.buffer.read(cx);
10927        let snapshot = buffer.snapshot(cx);
10928        let rows_iter = selections.iter().map(|s| s.head().row);
10929        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10930
10931        let has_some_cursor_in_whitespace = selections
10932            .iter()
10933            .filter(|selection| selection.is_empty())
10934            .any(|selection| {
10935                let cursor = selection.head();
10936                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10937                cursor.column < current_indent.len
10938            });
10939
10940        let mut edits = Vec::new();
10941        let mut prev_edited_row = 0;
10942        let mut row_delta = 0;
10943        for selection in &mut selections {
10944            if selection.start.row != prev_edited_row {
10945                row_delta = 0;
10946            }
10947            prev_edited_row = selection.end.row;
10948
10949            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10950            if selection.is_empty() {
10951                let cursor = selection.head();
10952                let settings = buffer.language_settings_at(cursor, cx);
10953                if settings.indent_list_on_tab {
10954                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10955                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10956                            row_delta = Self::indent_selection(
10957                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10958                            );
10959                            continue;
10960                        }
10961                    }
10962                }
10963            }
10964
10965            // If the selection is non-empty, then increase the indentation of the selected lines.
10966            if !selection.is_empty() {
10967                row_delta =
10968                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10969                continue;
10970            }
10971
10972            let cursor = selection.head();
10973            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10974            if let Some(suggested_indent) =
10975                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10976            {
10977                // Don't do anything if already at suggested indent
10978                // and there is any other cursor which is not
10979                if has_some_cursor_in_whitespace
10980                    && cursor.column == current_indent.len
10981                    && current_indent.len == suggested_indent.len
10982                {
10983                    continue;
10984                }
10985
10986                // Adjust line and move cursor to suggested indent
10987                // if cursor is not at suggested indent
10988                if cursor.column < suggested_indent.len
10989                    && cursor.column <= current_indent.len
10990                    && current_indent.len <= suggested_indent.len
10991                {
10992                    selection.start = Point::new(cursor.row, suggested_indent.len);
10993                    selection.end = selection.start;
10994                    if row_delta == 0 {
10995                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10996                            cursor.row,
10997                            current_indent,
10998                            suggested_indent,
10999                        ));
11000                        row_delta = suggested_indent.len - current_indent.len;
11001                    }
11002                    continue;
11003                }
11004
11005                // If current indent is more than suggested indent
11006                // only move cursor to current indent and skip indent
11007                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
11008                    selection.start = Point::new(cursor.row, current_indent.len);
11009                    selection.end = selection.start;
11010                    continue;
11011                }
11012            }
11013
11014            // Otherwise, insert a hard or soft tab.
11015            let settings = buffer.language_settings_at(cursor, cx);
11016            let tab_size = if settings.hard_tabs {
11017                IndentSize::tab()
11018            } else {
11019                let tab_size = settings.tab_size.get();
11020                let indent_remainder = snapshot
11021                    .text_for_range(Point::new(cursor.row, 0)..cursor)
11022                    .flat_map(str::chars)
11023                    .fold(row_delta % tab_size, |counter: u32, c| {
11024                        if c == '\t' {
11025                            0
11026                        } else {
11027                            (counter + 1) % tab_size
11028                        }
11029                    });
11030
11031                let chars_to_next_tab_stop = tab_size - indent_remainder;
11032                IndentSize::spaces(chars_to_next_tab_stop)
11033            };
11034            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
11035            selection.end = selection.start;
11036            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
11037            row_delta += tab_size.len;
11038        }
11039
11040        self.transact(window, cx, |this, window, cx| {
11041            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11042            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11043            this.refresh_edit_prediction(true, false, window, cx);
11044        });
11045    }
11046
11047    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
11048        if self.read_only(cx) {
11049            return;
11050        }
11051        if self.mode.is_single_line() {
11052            cx.propagate();
11053            return;
11054        }
11055
11056        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11057        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11058        let mut prev_edited_row = 0;
11059        let mut row_delta = 0;
11060        let mut edits = Vec::new();
11061        let buffer = self.buffer.read(cx);
11062        let snapshot = buffer.snapshot(cx);
11063        for selection in &mut selections {
11064            if selection.start.row != prev_edited_row {
11065                row_delta = 0;
11066            }
11067            prev_edited_row = selection.end.row;
11068
11069            row_delta =
11070                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11071        }
11072
11073        self.transact(window, cx, |this, window, cx| {
11074            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11075            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11076        });
11077    }
11078
11079    fn indent_selection(
11080        buffer: &MultiBuffer,
11081        snapshot: &MultiBufferSnapshot,
11082        selection: &mut Selection<Point>,
11083        edits: &mut Vec<(Range<Point>, String)>,
11084        delta_for_start_row: u32,
11085        cx: &App,
11086    ) -> u32 {
11087        let settings = buffer.language_settings_at(selection.start, cx);
11088        let tab_size = settings.tab_size.get();
11089        let indent_kind = if settings.hard_tabs {
11090            IndentKind::Tab
11091        } else {
11092            IndentKind::Space
11093        };
11094        let mut start_row = selection.start.row;
11095        let mut end_row = selection.end.row + 1;
11096
11097        // If a selection ends at the beginning of a line, don't indent
11098        // that last line.
11099        if selection.end.column == 0 && selection.end.row > selection.start.row {
11100            end_row -= 1;
11101        }
11102
11103        // Avoid re-indenting a row that has already been indented by a
11104        // previous selection, but still update this selection's column
11105        // to reflect that indentation.
11106        if delta_for_start_row > 0 {
11107            start_row += 1;
11108            selection.start.column += delta_for_start_row;
11109            if selection.end.row == selection.start.row {
11110                selection.end.column += delta_for_start_row;
11111            }
11112        }
11113
11114        let mut delta_for_end_row = 0;
11115        let has_multiple_rows = start_row + 1 != end_row;
11116        for row in start_row..end_row {
11117            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11118            let indent_delta = match (current_indent.kind, indent_kind) {
11119                (IndentKind::Space, IndentKind::Space) => {
11120                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11121                    IndentSize::spaces(columns_to_next_tab_stop)
11122                }
11123                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11124                (_, IndentKind::Tab) => IndentSize::tab(),
11125            };
11126
11127            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11128                0
11129            } else {
11130                selection.start.column
11131            };
11132            let row_start = Point::new(row, start);
11133            edits.push((
11134                row_start..row_start,
11135                indent_delta.chars().collect::<String>(),
11136            ));
11137
11138            // Update this selection's endpoints to reflect the indentation.
11139            if row == selection.start.row {
11140                selection.start.column += indent_delta.len;
11141            }
11142            if row == selection.end.row {
11143                selection.end.column += indent_delta.len;
11144                delta_for_end_row = indent_delta.len;
11145            }
11146        }
11147
11148        if selection.start.row == selection.end.row {
11149            delta_for_start_row + delta_for_end_row
11150        } else {
11151            delta_for_end_row
11152        }
11153    }
11154
11155    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11156        if self.read_only(cx) {
11157            return;
11158        }
11159        if self.mode.is_single_line() {
11160            cx.propagate();
11161            return;
11162        }
11163
11164        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11165        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11166        let selections = self.selections.all::<Point>(&display_map);
11167        let mut deletion_ranges = Vec::new();
11168        let mut last_outdent = None;
11169        {
11170            let buffer = self.buffer.read(cx);
11171            let snapshot = buffer.snapshot(cx);
11172            for selection in &selections {
11173                let settings = buffer.language_settings_at(selection.start, cx);
11174                let tab_size = settings.tab_size.get();
11175                let mut rows = selection.spanned_rows(false, &display_map);
11176
11177                // Avoid re-outdenting a row that has already been outdented by a
11178                // previous selection.
11179                if let Some(last_row) = last_outdent
11180                    && last_row == rows.start
11181                {
11182                    rows.start = rows.start.next_row();
11183                }
11184                let has_multiple_rows = rows.len() > 1;
11185                for row in rows.iter_rows() {
11186                    let indent_size = snapshot.indent_size_for_line(row);
11187                    if indent_size.len > 0 {
11188                        let deletion_len = match indent_size.kind {
11189                            IndentKind::Space => {
11190                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11191                                if columns_to_prev_tab_stop == 0 {
11192                                    tab_size
11193                                } else {
11194                                    columns_to_prev_tab_stop
11195                                }
11196                            }
11197                            IndentKind::Tab => 1,
11198                        };
11199                        let start = if has_multiple_rows
11200                            || deletion_len > selection.start.column
11201                            || indent_size.len < selection.start.column
11202                        {
11203                            0
11204                        } else {
11205                            selection.start.column - deletion_len
11206                        };
11207                        deletion_ranges.push(
11208                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11209                        );
11210                        last_outdent = Some(row);
11211                    }
11212                }
11213            }
11214        }
11215
11216        self.transact(window, cx, |this, window, cx| {
11217            this.buffer.update(cx, |buffer, cx| {
11218                let empty_str: Arc<str> = Arc::default();
11219                buffer.edit(
11220                    deletion_ranges
11221                        .into_iter()
11222                        .map(|range| (range, empty_str.clone())),
11223                    None,
11224                    cx,
11225                );
11226            });
11227            let selections = this
11228                .selections
11229                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11230            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11231        });
11232    }
11233
11234    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11235        if self.read_only(cx) {
11236            return;
11237        }
11238        if self.mode.is_single_line() {
11239            cx.propagate();
11240            return;
11241        }
11242
11243        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11244        let selections = self
11245            .selections
11246            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11247            .into_iter()
11248            .map(|s| s.range());
11249
11250        self.transact(window, cx, |this, window, cx| {
11251            this.buffer.update(cx, |buffer, cx| {
11252                buffer.autoindent_ranges(selections, cx);
11253            });
11254            let selections = this
11255                .selections
11256                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11257            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11258        });
11259    }
11260
11261    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11262        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11263        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11264        let selections = self.selections.all::<Point>(&display_map);
11265
11266        let mut new_cursors = Vec::new();
11267        let mut edit_ranges = Vec::new();
11268        let mut selections = selections.iter().peekable();
11269        while let Some(selection) = selections.next() {
11270            let mut rows = selection.spanned_rows(false, &display_map);
11271
11272            // Accumulate contiguous regions of rows that we want to delete.
11273            while let Some(next_selection) = selections.peek() {
11274                let next_rows = next_selection.spanned_rows(false, &display_map);
11275                if next_rows.start <= rows.end {
11276                    rows.end = next_rows.end;
11277                    selections.next().unwrap();
11278                } else {
11279                    break;
11280                }
11281            }
11282
11283            let buffer = display_map.buffer_snapshot();
11284            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11285            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11286                // If there's a line after the range, delete the \n from the end of the row range
11287                (
11288                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11289                    rows.end,
11290                )
11291            } else {
11292                // If there isn't a line after the range, delete the \n from the line before the
11293                // start of the row range
11294                edit_start = edit_start.saturating_sub_usize(1);
11295                (buffer.len(), rows.start.previous_row())
11296            };
11297
11298            let text_layout_details = self.text_layout_details(window, cx);
11299            let x = display_map.x_for_display_point(
11300                selection.head().to_display_point(&display_map),
11301                &text_layout_details,
11302            );
11303            let row = Point::new(target_row.0, 0)
11304                .to_display_point(&display_map)
11305                .row();
11306            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11307
11308            new_cursors.push((
11309                selection.id,
11310                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11311                SelectionGoal::None,
11312            ));
11313            edit_ranges.push(edit_start..edit_end);
11314        }
11315
11316        self.transact(window, cx, |this, window, cx| {
11317            let buffer = this.buffer.update(cx, |buffer, cx| {
11318                let empty_str: Arc<str> = Arc::default();
11319                buffer.edit(
11320                    edit_ranges
11321                        .into_iter()
11322                        .map(|range| (range, empty_str.clone())),
11323                    None,
11324                    cx,
11325                );
11326                buffer.snapshot(cx)
11327            });
11328            let new_selections = new_cursors
11329                .into_iter()
11330                .map(|(id, cursor, goal)| {
11331                    let cursor = cursor.to_point(&buffer);
11332                    Selection {
11333                        id,
11334                        start: cursor,
11335                        end: cursor,
11336                        reversed: false,
11337                        goal,
11338                    }
11339                })
11340                .collect();
11341
11342            this.change_selections(Default::default(), window, cx, |s| {
11343                s.select(new_selections);
11344            });
11345        });
11346    }
11347
11348    pub fn join_lines_impl(
11349        &mut self,
11350        insert_whitespace: bool,
11351        window: &mut Window,
11352        cx: &mut Context<Self>,
11353    ) {
11354        if self.read_only(cx) {
11355            return;
11356        }
11357        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11358        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11359            let start = MultiBufferRow(selection.start.row);
11360            // Treat single line selections as if they include the next line. Otherwise this action
11361            // would do nothing for single line selections individual cursors.
11362            let end = if selection.start.row == selection.end.row {
11363                MultiBufferRow(selection.start.row + 1)
11364            } else if selection.end.column == 0 {
11365                // If the selection ends at the start of a line, it's logically at the end of the
11366                // previous line (plus its newline).
11367                // Don't include the end line unless there's only one line selected.
11368                if selection.start.row + 1 == selection.end.row {
11369                    MultiBufferRow(selection.end.row)
11370                } else {
11371                    MultiBufferRow(selection.end.row - 1)
11372                }
11373            } else {
11374                MultiBufferRow(selection.end.row)
11375            };
11376
11377            if let Some(last_row_range) = row_ranges.last_mut()
11378                && start <= last_row_range.end
11379            {
11380                last_row_range.end = end;
11381                continue;
11382            }
11383            row_ranges.push(start..end);
11384        }
11385
11386        let snapshot = self.buffer.read(cx).snapshot(cx);
11387        let mut cursor_positions = Vec::new();
11388        for row_range in &row_ranges {
11389            let anchor = snapshot.anchor_before(Point::new(
11390                row_range.end.previous_row().0,
11391                snapshot.line_len(row_range.end.previous_row()),
11392            ));
11393            cursor_positions.push(anchor..anchor);
11394        }
11395
11396        self.transact(window, cx, |this, window, cx| {
11397            for row_range in row_ranges.into_iter().rev() {
11398                for row in row_range.iter_rows().rev() {
11399                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11400                    let next_line_row = row.next_row();
11401                    let indent = snapshot.indent_size_for_line(next_line_row);
11402                    let mut join_start_column = indent.len;
11403
11404                    if let Some(language_scope) =
11405                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11406                    {
11407                        let line_end =
11408                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11409                        let line_text_after_indent = snapshot
11410                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11411                            .collect::<String>();
11412
11413                        if !line_text_after_indent.is_empty() {
11414                            let block_prefix = language_scope
11415                                .block_comment()
11416                                .map(|c| c.prefix.as_ref())
11417                                .filter(|p| !p.is_empty());
11418                            let doc_prefix = language_scope
11419                                .documentation_comment()
11420                                .map(|c| c.prefix.as_ref())
11421                                .filter(|p| !p.is_empty());
11422                            let all_prefixes = language_scope
11423                                .line_comment_prefixes()
11424                                .iter()
11425                                .map(|p| p.as_ref())
11426                                .chain(block_prefix)
11427                                .chain(doc_prefix)
11428                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11429
11430                            let mut longest_prefix_len = None;
11431                            for prefix in all_prefixes {
11432                                let trimmed = prefix.trim_end();
11433                                if line_text_after_indent.starts_with(trimmed) {
11434                                    let candidate_len =
11435                                        if line_text_after_indent.starts_with(prefix) {
11436                                            prefix.len()
11437                                        } else {
11438                                            trimmed.len()
11439                                        };
11440                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11441                                        longest_prefix_len = Some(candidate_len);
11442                                    }
11443                                }
11444                            }
11445
11446                            if let Some(prefix_len) = longest_prefix_len {
11447                                join_start_column =
11448                                    join_start_column.saturating_add(prefix_len as u32);
11449                            }
11450                        }
11451                    }
11452
11453                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11454
11455                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11456                        && insert_whitespace
11457                    {
11458                        " "
11459                    } else {
11460                        ""
11461                    };
11462
11463                    this.buffer.update(cx, |buffer, cx| {
11464                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11465                    });
11466                }
11467            }
11468
11469            this.change_selections(Default::default(), window, cx, |s| {
11470                s.select_anchor_ranges(cursor_positions)
11471            });
11472        });
11473    }
11474
11475    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11476        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11477        self.join_lines_impl(true, window, cx);
11478    }
11479
11480    pub fn sort_lines_case_sensitive(
11481        &mut self,
11482        _: &SortLinesCaseSensitive,
11483        window: &mut Window,
11484        cx: &mut Context<Self>,
11485    ) {
11486        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11487    }
11488
11489    pub fn sort_lines_by_length(
11490        &mut self,
11491        _: &SortLinesByLength,
11492        window: &mut Window,
11493        cx: &mut Context<Self>,
11494    ) {
11495        self.manipulate_immutable_lines(window, cx, |lines| {
11496            lines.sort_by_key(|&line| line.chars().count())
11497        })
11498    }
11499
11500    pub fn sort_lines_case_insensitive(
11501        &mut self,
11502        _: &SortLinesCaseInsensitive,
11503        window: &mut Window,
11504        cx: &mut Context<Self>,
11505    ) {
11506        self.manipulate_immutable_lines(window, cx, |lines| {
11507            lines.sort_by_key(|line| line.to_lowercase())
11508        })
11509    }
11510
11511    pub fn unique_lines_case_insensitive(
11512        &mut self,
11513        _: &UniqueLinesCaseInsensitive,
11514        window: &mut Window,
11515        cx: &mut Context<Self>,
11516    ) {
11517        self.manipulate_immutable_lines(window, cx, |lines| {
11518            let mut seen = HashSet::default();
11519            lines.retain(|line| seen.insert(line.to_lowercase()));
11520        })
11521    }
11522
11523    pub fn unique_lines_case_sensitive(
11524        &mut self,
11525        _: &UniqueLinesCaseSensitive,
11526        window: &mut Window,
11527        cx: &mut Context<Self>,
11528    ) {
11529        self.manipulate_immutable_lines(window, cx, |lines| {
11530            let mut seen = HashSet::default();
11531            lines.retain(|line| seen.insert(*line));
11532        })
11533    }
11534
11535    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11536        let snapshot = self.buffer.read(cx).snapshot(cx);
11537        for selection in self.selections.disjoint_anchors_arc().iter() {
11538            if snapshot
11539                .language_at(selection.start)
11540                .and_then(|lang| lang.config().wrap_characters.as_ref())
11541                .is_some()
11542            {
11543                return true;
11544            }
11545        }
11546        false
11547    }
11548
11549    fn wrap_selections_in_tag(
11550        &mut self,
11551        _: &WrapSelectionsInTag,
11552        window: &mut Window,
11553        cx: &mut Context<Self>,
11554    ) {
11555        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11556
11557        let snapshot = self.buffer.read(cx).snapshot(cx);
11558
11559        let mut edits = Vec::new();
11560        let mut boundaries = Vec::new();
11561
11562        for selection in self
11563            .selections
11564            .all_adjusted(&self.display_snapshot(cx))
11565            .iter()
11566        {
11567            let Some(wrap_config) = snapshot
11568                .language_at(selection.start)
11569                .and_then(|lang| lang.config().wrap_characters.clone())
11570            else {
11571                continue;
11572            };
11573
11574            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11575            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11576
11577            let start_before = snapshot.anchor_before(selection.start);
11578            let end_after = snapshot.anchor_after(selection.end);
11579
11580            edits.push((start_before..start_before, open_tag));
11581            edits.push((end_after..end_after, close_tag));
11582
11583            boundaries.push((
11584                start_before,
11585                end_after,
11586                wrap_config.start_prefix.len(),
11587                wrap_config.end_suffix.len(),
11588            ));
11589        }
11590
11591        if edits.is_empty() {
11592            return;
11593        }
11594
11595        self.transact(window, cx, |this, window, cx| {
11596            let buffer = this.buffer.update(cx, |buffer, cx| {
11597                buffer.edit(edits, None, cx);
11598                buffer.snapshot(cx)
11599            });
11600
11601            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11602            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11603                boundaries.into_iter()
11604            {
11605                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11606                let close_offset = end_after
11607                    .to_offset(&buffer)
11608                    .saturating_sub_usize(end_suffix_len);
11609                new_selections.push(open_offset..open_offset);
11610                new_selections.push(close_offset..close_offset);
11611            }
11612
11613            this.change_selections(Default::default(), window, cx, |s| {
11614                s.select_ranges(new_selections);
11615            });
11616
11617            this.request_autoscroll(Autoscroll::fit(), cx);
11618        });
11619    }
11620
11621    pub fn toggle_read_only(
11622        &mut self,
11623        _: &workspace::ToggleReadOnlyFile,
11624        _: &mut Window,
11625        cx: &mut Context<Self>,
11626    ) {
11627        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11628            buffer.update(cx, |buffer, cx| {
11629                buffer.set_capability(
11630                    match buffer.capability() {
11631                        Capability::ReadWrite => Capability::Read,
11632                        Capability::Read => Capability::ReadWrite,
11633                        Capability::ReadOnly => Capability::ReadOnly,
11634                    },
11635                    cx,
11636                );
11637            })
11638        }
11639    }
11640
11641    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11642        let Some(project) = self.project.clone() else {
11643            return;
11644        };
11645        let task = self.reload(project, window, cx);
11646        self.detach_and_notify_err(task, window, cx);
11647    }
11648
11649    pub fn restore_file(
11650        &mut self,
11651        _: &::git::RestoreFile,
11652        window: &mut Window,
11653        cx: &mut Context<Self>,
11654    ) {
11655        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11656        let mut buffer_ids = HashSet::default();
11657        let snapshot = self.buffer().read(cx).snapshot(cx);
11658        for selection in self
11659            .selections
11660            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11661        {
11662            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11663        }
11664
11665        let buffer = self.buffer().read(cx);
11666        let ranges = buffer_ids
11667            .into_iter()
11668            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11669            .collect::<Vec<_>>();
11670
11671        self.restore_hunks_in_ranges(ranges, window, cx);
11672    }
11673
11674    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11675        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11676        let selections = self
11677            .selections
11678            .all(&self.display_snapshot(cx))
11679            .into_iter()
11680            .map(|s| s.range())
11681            .collect();
11682        self.restore_hunks_in_ranges(selections, window, cx);
11683    }
11684
11685    /// Restores the diff hunks in the editor's selections and moves the cursor
11686    /// to the next diff hunk. Wraps around to the beginning of the buffer if
11687    /// not all diff hunks are expanded.
11688    pub fn restore_and_next(
11689        &mut self,
11690        _: &::git::RestoreAndNext,
11691        window: &mut Window,
11692        cx: &mut Context<Self>,
11693    ) {
11694        let selections = self
11695            .selections
11696            .all(&self.display_snapshot(cx))
11697            .into_iter()
11698            .map(|selection| selection.range())
11699            .collect();
11700
11701        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11702        self.restore_hunks_in_ranges(selections, window, cx);
11703
11704        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
11705        let wrap_around = !all_diff_hunks_expanded;
11706        let snapshot = self.snapshot(window, cx);
11707        let position = self
11708            .selections
11709            .newest::<Point>(&snapshot.display_snapshot)
11710            .head();
11711
11712        self.go_to_hunk_before_or_after_position(
11713            &snapshot,
11714            position,
11715            Direction::Next,
11716            wrap_around,
11717            window,
11718            cx,
11719        );
11720    }
11721
11722    pub fn restore_hunks_in_ranges(
11723        &mut self,
11724        ranges: Vec<Range<Point>>,
11725        window: &mut Window,
11726        cx: &mut Context<Editor>,
11727    ) {
11728        if self.delegate_stage_and_restore {
11729            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11730            if !hunks.is_empty() {
11731                cx.emit(EditorEvent::RestoreRequested { hunks });
11732            }
11733            return;
11734        }
11735        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11736        self.transact(window, cx, |editor, window, cx| {
11737            editor.restore_diff_hunks(hunks, cx);
11738            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11739                selections.refresh()
11740            });
11741        });
11742    }
11743
11744    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11745        let mut revert_changes = HashMap::default();
11746        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11747        for (buffer_id, hunks) in &chunk_by {
11748            let hunks = hunks.collect::<Vec<_>>();
11749            for hunk in &hunks {
11750                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11751            }
11752            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11753        }
11754        if !revert_changes.is_empty() {
11755            self.buffer().update(cx, |multi_buffer, cx| {
11756                for (buffer_id, changes) in revert_changes {
11757                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11758                        buffer.update(cx, |buffer, cx| {
11759                            buffer.edit(
11760                                changes
11761                                    .into_iter()
11762                                    .map(|(range, text)| (range, text.to_string())),
11763                                None,
11764                                cx,
11765                            );
11766                        });
11767                    }
11768                }
11769            });
11770        }
11771    }
11772
11773    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11774        if let Some(status) = self
11775            .addons
11776            .iter()
11777            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11778        {
11779            return Some(status);
11780        }
11781        self.project
11782            .as_ref()?
11783            .read(cx)
11784            .status_for_buffer_id(buffer_id, cx)
11785    }
11786
11787    pub fn open_active_item_in_terminal(
11788        &mut self,
11789        _: &OpenInTerminal,
11790        window: &mut Window,
11791        cx: &mut Context<Self>,
11792    ) {
11793        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11794            let project_path = buffer.read(cx).project_path(cx)?;
11795            let project = self.project()?.read(cx);
11796            let entry = project.entry_for_path(&project_path, cx)?;
11797            let parent = match &entry.canonical_path {
11798                Some(canonical_path) => canonical_path.to_path_buf(),
11799                None => project.absolute_path(&project_path, cx)?,
11800            }
11801            .parent()?
11802            .to_path_buf();
11803            Some(parent)
11804        }) {
11805            window.dispatch_action(
11806                OpenTerminal {
11807                    working_directory,
11808                    local: false,
11809                }
11810                .boxed_clone(),
11811                cx,
11812            );
11813        }
11814    }
11815
11816    fn set_breakpoint_context_menu(
11817        &mut self,
11818        display_row: DisplayRow,
11819        position: Option<Anchor>,
11820        clicked_point: gpui::Point<Pixels>,
11821        window: &mut Window,
11822        cx: &mut Context<Self>,
11823    ) {
11824        let source = self
11825            .buffer
11826            .read(cx)
11827            .snapshot(cx)
11828            .anchor_before(Point::new(display_row.0, 0u32));
11829
11830        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11831
11832        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11833            self,
11834            source,
11835            clicked_point,
11836            context_menu,
11837            window,
11838            cx,
11839        );
11840    }
11841
11842    fn add_edit_breakpoint_block(
11843        &mut self,
11844        anchor: Anchor,
11845        breakpoint: &Breakpoint,
11846        edit_action: BreakpointPromptEditAction,
11847        window: &mut Window,
11848        cx: &mut Context<Self>,
11849    ) {
11850        let weak_editor = cx.weak_entity();
11851        let bp_prompt = cx.new(|cx| {
11852            BreakpointPromptEditor::new(
11853                weak_editor,
11854                anchor,
11855                breakpoint.clone(),
11856                edit_action,
11857                window,
11858                cx,
11859            )
11860        });
11861
11862        let height = bp_prompt.update(cx, |this, cx| {
11863            this.prompt
11864                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11865        });
11866        let cloned_prompt = bp_prompt.clone();
11867        let blocks = vec![BlockProperties {
11868            style: BlockStyle::Sticky,
11869            placement: BlockPlacement::Above(anchor),
11870            height: Some(height),
11871            render: Arc::new(move |cx| {
11872                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11873                cloned_prompt.clone().into_any_element()
11874            }),
11875            priority: 0,
11876        }];
11877
11878        let focus_handle = bp_prompt.focus_handle(cx);
11879        window.focus(&focus_handle, cx);
11880
11881        let block_ids = self.insert_blocks(blocks, None, cx);
11882        bp_prompt.update(cx, |prompt, _| {
11883            prompt.add_block_ids(block_ids);
11884        });
11885    }
11886
11887    pub(crate) fn breakpoint_at_row(
11888        &self,
11889        row: u32,
11890        window: &mut Window,
11891        cx: &mut Context<Self>,
11892    ) -> Option<(Anchor, Breakpoint)> {
11893        let snapshot = self.snapshot(window, cx);
11894        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11895
11896        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11897    }
11898
11899    pub(crate) fn breakpoint_at_anchor(
11900        &self,
11901        breakpoint_position: Anchor,
11902        snapshot: &EditorSnapshot,
11903        cx: &mut Context<Self>,
11904    ) -> Option<(Anchor, Breakpoint)> {
11905        let buffer = self
11906            .buffer
11907            .read(cx)
11908            .buffer_for_anchor(breakpoint_position, cx)?;
11909
11910        let enclosing_excerpt = breakpoint_position.excerpt_id;
11911        let buffer_snapshot = buffer.read(cx).snapshot();
11912
11913        let row = buffer_snapshot
11914            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11915            .row;
11916
11917        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11918        let anchor_end = snapshot
11919            .buffer_snapshot()
11920            .anchor_after(Point::new(row, line_len));
11921
11922        self.breakpoint_store
11923            .as_ref()?
11924            .read_with(cx, |breakpoint_store, cx| {
11925                breakpoint_store
11926                    .breakpoints(
11927                        &buffer,
11928                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11929                        &buffer_snapshot,
11930                        cx,
11931                    )
11932                    .next()
11933                    .and_then(|(bp, _)| {
11934                        let breakpoint_row = buffer_snapshot
11935                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11936                            .row;
11937
11938                        if breakpoint_row == row {
11939                            snapshot
11940                                .buffer_snapshot()
11941                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11942                                .map(|position| (position, bp.bp.clone()))
11943                        } else {
11944                            None
11945                        }
11946                    })
11947            })
11948    }
11949
11950    pub fn edit_log_breakpoint(
11951        &mut self,
11952        _: &EditLogBreakpoint,
11953        window: &mut Window,
11954        cx: &mut Context<Self>,
11955    ) {
11956        if self.breakpoint_store.is_none() {
11957            return;
11958        }
11959
11960        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11961            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11962                message: None,
11963                state: BreakpointState::Enabled,
11964                condition: None,
11965                hit_condition: None,
11966            });
11967
11968            self.add_edit_breakpoint_block(
11969                anchor,
11970                &breakpoint,
11971                BreakpointPromptEditAction::Log,
11972                window,
11973                cx,
11974            );
11975        }
11976    }
11977
11978    fn breakpoints_at_cursors(
11979        &self,
11980        window: &mut Window,
11981        cx: &mut Context<Self>,
11982    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11983        let snapshot = self.snapshot(window, cx);
11984        let cursors = self
11985            .selections
11986            .disjoint_anchors_arc()
11987            .iter()
11988            .map(|selection| {
11989                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11990
11991                let breakpoint_position = self
11992                    .breakpoint_at_row(cursor_position.row, window, cx)
11993                    .map(|bp| bp.0)
11994                    .unwrap_or_else(|| {
11995                        snapshot
11996                            .display_snapshot
11997                            .buffer_snapshot()
11998                            .anchor_after(Point::new(cursor_position.row, 0))
11999                    });
12000
12001                let breakpoint = self
12002                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
12003                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
12004
12005                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
12006            })
12007            // 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.
12008            .collect::<HashMap<Anchor, _>>();
12009
12010        cursors.into_iter().collect()
12011    }
12012
12013    pub fn enable_breakpoint(
12014        &mut self,
12015        _: &crate::actions::EnableBreakpoint,
12016        window: &mut Window,
12017        cx: &mut Context<Self>,
12018    ) {
12019        if self.breakpoint_store.is_none() {
12020            return;
12021        }
12022
12023        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12024            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
12025                continue;
12026            };
12027            self.edit_breakpoint_at_anchor(
12028                anchor,
12029                breakpoint,
12030                BreakpointEditAction::InvertState,
12031                cx,
12032            );
12033        }
12034    }
12035
12036    pub fn align_selections(
12037        &mut self,
12038        _: &crate::actions::AlignSelections,
12039        window: &mut Window,
12040        cx: &mut Context<Self>,
12041    ) {
12042        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12043
12044        let display_snapshot = self.display_snapshot(cx);
12045
12046        struct CursorData {
12047            anchor: Anchor,
12048            point: Point,
12049        }
12050        let cursor_data: Vec<CursorData> = self
12051            .selections
12052            .disjoint_anchors()
12053            .iter()
12054            .map(|selection| {
12055                let anchor = if selection.reversed {
12056                    selection.head()
12057                } else {
12058                    selection.tail()
12059                };
12060                CursorData {
12061                    anchor: anchor,
12062                    point: anchor.to_point(&display_snapshot.buffer_snapshot()),
12063                }
12064            })
12065            .collect();
12066
12067        let rows_anchors_count: Vec<usize> = cursor_data
12068            .iter()
12069            .map(|cursor| cursor.point.row)
12070            .chunk_by(|&row| row)
12071            .into_iter()
12072            .map(|(_, group)| group.count())
12073            .collect();
12074        let max_columns = rows_anchors_count.iter().max().copied().unwrap_or(0);
12075        let mut rows_column_offset = vec![0; rows_anchors_count.len()];
12076        let mut edits = Vec::new();
12077
12078        for column_idx in 0..max_columns {
12079            let mut cursor_index = 0;
12080
12081            // Calculate target_column => position that the selections will go
12082            let mut target_column = 0;
12083            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12084                // Skip rows that don't have this column
12085                if column_idx >= *cursor_count {
12086                    cursor_index += cursor_count;
12087                    continue;
12088                }
12089
12090                let point = &cursor_data[cursor_index + column_idx].point;
12091                let adjusted_column = point.column + rows_column_offset[row_idx];
12092                if adjusted_column > target_column {
12093                    target_column = adjusted_column;
12094                }
12095                cursor_index += cursor_count;
12096            }
12097
12098            // Collect edits for this column
12099            cursor_index = 0;
12100            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12101                // Skip rows that don't have this column
12102                if column_idx >= *cursor_count {
12103                    cursor_index += *cursor_count;
12104                    continue;
12105                }
12106
12107                let point = &cursor_data[cursor_index + column_idx].point;
12108                let spaces_needed = target_column - point.column - rows_column_offset[row_idx];
12109                if spaces_needed > 0 {
12110                    let anchor = cursor_data[cursor_index + column_idx]
12111                        .anchor
12112                        .bias_left(&display_snapshot);
12113                    edits.push((anchor..anchor, " ".repeat(spaces_needed as usize)));
12114                }
12115                rows_column_offset[row_idx] += spaces_needed;
12116
12117                cursor_index += *cursor_count;
12118            }
12119        }
12120
12121        if !edits.is_empty() {
12122            self.transact(window, cx, |editor, _window, cx| {
12123                editor.edit(edits, cx);
12124            });
12125        }
12126    }
12127
12128    pub fn disable_breakpoint(
12129        &mut self,
12130        _: &crate::actions::DisableBreakpoint,
12131        window: &mut Window,
12132        cx: &mut Context<Self>,
12133    ) {
12134        if self.breakpoint_store.is_none() {
12135            return;
12136        }
12137
12138        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12139            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
12140                continue;
12141            };
12142            self.edit_breakpoint_at_anchor(
12143                anchor,
12144                breakpoint,
12145                BreakpointEditAction::InvertState,
12146                cx,
12147            );
12148        }
12149    }
12150
12151    pub fn toggle_breakpoint(
12152        &mut self,
12153        _: &crate::actions::ToggleBreakpoint,
12154        window: &mut Window,
12155        cx: &mut Context<Self>,
12156    ) {
12157        if self.breakpoint_store.is_none() {
12158            return;
12159        }
12160
12161        let snapshot = self.snapshot(window, cx);
12162        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12163            if self.gutter_breakpoint_indicator.0.is_some() {
12164                let display_row = anchor
12165                    .to_point(snapshot.buffer_snapshot())
12166                    .to_display_point(&snapshot.display_snapshot)
12167                    .row();
12168                self.update_breakpoint_collision_on_toggle(
12169                    display_row,
12170                    &BreakpointEditAction::Toggle,
12171                );
12172            }
12173
12174            if let Some(breakpoint) = breakpoint {
12175                self.edit_breakpoint_at_anchor(
12176                    anchor,
12177                    breakpoint,
12178                    BreakpointEditAction::Toggle,
12179                    cx,
12180                );
12181            } else {
12182                self.edit_breakpoint_at_anchor(
12183                    anchor,
12184                    Breakpoint::new_standard(),
12185                    BreakpointEditAction::Toggle,
12186                    cx,
12187                );
12188            }
12189        }
12190    }
12191
12192    fn update_breakpoint_collision_on_toggle(
12193        &mut self,
12194        display_row: DisplayRow,
12195        edit_action: &BreakpointEditAction,
12196    ) {
12197        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12198            if breakpoint_indicator.display_row == display_row
12199                && matches!(edit_action, BreakpointEditAction::Toggle)
12200            {
12201                breakpoint_indicator.collides_with_existing_breakpoint =
12202                    !breakpoint_indicator.collides_with_existing_breakpoint;
12203            }
12204        }
12205    }
12206
12207    pub fn edit_breakpoint_at_anchor(
12208        &mut self,
12209        breakpoint_position: Anchor,
12210        breakpoint: Breakpoint,
12211        edit_action: BreakpointEditAction,
12212        cx: &mut Context<Self>,
12213    ) {
12214        let Some(breakpoint_store) = &self.breakpoint_store else {
12215            return;
12216        };
12217
12218        let Some(buffer) = self
12219            .buffer
12220            .read(cx)
12221            .buffer_for_anchor(breakpoint_position, cx)
12222        else {
12223            return;
12224        };
12225
12226        breakpoint_store.update(cx, |breakpoint_store, cx| {
12227            breakpoint_store.toggle_breakpoint(
12228                buffer,
12229                BreakpointWithPosition {
12230                    position: breakpoint_position.text_anchor,
12231                    bp: breakpoint,
12232                },
12233                edit_action,
12234                cx,
12235            );
12236        });
12237
12238        cx.notify();
12239    }
12240
12241    #[cfg(any(test, feature = "test-support"))]
12242    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12243        self.breakpoint_store.clone()
12244    }
12245
12246    pub fn prepare_restore_change(
12247        &self,
12248        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12249        hunk: &MultiBufferDiffHunk,
12250        cx: &mut App,
12251    ) -> Option<()> {
12252        if hunk.is_created_file() {
12253            return None;
12254        }
12255        let buffer = self.buffer.read(cx);
12256        let diff = buffer.diff_for(hunk.buffer_id)?;
12257        let buffer = buffer.buffer(hunk.buffer_id)?;
12258        let buffer = buffer.read(cx);
12259        let original_text = diff
12260            .read(cx)
12261            .base_text(cx)
12262            .as_rope()
12263            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12264        let buffer_snapshot = buffer.snapshot();
12265        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12266        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12267            probe
12268                .0
12269                .start
12270                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12271                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12272        }) {
12273            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12274            Some(())
12275        } else {
12276            None
12277        }
12278    }
12279
12280    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12281        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12282    }
12283
12284    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12285        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12286    }
12287
12288    pub fn rotate_selections_forward(
12289        &mut self,
12290        _: &RotateSelectionsForward,
12291        window: &mut Window,
12292        cx: &mut Context<Self>,
12293    ) {
12294        self.rotate_selections(window, cx, false)
12295    }
12296
12297    pub fn rotate_selections_backward(
12298        &mut self,
12299        _: &RotateSelectionsBackward,
12300        window: &mut Window,
12301        cx: &mut Context<Self>,
12302    ) {
12303        self.rotate_selections(window, cx, true)
12304    }
12305
12306    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12307        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12308        let display_snapshot = self.display_snapshot(cx);
12309        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12310
12311        if selections.len() < 2 {
12312            return;
12313        }
12314
12315        let (edits, new_selections) = {
12316            let buffer = self.buffer.read(cx).read(cx);
12317            let has_selections = selections.iter().any(|s| !s.is_empty());
12318            if has_selections {
12319                let mut selected_texts: Vec<String> = selections
12320                    .iter()
12321                    .map(|selection| {
12322                        buffer
12323                            .text_for_range(selection.start..selection.end)
12324                            .collect()
12325                    })
12326                    .collect();
12327
12328                if reverse {
12329                    selected_texts.rotate_left(1);
12330                } else {
12331                    selected_texts.rotate_right(1);
12332                }
12333
12334                let mut offset_delta: i64 = 0;
12335                let mut new_selections = Vec::new();
12336                let edits: Vec<_> = selections
12337                    .iter()
12338                    .zip(selected_texts.iter())
12339                    .map(|(selection, new_text)| {
12340                        let old_len = (selection.end.0 - selection.start.0) as i64;
12341                        let new_len = new_text.len() as i64;
12342                        let adjusted_start =
12343                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12344                        let adjusted_end =
12345                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12346
12347                        new_selections.push(Selection {
12348                            id: selection.id,
12349                            start: adjusted_start,
12350                            end: adjusted_end,
12351                            reversed: selection.reversed,
12352                            goal: selection.goal,
12353                        });
12354
12355                        offset_delta += new_len - old_len;
12356                        (selection.start..selection.end, new_text.clone())
12357                    })
12358                    .collect();
12359                (edits, new_selections)
12360            } else {
12361                let mut all_rows: Vec<u32> = selections
12362                    .iter()
12363                    .map(|selection| buffer.offset_to_point(selection.start).row)
12364                    .collect();
12365                all_rows.sort_unstable();
12366                all_rows.dedup();
12367
12368                if all_rows.len() < 2 {
12369                    return;
12370                }
12371
12372                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12373                    .iter()
12374                    .map(|&row| {
12375                        let start = Point::new(row, 0);
12376                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12377                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12378                    })
12379                    .collect();
12380
12381                let mut line_texts: Vec<String> = line_ranges
12382                    .iter()
12383                    .map(|range| buffer.text_for_range(range.clone()).collect())
12384                    .collect();
12385
12386                if reverse {
12387                    line_texts.rotate_left(1);
12388                } else {
12389                    line_texts.rotate_right(1);
12390                }
12391
12392                let edits = line_ranges
12393                    .iter()
12394                    .zip(line_texts.iter())
12395                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12396                    .collect();
12397
12398                let num_rows = all_rows.len();
12399                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12400                    .iter()
12401                    .enumerate()
12402                    .map(|(i, &row)| (row, i))
12403                    .collect();
12404
12405                // Compute new line start offsets after rotation (handles CRLF)
12406                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12407                let first_line_start = line_ranges[0].start.0;
12408                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12409                for text in line_texts.iter().take(num_rows - 1) {
12410                    let prev_start = *new_line_starts.last().unwrap();
12411                    new_line_starts.push(prev_start + text.len() + newline_len);
12412                }
12413
12414                let new_selections = selections
12415                    .iter()
12416                    .map(|selection| {
12417                        let point = buffer.offset_to_point(selection.start);
12418                        let old_index = row_to_index[&point.row];
12419                        let new_index = if reverse {
12420                            (old_index + num_rows - 1) % num_rows
12421                        } else {
12422                            (old_index + 1) % num_rows
12423                        };
12424                        let new_offset =
12425                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12426                        Selection {
12427                            id: selection.id,
12428                            start: new_offset,
12429                            end: new_offset,
12430                            reversed: selection.reversed,
12431                            goal: selection.goal,
12432                        }
12433                    })
12434                    .collect();
12435
12436                (edits, new_selections)
12437            }
12438        };
12439
12440        self.transact(window, cx, |this, window, cx| {
12441            this.buffer.update(cx, |buffer, cx| {
12442                buffer.edit(edits, None, cx);
12443            });
12444            this.change_selections(Default::default(), window, cx, |s| {
12445                s.select(new_selections);
12446            });
12447        });
12448    }
12449
12450    fn manipulate_lines<M>(
12451        &mut self,
12452        window: &mut Window,
12453        cx: &mut Context<Self>,
12454        mut manipulate: M,
12455    ) where
12456        M: FnMut(&str) -> LineManipulationResult,
12457    {
12458        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12459
12460        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12461        let buffer = self.buffer.read(cx).snapshot(cx);
12462
12463        let mut edits = Vec::new();
12464
12465        let selections = self.selections.all::<Point>(&display_map);
12466        let mut selections = selections.iter().peekable();
12467        let mut contiguous_row_selections = Vec::new();
12468        let mut new_selections = Vec::new();
12469        let mut added_lines = 0;
12470        let mut removed_lines = 0;
12471
12472        while let Some(selection) = selections.next() {
12473            let (start_row, end_row) = consume_contiguous_rows(
12474                &mut contiguous_row_selections,
12475                selection,
12476                &display_map,
12477                &mut selections,
12478            );
12479
12480            let start_point = Point::new(start_row.0, 0);
12481            let end_point = Point::new(
12482                end_row.previous_row().0,
12483                buffer.line_len(end_row.previous_row()),
12484            );
12485            let text = buffer
12486                .text_for_range(start_point..end_point)
12487                .collect::<String>();
12488
12489            let LineManipulationResult {
12490                new_text,
12491                line_count_before,
12492                line_count_after,
12493            } = manipulate(&text);
12494
12495            edits.push((start_point..end_point, new_text));
12496
12497            // Selections must change based on added and removed line count
12498            let start_row =
12499                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12500            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12501            new_selections.push(Selection {
12502                id: selection.id,
12503                start: start_row,
12504                end: end_row,
12505                goal: SelectionGoal::None,
12506                reversed: selection.reversed,
12507            });
12508
12509            if line_count_after > line_count_before {
12510                added_lines += line_count_after - line_count_before;
12511            } else if line_count_before > line_count_after {
12512                removed_lines += line_count_before - line_count_after;
12513            }
12514        }
12515
12516        self.transact(window, cx, |this, window, cx| {
12517            let buffer = this.buffer.update(cx, |buffer, cx| {
12518                buffer.edit(edits, None, cx);
12519                buffer.snapshot(cx)
12520            });
12521
12522            // Recalculate offsets on newly edited buffer
12523            let new_selections = new_selections
12524                .iter()
12525                .map(|s| {
12526                    let start_point = Point::new(s.start.0, 0);
12527                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12528                    Selection {
12529                        id: s.id,
12530                        start: buffer.point_to_offset(start_point),
12531                        end: buffer.point_to_offset(end_point),
12532                        goal: s.goal,
12533                        reversed: s.reversed,
12534                    }
12535                })
12536                .collect();
12537
12538            this.change_selections(Default::default(), window, cx, |s| {
12539                s.select(new_selections);
12540            });
12541
12542            this.request_autoscroll(Autoscroll::fit(), cx);
12543        });
12544    }
12545
12546    fn manipulate_immutable_lines<Fn>(
12547        &mut self,
12548        window: &mut Window,
12549        cx: &mut Context<Self>,
12550        mut callback: Fn,
12551    ) where
12552        Fn: FnMut(&mut Vec<&str>),
12553    {
12554        self.manipulate_lines(window, cx, |text| {
12555            let mut lines: Vec<&str> = text.split('\n').collect();
12556            let line_count_before = lines.len();
12557
12558            callback(&mut lines);
12559
12560            LineManipulationResult {
12561                new_text: lines.join("\n"),
12562                line_count_before,
12563                line_count_after: lines.len(),
12564            }
12565        });
12566    }
12567
12568    fn manipulate_mutable_lines<Fn>(
12569        &mut self,
12570        window: &mut Window,
12571        cx: &mut Context<Self>,
12572        mut callback: Fn,
12573    ) where
12574        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12575    {
12576        self.manipulate_lines(window, cx, |text| {
12577            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12578            let line_count_before = lines.len();
12579
12580            callback(&mut lines);
12581
12582            LineManipulationResult {
12583                new_text: lines.join("\n"),
12584                line_count_before,
12585                line_count_after: lines.len(),
12586            }
12587        });
12588    }
12589
12590    pub fn convert_indentation_to_spaces(
12591        &mut self,
12592        _: &ConvertIndentationToSpaces,
12593        window: &mut Window,
12594        cx: &mut Context<Self>,
12595    ) {
12596        let settings = self.buffer.read(cx).language_settings(cx);
12597        let tab_size = settings.tab_size.get() as usize;
12598
12599        self.manipulate_mutable_lines(window, cx, |lines| {
12600            // Allocates a reasonably sized scratch buffer once for the whole loop
12601            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12602            // Avoids recomputing spaces that could be inserted many times
12603            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12604                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12605                .collect();
12606
12607            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12608                let mut chars = line.as_ref().chars();
12609                let mut col = 0;
12610                let mut changed = false;
12611
12612                for ch in chars.by_ref() {
12613                    match ch {
12614                        ' ' => {
12615                            reindented_line.push(' ');
12616                            col += 1;
12617                        }
12618                        '\t' => {
12619                            // \t are converted to spaces depending on the current column
12620                            let spaces_len = tab_size - (col % tab_size);
12621                            reindented_line.extend(&space_cache[spaces_len - 1]);
12622                            col += spaces_len;
12623                            changed = true;
12624                        }
12625                        _ => {
12626                            // If we dont append before break, the character is consumed
12627                            reindented_line.push(ch);
12628                            break;
12629                        }
12630                    }
12631                }
12632
12633                if !changed {
12634                    reindented_line.clear();
12635                    continue;
12636                }
12637                // Append the rest of the line and replace old reference with new one
12638                reindented_line.extend(chars);
12639                *line = Cow::Owned(reindented_line.clone());
12640                reindented_line.clear();
12641            }
12642        });
12643    }
12644
12645    pub fn convert_indentation_to_tabs(
12646        &mut self,
12647        _: &ConvertIndentationToTabs,
12648        window: &mut Window,
12649        cx: &mut Context<Self>,
12650    ) {
12651        let settings = self.buffer.read(cx).language_settings(cx);
12652        let tab_size = settings.tab_size.get() as usize;
12653
12654        self.manipulate_mutable_lines(window, cx, |lines| {
12655            // Allocates a reasonably sized buffer once for the whole loop
12656            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12657            // Avoids recomputing spaces that could be inserted many times
12658            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12659                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12660                .collect();
12661
12662            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12663                let mut chars = line.chars();
12664                let mut spaces_count = 0;
12665                let mut first_non_indent_char = None;
12666                let mut changed = false;
12667
12668                for ch in chars.by_ref() {
12669                    match ch {
12670                        ' ' => {
12671                            // Keep track of spaces. Append \t when we reach tab_size
12672                            spaces_count += 1;
12673                            changed = true;
12674                            if spaces_count == tab_size {
12675                                reindented_line.push('\t');
12676                                spaces_count = 0;
12677                            }
12678                        }
12679                        '\t' => {
12680                            reindented_line.push('\t');
12681                            spaces_count = 0;
12682                        }
12683                        _ => {
12684                            // Dont append it yet, we might have remaining spaces
12685                            first_non_indent_char = Some(ch);
12686                            break;
12687                        }
12688                    }
12689                }
12690
12691                if !changed {
12692                    reindented_line.clear();
12693                    continue;
12694                }
12695                // Remaining spaces that didn't make a full tab stop
12696                if spaces_count > 0 {
12697                    reindented_line.extend(&space_cache[spaces_count - 1]);
12698                }
12699                // If we consume an extra character that was not indentation, add it back
12700                if let Some(extra_char) = first_non_indent_char {
12701                    reindented_line.push(extra_char);
12702                }
12703                // Append the rest of the line and replace old reference with new one
12704                reindented_line.extend(chars);
12705                *line = Cow::Owned(reindented_line.clone());
12706                reindented_line.clear();
12707            }
12708        });
12709    }
12710
12711    pub fn convert_to_upper_case(
12712        &mut self,
12713        _: &ConvertToUpperCase,
12714        window: &mut Window,
12715        cx: &mut Context<Self>,
12716    ) {
12717        self.manipulate_text(window, cx, |text| text.to_uppercase())
12718    }
12719
12720    pub fn convert_to_lower_case(
12721        &mut self,
12722        _: &ConvertToLowerCase,
12723        window: &mut Window,
12724        cx: &mut Context<Self>,
12725    ) {
12726        self.manipulate_text(window, cx, |text| text.to_lowercase())
12727    }
12728
12729    pub fn convert_to_title_case(
12730        &mut self,
12731        _: &ConvertToTitleCase,
12732        window: &mut Window,
12733        cx: &mut Context<Self>,
12734    ) {
12735        self.manipulate_text(window, cx, |text| {
12736            Self::convert_text_case(text, Case::Title)
12737        })
12738    }
12739
12740    pub fn convert_to_snake_case(
12741        &mut self,
12742        _: &ConvertToSnakeCase,
12743        window: &mut Window,
12744        cx: &mut Context<Self>,
12745    ) {
12746        self.manipulate_text(window, cx, |text| {
12747            Self::convert_text_case(text, Case::Snake)
12748        })
12749    }
12750
12751    pub fn convert_to_kebab_case(
12752        &mut self,
12753        _: &ConvertToKebabCase,
12754        window: &mut Window,
12755        cx: &mut Context<Self>,
12756    ) {
12757        self.manipulate_text(window, cx, |text| {
12758            Self::convert_text_case(text, Case::Kebab)
12759        })
12760    }
12761
12762    pub fn convert_to_upper_camel_case(
12763        &mut self,
12764        _: &ConvertToUpperCamelCase,
12765        window: &mut Window,
12766        cx: &mut Context<Self>,
12767    ) {
12768        self.manipulate_text(window, cx, |text| {
12769            Self::convert_text_case(text, Case::UpperCamel)
12770        })
12771    }
12772
12773    pub fn convert_to_lower_camel_case(
12774        &mut self,
12775        _: &ConvertToLowerCamelCase,
12776        window: &mut Window,
12777        cx: &mut Context<Self>,
12778    ) {
12779        self.manipulate_text(window, cx, |text| {
12780            Self::convert_text_case(text, Case::Camel)
12781        })
12782    }
12783
12784    pub fn convert_to_opposite_case(
12785        &mut self,
12786        _: &ConvertToOppositeCase,
12787        window: &mut Window,
12788        cx: &mut Context<Self>,
12789    ) {
12790        self.manipulate_text(window, cx, |text| {
12791            text.chars()
12792                .fold(String::with_capacity(text.len()), |mut t, c| {
12793                    if c.is_uppercase() {
12794                        t.extend(c.to_lowercase());
12795                    } else {
12796                        t.extend(c.to_uppercase());
12797                    }
12798                    t
12799                })
12800        })
12801    }
12802
12803    pub fn convert_to_sentence_case(
12804        &mut self,
12805        _: &ConvertToSentenceCase,
12806        window: &mut Window,
12807        cx: &mut Context<Self>,
12808    ) {
12809        self.manipulate_text(window, cx, |text| {
12810            Self::convert_text_case(text, Case::Sentence)
12811        })
12812    }
12813
12814    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12815        self.manipulate_text(window, cx, |text| {
12816            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12817            if has_upper_case_characters {
12818                text.to_lowercase()
12819            } else {
12820                text.to_uppercase()
12821            }
12822        })
12823    }
12824
12825    pub fn convert_to_rot13(
12826        &mut self,
12827        _: &ConvertToRot13,
12828        window: &mut Window,
12829        cx: &mut Context<Self>,
12830    ) {
12831        self.manipulate_text(window, cx, |text| {
12832            text.chars()
12833                .map(|c| match c {
12834                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12835                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12836                    _ => c,
12837                })
12838                .collect()
12839        })
12840    }
12841
12842    fn convert_text_case(text: &str, case: Case) -> String {
12843        text.lines()
12844            .map(|line| {
12845                let trimmed_start = line.trim_start();
12846                let leading = &line[..line.len() - trimmed_start.len()];
12847                let trimmed = trimmed_start.trim_end();
12848                let trailing = &trimmed_start[trimmed.len()..];
12849                format!("{}{}{}", leading, trimmed.to_case(case), trailing)
12850            })
12851            .join("\n")
12852    }
12853
12854    pub fn convert_to_rot47(
12855        &mut self,
12856        _: &ConvertToRot47,
12857        window: &mut Window,
12858        cx: &mut Context<Self>,
12859    ) {
12860        self.manipulate_text(window, cx, |text| {
12861            text.chars()
12862                .map(|c| {
12863                    let code_point = c as u32;
12864                    if code_point >= 33 && code_point <= 126 {
12865                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12866                    }
12867                    c
12868                })
12869                .collect()
12870        })
12871    }
12872
12873    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12874    where
12875        Fn: FnMut(&str) -> String,
12876    {
12877        let buffer = self.buffer.read(cx).snapshot(cx);
12878
12879        let mut new_selections = Vec::new();
12880        let mut edits = Vec::new();
12881        let mut selection_adjustment = 0isize;
12882
12883        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12884            let selection_is_empty = selection.is_empty();
12885
12886            let (start, end) = if selection_is_empty {
12887                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12888                (word_range.start, word_range.end)
12889            } else {
12890                (
12891                    buffer.point_to_offset(selection.start),
12892                    buffer.point_to_offset(selection.end),
12893                )
12894            };
12895
12896            let text = buffer.text_for_range(start..end).collect::<String>();
12897            let old_length = text.len() as isize;
12898            let text = callback(&text);
12899
12900            new_selections.push(Selection {
12901                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12902                end: MultiBufferOffset(
12903                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12904                ),
12905                goal: SelectionGoal::None,
12906                id: selection.id,
12907                reversed: selection.reversed,
12908            });
12909
12910            selection_adjustment += old_length - text.len() as isize;
12911
12912            edits.push((start..end, text));
12913        }
12914
12915        self.transact(window, cx, |this, window, cx| {
12916            this.buffer.update(cx, |buffer, cx| {
12917                buffer.edit(edits, None, cx);
12918            });
12919
12920            this.change_selections(Default::default(), window, cx, |s| {
12921                s.select(new_selections);
12922            });
12923
12924            this.request_autoscroll(Autoscroll::fit(), cx);
12925        });
12926    }
12927
12928    pub fn move_selection_on_drop(
12929        &mut self,
12930        selection: &Selection<Anchor>,
12931        target: DisplayPoint,
12932        is_cut: bool,
12933        window: &mut Window,
12934        cx: &mut Context<Self>,
12935    ) {
12936        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12937        let buffer = display_map.buffer_snapshot();
12938        let mut edits = Vec::new();
12939        let insert_point = display_map
12940            .clip_point(target, Bias::Left)
12941            .to_point(&display_map);
12942        let text = buffer
12943            .text_for_range(selection.start..selection.end)
12944            .collect::<String>();
12945        if is_cut {
12946            edits.push(((selection.start..selection.end), String::new()));
12947        }
12948        let insert_anchor = buffer.anchor_before(insert_point);
12949        edits.push(((insert_anchor..insert_anchor), text));
12950        let last_edit_start = insert_anchor.bias_left(buffer);
12951        let last_edit_end = insert_anchor.bias_right(buffer);
12952        self.transact(window, cx, |this, window, cx| {
12953            this.buffer.update(cx, |buffer, cx| {
12954                buffer.edit(edits, None, cx);
12955            });
12956            this.change_selections(Default::default(), window, cx, |s| {
12957                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12958            });
12959        });
12960    }
12961
12962    pub fn clear_selection_drag_state(&mut self) {
12963        self.selection_drag_state = SelectionDragState::None;
12964    }
12965
12966    pub fn duplicate(
12967        &mut self,
12968        upwards: bool,
12969        whole_lines: bool,
12970        window: &mut Window,
12971        cx: &mut Context<Self>,
12972    ) {
12973        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12974
12975        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12976        let buffer = display_map.buffer_snapshot();
12977        let selections = self.selections.all::<Point>(&display_map);
12978
12979        let mut edits = Vec::new();
12980        let mut selections_iter = selections.iter().peekable();
12981        while let Some(selection) = selections_iter.next() {
12982            let mut rows = selection.spanned_rows(false, &display_map);
12983            // duplicate line-wise
12984            if whole_lines || selection.start == selection.end {
12985                // Avoid duplicating the same lines twice.
12986                while let Some(next_selection) = selections_iter.peek() {
12987                    let next_rows = next_selection.spanned_rows(false, &display_map);
12988                    if next_rows.start < rows.end {
12989                        rows.end = next_rows.end;
12990                        selections_iter.next().unwrap();
12991                    } else {
12992                        break;
12993                    }
12994                }
12995
12996                // Copy the text from the selected row region and splice it either at the start
12997                // or end of the region.
12998                let start = Point::new(rows.start.0, 0);
12999                let end = Point::new(
13000                    rows.end.previous_row().0,
13001                    buffer.line_len(rows.end.previous_row()),
13002                );
13003
13004                let mut text = buffer.text_for_range(start..end).collect::<String>();
13005
13006                let insert_location = if upwards {
13007                    // When duplicating upward, we need to insert before the current line.
13008                    // If we're on the last line and it doesn't end with a newline,
13009                    // we need to add a newline before the duplicated content.
13010                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
13011                        && buffer.max_point().column > 0
13012                        && !text.ends_with('\n');
13013
13014                    if needs_leading_newline {
13015                        text.insert(0, '\n');
13016                        end
13017                    } else {
13018                        text.push('\n');
13019                        Point::new(rows.start.0, 0)
13020                    }
13021                } else {
13022                    text.push('\n');
13023                    start
13024                };
13025                edits.push((insert_location..insert_location, text));
13026            } else {
13027                // duplicate character-wise
13028                let start = selection.start;
13029                let end = selection.end;
13030                let text = buffer.text_for_range(start..end).collect::<String>();
13031                edits.push((selection.end..selection.end, text));
13032            }
13033        }
13034
13035        self.transact(window, cx, |this, window, cx| {
13036            this.buffer.update(cx, |buffer, cx| {
13037                buffer.edit(edits, None, cx);
13038            });
13039
13040            // When duplicating upward with whole lines, move the cursor to the duplicated line
13041            if upwards && whole_lines {
13042                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
13043
13044                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13045                    let mut new_ranges = Vec::new();
13046                    let selections = s.all::<Point>(&display_map);
13047                    let mut selections_iter = selections.iter().peekable();
13048
13049                    while let Some(first_selection) = selections_iter.next() {
13050                        // Group contiguous selections together to find the total row span
13051                        let mut group_selections = vec![first_selection];
13052                        let mut rows = first_selection.spanned_rows(false, &display_map);
13053
13054                        while let Some(next_selection) = selections_iter.peek() {
13055                            let next_rows = next_selection.spanned_rows(false, &display_map);
13056                            if next_rows.start < rows.end {
13057                                rows.end = next_rows.end;
13058                                group_selections.push(selections_iter.next().unwrap());
13059                            } else {
13060                                break;
13061                            }
13062                        }
13063
13064                        let row_count = rows.end.0 - rows.start.0;
13065
13066                        // Move all selections in this group up by the total number of duplicated rows
13067                        for selection in group_selections {
13068                            let new_start = Point::new(
13069                                selection.start.row.saturating_sub(row_count),
13070                                selection.start.column,
13071                            );
13072
13073                            let new_end = Point::new(
13074                                selection.end.row.saturating_sub(row_count),
13075                                selection.end.column,
13076                            );
13077
13078                            new_ranges.push(new_start..new_end);
13079                        }
13080                    }
13081
13082                    s.select_ranges(new_ranges);
13083                });
13084            }
13085
13086            this.request_autoscroll(Autoscroll::fit(), cx);
13087        });
13088    }
13089
13090    pub fn duplicate_line_up(
13091        &mut self,
13092        _: &DuplicateLineUp,
13093        window: &mut Window,
13094        cx: &mut Context<Self>,
13095    ) {
13096        self.duplicate(true, true, window, cx);
13097    }
13098
13099    pub fn duplicate_line_down(
13100        &mut self,
13101        _: &DuplicateLineDown,
13102        window: &mut Window,
13103        cx: &mut Context<Self>,
13104    ) {
13105        self.duplicate(false, true, window, cx);
13106    }
13107
13108    pub fn duplicate_selection(
13109        &mut self,
13110        _: &DuplicateSelection,
13111        window: &mut Window,
13112        cx: &mut Context<Self>,
13113    ) {
13114        self.duplicate(false, false, window, cx);
13115    }
13116
13117    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
13118        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13119        if self.mode.is_single_line() {
13120            cx.propagate();
13121            return;
13122        }
13123
13124        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13125        let buffer = self.buffer.read(cx).snapshot(cx);
13126
13127        let mut edits = Vec::new();
13128        let mut unfold_ranges = Vec::new();
13129        let mut refold_creases = Vec::new();
13130
13131        let selections = self.selections.all::<Point>(&display_map);
13132        let mut selections = selections.iter().peekable();
13133        let mut contiguous_row_selections = Vec::new();
13134        let mut new_selections = Vec::new();
13135
13136        while let Some(selection) = selections.next() {
13137            // Find all the selections that span a contiguous row range
13138            let (start_row, end_row) = consume_contiguous_rows(
13139                &mut contiguous_row_selections,
13140                selection,
13141                &display_map,
13142                &mut selections,
13143            );
13144
13145            // Move the text spanned by the row range to be before the line preceding the row range
13146            if start_row.0 > 0 {
13147                let range_to_move = Point::new(
13148                    start_row.previous_row().0,
13149                    buffer.line_len(start_row.previous_row()),
13150                )
13151                    ..Point::new(
13152                        end_row.previous_row().0,
13153                        buffer.line_len(end_row.previous_row()),
13154                    );
13155                let insertion_point = display_map
13156                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
13157                    .0;
13158
13159                // Don't move lines across excerpts
13160                if buffer
13161                    .excerpt_containing(insertion_point..range_to_move.end)
13162                    .is_some()
13163                {
13164                    let text = buffer
13165                        .text_for_range(range_to_move.clone())
13166                        .flat_map(|s| s.chars())
13167                        .skip(1)
13168                        .chain(['\n'])
13169                        .collect::<String>();
13170
13171                    edits.push((
13172                        buffer.anchor_after(range_to_move.start)
13173                            ..buffer.anchor_before(range_to_move.end),
13174                        String::new(),
13175                    ));
13176                    let insertion_anchor = buffer.anchor_after(insertion_point);
13177                    edits.push((insertion_anchor..insertion_anchor, text));
13178
13179                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
13180
13181                    // Move selections up
13182                    new_selections.extend(contiguous_row_selections.drain(..).map(
13183                        |mut selection| {
13184                            selection.start.row -= row_delta;
13185                            selection.end.row -= row_delta;
13186                            selection
13187                        },
13188                    ));
13189
13190                    // Move folds up
13191                    unfold_ranges.push(range_to_move.clone());
13192                    for fold in display_map.folds_in_range(
13193                        buffer.anchor_before(range_to_move.start)
13194                            ..buffer.anchor_after(range_to_move.end),
13195                    ) {
13196                        let mut start = fold.range.start.to_point(&buffer);
13197                        let mut end = fold.range.end.to_point(&buffer);
13198                        start.row -= row_delta;
13199                        end.row -= row_delta;
13200                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13201                    }
13202                }
13203            }
13204
13205            // If we didn't move line(s), preserve the existing selections
13206            new_selections.append(&mut contiguous_row_selections);
13207        }
13208
13209        self.transact(window, cx, |this, window, cx| {
13210            this.unfold_ranges(&unfold_ranges, true, true, cx);
13211            this.buffer.update(cx, |buffer, cx| {
13212                for (range, text) in edits {
13213                    buffer.edit([(range, text)], None, cx);
13214                }
13215            });
13216            this.fold_creases(refold_creases, true, window, cx);
13217            this.change_selections(Default::default(), window, cx, |s| {
13218                s.select(new_selections);
13219            })
13220        });
13221    }
13222
13223    pub fn move_line_down(
13224        &mut self,
13225        _: &MoveLineDown,
13226        window: &mut Window,
13227        cx: &mut Context<Self>,
13228    ) {
13229        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13230        if self.mode.is_single_line() {
13231            cx.propagate();
13232            return;
13233        }
13234
13235        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13236        let buffer = self.buffer.read(cx).snapshot(cx);
13237
13238        let mut edits = Vec::new();
13239        let mut unfold_ranges = Vec::new();
13240        let mut refold_creases = Vec::new();
13241
13242        let selections = self.selections.all::<Point>(&display_map);
13243        let mut selections = selections.iter().peekable();
13244        let mut contiguous_row_selections = Vec::new();
13245        let mut new_selections = Vec::new();
13246
13247        while let Some(selection) = selections.next() {
13248            // Find all the selections that span a contiguous row range
13249            let (start_row, end_row) = consume_contiguous_rows(
13250                &mut contiguous_row_selections,
13251                selection,
13252                &display_map,
13253                &mut selections,
13254            );
13255
13256            // Move the text spanned by the row range to be after the last line of the row range
13257            if end_row.0 <= buffer.max_point().row {
13258                let range_to_move =
13259                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13260                let insertion_point = display_map
13261                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13262                    .0;
13263
13264                // Don't move lines across excerpt boundaries
13265                if buffer
13266                    .excerpt_containing(range_to_move.start..insertion_point)
13267                    .is_some()
13268                {
13269                    let mut text = String::from("\n");
13270                    text.extend(buffer.text_for_range(range_to_move.clone()));
13271                    text.pop(); // Drop trailing newline
13272                    edits.push((
13273                        buffer.anchor_after(range_to_move.start)
13274                            ..buffer.anchor_before(range_to_move.end),
13275                        String::new(),
13276                    ));
13277                    let insertion_anchor = buffer.anchor_after(insertion_point);
13278                    edits.push((insertion_anchor..insertion_anchor, text));
13279
13280                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13281
13282                    // Move selections down
13283                    new_selections.extend(contiguous_row_selections.drain(..).map(
13284                        |mut selection| {
13285                            selection.start.row += row_delta;
13286                            selection.end.row += row_delta;
13287                            selection
13288                        },
13289                    ));
13290
13291                    // Move folds down
13292                    unfold_ranges.push(range_to_move.clone());
13293                    for fold in display_map.folds_in_range(
13294                        buffer.anchor_before(range_to_move.start)
13295                            ..buffer.anchor_after(range_to_move.end),
13296                    ) {
13297                        let mut start = fold.range.start.to_point(&buffer);
13298                        let mut end = fold.range.end.to_point(&buffer);
13299                        start.row += row_delta;
13300                        end.row += row_delta;
13301                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13302                    }
13303                }
13304            }
13305
13306            // If we didn't move line(s), preserve the existing selections
13307            new_selections.append(&mut contiguous_row_selections);
13308        }
13309
13310        self.transact(window, cx, |this, window, cx| {
13311            this.unfold_ranges(&unfold_ranges, true, true, cx);
13312            this.buffer.update(cx, |buffer, cx| {
13313                for (range, text) in edits {
13314                    buffer.edit([(range, text)], None, cx);
13315                }
13316            });
13317            this.fold_creases(refold_creases, true, window, cx);
13318            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13319        });
13320    }
13321
13322    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13323        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13324        let text_layout_details = &self.text_layout_details(window, cx);
13325        self.transact(window, cx, |this, window, cx| {
13326            let edits = this.change_selections(Default::default(), window, cx, |s| {
13327                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13328                s.move_with(&mut |display_map, selection| {
13329                    if !selection.is_empty() {
13330                        return;
13331                    }
13332
13333                    let mut head = selection.head();
13334                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13335                    if head.column() == display_map.line_len(head.row()) {
13336                        transpose_offset = display_map
13337                            .buffer_snapshot()
13338                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13339                    }
13340
13341                    if transpose_offset == MultiBufferOffset(0) {
13342                        return;
13343                    }
13344
13345                    *head.column_mut() += 1;
13346                    head = display_map.clip_point(head, Bias::Right);
13347                    let goal = SelectionGoal::HorizontalPosition(
13348                        display_map
13349                            .x_for_display_point(head, text_layout_details)
13350                            .into(),
13351                    );
13352                    selection.collapse_to(head, goal);
13353
13354                    let transpose_start = display_map
13355                        .buffer_snapshot()
13356                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13357                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13358                        let transpose_end = display_map
13359                            .buffer_snapshot()
13360                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13361                        if let Some(ch) = display_map
13362                            .buffer_snapshot()
13363                            .chars_at(transpose_start)
13364                            .next()
13365                        {
13366                            edits.push((transpose_start..transpose_offset, String::new()));
13367                            edits.push((transpose_end..transpose_end, ch.to_string()));
13368                        }
13369                    }
13370                });
13371                edits
13372            });
13373            this.buffer
13374                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13375            let selections = this
13376                .selections
13377                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13378            this.change_selections(Default::default(), window, cx, |s| {
13379                s.select(selections);
13380            });
13381        });
13382    }
13383
13384    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13385        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13386        if self.mode.is_single_line() {
13387            cx.propagate();
13388            return;
13389        }
13390
13391        self.rewrap_impl(RewrapOptions::default(), cx)
13392    }
13393
13394    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13395        let buffer = self.buffer.read(cx).snapshot(cx);
13396        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13397
13398        #[derive(Clone, Debug, PartialEq)]
13399        enum CommentFormat {
13400            /// single line comment, with prefix for line
13401            Line(String),
13402            /// single line within a block comment, with prefix for line
13403            BlockLine(String),
13404            /// a single line of a block comment that includes the initial delimiter
13405            BlockCommentWithStart(BlockCommentConfig),
13406            /// a single line of a block comment that includes the ending delimiter
13407            BlockCommentWithEnd(BlockCommentConfig),
13408        }
13409
13410        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13411        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13412            let language_settings = buffer.language_settings_at(selection.head(), cx);
13413            let language_scope = buffer.language_scope_at(selection.head());
13414
13415            let indent_and_prefix_for_row =
13416                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13417                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13418                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13419                        &language_scope
13420                    {
13421                        let indent_end = Point::new(row, indent.len);
13422                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13423                        let line_text_after_indent = buffer
13424                            .text_for_range(indent_end..line_end)
13425                            .collect::<String>();
13426
13427                        let is_within_comment_override = buffer
13428                            .language_scope_at(indent_end)
13429                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13430                        let comment_delimiters = if is_within_comment_override {
13431                            // we are within a comment syntax node, but we don't
13432                            // yet know what kind of comment: block, doc or line
13433                            match (
13434                                language_scope.documentation_comment(),
13435                                language_scope.block_comment(),
13436                            ) {
13437                                (Some(config), _) | (_, Some(config))
13438                                    if buffer.contains_str_at(indent_end, &config.start) =>
13439                                {
13440                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13441                                }
13442                                (Some(config), _) | (_, Some(config))
13443                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13444                                {
13445                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13446                                }
13447                                (Some(config), _) | (_, Some(config))
13448                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13449                                {
13450                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13451                                }
13452                                (_, _) => language_scope
13453                                    .line_comment_prefixes()
13454                                    .iter()
13455                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13456                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13457                            }
13458                        } else {
13459                            // we not in an overridden comment node, but we may
13460                            // be within a non-overridden line comment node
13461                            language_scope
13462                                .line_comment_prefixes()
13463                                .iter()
13464                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13465                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13466                        };
13467
13468                        let rewrap_prefix = language_scope
13469                            .rewrap_prefixes()
13470                            .iter()
13471                            .find_map(|prefix_regex| {
13472                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13473                                    if mat.start() == 0 {
13474                                        Some(mat.as_str().to_string())
13475                                    } else {
13476                                        None
13477                                    }
13478                                })
13479                            })
13480                            .flatten();
13481                        (comment_delimiters, rewrap_prefix)
13482                    } else {
13483                        (None, None)
13484                    };
13485                    (indent, comment_prefix, rewrap_prefix)
13486                };
13487
13488            let mut start_row = selection.start.row;
13489            let mut end_row = selection.end.row;
13490
13491            if selection.is_empty() {
13492                let cursor_row = selection.start.row;
13493
13494                let (mut indent_size, comment_prefix, _) = indent_and_prefix_for_row(cursor_row);
13495                let line_prefix = match &comment_prefix {
13496                    Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13497                        Some(prefix.as_str())
13498                    }
13499                    Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13500                        prefix, ..
13501                    })) => Some(prefix.as_ref()),
13502                    Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13503                        start: _,
13504                        end: _,
13505                        prefix,
13506                        tab_size,
13507                    })) => {
13508                        indent_size.len += tab_size;
13509                        Some(prefix.as_ref())
13510                    }
13511                    None => None,
13512                };
13513                let indent_prefix = indent_size.chars().collect::<String>();
13514                let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13515
13516                'expand_upwards: while start_row > 0 {
13517                    let prev_row = start_row - 1;
13518                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13519                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13520                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13521                    {
13522                        start_row = prev_row;
13523                    } else {
13524                        break 'expand_upwards;
13525                    }
13526                }
13527
13528                'expand_downwards: while end_row < buffer.max_point().row {
13529                    let next_row = end_row + 1;
13530                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13531                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13532                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13533                    {
13534                        end_row = next_row;
13535                    } else {
13536                        break 'expand_downwards;
13537                    }
13538                }
13539            }
13540
13541            let mut non_blank_rows_iter = (start_row..=end_row)
13542                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13543                .peekable();
13544
13545            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13546                row
13547            } else {
13548                return Vec::new();
13549            };
13550
13551            let mut ranges = Vec::new();
13552
13553            let mut current_range_start = first_row;
13554            let mut prev_row = first_row;
13555            let (
13556                mut current_range_indent,
13557                mut current_range_comment_delimiters,
13558                mut current_range_rewrap_prefix,
13559            ) = indent_and_prefix_for_row(first_row);
13560
13561            for row in non_blank_rows_iter.skip(1) {
13562                let has_paragraph_break = row > prev_row + 1;
13563
13564                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13565                    indent_and_prefix_for_row(row);
13566
13567                let has_indent_change = row_indent != current_range_indent;
13568                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13569
13570                let has_boundary_change = has_comment_change
13571                    || row_rewrap_prefix.is_some()
13572                    || (has_indent_change && current_range_comment_delimiters.is_some());
13573
13574                if has_paragraph_break || has_boundary_change {
13575                    ranges.push((
13576                        language_settings.clone(),
13577                        Point::new(current_range_start, 0)
13578                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13579                        current_range_indent,
13580                        current_range_comment_delimiters.clone(),
13581                        current_range_rewrap_prefix.clone(),
13582                    ));
13583                    current_range_start = row;
13584                    current_range_indent = row_indent;
13585                    current_range_comment_delimiters = row_comment_delimiters;
13586                    current_range_rewrap_prefix = row_rewrap_prefix;
13587                }
13588                prev_row = row;
13589            }
13590
13591            ranges.push((
13592                language_settings.clone(),
13593                Point::new(current_range_start, 0)
13594                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13595                current_range_indent,
13596                current_range_comment_delimiters,
13597                current_range_rewrap_prefix,
13598            ));
13599
13600            ranges
13601        });
13602
13603        let mut edits = Vec::new();
13604        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13605
13606        for (language_settings, wrap_range, mut indent_size, comment_prefix, rewrap_prefix) in
13607            wrap_ranges
13608        {
13609            let start_row = wrap_range.start.row;
13610            let end_row = wrap_range.end.row;
13611
13612            // Skip selections that overlap with a range that has already been rewrapped.
13613            let selection_range = start_row..end_row;
13614            if rewrapped_row_ranges
13615                .iter()
13616                .any(|range| range.overlaps(&selection_range))
13617            {
13618                continue;
13619            }
13620
13621            let tab_size = language_settings.tab_size;
13622
13623            let (line_prefix, inside_comment) = match &comment_prefix {
13624                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13625                    (Some(prefix.as_str()), true)
13626                }
13627                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13628                    (Some(prefix.as_ref()), true)
13629                }
13630                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13631                    start: _,
13632                    end: _,
13633                    prefix,
13634                    tab_size,
13635                })) => {
13636                    indent_size.len += tab_size;
13637                    (Some(prefix.as_ref()), true)
13638                }
13639                None => (None, false),
13640            };
13641            let indent_prefix = indent_size.chars().collect::<String>();
13642            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13643
13644            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13645                RewrapBehavior::InComments => inside_comment,
13646                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13647                RewrapBehavior::Anywhere => true,
13648            };
13649
13650            let should_rewrap = options.override_language_settings
13651                || allow_rewrap_based_on_language
13652                || self.hard_wrap.is_some();
13653            if !should_rewrap {
13654                continue;
13655            }
13656
13657            let start = Point::new(start_row, 0);
13658            let start_offset = ToOffset::to_offset(&start, &buffer);
13659            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13660            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13661            let mut first_line_delimiter = None;
13662            let mut last_line_delimiter = None;
13663            let Some(lines_without_prefixes) = selection_text
13664                .lines()
13665                .enumerate()
13666                .map(|(ix, line)| {
13667                    let line_trimmed = line.trim_start();
13668                    if rewrap_prefix.is_some() && ix > 0 {
13669                        Ok(line_trimmed)
13670                    } else if let Some(
13671                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13672                            start,
13673                            prefix,
13674                            end,
13675                            tab_size,
13676                        })
13677                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13678                            start,
13679                            prefix,
13680                            end,
13681                            tab_size,
13682                        }),
13683                    ) = &comment_prefix
13684                    {
13685                        let line_trimmed = line_trimmed
13686                            .strip_prefix(start.as_ref())
13687                            .map(|s| {
13688                                let mut indent_size = indent_size;
13689                                indent_size.len -= tab_size;
13690                                let indent_prefix: String = indent_size.chars().collect();
13691                                first_line_delimiter = Some((indent_prefix, start));
13692                                s.trim_start()
13693                            })
13694                            .unwrap_or(line_trimmed);
13695                        let line_trimmed = line_trimmed
13696                            .strip_suffix(end.as_ref())
13697                            .map(|s| {
13698                                last_line_delimiter = Some(end);
13699                                s.trim_end()
13700                            })
13701                            .unwrap_or(line_trimmed);
13702                        let line_trimmed = line_trimmed
13703                            .strip_prefix(prefix.as_ref())
13704                            .unwrap_or(line_trimmed);
13705                        Ok(line_trimmed)
13706                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13707                        line_trimmed.strip_prefix(prefix).with_context(|| {
13708                            format!("line did not start with prefix {prefix:?}: {line:?}")
13709                        })
13710                    } else {
13711                        line_trimmed
13712                            .strip_prefix(&line_prefix.trim_start())
13713                            .with_context(|| {
13714                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13715                            })
13716                    }
13717                })
13718                .collect::<Result<Vec<_>, _>>()
13719                .log_err()
13720            else {
13721                continue;
13722            };
13723
13724            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13725                buffer
13726                    .language_settings_at(Point::new(start_row, 0), cx)
13727                    .preferred_line_length as usize
13728            });
13729
13730            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13731                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13732            } else {
13733                line_prefix.clone()
13734            };
13735
13736            let wrapped_text = {
13737                let mut wrapped_text = wrap_with_prefix(
13738                    line_prefix,
13739                    subsequent_lines_prefix,
13740                    lines_without_prefixes.join("\n"),
13741                    wrap_column,
13742                    tab_size,
13743                    options.preserve_existing_whitespace,
13744                );
13745
13746                if let Some((indent, delimiter)) = first_line_delimiter {
13747                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13748                }
13749                if let Some(last_line) = last_line_delimiter {
13750                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13751                }
13752
13753                wrapped_text
13754            };
13755
13756            // TODO: should always use char-based diff while still supporting cursor behavior that
13757            // matches vim.
13758            let mut diff_options = DiffOptions::default();
13759            if options.override_language_settings {
13760                diff_options.max_word_diff_len = 0;
13761                diff_options.max_word_diff_line_count = 0;
13762            } else {
13763                diff_options.max_word_diff_len = usize::MAX;
13764                diff_options.max_word_diff_line_count = usize::MAX;
13765            }
13766
13767            for (old_range, new_text) in
13768                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13769            {
13770                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13771                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13772                edits.push((edit_start..edit_end, new_text));
13773            }
13774
13775            rewrapped_row_ranges.push(start_row..=end_row);
13776        }
13777
13778        self.buffer
13779            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13780    }
13781
13782    pub fn cut_common(
13783        &mut self,
13784        cut_no_selection_line: bool,
13785        window: &mut Window,
13786        cx: &mut Context<Self>,
13787    ) -> ClipboardItem {
13788        let mut text = String::new();
13789        let buffer = self.buffer.read(cx).snapshot(cx);
13790        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13791        let mut clipboard_selections = Vec::with_capacity(selections.len());
13792        {
13793            let max_point = buffer.max_point();
13794            let mut is_first = true;
13795            let mut prev_selection_was_entire_line = false;
13796            for selection in &mut selections {
13797                let is_entire_line =
13798                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13799                if is_entire_line {
13800                    selection.start = Point::new(selection.start.row, 0);
13801                    if !selection.is_empty() && selection.end.column == 0 {
13802                        selection.end = cmp::min(max_point, selection.end);
13803                    } else {
13804                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13805                    }
13806                    selection.goal = SelectionGoal::None;
13807                }
13808                if is_first {
13809                    is_first = false;
13810                } else if !prev_selection_was_entire_line {
13811                    text += "\n";
13812                }
13813                prev_selection_was_entire_line = is_entire_line;
13814                let mut len = 0;
13815                for chunk in buffer.text_for_range(selection.start..selection.end) {
13816                    text.push_str(chunk);
13817                    len += chunk.len();
13818                }
13819
13820                clipboard_selections.push(ClipboardSelection::for_buffer(
13821                    len,
13822                    is_entire_line,
13823                    selection.range(),
13824                    &buffer,
13825                    self.project.as_ref(),
13826                    cx,
13827                ));
13828            }
13829        }
13830
13831        self.transact(window, cx, |this, window, cx| {
13832            this.change_selections(Default::default(), window, cx, |s| {
13833                s.select(selections);
13834            });
13835            this.insert("", window, cx);
13836        });
13837        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13838    }
13839
13840    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13841        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13842        let item = self.cut_common(true, window, cx);
13843        cx.write_to_clipboard(item);
13844    }
13845
13846    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13847        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13848        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13849            s.move_with(&mut |snapshot, sel| {
13850                if sel.is_empty() {
13851                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13852                }
13853                if sel.is_empty() {
13854                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13855                }
13856            });
13857        });
13858        let item = self.cut_common(false, window, cx);
13859        cx.set_global(KillRing(item))
13860    }
13861
13862    pub fn kill_ring_yank(
13863        &mut self,
13864        _: &KillRingYank,
13865        window: &mut Window,
13866        cx: &mut Context<Self>,
13867    ) {
13868        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13869        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13870            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13871                (kill_ring.text().to_string(), kill_ring.metadata_json())
13872            } else {
13873                return;
13874            }
13875        } else {
13876            return;
13877        };
13878        self.do_paste(&text, metadata, false, window, cx);
13879    }
13880
13881    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13882        self.do_copy(true, cx);
13883    }
13884
13885    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13886        self.do_copy(false, cx);
13887    }
13888
13889    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13890        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13891        let buffer = self.buffer.read(cx).read(cx);
13892        let mut text = String::new();
13893        let mut clipboard_selections = Vec::with_capacity(selections.len());
13894
13895        let max_point = buffer.max_point();
13896        let mut is_first = true;
13897        for selection in &selections {
13898            let mut start = selection.start;
13899            let mut end = selection.end;
13900            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13901            let mut add_trailing_newline = false;
13902            if is_entire_line {
13903                start = Point::new(start.row, 0);
13904                let next_line_start = Point::new(end.row + 1, 0);
13905                if next_line_start <= max_point {
13906                    end = next_line_start;
13907                } else {
13908                    // We're on the last line without a trailing newline.
13909                    // Copy to the end of the line and add a newline afterwards.
13910                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13911                    add_trailing_newline = true;
13912                }
13913            }
13914
13915            let mut trimmed_selections = Vec::new();
13916            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13917                let row = MultiBufferRow(start.row);
13918                let first_indent = buffer.indent_size_for_line(row);
13919                if first_indent.len == 0 || start.column > first_indent.len {
13920                    trimmed_selections.push(start..end);
13921                } else {
13922                    trimmed_selections.push(
13923                        Point::new(row.0, first_indent.len)
13924                            ..Point::new(row.0, buffer.line_len(row)),
13925                    );
13926                    for row in start.row + 1..=end.row {
13927                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13928                        if row == end.row {
13929                            line_len = end.column;
13930                        }
13931                        if line_len == 0 {
13932                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13933                            continue;
13934                        }
13935                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13936                        if row_indent_size.len >= first_indent.len {
13937                            trimmed_selections
13938                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13939                        } else {
13940                            trimmed_selections.clear();
13941                            trimmed_selections.push(start..end);
13942                            break;
13943                        }
13944                    }
13945                }
13946            } else {
13947                trimmed_selections.push(start..end);
13948            }
13949
13950            let is_multiline_trim = trimmed_selections.len() > 1;
13951            let mut selection_len: usize = 0;
13952            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13953
13954            for trimmed_range in trimmed_selections {
13955                if is_first {
13956                    is_first = false;
13957                } else if is_multiline_trim || !prev_selection_was_entire_line {
13958                    text.push('\n');
13959                    if is_multiline_trim {
13960                        selection_len += 1;
13961                    }
13962                }
13963                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13964                    text.push_str(chunk);
13965                    selection_len += chunk.len();
13966                }
13967                if add_trailing_newline {
13968                    text.push('\n');
13969                    selection_len += 1;
13970                }
13971            }
13972
13973            clipboard_selections.push(ClipboardSelection::for_buffer(
13974                selection_len,
13975                is_entire_line,
13976                start..end,
13977                &buffer,
13978                self.project.as_ref(),
13979                cx,
13980            ));
13981        }
13982
13983        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13984            text,
13985            clipboard_selections,
13986        ));
13987    }
13988
13989    pub fn do_paste(
13990        &mut self,
13991        text: &String,
13992        clipboard_selections: Option<Vec<ClipboardSelection>>,
13993        handle_entire_lines: bool,
13994        window: &mut Window,
13995        cx: &mut Context<Self>,
13996    ) {
13997        if self.read_only(cx) {
13998            return;
13999        }
14000
14001        let clipboard_text = Cow::Borrowed(text.as_str());
14002
14003        self.transact(window, cx, |this, window, cx| {
14004            let had_active_edit_prediction = this.has_active_edit_prediction();
14005            let display_map = this.display_snapshot(cx);
14006            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
14007            let cursor_offset = this
14008                .selections
14009                .last::<MultiBufferOffset>(&display_map)
14010                .head();
14011
14012            if let Some(mut clipboard_selections) = clipboard_selections {
14013                let all_selections_were_entire_line =
14014                    clipboard_selections.iter().all(|s| s.is_entire_line);
14015                let first_selection_indent_column =
14016                    clipboard_selections.first().map(|s| s.first_line_indent);
14017                if clipboard_selections.len() != old_selections.len() {
14018                    clipboard_selections.drain(..);
14019                }
14020                let mut auto_indent_on_paste = true;
14021
14022                this.buffer.update(cx, |buffer, cx| {
14023                    let snapshot = buffer.read(cx);
14024                    auto_indent_on_paste = snapshot
14025                        .language_settings_at(cursor_offset, cx)
14026                        .auto_indent_on_paste;
14027
14028                    let mut start_offset = 0;
14029                    let mut edits = Vec::new();
14030                    let mut original_indent_columns = Vec::new();
14031                    for (ix, selection) in old_selections.iter().enumerate() {
14032                        let to_insert;
14033                        let entire_line;
14034                        let original_indent_column;
14035                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
14036                            let end_offset = start_offset + clipboard_selection.len;
14037                            to_insert = &clipboard_text[start_offset..end_offset];
14038                            entire_line = clipboard_selection.is_entire_line;
14039                            start_offset = if entire_line {
14040                                end_offset
14041                            } else {
14042                                end_offset + 1
14043                            };
14044                            original_indent_column = Some(clipboard_selection.first_line_indent);
14045                        } else {
14046                            to_insert = &*clipboard_text;
14047                            entire_line = all_selections_were_entire_line;
14048                            original_indent_column = first_selection_indent_column
14049                        }
14050
14051                        let (range, to_insert) =
14052                            if selection.is_empty() && handle_entire_lines && entire_line {
14053                                // If the corresponding selection was empty when this slice of the
14054                                // clipboard text was written, then the entire line containing the
14055                                // selection was copied. If this selection is also currently empty,
14056                                // then paste the line before the current line of the buffer.
14057                                let column = selection.start.to_point(&snapshot).column as usize;
14058                                let line_start = selection.start - column;
14059                                (line_start..line_start, Cow::Borrowed(to_insert))
14060                            } else {
14061                                let language = snapshot.language_at(selection.head());
14062                                let range = selection.range();
14063                                if let Some(language) = language
14064                                    && language.name() == "Markdown"
14065                                {
14066                                    edit_for_markdown_paste(
14067                                        &snapshot,
14068                                        range,
14069                                        to_insert,
14070                                        url::Url::parse(to_insert).ok(),
14071                                    )
14072                                } else {
14073                                    (range, Cow::Borrowed(to_insert))
14074                                }
14075                            };
14076
14077                        edits.push((range, to_insert));
14078                        original_indent_columns.push(original_indent_column);
14079                    }
14080                    drop(snapshot);
14081
14082                    buffer.edit(
14083                        edits,
14084                        if auto_indent_on_paste {
14085                            Some(AutoindentMode::Block {
14086                                original_indent_columns,
14087                            })
14088                        } else {
14089                            None
14090                        },
14091                        cx,
14092                    );
14093                });
14094
14095                let selections = this
14096                    .selections
14097                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
14098                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
14099            } else {
14100                let url = url::Url::parse(&clipboard_text).ok();
14101
14102                let auto_indent_mode = if !clipboard_text.is_empty() {
14103                    Some(AutoindentMode::Block {
14104                        original_indent_columns: Vec::new(),
14105                    })
14106                } else {
14107                    None
14108                };
14109
14110                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
14111                    let snapshot = buffer.snapshot(cx);
14112
14113                    let anchors = old_selections
14114                        .iter()
14115                        .map(|s| {
14116                            let anchor = snapshot.anchor_after(s.head());
14117                            s.map(|_| anchor)
14118                        })
14119                        .collect::<Vec<_>>();
14120
14121                    let mut edits = Vec::new();
14122
14123                    // When pasting text without metadata (e.g. copied from an
14124                    // external editor using multiple cursors) and the number of
14125                    // lines matches the number of selections, distribute one
14126                    // line per cursor instead of pasting the whole text at each.
14127                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
14128                    let distribute_lines =
14129                        old_selections.len() > 1 && lines.len() == old_selections.len();
14130
14131                    for (ix, selection) in old_selections.iter().enumerate() {
14132                        let language = snapshot.language_at(selection.head());
14133                        let range = selection.range();
14134
14135                        let text_for_cursor: &str = if distribute_lines {
14136                            lines[ix]
14137                        } else {
14138                            &clipboard_text
14139                        };
14140
14141                        let (edit_range, edit_text) = if let Some(language) = language
14142                            && language.name() == "Markdown"
14143                        {
14144                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
14145                        } else {
14146                            (range, Cow::Borrowed(text_for_cursor))
14147                        };
14148
14149                        edits.push((edit_range, edit_text));
14150                    }
14151
14152                    drop(snapshot);
14153                    buffer.edit(edits, auto_indent_mode, cx);
14154
14155                    anchors
14156                });
14157
14158                this.change_selections(Default::default(), window, cx, |s| {
14159                    s.select_anchors(selection_anchors);
14160                });
14161            }
14162
14163            //   🤔                 |    ..     | show_in_menu |
14164            // | ..                  |   true        true
14165            // | had_edit_prediction |   false       true
14166
14167            let trigger_in_words =
14168                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
14169
14170            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
14171        });
14172    }
14173
14174    pub fn diff_clipboard_with_selection(
14175        &mut self,
14176        _: &DiffClipboardWithSelection,
14177        window: &mut Window,
14178        cx: &mut Context<Self>,
14179    ) {
14180        let selections = self
14181            .selections
14182            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
14183
14184        if selections.is_empty() {
14185            log::warn!("There should always be at least one selection in Zed. This is a bug.");
14186            return;
14187        };
14188
14189        let clipboard_text = cx.read_from_clipboard().and_then(|item| {
14190            item.entries().iter().find_map(|entry| match entry {
14191                ClipboardEntry::String(text) => Some(text.text().to_string()),
14192                _ => None,
14193            })
14194        });
14195
14196        let Some(clipboard_text) = clipboard_text else {
14197            log::warn!("Clipboard doesn't contain text.");
14198            return;
14199        };
14200
14201        window.dispatch_action(
14202            Box::new(DiffClipboardWithSelectionData {
14203                clipboard_text,
14204                editor: cx.entity(),
14205            }),
14206            cx,
14207        );
14208    }
14209
14210    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
14211        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14212        if let Some(item) = cx.read_from_clipboard() {
14213            let clipboard_string = item.entries().iter().find_map(|entry| match entry {
14214                ClipboardEntry::String(s) => Some(s),
14215                _ => None,
14216            });
14217            match clipboard_string {
14218                Some(clipboard_string) => self.do_paste(
14219                    clipboard_string.text(),
14220                    clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
14221                    true,
14222                    window,
14223                    cx,
14224                ),
14225                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14226            }
14227        }
14228    }
14229
14230    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14231        if self.read_only(cx) {
14232            return;
14233        }
14234
14235        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14236
14237        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14238            if let Some((selections, _)) =
14239                self.selection_history.transaction(transaction_id).cloned()
14240            {
14241                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14242                    s.select_anchors(selections.to_vec());
14243                });
14244            } else {
14245                log::error!(
14246                    "No entry in selection_history found for undo. \
14247                     This may correspond to a bug where undo does not update the selection. \
14248                     If this is occurring, please add details to \
14249                     https://github.com/zed-industries/zed/issues/22692"
14250                );
14251            }
14252            self.request_autoscroll(Autoscroll::fit(), cx);
14253            self.unmark_text(window, cx);
14254            self.refresh_edit_prediction(true, false, window, cx);
14255            cx.emit(EditorEvent::Edited { transaction_id });
14256            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14257        }
14258    }
14259
14260    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14261        if self.read_only(cx) {
14262            return;
14263        }
14264
14265        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14266
14267        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14268            if let Some((_, Some(selections))) =
14269                self.selection_history.transaction(transaction_id).cloned()
14270            {
14271                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14272                    s.select_anchors(selections.to_vec());
14273                });
14274            } else {
14275                log::error!(
14276                    "No entry in selection_history found for redo. \
14277                     This may correspond to a bug where undo does not update the selection. \
14278                     If this is occurring, please add details to \
14279                     https://github.com/zed-industries/zed/issues/22692"
14280                );
14281            }
14282            self.request_autoscroll(Autoscroll::fit(), cx);
14283            self.unmark_text(window, cx);
14284            self.refresh_edit_prediction(true, false, window, cx);
14285            cx.emit(EditorEvent::Edited { transaction_id });
14286        }
14287    }
14288
14289    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14290        self.buffer
14291            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14292    }
14293
14294    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14295        self.buffer
14296            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14297    }
14298
14299    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14300        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14301        self.change_selections(Default::default(), window, cx, |s| {
14302            s.move_with(&mut |map, selection| {
14303                let cursor = if selection.is_empty() {
14304                    movement::left(map, selection.start)
14305                } else {
14306                    selection.start
14307                };
14308                selection.collapse_to(cursor, SelectionGoal::None);
14309            });
14310        })
14311    }
14312
14313    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14314        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14315        self.change_selections(Default::default(), window, cx, |s| {
14316            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14317        })
14318    }
14319
14320    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14321        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14322        self.change_selections(Default::default(), window, cx, |s| {
14323            s.move_with(&mut |map, selection| {
14324                let cursor = if selection.is_empty() {
14325                    movement::right(map, selection.end)
14326                } else {
14327                    selection.end
14328                };
14329                selection.collapse_to(cursor, SelectionGoal::None)
14330            });
14331        })
14332    }
14333
14334    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14335        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14336        self.change_selections(Default::default(), window, cx, |s| {
14337            s.move_heads_with(&mut |map, head, _| {
14338                (movement::right(map, head), SelectionGoal::None)
14339            });
14340        });
14341    }
14342
14343    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14344        if self.take_rename(true, window, cx).is_some() {
14345            return;
14346        }
14347
14348        if self.mode.is_single_line() {
14349            cx.propagate();
14350            return;
14351        }
14352
14353        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14354
14355        let text_layout_details = &self.text_layout_details(window, cx);
14356        let selection_count = self.selections.count();
14357        let first_selection = self.selections.first_anchor();
14358
14359        self.change_selections(Default::default(), window, cx, |s| {
14360            s.move_with(&mut |map, selection| {
14361                if !selection.is_empty() {
14362                    selection.goal = SelectionGoal::None;
14363                }
14364                let (cursor, goal) = movement::up(
14365                    map,
14366                    selection.start,
14367                    selection.goal,
14368                    false,
14369                    text_layout_details,
14370                );
14371                selection.collapse_to(cursor, goal);
14372            });
14373        });
14374
14375        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14376        {
14377            cx.propagate();
14378        }
14379    }
14380
14381    pub fn move_up_by_lines(
14382        &mut self,
14383        action: &MoveUpByLines,
14384        window: &mut Window,
14385        cx: &mut Context<Self>,
14386    ) {
14387        if self.take_rename(true, window, cx).is_some() {
14388            return;
14389        }
14390
14391        if self.mode.is_single_line() {
14392            cx.propagate();
14393            return;
14394        }
14395
14396        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14397
14398        let text_layout_details = &self.text_layout_details(window, cx);
14399
14400        self.change_selections(Default::default(), window, cx, |s| {
14401            s.move_with(&mut |map, selection| {
14402                if !selection.is_empty() {
14403                    selection.goal = SelectionGoal::None;
14404                }
14405                let (cursor, goal) = movement::up_by_rows(
14406                    map,
14407                    selection.start,
14408                    action.lines,
14409                    selection.goal,
14410                    false,
14411                    text_layout_details,
14412                );
14413                selection.collapse_to(cursor, goal);
14414            });
14415        })
14416    }
14417
14418    pub fn move_down_by_lines(
14419        &mut self,
14420        action: &MoveDownByLines,
14421        window: &mut Window,
14422        cx: &mut Context<Self>,
14423    ) {
14424        if self.take_rename(true, window, cx).is_some() {
14425            return;
14426        }
14427
14428        if self.mode.is_single_line() {
14429            cx.propagate();
14430            return;
14431        }
14432
14433        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14434
14435        let text_layout_details = &self.text_layout_details(window, cx);
14436
14437        self.change_selections(Default::default(), window, cx, |s| {
14438            s.move_with(&mut |map, selection| {
14439                if !selection.is_empty() {
14440                    selection.goal = SelectionGoal::None;
14441                }
14442                let (cursor, goal) = movement::down_by_rows(
14443                    map,
14444                    selection.start,
14445                    action.lines,
14446                    selection.goal,
14447                    false,
14448                    text_layout_details,
14449                );
14450                selection.collapse_to(cursor, goal);
14451            });
14452        })
14453    }
14454
14455    pub fn select_down_by_lines(
14456        &mut self,
14457        action: &SelectDownByLines,
14458        window: &mut Window,
14459        cx: &mut Context<Self>,
14460    ) {
14461        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14462        let text_layout_details = &self.text_layout_details(window, cx);
14463        self.change_selections(Default::default(), window, cx, |s| {
14464            s.move_heads_with(&mut |map, head, goal| {
14465                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14466            })
14467        })
14468    }
14469
14470    pub fn select_up_by_lines(
14471        &mut self,
14472        action: &SelectUpByLines,
14473        window: &mut Window,
14474        cx: &mut Context<Self>,
14475    ) {
14476        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14477        let text_layout_details = &self.text_layout_details(window, cx);
14478        self.change_selections(Default::default(), window, cx, |s| {
14479            s.move_heads_with(&mut |map, head, goal| {
14480                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14481            })
14482        })
14483    }
14484
14485    pub fn select_page_up(
14486        &mut self,
14487        _: &SelectPageUp,
14488        window: &mut Window,
14489        cx: &mut Context<Self>,
14490    ) {
14491        let Some(row_count) = self.visible_row_count() else {
14492            return;
14493        };
14494
14495        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14496
14497        let text_layout_details = &self.text_layout_details(window, cx);
14498
14499        self.change_selections(Default::default(), window, cx, |s| {
14500            s.move_heads_with(&mut |map, head, goal| {
14501                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14502            })
14503        })
14504    }
14505
14506    pub fn move_page_up(
14507        &mut self,
14508        action: &MovePageUp,
14509        window: &mut Window,
14510        cx: &mut Context<Self>,
14511    ) {
14512        if self.take_rename(true, window, cx).is_some() {
14513            return;
14514        }
14515
14516        if self
14517            .context_menu
14518            .borrow_mut()
14519            .as_mut()
14520            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14521            .unwrap_or(false)
14522        {
14523            return;
14524        }
14525
14526        if matches!(self.mode, EditorMode::SingleLine) {
14527            cx.propagate();
14528            return;
14529        }
14530
14531        let Some(row_count) = self.visible_row_count() else {
14532            return;
14533        };
14534
14535        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14536
14537        let effects = if action.center_cursor {
14538            SelectionEffects::scroll(Autoscroll::center())
14539        } else {
14540            SelectionEffects::default()
14541        };
14542
14543        let text_layout_details = &self.text_layout_details(window, cx);
14544
14545        self.change_selections(effects, window, cx, |s| {
14546            s.move_with(&mut |map, selection| {
14547                if !selection.is_empty() {
14548                    selection.goal = SelectionGoal::None;
14549                }
14550                let (cursor, goal) = movement::up_by_rows(
14551                    map,
14552                    selection.end,
14553                    row_count,
14554                    selection.goal,
14555                    false,
14556                    text_layout_details,
14557                );
14558                selection.collapse_to(cursor, goal);
14559            });
14560        });
14561    }
14562
14563    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14564        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14565        let text_layout_details = &self.text_layout_details(window, cx);
14566        self.change_selections(Default::default(), window, cx, |s| {
14567            s.move_heads_with(&mut |map, head, goal| {
14568                movement::up(map, head, goal, false, text_layout_details)
14569            })
14570        })
14571    }
14572
14573    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14574        self.take_rename(true, window, cx);
14575
14576        if self.mode.is_single_line() {
14577            cx.propagate();
14578            return;
14579        }
14580
14581        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14582
14583        let text_layout_details = &self.text_layout_details(window, cx);
14584        let selection_count = self.selections.count();
14585        let first_selection = self.selections.first_anchor();
14586
14587        self.change_selections(Default::default(), window, cx, |s| {
14588            s.move_with(&mut |map, selection| {
14589                if !selection.is_empty() {
14590                    selection.goal = SelectionGoal::None;
14591                }
14592                let (cursor, goal) = movement::down(
14593                    map,
14594                    selection.end,
14595                    selection.goal,
14596                    false,
14597                    text_layout_details,
14598                );
14599                selection.collapse_to(cursor, goal);
14600            });
14601        });
14602
14603        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14604        {
14605            cx.propagate();
14606        }
14607    }
14608
14609    pub fn select_page_down(
14610        &mut self,
14611        _: &SelectPageDown,
14612        window: &mut Window,
14613        cx: &mut Context<Self>,
14614    ) {
14615        let Some(row_count) = self.visible_row_count() else {
14616            return;
14617        };
14618
14619        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14620
14621        let text_layout_details = &self.text_layout_details(window, cx);
14622
14623        self.change_selections(Default::default(), window, cx, |s| {
14624            s.move_heads_with(&mut |map, head, goal| {
14625                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14626            })
14627        })
14628    }
14629
14630    pub fn move_page_down(
14631        &mut self,
14632        action: &MovePageDown,
14633        window: &mut Window,
14634        cx: &mut Context<Self>,
14635    ) {
14636        if self.take_rename(true, window, cx).is_some() {
14637            return;
14638        }
14639
14640        if self
14641            .context_menu
14642            .borrow_mut()
14643            .as_mut()
14644            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14645            .unwrap_or(false)
14646        {
14647            return;
14648        }
14649
14650        if matches!(self.mode, EditorMode::SingleLine) {
14651            cx.propagate();
14652            return;
14653        }
14654
14655        let Some(row_count) = self.visible_row_count() else {
14656            return;
14657        };
14658
14659        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14660
14661        let effects = if action.center_cursor {
14662            SelectionEffects::scroll(Autoscroll::center())
14663        } else {
14664            SelectionEffects::default()
14665        };
14666
14667        let text_layout_details = &self.text_layout_details(window, cx);
14668        self.change_selections(effects, window, cx, |s| {
14669            s.move_with(&mut |map, selection| {
14670                if !selection.is_empty() {
14671                    selection.goal = SelectionGoal::None;
14672                }
14673                let (cursor, goal) = movement::down_by_rows(
14674                    map,
14675                    selection.end,
14676                    row_count,
14677                    selection.goal,
14678                    false,
14679                    text_layout_details,
14680                );
14681                selection.collapse_to(cursor, goal);
14682            });
14683        });
14684    }
14685
14686    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14687        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14688        let text_layout_details = &self.text_layout_details(window, cx);
14689        self.change_selections(Default::default(), window, cx, |s| {
14690            s.move_heads_with(&mut |map, head, goal| {
14691                movement::down(map, head, goal, false, text_layout_details)
14692            })
14693        });
14694    }
14695
14696    pub fn context_menu_first(
14697        &mut self,
14698        _: &ContextMenuFirst,
14699        window: &mut Window,
14700        cx: &mut Context<Self>,
14701    ) {
14702        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14703            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14704        }
14705    }
14706
14707    pub fn context_menu_prev(
14708        &mut self,
14709        _: &ContextMenuPrevious,
14710        window: &mut Window,
14711        cx: &mut Context<Self>,
14712    ) {
14713        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14714            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14715        }
14716    }
14717
14718    pub fn context_menu_next(
14719        &mut self,
14720        _: &ContextMenuNext,
14721        window: &mut Window,
14722        cx: &mut Context<Self>,
14723    ) {
14724        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14725            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14726        }
14727    }
14728
14729    pub fn context_menu_last(
14730        &mut self,
14731        _: &ContextMenuLast,
14732        window: &mut Window,
14733        cx: &mut Context<Self>,
14734    ) {
14735        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14736            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14737        }
14738    }
14739
14740    pub fn signature_help_prev(
14741        &mut self,
14742        _: &SignatureHelpPrevious,
14743        _: &mut Window,
14744        cx: &mut Context<Self>,
14745    ) {
14746        if let Some(popover) = self.signature_help_state.popover_mut() {
14747            if popover.current_signature == 0 {
14748                popover.current_signature = popover.signatures.len() - 1;
14749            } else {
14750                popover.current_signature -= 1;
14751            }
14752            cx.notify();
14753        }
14754    }
14755
14756    pub fn signature_help_next(
14757        &mut self,
14758        _: &SignatureHelpNext,
14759        _: &mut Window,
14760        cx: &mut Context<Self>,
14761    ) {
14762        if let Some(popover) = self.signature_help_state.popover_mut() {
14763            if popover.current_signature + 1 == popover.signatures.len() {
14764                popover.current_signature = 0;
14765            } else {
14766                popover.current_signature += 1;
14767            }
14768            cx.notify();
14769        }
14770    }
14771
14772    pub fn move_to_previous_word_start(
14773        &mut self,
14774        _: &MoveToPreviousWordStart,
14775        window: &mut Window,
14776        cx: &mut Context<Self>,
14777    ) {
14778        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14779        self.change_selections(Default::default(), window, cx, |s| {
14780            s.move_cursors_with(&mut |map, head, _| {
14781                (
14782                    movement::previous_word_start(map, head),
14783                    SelectionGoal::None,
14784                )
14785            });
14786        })
14787    }
14788
14789    pub fn move_to_previous_subword_start(
14790        &mut self,
14791        _: &MoveToPreviousSubwordStart,
14792        window: &mut Window,
14793        cx: &mut Context<Self>,
14794    ) {
14795        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14796        self.change_selections(Default::default(), window, cx, |s| {
14797            s.move_cursors_with(&mut |map, head, _| {
14798                (
14799                    movement::previous_subword_start(map, head),
14800                    SelectionGoal::None,
14801                )
14802            });
14803        })
14804    }
14805
14806    pub fn select_to_previous_word_start(
14807        &mut self,
14808        _: &SelectToPreviousWordStart,
14809        window: &mut Window,
14810        cx: &mut Context<Self>,
14811    ) {
14812        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14813        self.change_selections(Default::default(), window, cx, |s| {
14814            s.move_heads_with(&mut |map, head, _| {
14815                (
14816                    movement::previous_word_start(map, head),
14817                    SelectionGoal::None,
14818                )
14819            });
14820        })
14821    }
14822
14823    pub fn select_to_previous_subword_start(
14824        &mut self,
14825        _: &SelectToPreviousSubwordStart,
14826        window: &mut Window,
14827        cx: &mut Context<Self>,
14828    ) {
14829        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14830        self.change_selections(Default::default(), window, cx, |s| {
14831            s.move_heads_with(&mut |map, head, _| {
14832                (
14833                    movement::previous_subword_start(map, head),
14834                    SelectionGoal::None,
14835                )
14836            });
14837        })
14838    }
14839
14840    pub fn delete_to_previous_word_start(
14841        &mut self,
14842        action: &DeleteToPreviousWordStart,
14843        window: &mut Window,
14844        cx: &mut Context<Self>,
14845    ) {
14846        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14847        self.transact(window, cx, |this, window, cx| {
14848            this.select_autoclose_pair(window, cx);
14849            this.change_selections(Default::default(), window, cx, |s| {
14850                s.move_with(&mut |map, selection| {
14851                    if selection.is_empty() {
14852                        let mut cursor = if action.ignore_newlines {
14853                            movement::previous_word_start(map, selection.head())
14854                        } else {
14855                            movement::previous_word_start_or_newline(map, selection.head())
14856                        };
14857                        cursor = movement::adjust_greedy_deletion(
14858                            map,
14859                            selection.head(),
14860                            cursor,
14861                            action.ignore_brackets,
14862                        );
14863                        selection.set_head(cursor, SelectionGoal::None);
14864                    }
14865                });
14866            });
14867            this.insert("", window, cx);
14868        });
14869    }
14870
14871    pub fn delete_to_previous_subword_start(
14872        &mut self,
14873        action: &DeleteToPreviousSubwordStart,
14874        window: &mut Window,
14875        cx: &mut Context<Self>,
14876    ) {
14877        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14878        self.transact(window, cx, |this, window, cx| {
14879            this.select_autoclose_pair(window, cx);
14880            this.change_selections(Default::default(), window, cx, |s| {
14881                s.move_with(&mut |map, selection| {
14882                    if selection.is_empty() {
14883                        let mut cursor = if action.ignore_newlines {
14884                            movement::previous_subword_start(map, selection.head())
14885                        } else {
14886                            movement::previous_subword_start_or_newline(map, selection.head())
14887                        };
14888                        cursor = movement::adjust_greedy_deletion(
14889                            map,
14890                            selection.head(),
14891                            cursor,
14892                            action.ignore_brackets,
14893                        );
14894                        selection.set_head(cursor, SelectionGoal::None);
14895                    }
14896                });
14897            });
14898            this.insert("", window, cx);
14899        });
14900    }
14901
14902    pub fn move_to_next_word_end(
14903        &mut self,
14904        _: &MoveToNextWordEnd,
14905        window: &mut Window,
14906        cx: &mut Context<Self>,
14907    ) {
14908        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14909        self.change_selections(Default::default(), window, cx, |s| {
14910            s.move_cursors_with(&mut |map, head, _| {
14911                (movement::next_word_end(map, head), SelectionGoal::None)
14912            });
14913        })
14914    }
14915
14916    pub fn move_to_next_subword_end(
14917        &mut self,
14918        _: &MoveToNextSubwordEnd,
14919        window: &mut Window,
14920        cx: &mut Context<Self>,
14921    ) {
14922        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14923        self.change_selections(Default::default(), window, cx, |s| {
14924            s.move_cursors_with(&mut |map, head, _| {
14925                (movement::next_subword_end(map, head), SelectionGoal::None)
14926            });
14927        })
14928    }
14929
14930    pub fn select_to_next_word_end(
14931        &mut self,
14932        _: &SelectToNextWordEnd,
14933        window: &mut Window,
14934        cx: &mut Context<Self>,
14935    ) {
14936        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14937        self.change_selections(Default::default(), window, cx, |s| {
14938            s.move_heads_with(&mut |map, head, _| {
14939                (movement::next_word_end(map, head), SelectionGoal::None)
14940            });
14941        })
14942    }
14943
14944    pub fn select_to_next_subword_end(
14945        &mut self,
14946        _: &SelectToNextSubwordEnd,
14947        window: &mut Window,
14948        cx: &mut Context<Self>,
14949    ) {
14950        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14951        self.change_selections(Default::default(), window, cx, |s| {
14952            s.move_heads_with(&mut |map, head, _| {
14953                (movement::next_subword_end(map, head), SelectionGoal::None)
14954            });
14955        })
14956    }
14957
14958    pub fn delete_to_next_word_end(
14959        &mut self,
14960        action: &DeleteToNextWordEnd,
14961        window: &mut Window,
14962        cx: &mut Context<Self>,
14963    ) {
14964        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14965        self.transact(window, cx, |this, window, cx| {
14966            this.change_selections(Default::default(), window, cx, |s| {
14967                s.move_with(&mut |map, selection| {
14968                    if selection.is_empty() {
14969                        let mut cursor = if action.ignore_newlines {
14970                            movement::next_word_end(map, selection.head())
14971                        } else {
14972                            movement::next_word_end_or_newline(map, selection.head())
14973                        };
14974                        cursor = movement::adjust_greedy_deletion(
14975                            map,
14976                            selection.head(),
14977                            cursor,
14978                            action.ignore_brackets,
14979                        );
14980                        selection.set_head(cursor, SelectionGoal::None);
14981                    }
14982                });
14983            });
14984            this.insert("", window, cx);
14985        });
14986    }
14987
14988    pub fn delete_to_next_subword_end(
14989        &mut self,
14990        action: &DeleteToNextSubwordEnd,
14991        window: &mut Window,
14992        cx: &mut Context<Self>,
14993    ) {
14994        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14995        self.transact(window, cx, |this, window, cx| {
14996            this.change_selections(Default::default(), window, cx, |s| {
14997                s.move_with(&mut |map, selection| {
14998                    if selection.is_empty() {
14999                        let mut cursor = if action.ignore_newlines {
15000                            movement::next_subword_end(map, selection.head())
15001                        } else {
15002                            movement::next_subword_end_or_newline(map, selection.head())
15003                        };
15004                        cursor = movement::adjust_greedy_deletion(
15005                            map,
15006                            selection.head(),
15007                            cursor,
15008                            action.ignore_brackets,
15009                        );
15010                        selection.set_head(cursor, SelectionGoal::None);
15011                    }
15012                });
15013            });
15014            this.insert("", window, cx);
15015        });
15016    }
15017
15018    pub fn move_to_beginning_of_line(
15019        &mut self,
15020        action: &MoveToBeginningOfLine,
15021        window: &mut Window,
15022        cx: &mut Context<Self>,
15023    ) {
15024        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15025        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15026        self.change_selections(Default::default(), window, cx, |s| {
15027            s.move_cursors_with(&mut |map, head, _| {
15028                (
15029                    movement::indented_line_beginning(
15030                        map,
15031                        head,
15032                        action.stop_at_soft_wraps,
15033                        stop_at_indent,
15034                    ),
15035                    SelectionGoal::None,
15036                )
15037            });
15038        })
15039    }
15040
15041    pub fn select_to_beginning_of_line(
15042        &mut self,
15043        action: &SelectToBeginningOfLine,
15044        window: &mut Window,
15045        cx: &mut Context<Self>,
15046    ) {
15047        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15048        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15049        self.change_selections(Default::default(), window, cx, |s| {
15050            s.move_heads_with(&mut |map, head, _| {
15051                (
15052                    movement::indented_line_beginning(
15053                        map,
15054                        head,
15055                        action.stop_at_soft_wraps,
15056                        stop_at_indent,
15057                    ),
15058                    SelectionGoal::None,
15059                )
15060            });
15061        });
15062    }
15063
15064    pub fn delete_to_beginning_of_line(
15065        &mut self,
15066        action: &DeleteToBeginningOfLine,
15067        window: &mut Window,
15068        cx: &mut Context<Self>,
15069    ) {
15070        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15071        self.transact(window, cx, |this, window, cx| {
15072            this.change_selections(Default::default(), window, cx, |s| {
15073                s.move_with(&mut |_, selection| {
15074                    selection.reversed = true;
15075                });
15076            });
15077
15078            this.select_to_beginning_of_line(
15079                &SelectToBeginningOfLine {
15080                    stop_at_soft_wraps: false,
15081                    stop_at_indent: action.stop_at_indent,
15082                },
15083                window,
15084                cx,
15085            );
15086            this.backspace(&Backspace, window, cx);
15087        });
15088    }
15089
15090    pub fn move_to_end_of_line(
15091        &mut self,
15092        action: &MoveToEndOfLine,
15093        window: &mut Window,
15094        cx: &mut Context<Self>,
15095    ) {
15096        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15097        self.change_selections(Default::default(), window, cx, |s| {
15098            s.move_cursors_with(&mut |map, head, _| {
15099                (
15100                    movement::line_end(map, head, action.stop_at_soft_wraps),
15101                    SelectionGoal::None,
15102                )
15103            });
15104        })
15105    }
15106
15107    pub fn select_to_end_of_line(
15108        &mut self,
15109        action: &SelectToEndOfLine,
15110        window: &mut Window,
15111        cx: &mut Context<Self>,
15112    ) {
15113        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15114        self.change_selections(Default::default(), window, cx, |s| {
15115            s.move_heads_with(&mut |map, head, _| {
15116                (
15117                    movement::line_end(map, head, action.stop_at_soft_wraps),
15118                    SelectionGoal::None,
15119                )
15120            });
15121        })
15122    }
15123
15124    pub fn delete_to_end_of_line(
15125        &mut self,
15126        _: &DeleteToEndOfLine,
15127        window: &mut Window,
15128        cx: &mut Context<Self>,
15129    ) {
15130        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15131        self.transact(window, cx, |this, window, cx| {
15132            this.select_to_end_of_line(
15133                &SelectToEndOfLine {
15134                    stop_at_soft_wraps: false,
15135                },
15136                window,
15137                cx,
15138            );
15139            this.delete(&Delete, window, cx);
15140        });
15141    }
15142
15143    pub fn cut_to_end_of_line(
15144        &mut self,
15145        action: &CutToEndOfLine,
15146        window: &mut Window,
15147        cx: &mut Context<Self>,
15148    ) {
15149        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15150        self.transact(window, cx, |this, window, cx| {
15151            this.select_to_end_of_line(
15152                &SelectToEndOfLine {
15153                    stop_at_soft_wraps: false,
15154                },
15155                window,
15156                cx,
15157            );
15158            if !action.stop_at_newlines {
15159                this.change_selections(Default::default(), window, cx, |s| {
15160                    s.move_with(&mut |_, sel| {
15161                        if sel.is_empty() {
15162                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
15163                        }
15164                    });
15165                });
15166            }
15167            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15168            let item = this.cut_common(false, window, cx);
15169            cx.write_to_clipboard(item);
15170        });
15171    }
15172
15173    pub fn move_to_start_of_paragraph(
15174        &mut self,
15175        _: &MoveToStartOfParagraph,
15176        window: &mut Window,
15177        cx: &mut Context<Self>,
15178    ) {
15179        if matches!(self.mode, EditorMode::SingleLine) {
15180            cx.propagate();
15181            return;
15182        }
15183        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15184        self.change_selections(Default::default(), window, cx, |s| {
15185            s.move_with(&mut |map, selection| {
15186                selection.collapse_to(
15187                    movement::start_of_paragraph(map, selection.head(), 1),
15188                    SelectionGoal::None,
15189                )
15190            });
15191        })
15192    }
15193
15194    pub fn move_to_end_of_paragraph(
15195        &mut self,
15196        _: &MoveToEndOfParagraph,
15197        window: &mut Window,
15198        cx: &mut Context<Self>,
15199    ) {
15200        if matches!(self.mode, EditorMode::SingleLine) {
15201            cx.propagate();
15202            return;
15203        }
15204        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15205        self.change_selections(Default::default(), window, cx, |s| {
15206            s.move_with(&mut |map, selection| {
15207                selection.collapse_to(
15208                    movement::end_of_paragraph(map, selection.head(), 1),
15209                    SelectionGoal::None,
15210                )
15211            });
15212        })
15213    }
15214
15215    pub fn select_to_start_of_paragraph(
15216        &mut self,
15217        _: &SelectToStartOfParagraph,
15218        window: &mut Window,
15219        cx: &mut Context<Self>,
15220    ) {
15221        if matches!(self.mode, EditorMode::SingleLine) {
15222            cx.propagate();
15223            return;
15224        }
15225        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15226        self.change_selections(Default::default(), window, cx, |s| {
15227            s.move_heads_with(&mut |map, head, _| {
15228                (
15229                    movement::start_of_paragraph(map, head, 1),
15230                    SelectionGoal::None,
15231                )
15232            });
15233        })
15234    }
15235
15236    pub fn select_to_end_of_paragraph(
15237        &mut self,
15238        _: &SelectToEndOfParagraph,
15239        window: &mut Window,
15240        cx: &mut Context<Self>,
15241    ) {
15242        if matches!(self.mode, EditorMode::SingleLine) {
15243            cx.propagate();
15244            return;
15245        }
15246        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15247        self.change_selections(Default::default(), window, cx, |s| {
15248            s.move_heads_with(&mut |map, head, _| {
15249                (
15250                    movement::end_of_paragraph(map, head, 1),
15251                    SelectionGoal::None,
15252                )
15253            });
15254        })
15255    }
15256
15257    pub fn move_to_start_of_excerpt(
15258        &mut self,
15259        _: &MoveToStartOfExcerpt,
15260        window: &mut Window,
15261        cx: &mut Context<Self>,
15262    ) {
15263        if matches!(self.mode, EditorMode::SingleLine) {
15264            cx.propagate();
15265            return;
15266        }
15267        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15268        self.change_selections(Default::default(), window, cx, |s| {
15269            s.move_with(&mut |map, selection| {
15270                selection.collapse_to(
15271                    movement::start_of_excerpt(
15272                        map,
15273                        selection.head(),
15274                        workspace::searchable::Direction::Prev,
15275                    ),
15276                    SelectionGoal::None,
15277                )
15278            });
15279        })
15280    }
15281
15282    pub fn move_to_start_of_next_excerpt(
15283        &mut self,
15284        _: &MoveToStartOfNextExcerpt,
15285        window: &mut Window,
15286        cx: &mut Context<Self>,
15287    ) {
15288        if matches!(self.mode, EditorMode::SingleLine) {
15289            cx.propagate();
15290            return;
15291        }
15292
15293        self.change_selections(Default::default(), window, cx, |s| {
15294            s.move_with(&mut |map, selection| {
15295                selection.collapse_to(
15296                    movement::start_of_excerpt(
15297                        map,
15298                        selection.head(),
15299                        workspace::searchable::Direction::Next,
15300                    ),
15301                    SelectionGoal::None,
15302                )
15303            });
15304        })
15305    }
15306
15307    pub fn move_to_end_of_excerpt(
15308        &mut self,
15309        _: &MoveToEndOfExcerpt,
15310        window: &mut Window,
15311        cx: &mut Context<Self>,
15312    ) {
15313        if matches!(self.mode, EditorMode::SingleLine) {
15314            cx.propagate();
15315            return;
15316        }
15317        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15318        self.change_selections(Default::default(), window, cx, |s| {
15319            s.move_with(&mut |map, selection| {
15320                selection.collapse_to(
15321                    movement::end_of_excerpt(
15322                        map,
15323                        selection.head(),
15324                        workspace::searchable::Direction::Next,
15325                    ),
15326                    SelectionGoal::None,
15327                )
15328            });
15329        })
15330    }
15331
15332    pub fn move_to_end_of_previous_excerpt(
15333        &mut self,
15334        _: &MoveToEndOfPreviousExcerpt,
15335        window: &mut Window,
15336        cx: &mut Context<Self>,
15337    ) {
15338        if matches!(self.mode, EditorMode::SingleLine) {
15339            cx.propagate();
15340            return;
15341        }
15342        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15343        self.change_selections(Default::default(), window, cx, |s| {
15344            s.move_with(&mut |map, selection| {
15345                selection.collapse_to(
15346                    movement::end_of_excerpt(
15347                        map,
15348                        selection.head(),
15349                        workspace::searchable::Direction::Prev,
15350                    ),
15351                    SelectionGoal::None,
15352                )
15353            });
15354        })
15355    }
15356
15357    pub fn select_to_start_of_excerpt(
15358        &mut self,
15359        _: &SelectToStartOfExcerpt,
15360        window: &mut Window,
15361        cx: &mut Context<Self>,
15362    ) {
15363        if matches!(self.mode, EditorMode::SingleLine) {
15364            cx.propagate();
15365            return;
15366        }
15367        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15368        self.change_selections(Default::default(), window, cx, |s| {
15369            s.move_heads_with(&mut |map, head, _| {
15370                (
15371                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15372                    SelectionGoal::None,
15373                )
15374            });
15375        })
15376    }
15377
15378    pub fn select_to_start_of_next_excerpt(
15379        &mut self,
15380        _: &SelectToStartOfNextExcerpt,
15381        window: &mut Window,
15382        cx: &mut Context<Self>,
15383    ) {
15384        if matches!(self.mode, EditorMode::SingleLine) {
15385            cx.propagate();
15386            return;
15387        }
15388        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15389        self.change_selections(Default::default(), window, cx, |s| {
15390            s.move_heads_with(&mut |map, head, _| {
15391                (
15392                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15393                    SelectionGoal::None,
15394                )
15395            });
15396        })
15397    }
15398
15399    pub fn select_to_end_of_excerpt(
15400        &mut self,
15401        _: &SelectToEndOfExcerpt,
15402        window: &mut Window,
15403        cx: &mut Context<Self>,
15404    ) {
15405        if matches!(self.mode, EditorMode::SingleLine) {
15406            cx.propagate();
15407            return;
15408        }
15409        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15410        self.change_selections(Default::default(), window, cx, |s| {
15411            s.move_heads_with(&mut |map, head, _| {
15412                (
15413                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15414                    SelectionGoal::None,
15415                )
15416            });
15417        })
15418    }
15419
15420    pub fn select_to_end_of_previous_excerpt(
15421        &mut self,
15422        _: &SelectToEndOfPreviousExcerpt,
15423        window: &mut Window,
15424        cx: &mut Context<Self>,
15425    ) {
15426        if matches!(self.mode, EditorMode::SingleLine) {
15427            cx.propagate();
15428            return;
15429        }
15430        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15431        self.change_selections(Default::default(), window, cx, |s| {
15432            s.move_heads_with(&mut |map, head, _| {
15433                (
15434                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15435                    SelectionGoal::None,
15436                )
15437            });
15438        })
15439    }
15440
15441    pub fn move_to_beginning(
15442        &mut self,
15443        _: &MoveToBeginning,
15444        window: &mut Window,
15445        cx: &mut Context<Self>,
15446    ) {
15447        if matches!(self.mode, EditorMode::SingleLine) {
15448            cx.propagate();
15449            return;
15450        }
15451        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15452        self.change_selections(Default::default(), window, cx, |s| {
15453            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15454        });
15455    }
15456
15457    pub fn select_to_beginning(
15458        &mut self,
15459        _: &SelectToBeginning,
15460        window: &mut Window,
15461        cx: &mut Context<Self>,
15462    ) {
15463        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15464        selection.set_head(Point::zero(), SelectionGoal::None);
15465        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15466        self.change_selections(Default::default(), window, cx, |s| {
15467            s.select(vec![selection]);
15468        });
15469    }
15470
15471    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15472        if matches!(self.mode, EditorMode::SingleLine) {
15473            cx.propagate();
15474            return;
15475        }
15476        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15477        let cursor = self.buffer.read(cx).read(cx).len();
15478        self.change_selections(Default::default(), window, cx, |s| {
15479            s.select_ranges(vec![cursor..cursor])
15480        });
15481    }
15482
15483    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15484        self.nav_history = nav_history;
15485    }
15486
15487    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15488        self.nav_history.as_ref()
15489    }
15490
15491    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15492        self.push_to_nav_history(
15493            self.selections.newest_anchor().head(),
15494            None,
15495            false,
15496            true,
15497            cx,
15498        );
15499    }
15500
15501    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15502        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15503        let buffer = self.buffer.read(cx).read(cx);
15504        let cursor_position = cursor_anchor.to_point(&buffer);
15505        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15506        let scroll_top_row = scroll_anchor.top_row(&buffer);
15507        drop(buffer);
15508
15509        NavigationData {
15510            cursor_anchor,
15511            cursor_position,
15512            scroll_anchor,
15513            scroll_top_row,
15514        }
15515    }
15516
15517    fn navigation_entry(
15518        &self,
15519        cursor_anchor: Anchor,
15520        cx: &mut Context<Self>,
15521    ) -> Option<NavigationEntry> {
15522        let Some(history) = self.nav_history.clone() else {
15523            return None;
15524        };
15525        let data = self.navigation_data(cursor_anchor, cx);
15526        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15527    }
15528
15529    fn push_to_nav_history(
15530        &mut self,
15531        cursor_anchor: Anchor,
15532        new_position: Option<Point>,
15533        is_deactivate: bool,
15534        always: bool,
15535        cx: &mut Context<Self>,
15536    ) {
15537        let data = self.navigation_data(cursor_anchor, cx);
15538        if let Some(nav_history) = self.nav_history.as_mut() {
15539            if let Some(new_position) = new_position {
15540                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15541                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15542                    return;
15543                }
15544            }
15545
15546            nav_history.push(Some(data), cx);
15547            cx.emit(EditorEvent::PushedToNavHistory {
15548                anchor: cursor_anchor,
15549                is_deactivate,
15550            })
15551        }
15552    }
15553
15554    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15555        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15556        let buffer = self.buffer.read(cx).snapshot(cx);
15557        let mut selection = self
15558            .selections
15559            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15560        selection.set_head(buffer.len(), SelectionGoal::None);
15561        self.change_selections(Default::default(), window, cx, |s| {
15562            s.select(vec![selection]);
15563        });
15564    }
15565
15566    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15567        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15568        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15569            s.select_ranges([Anchor::min()..Anchor::max()]);
15570        });
15571    }
15572
15573    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15574        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15575        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15576        let mut selections = self.selections.all::<Point>(&display_map);
15577        let max_point = display_map.buffer_snapshot().max_point();
15578        for selection in &mut selections {
15579            let rows = selection.spanned_rows(true, &display_map);
15580            selection.start = Point::new(rows.start.0, 0);
15581            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15582            selection.reversed = false;
15583        }
15584        self.change_selections(Default::default(), window, cx, |s| {
15585            s.select(selections);
15586        });
15587    }
15588
15589    pub fn split_selection_into_lines(
15590        &mut self,
15591        action: &SplitSelectionIntoLines,
15592        window: &mut Window,
15593        cx: &mut Context<Self>,
15594    ) {
15595        let selections = self
15596            .selections
15597            .all::<Point>(&self.display_snapshot(cx))
15598            .into_iter()
15599            .map(|selection| selection.start..selection.end)
15600            .collect::<Vec<_>>();
15601        self.unfold_ranges(&selections, true, false, cx);
15602
15603        let mut new_selection_ranges = Vec::new();
15604        {
15605            let buffer = self.buffer.read(cx).read(cx);
15606            for selection in selections {
15607                for row in selection.start.row..selection.end.row {
15608                    let line_start = Point::new(row, 0);
15609                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15610
15611                    if action.keep_selections {
15612                        // Keep the selection range for each line
15613                        let selection_start = if row == selection.start.row {
15614                            selection.start
15615                        } else {
15616                            line_start
15617                        };
15618                        new_selection_ranges.push(selection_start..line_end);
15619                    } else {
15620                        // Collapse to cursor at end of line
15621                        new_selection_ranges.push(line_end..line_end);
15622                    }
15623                }
15624
15625                let is_multiline_selection = selection.start.row != selection.end.row;
15626                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15627                // so this action feels more ergonomic when paired with other selection operations
15628                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15629                if !should_skip_last {
15630                    if action.keep_selections {
15631                        if is_multiline_selection {
15632                            let line_start = Point::new(selection.end.row, 0);
15633                            new_selection_ranges.push(line_start..selection.end);
15634                        } else {
15635                            new_selection_ranges.push(selection.start..selection.end);
15636                        }
15637                    } else {
15638                        new_selection_ranges.push(selection.end..selection.end);
15639                    }
15640                }
15641            }
15642        }
15643        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15644            s.select_ranges(new_selection_ranges);
15645        });
15646    }
15647
15648    pub fn add_selection_above(
15649        &mut self,
15650        action: &AddSelectionAbove,
15651        window: &mut Window,
15652        cx: &mut Context<Self>,
15653    ) {
15654        self.add_selection(true, action.skip_soft_wrap, window, cx);
15655    }
15656
15657    pub fn add_selection_below(
15658        &mut self,
15659        action: &AddSelectionBelow,
15660        window: &mut Window,
15661        cx: &mut Context<Self>,
15662    ) {
15663        self.add_selection(false, action.skip_soft_wrap, window, cx);
15664    }
15665
15666    fn add_selection(
15667        &mut self,
15668        above: bool,
15669        skip_soft_wrap: bool,
15670        window: &mut Window,
15671        cx: &mut Context<Self>,
15672    ) {
15673        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15674
15675        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15676        let all_selections = self.selections.all::<Point>(&display_map);
15677        let text_layout_details = self.text_layout_details(window, cx);
15678
15679        let (mut columnar_selections, new_selections_to_columnarize) = {
15680            if let Some(state) = self.add_selections_state.as_ref() {
15681                let columnar_selection_ids: HashSet<_> = state
15682                    .groups
15683                    .iter()
15684                    .flat_map(|group| group.stack.iter())
15685                    .copied()
15686                    .collect();
15687
15688                all_selections
15689                    .into_iter()
15690                    .partition(|s| columnar_selection_ids.contains(&s.id))
15691            } else {
15692                (Vec::new(), all_selections)
15693            }
15694        };
15695
15696        let mut state = self
15697            .add_selections_state
15698            .take()
15699            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15700
15701        for selection in new_selections_to_columnarize {
15702            let range = selection.display_range(&display_map).sorted();
15703            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15704            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15705            let positions = start_x.min(end_x)..start_x.max(end_x);
15706            let mut stack = Vec::new();
15707            for row in range.start.row().0..=range.end.row().0 {
15708                if let Some(selection) = self.selections.build_columnar_selection(
15709                    &display_map,
15710                    DisplayRow(row),
15711                    &positions,
15712                    selection.reversed,
15713                    &text_layout_details,
15714                ) {
15715                    stack.push(selection.id);
15716                    columnar_selections.push(selection);
15717                }
15718            }
15719            if !stack.is_empty() {
15720                if above {
15721                    stack.reverse();
15722                }
15723                state.groups.push(AddSelectionsGroup { above, stack });
15724            }
15725        }
15726
15727        let mut final_selections = Vec::new();
15728        let end_row = if above {
15729            DisplayRow(0)
15730        } else {
15731            display_map.max_point().row()
15732        };
15733
15734        // When `skip_soft_wrap` is true, we use UTF-16 columns instead of pixel
15735        // positions to place new selections, so we need to keep track of the
15736        // column range of the oldest selection in each group, because
15737        // intermediate selections may have been clamped to shorter lines.
15738        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15739            let mut map = HashMap::default();
15740            for group in state.groups.iter() {
15741                if let Some(oldest_id) = group.stack.first() {
15742                    if let Some(oldest_selection) =
15743                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15744                    {
15745                        let snapshot = display_map.buffer_snapshot();
15746                        let start_col =
15747                            snapshot.point_to_point_utf16(oldest_selection.start).column;
15748                        let end_col = snapshot.point_to_point_utf16(oldest_selection.end).column;
15749                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15750                        for id in &group.stack {
15751                            map.insert(*id, goal_columns.clone());
15752                        }
15753                    }
15754                }
15755            }
15756            map
15757        } else {
15758            HashMap::default()
15759        };
15760
15761        let mut last_added_item_per_group = HashMap::default();
15762        for group in state.groups.iter_mut() {
15763            if let Some(last_id) = group.stack.last() {
15764                last_added_item_per_group.insert(*last_id, group);
15765            }
15766        }
15767
15768        for selection in columnar_selections {
15769            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15770                if above == group.above {
15771                    let range = selection.display_range(&display_map).sorted();
15772                    debug_assert_eq!(range.start.row(), range.end.row());
15773                    let row = range.start.row();
15774                    let positions =
15775                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15776                            Pixels::from(start)..Pixels::from(end)
15777                        } else {
15778                            let start_x =
15779                                display_map.x_for_display_point(range.start, &text_layout_details);
15780                            let end_x =
15781                                display_map.x_for_display_point(range.end, &text_layout_details);
15782                            start_x.min(end_x)..start_x.max(end_x)
15783                        };
15784
15785                    let maybe_new_selection = if skip_soft_wrap {
15786                        let goal_columns = goal_columns_by_selection_id
15787                            .remove(&selection.id)
15788                            .unwrap_or_else(|| {
15789                                let snapshot = display_map.buffer_snapshot();
15790                                let start_col =
15791                                    snapshot.point_to_point_utf16(selection.start).column;
15792                                let end_col = snapshot.point_to_point_utf16(selection.end).column;
15793                                start_col.min(end_col)..start_col.max(end_col)
15794                            });
15795                        self.selections.find_next_columnar_selection_by_buffer_row(
15796                            &display_map,
15797                            row,
15798                            end_row,
15799                            above,
15800                            &goal_columns,
15801                            selection.reversed,
15802                            &text_layout_details,
15803                        )
15804                    } else {
15805                        self.selections.find_next_columnar_selection_by_display_row(
15806                            &display_map,
15807                            row,
15808                            end_row,
15809                            above,
15810                            &positions,
15811                            selection.reversed,
15812                            &text_layout_details,
15813                        )
15814                    };
15815
15816                    if let Some(new_selection) = maybe_new_selection {
15817                        group.stack.push(new_selection.id);
15818                        if above {
15819                            final_selections.push(new_selection);
15820                            final_selections.push(selection);
15821                        } else {
15822                            final_selections.push(selection);
15823                            final_selections.push(new_selection);
15824                        }
15825                    } else {
15826                        final_selections.push(selection);
15827                    }
15828                } else {
15829                    group.stack.pop();
15830                }
15831            } else {
15832                final_selections.push(selection);
15833            }
15834        }
15835
15836        self.change_selections(Default::default(), window, cx, |s| {
15837            s.select(final_selections);
15838        });
15839
15840        let final_selection_ids: HashSet<_> = self
15841            .selections
15842            .all::<Point>(&display_map)
15843            .iter()
15844            .map(|s| s.id)
15845            .collect();
15846        state.groups.retain_mut(|group| {
15847            // selections might get merged above so we remove invalid items from stacks
15848            group.stack.retain(|id| final_selection_ids.contains(id));
15849
15850            // single selection in stack can be treated as initial state
15851            group.stack.len() > 1
15852        });
15853
15854        if !state.groups.is_empty() {
15855            self.add_selections_state = Some(state);
15856        }
15857    }
15858
15859    pub fn insert_snippet_at_selections(
15860        &mut self,
15861        action: &InsertSnippet,
15862        window: &mut Window,
15863        cx: &mut Context<Self>,
15864    ) {
15865        self.try_insert_snippet_at_selections(action, window, cx)
15866            .log_err();
15867    }
15868
15869    fn try_insert_snippet_at_selections(
15870        &mut self,
15871        action: &InsertSnippet,
15872        window: &mut Window,
15873        cx: &mut Context<Self>,
15874    ) -> Result<()> {
15875        let insertion_ranges = self
15876            .selections
15877            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15878            .into_iter()
15879            .map(|selection| selection.range())
15880            .collect_vec();
15881
15882        let snippet = if let Some(snippet_body) = &action.snippet {
15883            if action.language.is_none() && action.name.is_none() {
15884                Snippet::parse(snippet_body)?
15885            } else {
15886                bail!("`snippet` is mutually exclusive with `language` and `name`")
15887            }
15888        } else if let Some(name) = &action.name {
15889            let project = self.project().context("no project")?;
15890            let snippet_store = project.read(cx).snippets().read(cx);
15891            let snippet = snippet_store
15892                .snippets_for(action.language.clone(), cx)
15893                .into_iter()
15894                .find(|snippet| snippet.name == *name)
15895                .context("snippet not found")?;
15896            Snippet::parse(&snippet.body)?
15897        } else {
15898            // todo(andrew): open modal to select snippet
15899            bail!("`name` or `snippet` is required")
15900        };
15901
15902        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15903    }
15904
15905    fn select_match_ranges(
15906        &mut self,
15907        range: Range<MultiBufferOffset>,
15908        reversed: bool,
15909        replace_newest: bool,
15910        auto_scroll: Option<Autoscroll>,
15911        window: &mut Window,
15912        cx: &mut Context<Editor>,
15913    ) {
15914        self.unfold_ranges(
15915            std::slice::from_ref(&range),
15916            false,
15917            auto_scroll.is_some(),
15918            cx,
15919        );
15920        let effects = if let Some(scroll) = auto_scroll {
15921            SelectionEffects::scroll(scroll)
15922        } else {
15923            SelectionEffects::no_scroll()
15924        };
15925        self.change_selections(effects, window, cx, |s| {
15926            if replace_newest {
15927                s.delete(s.newest_anchor().id);
15928            }
15929            if reversed {
15930                s.insert_range(range.end..range.start);
15931            } else {
15932                s.insert_range(range);
15933            }
15934        });
15935    }
15936
15937    pub fn select_next_match_internal(
15938        &mut self,
15939        display_map: &DisplaySnapshot,
15940        replace_newest: bool,
15941        autoscroll: Option<Autoscroll>,
15942        window: &mut Window,
15943        cx: &mut Context<Self>,
15944    ) -> Result<()> {
15945        let buffer = display_map.buffer_snapshot();
15946        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15947        if let Some(mut select_next_state) = self.select_next_state.take() {
15948            let query = &select_next_state.query;
15949            if !select_next_state.done {
15950                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15951                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15952                let mut next_selected_range = None;
15953
15954                let bytes_after_last_selection =
15955                    buffer.bytes_in_range(last_selection.end..buffer.len());
15956                let bytes_before_first_selection =
15957                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15958                let query_matches = query
15959                    .stream_find_iter(bytes_after_last_selection)
15960                    .map(|result| (last_selection.end, result))
15961                    .chain(
15962                        query
15963                            .stream_find_iter(bytes_before_first_selection)
15964                            .map(|result| (MultiBufferOffset(0), result)),
15965                    );
15966
15967                for (start_offset, query_match) in query_matches {
15968                    let query_match = query_match.unwrap(); // can only fail due to I/O
15969                    let offset_range =
15970                        start_offset + query_match.start()..start_offset + query_match.end();
15971
15972                    if !select_next_state.wordwise
15973                        || (!buffer.is_inside_word(offset_range.start, None)
15974                            && !buffer.is_inside_word(offset_range.end, None))
15975                    {
15976                        let idx = selections
15977                            .partition_point(|selection| selection.end <= offset_range.start);
15978                        let overlaps = selections
15979                            .get(idx)
15980                            .map_or(false, |selection| selection.start < offset_range.end);
15981
15982                        if !overlaps {
15983                            next_selected_range = Some(offset_range);
15984                            break;
15985                        }
15986                    }
15987                }
15988
15989                if let Some(next_selected_range) = next_selected_range {
15990                    self.select_match_ranges(
15991                        next_selected_range,
15992                        last_selection.reversed,
15993                        replace_newest,
15994                        autoscroll,
15995                        window,
15996                        cx,
15997                    );
15998                } else {
15999                    select_next_state.done = true;
16000                }
16001            }
16002
16003            self.select_next_state = Some(select_next_state);
16004        } else {
16005            let mut only_carets = true;
16006            let mut same_text_selected = true;
16007            let mut selected_text = None;
16008
16009            let mut selections_iter = selections.iter().peekable();
16010            while let Some(selection) = selections_iter.next() {
16011                if selection.start != selection.end {
16012                    only_carets = false;
16013                }
16014
16015                if same_text_selected {
16016                    if selected_text.is_none() {
16017                        selected_text =
16018                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16019                    }
16020
16021                    if let Some(next_selection) = selections_iter.peek() {
16022                        if next_selection.len() == selection.len() {
16023                            let next_selected_text = buffer
16024                                .text_for_range(next_selection.range())
16025                                .collect::<String>();
16026                            if Some(next_selected_text) != selected_text {
16027                                same_text_selected = false;
16028                                selected_text = None;
16029                            }
16030                        } else {
16031                            same_text_selected = false;
16032                            selected_text = None;
16033                        }
16034                    }
16035                }
16036            }
16037
16038            if only_carets {
16039                for selection in &mut selections {
16040                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16041                    selection.start = word_range.start;
16042                    selection.end = word_range.end;
16043                    selection.goal = SelectionGoal::None;
16044                    selection.reversed = false;
16045                    self.select_match_ranges(
16046                        selection.start..selection.end,
16047                        selection.reversed,
16048                        replace_newest,
16049                        autoscroll,
16050                        window,
16051                        cx,
16052                    );
16053                }
16054
16055                if selections.len() == 1 {
16056                    let selection = selections
16057                        .last()
16058                        .expect("ensured that there's only one selection");
16059                    let query = buffer
16060                        .text_for_range(selection.start..selection.end)
16061                        .collect::<String>();
16062                    let is_empty = query.is_empty();
16063                    let select_state = SelectNextState {
16064                        query: self.build_query(&[query], cx)?,
16065                        wordwise: true,
16066                        done: is_empty,
16067                    };
16068                    self.select_next_state = Some(select_state);
16069                } else {
16070                    self.select_next_state = None;
16071                }
16072            } else if let Some(selected_text) = selected_text {
16073                self.select_next_state = Some(SelectNextState {
16074                    query: self.build_query(&[selected_text], cx)?,
16075                    wordwise: false,
16076                    done: false,
16077                });
16078                self.select_next_match_internal(
16079                    display_map,
16080                    replace_newest,
16081                    autoscroll,
16082                    window,
16083                    cx,
16084                )?;
16085            }
16086        }
16087        Ok(())
16088    }
16089
16090    pub fn select_all_matches(
16091        &mut self,
16092        _action: &SelectAllMatches,
16093        window: &mut Window,
16094        cx: &mut Context<Self>,
16095    ) -> Result<()> {
16096        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16097
16098        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16099
16100        self.select_next_match_internal(&display_map, false, None, window, cx)?;
16101        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
16102        else {
16103            return Ok(());
16104        };
16105
16106        let mut new_selections = Vec::new();
16107
16108        let reversed = self
16109            .selections
16110            .oldest::<MultiBufferOffset>(&display_map)
16111            .reversed;
16112        let buffer = display_map.buffer_snapshot();
16113        let query_matches = select_next_state
16114            .query
16115            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
16116
16117        for query_match in query_matches.into_iter() {
16118            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
16119            let offset_range = if reversed {
16120                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
16121            } else {
16122                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
16123            };
16124
16125            if !select_next_state.wordwise
16126                || (!buffer.is_inside_word(offset_range.start, None)
16127                    && !buffer.is_inside_word(offset_range.end, None))
16128            {
16129                new_selections.push(offset_range.start..offset_range.end);
16130            }
16131        }
16132
16133        select_next_state.done = true;
16134
16135        if new_selections.is_empty() {
16136            log::error!("bug: new_selections is empty in select_all_matches");
16137            return Ok(());
16138        }
16139
16140        self.unfold_ranges(&new_selections, false, false, cx);
16141        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
16142            selections.select_ranges(new_selections)
16143        });
16144
16145        Ok(())
16146    }
16147
16148    pub fn select_next(
16149        &mut self,
16150        action: &SelectNext,
16151        window: &mut Window,
16152        cx: &mut Context<Self>,
16153    ) -> Result<()> {
16154        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16155        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16156        self.select_next_match_internal(
16157            &display_map,
16158            action.replace_newest,
16159            Some(Autoscroll::newest()),
16160            window,
16161            cx,
16162        )
16163    }
16164
16165    pub fn select_previous(
16166        &mut self,
16167        action: &SelectPrevious,
16168        window: &mut Window,
16169        cx: &mut Context<Self>,
16170    ) -> Result<()> {
16171        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16172        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16173        let buffer = display_map.buffer_snapshot();
16174        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
16175        if let Some(mut select_prev_state) = self.select_prev_state.take() {
16176            let query = &select_prev_state.query;
16177            if !select_prev_state.done {
16178                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
16179                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
16180                let mut next_selected_range = None;
16181                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
16182                let bytes_before_last_selection =
16183                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
16184                let bytes_after_first_selection =
16185                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
16186                let query_matches = query
16187                    .stream_find_iter(bytes_before_last_selection)
16188                    .map(|result| (last_selection.start, result))
16189                    .chain(
16190                        query
16191                            .stream_find_iter(bytes_after_first_selection)
16192                            .map(|result| (buffer.len(), result)),
16193                    );
16194                for (end_offset, query_match) in query_matches {
16195                    let query_match = query_match.unwrap(); // can only fail due to I/O
16196                    let offset_range =
16197                        end_offset - query_match.end()..end_offset - query_match.start();
16198
16199                    if !select_prev_state.wordwise
16200                        || (!buffer.is_inside_word(offset_range.start, None)
16201                            && !buffer.is_inside_word(offset_range.end, None))
16202                    {
16203                        next_selected_range = Some(offset_range);
16204                        break;
16205                    }
16206                }
16207
16208                if let Some(next_selected_range) = next_selected_range {
16209                    self.select_match_ranges(
16210                        next_selected_range,
16211                        last_selection.reversed,
16212                        action.replace_newest,
16213                        Some(Autoscroll::newest()),
16214                        window,
16215                        cx,
16216                    );
16217                } else {
16218                    select_prev_state.done = true;
16219                }
16220            }
16221
16222            self.select_prev_state = Some(select_prev_state);
16223        } else {
16224            let mut only_carets = true;
16225            let mut same_text_selected = true;
16226            let mut selected_text = None;
16227
16228            let mut selections_iter = selections.iter().peekable();
16229            while let Some(selection) = selections_iter.next() {
16230                if selection.start != selection.end {
16231                    only_carets = false;
16232                }
16233
16234                if same_text_selected {
16235                    if selected_text.is_none() {
16236                        selected_text =
16237                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16238                    }
16239
16240                    if let Some(next_selection) = selections_iter.peek() {
16241                        if next_selection.len() == selection.len() {
16242                            let next_selected_text = buffer
16243                                .text_for_range(next_selection.range())
16244                                .collect::<String>();
16245                            if Some(next_selected_text) != selected_text {
16246                                same_text_selected = false;
16247                                selected_text = None;
16248                            }
16249                        } else {
16250                            same_text_selected = false;
16251                            selected_text = None;
16252                        }
16253                    }
16254                }
16255            }
16256
16257            if only_carets {
16258                for selection in &mut selections {
16259                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16260                    selection.start = word_range.start;
16261                    selection.end = word_range.end;
16262                    selection.goal = SelectionGoal::None;
16263                    selection.reversed = false;
16264                    self.select_match_ranges(
16265                        selection.start..selection.end,
16266                        selection.reversed,
16267                        action.replace_newest,
16268                        Some(Autoscroll::newest()),
16269                        window,
16270                        cx,
16271                    );
16272                }
16273                if selections.len() == 1 {
16274                    let selection = selections
16275                        .last()
16276                        .expect("ensured that there's only one selection");
16277                    let query = buffer
16278                        .text_for_range(selection.start..selection.end)
16279                        .collect::<String>();
16280                    let is_empty = query.is_empty();
16281                    let select_state = SelectNextState {
16282                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16283                        wordwise: true,
16284                        done: is_empty,
16285                    };
16286                    self.select_prev_state = Some(select_state);
16287                } else {
16288                    self.select_prev_state = None;
16289                }
16290            } else if let Some(selected_text) = selected_text {
16291                self.select_prev_state = Some(SelectNextState {
16292                    query: self
16293                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16294                    wordwise: false,
16295                    done: false,
16296                });
16297                self.select_previous(action, window, cx)?;
16298            }
16299        }
16300        Ok(())
16301    }
16302
16303    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16304    /// setting the case sensitivity based on the global
16305    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16306    /// editor's settings.
16307    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16308    where
16309        I: IntoIterator<Item = P>,
16310        P: AsRef<[u8]>,
16311    {
16312        let case_sensitive = self
16313            .select_next_is_case_sensitive
16314            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16315
16316        let mut builder = AhoCorasickBuilder::new();
16317        builder.ascii_case_insensitive(!case_sensitive);
16318        builder.build(patterns)
16319    }
16320
16321    pub fn find_next_match(
16322        &mut self,
16323        _: &FindNextMatch,
16324        window: &mut Window,
16325        cx: &mut Context<Self>,
16326    ) -> Result<()> {
16327        let selections = self.selections.disjoint_anchors_arc();
16328        match selections.first() {
16329            Some(first) if selections.len() >= 2 => {
16330                self.change_selections(Default::default(), window, cx, |s| {
16331                    s.select_ranges([first.range()]);
16332                });
16333            }
16334            _ => self.select_next(
16335                &SelectNext {
16336                    replace_newest: true,
16337                },
16338                window,
16339                cx,
16340            )?,
16341        }
16342        Ok(())
16343    }
16344
16345    pub fn find_previous_match(
16346        &mut self,
16347        _: &FindPreviousMatch,
16348        window: &mut Window,
16349        cx: &mut Context<Self>,
16350    ) -> Result<()> {
16351        let selections = self.selections.disjoint_anchors_arc();
16352        match selections.last() {
16353            Some(last) if selections.len() >= 2 => {
16354                self.change_selections(Default::default(), window, cx, |s| {
16355                    s.select_ranges([last.range()]);
16356                });
16357            }
16358            _ => self.select_previous(
16359                &SelectPrevious {
16360                    replace_newest: true,
16361                },
16362                window,
16363                cx,
16364            )?,
16365        }
16366        Ok(())
16367    }
16368
16369    pub fn toggle_comments(
16370        &mut self,
16371        action: &ToggleComments,
16372        window: &mut Window,
16373        cx: &mut Context<Self>,
16374    ) {
16375        if self.read_only(cx) {
16376            return;
16377        }
16378        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16379        let text_layout_details = &self.text_layout_details(window, cx);
16380        self.transact(window, cx, |this, window, cx| {
16381            let mut selections = this
16382                .selections
16383                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16384            let mut edits = Vec::new();
16385            let mut selection_edit_ranges = Vec::new();
16386            let mut last_toggled_row = None;
16387            let snapshot = this.buffer.read(cx).read(cx);
16388            let empty_str: Arc<str> = Arc::default();
16389            let mut suffixes_inserted = Vec::new();
16390            let ignore_indent = action.ignore_indent;
16391
16392            fn comment_prefix_range(
16393                snapshot: &MultiBufferSnapshot,
16394                row: MultiBufferRow,
16395                comment_prefix: &str,
16396                comment_prefix_whitespace: &str,
16397                ignore_indent: bool,
16398            ) -> Range<Point> {
16399                let indent_size = if ignore_indent {
16400                    0
16401                } else {
16402                    snapshot.indent_size_for_line(row).len
16403                };
16404
16405                let start = Point::new(row.0, indent_size);
16406
16407                let mut line_bytes = snapshot
16408                    .bytes_in_range(start..snapshot.max_point())
16409                    .flatten()
16410                    .copied();
16411
16412                // If this line currently begins with the line comment prefix, then record
16413                // the range containing the prefix.
16414                if line_bytes
16415                    .by_ref()
16416                    .take(comment_prefix.len())
16417                    .eq(comment_prefix.bytes())
16418                {
16419                    // Include any whitespace that matches the comment prefix.
16420                    let matching_whitespace_len = line_bytes
16421                        .zip(comment_prefix_whitespace.bytes())
16422                        .take_while(|(a, b)| a == b)
16423                        .count() as u32;
16424                    let end = Point::new(
16425                        start.row,
16426                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16427                    );
16428                    start..end
16429                } else {
16430                    start..start
16431                }
16432            }
16433
16434            fn comment_suffix_range(
16435                snapshot: &MultiBufferSnapshot,
16436                row: MultiBufferRow,
16437                comment_suffix: &str,
16438                comment_suffix_has_leading_space: bool,
16439            ) -> Range<Point> {
16440                let end = Point::new(row.0, snapshot.line_len(row));
16441                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16442
16443                let mut line_end_bytes = snapshot
16444                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16445                    .flatten()
16446                    .copied();
16447
16448                let leading_space_len = if suffix_start_column > 0
16449                    && line_end_bytes.next() == Some(b' ')
16450                    && comment_suffix_has_leading_space
16451                {
16452                    1
16453                } else {
16454                    0
16455                };
16456
16457                // If this line currently begins with the line comment prefix, then record
16458                // the range containing the prefix.
16459                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16460                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16461                    start..end
16462                } else {
16463                    end..end
16464                }
16465            }
16466
16467            // TODO: Handle selections that cross excerpts
16468            for selection in &mut selections {
16469                let start_column = snapshot
16470                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16471                    .len;
16472                let language = if let Some(language) =
16473                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16474                {
16475                    language
16476                } else {
16477                    continue;
16478                };
16479
16480                selection_edit_ranges.clear();
16481
16482                // If multiple selections contain a given row, avoid processing that
16483                // row more than once.
16484                let mut start_row = MultiBufferRow(selection.start.row);
16485                if last_toggled_row == Some(start_row) {
16486                    start_row = start_row.next_row();
16487                }
16488                let end_row =
16489                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16490                        MultiBufferRow(selection.end.row - 1)
16491                    } else {
16492                        MultiBufferRow(selection.end.row)
16493                    };
16494                last_toggled_row = Some(end_row);
16495
16496                if start_row > end_row {
16497                    continue;
16498                }
16499
16500                // If the language has line comments, toggle those.
16501                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16502
16503                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16504                if ignore_indent {
16505                    full_comment_prefixes = full_comment_prefixes
16506                        .into_iter()
16507                        .map(|s| Arc::from(s.trim_end()))
16508                        .collect();
16509                }
16510
16511                if !full_comment_prefixes.is_empty() {
16512                    let first_prefix = full_comment_prefixes
16513                        .first()
16514                        .expect("prefixes is non-empty");
16515                    let prefix_trimmed_lengths = full_comment_prefixes
16516                        .iter()
16517                        .map(|p| p.trim_end_matches(' ').len())
16518                        .collect::<SmallVec<[usize; 4]>>();
16519
16520                    let mut all_selection_lines_are_comments = true;
16521
16522                    for row in start_row.0..=end_row.0 {
16523                        let row = MultiBufferRow(row);
16524                        if start_row < end_row && snapshot.is_line_blank(row) {
16525                            continue;
16526                        }
16527
16528                        let prefix_range = full_comment_prefixes
16529                            .iter()
16530                            .zip(prefix_trimmed_lengths.iter().copied())
16531                            .map(|(prefix, trimmed_prefix_len)| {
16532                                comment_prefix_range(
16533                                    snapshot.deref(),
16534                                    row,
16535                                    &prefix[..trimmed_prefix_len],
16536                                    &prefix[trimmed_prefix_len..],
16537                                    ignore_indent,
16538                                )
16539                            })
16540                            .max_by_key(|range| range.end.column - range.start.column)
16541                            .expect("prefixes is non-empty");
16542
16543                        if prefix_range.is_empty() {
16544                            all_selection_lines_are_comments = false;
16545                        }
16546
16547                        selection_edit_ranges.push(prefix_range);
16548                    }
16549
16550                    if all_selection_lines_are_comments {
16551                        edits.extend(
16552                            selection_edit_ranges
16553                                .iter()
16554                                .cloned()
16555                                .map(|range| (range, empty_str.clone())),
16556                        );
16557                    } else {
16558                        let min_column = selection_edit_ranges
16559                            .iter()
16560                            .map(|range| range.start.column)
16561                            .min()
16562                            .unwrap_or(0);
16563                        edits.extend(selection_edit_ranges.iter().map(|range| {
16564                            let position = Point::new(range.start.row, min_column);
16565                            (position..position, first_prefix.clone())
16566                        }));
16567                    }
16568                } else if let Some(BlockCommentConfig {
16569                    start: full_comment_prefix,
16570                    end: comment_suffix,
16571                    ..
16572                }) = language.block_comment()
16573                {
16574                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16575                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16576                    let prefix_range = comment_prefix_range(
16577                        snapshot.deref(),
16578                        start_row,
16579                        comment_prefix,
16580                        comment_prefix_whitespace,
16581                        ignore_indent,
16582                    );
16583                    let suffix_range = comment_suffix_range(
16584                        snapshot.deref(),
16585                        end_row,
16586                        comment_suffix.trim_start_matches(' '),
16587                        comment_suffix.starts_with(' '),
16588                    );
16589
16590                    if prefix_range.is_empty() || suffix_range.is_empty() {
16591                        edits.push((
16592                            prefix_range.start..prefix_range.start,
16593                            full_comment_prefix.clone(),
16594                        ));
16595                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16596                        suffixes_inserted.push((end_row, comment_suffix.len()));
16597                    } else {
16598                        edits.push((prefix_range, empty_str.clone()));
16599                        edits.push((suffix_range, empty_str.clone()));
16600                    }
16601                } else {
16602                    continue;
16603                }
16604            }
16605
16606            drop(snapshot);
16607            this.buffer.update(cx, |buffer, cx| {
16608                buffer.edit(edits, None, cx);
16609            });
16610
16611            // Adjust selections so that they end before any comment suffixes that
16612            // were inserted.
16613            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16614            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16615            let snapshot = this.buffer.read(cx).read(cx);
16616            for selection in &mut selections {
16617                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16618                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16619                        Ordering::Less => {
16620                            suffixes_inserted.next();
16621                            continue;
16622                        }
16623                        Ordering::Greater => break,
16624                        Ordering::Equal => {
16625                            if selection.end.column == snapshot.line_len(row) {
16626                                if selection.is_empty() {
16627                                    selection.start.column -= suffix_len as u32;
16628                                }
16629                                selection.end.column -= suffix_len as u32;
16630                            }
16631                            break;
16632                        }
16633                    }
16634                }
16635            }
16636
16637            drop(snapshot);
16638            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16639
16640            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16641            let selections_on_single_row = selections.windows(2).all(|selections| {
16642                selections[0].start.row == selections[1].start.row
16643                    && selections[0].end.row == selections[1].end.row
16644                    && selections[0].start.row == selections[0].end.row
16645            });
16646            let selections_selecting = selections
16647                .iter()
16648                .any(|selection| selection.start != selection.end);
16649            let advance_downwards = action.advance_downwards
16650                && selections_on_single_row
16651                && !selections_selecting
16652                && !matches!(this.mode, EditorMode::SingleLine);
16653
16654            if advance_downwards {
16655                let snapshot = this.buffer.read(cx).snapshot(cx);
16656
16657                this.change_selections(Default::default(), window, cx, |s| {
16658                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16659                        let mut point = display_point.to_point(display_snapshot);
16660                        point.row += 1;
16661                        point = snapshot.clip_point(point, Bias::Left);
16662                        let display_point = point.to_display_point(display_snapshot);
16663                        let goal = SelectionGoal::HorizontalPosition(
16664                            display_snapshot
16665                                .x_for_display_point(display_point, text_layout_details)
16666                                .into(),
16667                        );
16668                        (display_point, goal)
16669                    })
16670                });
16671            }
16672        });
16673    }
16674
16675    pub fn select_enclosing_symbol(
16676        &mut self,
16677        _: &SelectEnclosingSymbol,
16678        window: &mut Window,
16679        cx: &mut Context<Self>,
16680    ) {
16681        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16682
16683        let buffer = self.buffer.read(cx).snapshot(cx);
16684        let old_selections = self
16685            .selections
16686            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16687            .into_boxed_slice();
16688
16689        fn update_selection(
16690            selection: &Selection<MultiBufferOffset>,
16691            buffer_snap: &MultiBufferSnapshot,
16692        ) -> Option<Selection<MultiBufferOffset>> {
16693            let cursor = selection.head();
16694            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16695            for symbol in symbols.iter().rev() {
16696                let start = symbol.range.start.to_offset(buffer_snap);
16697                let end = symbol.range.end.to_offset(buffer_snap);
16698                let new_range = start..end;
16699                if start < selection.start || end > selection.end {
16700                    return Some(Selection {
16701                        id: selection.id,
16702                        start: new_range.start,
16703                        end: new_range.end,
16704                        goal: SelectionGoal::None,
16705                        reversed: selection.reversed,
16706                    });
16707                }
16708            }
16709            None
16710        }
16711
16712        let mut selected_larger_symbol = false;
16713        let new_selections = old_selections
16714            .iter()
16715            .map(|selection| match update_selection(selection, &buffer) {
16716                Some(new_selection) => {
16717                    if new_selection.range() != selection.range() {
16718                        selected_larger_symbol = true;
16719                    }
16720                    new_selection
16721                }
16722                None => selection.clone(),
16723            })
16724            .collect::<Vec<_>>();
16725
16726        if selected_larger_symbol {
16727            self.change_selections(Default::default(), window, cx, |s| {
16728                s.select(new_selections);
16729            });
16730        }
16731    }
16732
16733    pub fn select_larger_syntax_node(
16734        &mut self,
16735        _: &SelectLargerSyntaxNode,
16736        window: &mut Window,
16737        cx: &mut Context<Self>,
16738    ) {
16739        let Some(visible_row_count) = self.visible_row_count() else {
16740            return;
16741        };
16742        let old_selections: Box<[_]> = self
16743            .selections
16744            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16745            .into();
16746        if old_selections.is_empty() {
16747            return;
16748        }
16749
16750        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16751
16752        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16753        let buffer = self.buffer.read(cx).snapshot(cx);
16754
16755        let mut selected_larger_node = false;
16756        let mut new_selections = old_selections
16757            .iter()
16758            .map(|selection| {
16759                let old_range = selection.start..selection.end;
16760
16761                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16762                    // manually select word at selection
16763                    if ["string_content", "inline"].contains(&node.kind()) {
16764                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16765                        // ignore if word is already selected
16766                        if !word_range.is_empty() && old_range != word_range {
16767                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16768                            // only select word if start and end point belongs to same word
16769                            if word_range == last_word_range {
16770                                selected_larger_node = true;
16771                                return Selection {
16772                                    id: selection.id,
16773                                    start: word_range.start,
16774                                    end: word_range.end,
16775                                    goal: SelectionGoal::None,
16776                                    reversed: selection.reversed,
16777                                };
16778                            }
16779                        }
16780                    }
16781                }
16782
16783                let mut new_range = old_range.clone();
16784                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16785                    new_range = range;
16786                    if !node.is_named() {
16787                        continue;
16788                    }
16789                    if !display_map.intersects_fold(new_range.start)
16790                        && !display_map.intersects_fold(new_range.end)
16791                    {
16792                        break;
16793                    }
16794                }
16795
16796                selected_larger_node |= new_range != old_range;
16797                Selection {
16798                    id: selection.id,
16799                    start: new_range.start,
16800                    end: new_range.end,
16801                    goal: SelectionGoal::None,
16802                    reversed: selection.reversed,
16803                }
16804            })
16805            .collect::<Vec<_>>();
16806
16807        if !selected_larger_node {
16808            return; // don't put this call in the history
16809        }
16810
16811        // scroll based on transformation done to the last selection created by the user
16812        let (last_old, last_new) = old_selections
16813            .last()
16814            .zip(new_selections.last().cloned())
16815            .expect("old_selections isn't empty");
16816
16817        let is_selection_reversed = if new_selections.len() == 1 {
16818            let should_be_reversed = last_old.start != last_new.start;
16819            new_selections.last_mut().expect("checked above").reversed = should_be_reversed;
16820            should_be_reversed
16821        } else {
16822            last_new.reversed
16823        };
16824
16825        if selected_larger_node {
16826            self.select_syntax_node_history.disable_clearing = true;
16827            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16828                s.select(new_selections.clone());
16829            });
16830            self.select_syntax_node_history.disable_clearing = false;
16831        }
16832
16833        let start_row = last_new.start.to_display_point(&display_map).row().0;
16834        let end_row = last_new.end.to_display_point(&display_map).row().0;
16835        let selection_height = end_row - start_row + 1;
16836        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16837
16838        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16839        let scroll_behavior = if fits_on_the_screen {
16840            self.request_autoscroll(Autoscroll::fit(), cx);
16841            SelectSyntaxNodeScrollBehavior::FitSelection
16842        } else if is_selection_reversed {
16843            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16844            SelectSyntaxNodeScrollBehavior::CursorTop
16845        } else {
16846            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16847            SelectSyntaxNodeScrollBehavior::CursorBottom
16848        };
16849
16850        let old_selections: Box<[Selection<Anchor>]> = old_selections
16851            .iter()
16852            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16853            .collect();
16854        self.select_syntax_node_history.push((
16855            old_selections,
16856            scroll_behavior,
16857            is_selection_reversed,
16858        ));
16859    }
16860
16861    pub fn select_smaller_syntax_node(
16862        &mut self,
16863        _: &SelectSmallerSyntaxNode,
16864        window: &mut Window,
16865        cx: &mut Context<Self>,
16866    ) {
16867        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16868
16869        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16870            self.select_syntax_node_history.pop()
16871        {
16872            if let Some(selection) = selections.last_mut() {
16873                selection.reversed = is_selection_reversed;
16874            }
16875
16876            let snapshot = self.buffer.read(cx).snapshot(cx);
16877            let selections: Vec<Selection<MultiBufferOffset>> = selections
16878                .iter()
16879                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16880                .collect();
16881
16882            self.select_syntax_node_history.disable_clearing = true;
16883            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16884                s.select(selections);
16885            });
16886            self.select_syntax_node_history.disable_clearing = false;
16887
16888            match scroll_behavior {
16889                SelectSyntaxNodeScrollBehavior::CursorTop => {
16890                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16891                }
16892                SelectSyntaxNodeScrollBehavior::FitSelection => {
16893                    self.request_autoscroll(Autoscroll::fit(), cx);
16894                }
16895                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16896                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16897                }
16898            }
16899        }
16900    }
16901
16902    pub fn unwrap_syntax_node(
16903        &mut self,
16904        _: &UnwrapSyntaxNode,
16905        window: &mut Window,
16906        cx: &mut Context<Self>,
16907    ) {
16908        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16909
16910        let buffer = self.buffer.read(cx).snapshot(cx);
16911        let selections = self
16912            .selections
16913            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16914            .into_iter()
16915            // subtracting the offset requires sorting
16916            .sorted_by_key(|i| i.start);
16917
16918        let full_edits = selections
16919            .into_iter()
16920            .filter_map(|selection| {
16921                let child = if selection.is_empty()
16922                    && let Some((_, ancestor_range)) =
16923                        buffer.syntax_ancestor(selection.start..selection.end)
16924                {
16925                    ancestor_range
16926                } else {
16927                    selection.range()
16928                };
16929
16930                let mut parent = child.clone();
16931                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16932                    parent = ancestor_range;
16933                    if parent.start < child.start || parent.end > child.end {
16934                        break;
16935                    }
16936                }
16937
16938                if parent == child {
16939                    return None;
16940                }
16941                let text = buffer.text_for_range(child).collect::<String>();
16942                Some((selection.id, parent, text))
16943            })
16944            .collect::<Vec<_>>();
16945        if full_edits.is_empty() {
16946            return;
16947        }
16948
16949        self.transact(window, cx, |this, window, cx| {
16950            this.buffer.update(cx, |buffer, cx| {
16951                buffer.edit(
16952                    full_edits
16953                        .iter()
16954                        .map(|(_, p, t)| (p.clone(), t.clone()))
16955                        .collect::<Vec<_>>(),
16956                    None,
16957                    cx,
16958                );
16959            });
16960            this.change_selections(Default::default(), window, cx, |s| {
16961                let mut offset = 0;
16962                let mut selections = vec![];
16963                for (id, parent, text) in full_edits {
16964                    let start = parent.start - offset;
16965                    offset += (parent.end - parent.start) - text.len();
16966                    selections.push(Selection {
16967                        id,
16968                        start,
16969                        end: start + text.len(),
16970                        reversed: false,
16971                        goal: Default::default(),
16972                    });
16973                }
16974                s.select(selections);
16975            });
16976        });
16977    }
16978
16979    pub fn select_next_syntax_node(
16980        &mut self,
16981        _: &SelectNextSyntaxNode,
16982        window: &mut Window,
16983        cx: &mut Context<Self>,
16984    ) {
16985        let old_selections: Box<[_]> = self
16986            .selections
16987            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16988            .into();
16989        if old_selections.is_empty() {
16990            return;
16991        }
16992
16993        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16994
16995        let buffer = self.buffer.read(cx).snapshot(cx);
16996        let mut selected_sibling = false;
16997
16998        let new_selections = old_selections
16999            .iter()
17000            .map(|selection| {
17001                let old_range = selection.start..selection.end;
17002
17003                let old_range =
17004                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17005                let excerpt = buffer.excerpt_containing(old_range.clone());
17006
17007                if let Some(mut excerpt) = excerpt
17008                    && let Some(node) = excerpt
17009                        .buffer()
17010                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
17011                {
17012                    let new_range = excerpt.map_range_from_buffer(
17013                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17014                    );
17015                    selected_sibling = true;
17016                    Selection {
17017                        id: selection.id,
17018                        start: new_range.start,
17019                        end: new_range.end,
17020                        goal: SelectionGoal::None,
17021                        reversed: selection.reversed,
17022                    }
17023                } else {
17024                    selection.clone()
17025                }
17026            })
17027            .collect::<Vec<_>>();
17028
17029        if selected_sibling {
17030            self.change_selections(
17031                SelectionEffects::scroll(Autoscroll::fit()),
17032                window,
17033                cx,
17034                |s| {
17035                    s.select(new_selections);
17036                },
17037            );
17038        }
17039    }
17040
17041    pub fn select_prev_syntax_node(
17042        &mut self,
17043        _: &SelectPreviousSyntaxNode,
17044        window: &mut Window,
17045        cx: &mut Context<Self>,
17046    ) {
17047        let old_selections: Box<[_]> = self
17048            .selections
17049            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17050            .into();
17051        if old_selections.is_empty() {
17052            return;
17053        }
17054
17055        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17056
17057        let buffer = self.buffer.read(cx).snapshot(cx);
17058        let mut selected_sibling = false;
17059
17060        let new_selections = old_selections
17061            .iter()
17062            .map(|selection| {
17063                let old_range = selection.start..selection.end;
17064                let old_range =
17065                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17066                let excerpt = buffer.excerpt_containing(old_range.clone());
17067
17068                if let Some(mut excerpt) = excerpt
17069                    && let Some(node) = excerpt
17070                        .buffer()
17071                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
17072                {
17073                    let new_range = excerpt.map_range_from_buffer(
17074                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17075                    );
17076                    selected_sibling = true;
17077                    Selection {
17078                        id: selection.id,
17079                        start: new_range.start,
17080                        end: new_range.end,
17081                        goal: SelectionGoal::None,
17082                        reversed: selection.reversed,
17083                    }
17084                } else {
17085                    selection.clone()
17086                }
17087            })
17088            .collect::<Vec<_>>();
17089
17090        if selected_sibling {
17091            self.change_selections(
17092                SelectionEffects::scroll(Autoscroll::fit()),
17093                window,
17094                cx,
17095                |s| {
17096                    s.select(new_selections);
17097                },
17098            );
17099        }
17100    }
17101
17102    pub fn move_to_start_of_larger_syntax_node(
17103        &mut self,
17104        _: &MoveToStartOfLargerSyntaxNode,
17105        window: &mut Window,
17106        cx: &mut Context<Self>,
17107    ) {
17108        self.move_cursors_to_syntax_nodes(window, cx, false);
17109    }
17110
17111    pub fn move_to_end_of_larger_syntax_node(
17112        &mut self,
17113        _: &MoveToEndOfLargerSyntaxNode,
17114        window: &mut Window,
17115        cx: &mut Context<Self>,
17116    ) {
17117        self.move_cursors_to_syntax_nodes(window, cx, true);
17118    }
17119
17120    fn find_syntax_node_boundary(
17121        &self,
17122        selection_pos: MultiBufferOffset,
17123        move_to_end: bool,
17124        display_map: &DisplaySnapshot,
17125        buffer: &MultiBufferSnapshot,
17126    ) -> MultiBufferOffset {
17127        let old_range = selection_pos..selection_pos;
17128        let mut new_pos = selection_pos;
17129        let mut search_range = old_range;
17130        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
17131            search_range = range.clone();
17132            if !node.is_named()
17133                || display_map.intersects_fold(range.start)
17134                || display_map.intersects_fold(range.end)
17135                // If cursor is already at the end of the syntax node, continue searching
17136                || (move_to_end && range.end == selection_pos)
17137                // If cursor is already at the start of the syntax node, continue searching
17138                || (!move_to_end && range.start == selection_pos)
17139            {
17140                continue;
17141            }
17142
17143            // If we found a string_content node, find the largest parent that is still string_content
17144            // Enables us to skip to the end of strings without taking multiple steps inside the string
17145            let (_, final_range) = if node.kind() == "string_content" {
17146                let mut current_node = node;
17147                let mut current_range = range;
17148                while let Some((parent, parent_range)) =
17149                    buffer.syntax_ancestor(current_range.clone())
17150                {
17151                    if parent.kind() == "string_content" {
17152                        current_node = parent;
17153                        current_range = parent_range;
17154                    } else {
17155                        break;
17156                    }
17157                }
17158
17159                (current_node, current_range)
17160            } else {
17161                (node, range)
17162            };
17163
17164            new_pos = if move_to_end {
17165                final_range.end
17166            } else {
17167                final_range.start
17168            };
17169
17170            break;
17171        }
17172
17173        new_pos
17174    }
17175
17176    fn move_cursors_to_syntax_nodes(
17177        &mut self,
17178        window: &mut Window,
17179        cx: &mut Context<Self>,
17180        move_to_end: bool,
17181    ) -> bool {
17182        let old_selections: Box<[_]> = self
17183            .selections
17184            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17185            .into();
17186        if old_selections.is_empty() {
17187            return false;
17188        }
17189
17190        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17191
17192        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17193        let buffer = self.buffer.read(cx).snapshot(cx);
17194
17195        let mut any_cursor_moved = false;
17196        let new_selections = old_selections
17197            .iter()
17198            .map(|selection| {
17199                if !selection.is_empty() {
17200                    return selection.clone();
17201                }
17202
17203                let selection_pos = selection.head();
17204                let new_pos = self.find_syntax_node_boundary(
17205                    selection_pos,
17206                    move_to_end,
17207                    &display_map,
17208                    &buffer,
17209                );
17210
17211                any_cursor_moved |= new_pos != selection_pos;
17212
17213                Selection {
17214                    id: selection.id,
17215                    start: new_pos,
17216                    end: new_pos,
17217                    goal: SelectionGoal::None,
17218                    reversed: false,
17219                }
17220            })
17221            .collect::<Vec<_>>();
17222
17223        self.change_selections(Default::default(), window, cx, |s| {
17224            s.select(new_selections);
17225        });
17226        self.request_autoscroll(Autoscroll::newest(), cx);
17227
17228        any_cursor_moved
17229    }
17230
17231    pub fn select_to_start_of_larger_syntax_node(
17232        &mut self,
17233        _: &SelectToStartOfLargerSyntaxNode,
17234        window: &mut Window,
17235        cx: &mut Context<Self>,
17236    ) {
17237        self.select_to_syntax_nodes(window, cx, false);
17238    }
17239
17240    pub fn select_to_end_of_larger_syntax_node(
17241        &mut self,
17242        _: &SelectToEndOfLargerSyntaxNode,
17243        window: &mut Window,
17244        cx: &mut Context<Self>,
17245    ) {
17246        self.select_to_syntax_nodes(window, cx, true);
17247    }
17248
17249    fn select_to_syntax_nodes(
17250        &mut self,
17251        window: &mut Window,
17252        cx: &mut Context<Self>,
17253        move_to_end: bool,
17254    ) {
17255        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17256
17257        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17258        let buffer = self.buffer.read(cx).snapshot(cx);
17259        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17260
17261        let new_selections = old_selections
17262            .iter()
17263            .map(|selection| {
17264                let new_pos = self.find_syntax_node_boundary(
17265                    selection.head(),
17266                    move_to_end,
17267                    &display_map,
17268                    &buffer,
17269                );
17270
17271                let mut new_selection = selection.clone();
17272                new_selection.set_head(new_pos, SelectionGoal::None);
17273                new_selection
17274            })
17275            .collect::<Vec<_>>();
17276
17277        self.change_selections(Default::default(), window, cx, |s| {
17278            s.select(new_selections);
17279        });
17280    }
17281
17282    pub fn move_to_enclosing_bracket(
17283        &mut self,
17284        _: &MoveToEnclosingBracket,
17285        window: &mut Window,
17286        cx: &mut Context<Self>,
17287    ) {
17288        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17289        self.change_selections(Default::default(), window, cx, |s| {
17290            s.move_offsets_with(&mut |snapshot, selection| {
17291                let Some(enclosing_bracket_ranges) =
17292                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17293                else {
17294                    return;
17295                };
17296
17297                let mut best_length = usize::MAX;
17298                let mut best_inside = false;
17299                let mut best_in_bracket_range = false;
17300                let mut best_destination = None;
17301                for (open, close) in enclosing_bracket_ranges {
17302                    let close = close.to_inclusive();
17303                    let length = *close.end() - open.start;
17304                    let inside = selection.start >= open.end && selection.end <= *close.start();
17305                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17306                        || close.contains(&selection.head());
17307
17308                    // If best is next to a bracket and current isn't, skip
17309                    if !in_bracket_range && best_in_bracket_range {
17310                        continue;
17311                    }
17312
17313                    // Prefer smaller lengths unless best is inside and current isn't
17314                    if length > best_length && (best_inside || !inside) {
17315                        continue;
17316                    }
17317
17318                    best_length = length;
17319                    best_inside = inside;
17320                    best_in_bracket_range = in_bracket_range;
17321                    best_destination = Some(
17322                        if close.contains(&selection.start) && close.contains(&selection.end) {
17323                            if inside { open.end } else { open.start }
17324                        } else if inside {
17325                            *close.start()
17326                        } else {
17327                            *close.end()
17328                        },
17329                    );
17330                }
17331
17332                if let Some(destination) = best_destination {
17333                    selection.collapse_to(destination, SelectionGoal::None);
17334                }
17335            })
17336        });
17337    }
17338
17339    pub fn undo_selection(
17340        &mut self,
17341        _: &UndoSelection,
17342        window: &mut Window,
17343        cx: &mut Context<Self>,
17344    ) {
17345        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17346        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17347            self.selection_history.mode = SelectionHistoryMode::Undoing;
17348            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17349                this.end_selection(window, cx);
17350                this.change_selections(
17351                    SelectionEffects::scroll(Autoscroll::newest()),
17352                    window,
17353                    cx,
17354                    |s| s.select_anchors(entry.selections.to_vec()),
17355                );
17356            });
17357            self.selection_history.mode = SelectionHistoryMode::Normal;
17358
17359            self.select_next_state = entry.select_next_state;
17360            self.select_prev_state = entry.select_prev_state;
17361            self.add_selections_state = entry.add_selections_state;
17362        }
17363    }
17364
17365    pub fn redo_selection(
17366        &mut self,
17367        _: &RedoSelection,
17368        window: &mut Window,
17369        cx: &mut Context<Self>,
17370    ) {
17371        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17372        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17373            self.selection_history.mode = SelectionHistoryMode::Redoing;
17374            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17375                this.end_selection(window, cx);
17376                this.change_selections(
17377                    SelectionEffects::scroll(Autoscroll::newest()),
17378                    window,
17379                    cx,
17380                    |s| s.select_anchors(entry.selections.to_vec()),
17381                );
17382            });
17383            self.selection_history.mode = SelectionHistoryMode::Normal;
17384
17385            self.select_next_state = entry.select_next_state;
17386            self.select_prev_state = entry.select_prev_state;
17387            self.add_selections_state = entry.add_selections_state;
17388        }
17389    }
17390
17391    pub fn expand_excerpts(
17392        &mut self,
17393        action: &ExpandExcerpts,
17394        _: &mut Window,
17395        cx: &mut Context<Self>,
17396    ) {
17397        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17398    }
17399
17400    pub fn expand_excerpts_down(
17401        &mut self,
17402        action: &ExpandExcerptsDown,
17403        _: &mut Window,
17404        cx: &mut Context<Self>,
17405    ) {
17406        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17407    }
17408
17409    pub fn expand_excerpts_up(
17410        &mut self,
17411        action: &ExpandExcerptsUp,
17412        _: &mut Window,
17413        cx: &mut Context<Self>,
17414    ) {
17415        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17416    }
17417
17418    pub fn expand_excerpts_for_direction(
17419        &mut self,
17420        lines: u32,
17421        direction: ExpandExcerptDirection,
17422        cx: &mut Context<Self>,
17423    ) {
17424        let selections = self.selections.disjoint_anchors_arc();
17425
17426        let lines = if lines == 0 {
17427            EditorSettings::get_global(cx).expand_excerpt_lines
17428        } else {
17429            lines
17430        };
17431
17432        let snapshot = self.buffer.read(cx).snapshot(cx);
17433        let excerpt_ids = selections
17434            .iter()
17435            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17436            .unique()
17437            .sorted()
17438            .collect::<Vec<_>>();
17439
17440        if self.delegate_expand_excerpts {
17441            cx.emit(EditorEvent::ExpandExcerptsRequested {
17442                excerpt_ids,
17443                lines,
17444                direction,
17445            });
17446            return;
17447        }
17448
17449        self.buffer.update(cx, |buffer, cx| {
17450            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17451        })
17452    }
17453
17454    pub fn expand_excerpt(
17455        &mut self,
17456        excerpt: ExcerptId,
17457        direction: ExpandExcerptDirection,
17458        window: &mut Window,
17459        cx: &mut Context<Self>,
17460    ) {
17461        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17462
17463        if self.delegate_expand_excerpts {
17464            cx.emit(EditorEvent::ExpandExcerptsRequested {
17465                excerpt_ids: vec![excerpt],
17466                lines: lines_to_expand,
17467                direction,
17468            });
17469            return;
17470        }
17471
17472        let current_scroll_position = self.scroll_position(cx);
17473        let mut scroll = None;
17474
17475        if direction == ExpandExcerptDirection::Down {
17476            let multi_buffer = self.buffer.read(cx);
17477            let snapshot = multi_buffer.snapshot(cx);
17478            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17479                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17480                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17481            {
17482                let buffer_snapshot = buffer.read(cx).snapshot();
17483                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17484                let last_row = buffer_snapshot.max_point().row;
17485                let lines_below = last_row.saturating_sub(excerpt_end_row);
17486                if lines_below >= lines_to_expand {
17487                    scroll = Some(
17488                        current_scroll_position
17489                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17490                    );
17491                }
17492            }
17493        }
17494        if direction == ExpandExcerptDirection::Up
17495            && self
17496                .buffer
17497                .read(cx)
17498                .snapshot(cx)
17499                .excerpt_before(excerpt)
17500                .is_none()
17501        {
17502            scroll = Some(current_scroll_position);
17503        }
17504
17505        self.buffer.update(cx, |buffer, cx| {
17506            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17507        });
17508
17509        if let Some(new_scroll_position) = scroll {
17510            self.set_scroll_position(new_scroll_position, window, cx);
17511        }
17512    }
17513
17514    pub fn go_to_singleton_buffer_point(
17515        &mut self,
17516        point: Point,
17517        window: &mut Window,
17518        cx: &mut Context<Self>,
17519    ) {
17520        self.go_to_singleton_buffer_range(point..point, window, cx);
17521    }
17522
17523    pub fn go_to_singleton_buffer_range(
17524        &mut self,
17525        range: Range<Point>,
17526        window: &mut Window,
17527        cx: &mut Context<Self>,
17528    ) {
17529        let multibuffer = self.buffer().read(cx);
17530        let Some(buffer) = multibuffer.as_singleton() else {
17531            return;
17532        };
17533        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17534            return;
17535        };
17536        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17537            return;
17538        };
17539        self.change_selections(
17540            SelectionEffects::default().nav_history(true),
17541            window,
17542            cx,
17543            |s| s.select_anchor_ranges([start..end]),
17544        );
17545    }
17546
17547    pub fn go_to_diagnostic(
17548        &mut self,
17549        action: &GoToDiagnostic,
17550        window: &mut Window,
17551        cx: &mut Context<Self>,
17552    ) {
17553        if !self.diagnostics_enabled() {
17554            return;
17555        }
17556        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17557        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17558    }
17559
17560    pub fn go_to_prev_diagnostic(
17561        &mut self,
17562        action: &GoToPreviousDiagnostic,
17563        window: &mut Window,
17564        cx: &mut Context<Self>,
17565    ) {
17566        if !self.diagnostics_enabled() {
17567            return;
17568        }
17569        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17570        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17571    }
17572
17573    pub fn go_to_diagnostic_impl(
17574        &mut self,
17575        direction: Direction,
17576        severity: GoToDiagnosticSeverityFilter,
17577        window: &mut Window,
17578        cx: &mut Context<Self>,
17579    ) {
17580        let buffer = self.buffer.read(cx).snapshot(cx);
17581        let selection = self
17582            .selections
17583            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17584
17585        let mut active_group_id = None;
17586        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17587            && active_group.active_range.start.to_offset(&buffer) == selection.start
17588        {
17589            active_group_id = Some(active_group.group_id);
17590        }
17591
17592        fn filtered<'a>(
17593            severity: GoToDiagnosticSeverityFilter,
17594            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17595        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17596            diagnostics
17597                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17598                .filter(|entry| entry.range.start != entry.range.end)
17599                .filter(|entry| !entry.diagnostic.is_unnecessary)
17600        }
17601
17602        let before = filtered(
17603            severity,
17604            buffer
17605                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17606                .filter(|entry| entry.range.start <= selection.start),
17607        );
17608        let after = filtered(
17609            severity,
17610            buffer
17611                .diagnostics_in_range(selection.start..buffer.len())
17612                .filter(|entry| entry.range.start >= selection.start),
17613        );
17614
17615        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17616        if direction == Direction::Prev {
17617            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17618            {
17619                for diagnostic in prev_diagnostics.into_iter().rev() {
17620                    if diagnostic.range.start != selection.start
17621                        || active_group_id
17622                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17623                    {
17624                        found = Some(diagnostic);
17625                        break 'outer;
17626                    }
17627                }
17628            }
17629        } else {
17630            for diagnostic in after.chain(before) {
17631                if diagnostic.range.start != selection.start
17632                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17633                {
17634                    found = Some(diagnostic);
17635                    break;
17636                }
17637            }
17638        }
17639        let Some(next_diagnostic) = found else {
17640            return;
17641        };
17642
17643        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17644        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17645            return;
17646        };
17647        let snapshot = self.snapshot(window, cx);
17648        if snapshot.intersects_fold(next_diagnostic.range.start) {
17649            self.unfold_ranges(
17650                std::slice::from_ref(&next_diagnostic.range),
17651                true,
17652                false,
17653                cx,
17654            );
17655        }
17656        self.change_selections(Default::default(), window, cx, |s| {
17657            s.select_ranges(vec![
17658                next_diagnostic.range.start..next_diagnostic.range.start,
17659            ])
17660        });
17661        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17662        self.refresh_edit_prediction(false, true, window, cx);
17663    }
17664
17665    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17666        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17667        let snapshot = self.snapshot(window, cx);
17668        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17669        self.go_to_hunk_before_or_after_position(
17670            &snapshot,
17671            selection.head(),
17672            Direction::Next,
17673            true,
17674            window,
17675            cx,
17676        );
17677    }
17678
17679    pub fn go_to_hunk_before_or_after_position(
17680        &mut self,
17681        snapshot: &EditorSnapshot,
17682        position: Point,
17683        direction: Direction,
17684        wrap_around: bool,
17685        window: &mut Window,
17686        cx: &mut Context<Editor>,
17687    ) {
17688        let row = if direction == Direction::Next {
17689            self.hunk_after_position(snapshot, position, wrap_around)
17690                .map(|hunk| hunk.row_range.start)
17691        } else {
17692            self.hunk_before_position(snapshot, position, wrap_around)
17693        };
17694
17695        if let Some(row) = row {
17696            let destination = Point::new(row.0, 0);
17697            let autoscroll = Autoscroll::center();
17698
17699            self.unfold_ranges(&[destination..destination], false, false, cx);
17700            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17701                s.select_ranges([destination..destination]);
17702            });
17703        }
17704    }
17705
17706    fn hunk_after_position(
17707        &mut self,
17708        snapshot: &EditorSnapshot,
17709        position: Point,
17710        wrap_around: bool,
17711    ) -> Option<MultiBufferDiffHunk> {
17712        let result = snapshot
17713            .buffer_snapshot()
17714            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17715            .find(|hunk| hunk.row_range.start.0 > position.row);
17716
17717        if wrap_around {
17718            result.or_else(|| {
17719                snapshot
17720                    .buffer_snapshot()
17721                    .diff_hunks_in_range(Point::zero()..position)
17722                    .find(|hunk| hunk.row_range.end.0 < position.row)
17723            })
17724        } else {
17725            result
17726        }
17727    }
17728
17729    fn go_to_prev_hunk(
17730        &mut self,
17731        _: &GoToPreviousHunk,
17732        window: &mut Window,
17733        cx: &mut Context<Self>,
17734    ) {
17735        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17736        let snapshot = self.snapshot(window, cx);
17737        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17738        self.go_to_hunk_before_or_after_position(
17739            &snapshot,
17740            selection.head(),
17741            Direction::Prev,
17742            true,
17743            window,
17744            cx,
17745        );
17746    }
17747
17748    fn hunk_before_position(
17749        &mut self,
17750        snapshot: &EditorSnapshot,
17751        position: Point,
17752        wrap_around: bool,
17753    ) -> Option<MultiBufferRow> {
17754        let result = snapshot.buffer_snapshot().diff_hunk_before(position);
17755
17756        if wrap_around {
17757            result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17758        } else {
17759            result
17760        }
17761    }
17762
17763    fn go_to_next_change(
17764        &mut self,
17765        _: &GoToNextChange,
17766        window: &mut Window,
17767        cx: &mut Context<Self>,
17768    ) {
17769        if let Some(selections) = self
17770            .change_list
17771            .next_change(1, Direction::Next)
17772            .map(|s| s.to_vec())
17773        {
17774            self.change_selections(Default::default(), window, cx, |s| {
17775                let map = s.display_snapshot();
17776                s.select_display_ranges(selections.iter().map(|a| {
17777                    let point = a.to_display_point(&map);
17778                    point..point
17779                }))
17780            })
17781        }
17782    }
17783
17784    fn go_to_previous_change(
17785        &mut self,
17786        _: &GoToPreviousChange,
17787        window: &mut Window,
17788        cx: &mut Context<Self>,
17789    ) {
17790        if let Some(selections) = self
17791            .change_list
17792            .next_change(1, Direction::Prev)
17793            .map(|s| s.to_vec())
17794        {
17795            self.change_selections(Default::default(), window, cx, |s| {
17796                let map = s.display_snapshot();
17797                s.select_display_ranges(selections.iter().map(|a| {
17798                    let point = a.to_display_point(&map);
17799                    point..point
17800                }))
17801            })
17802        }
17803    }
17804
17805    pub fn go_to_next_document_highlight(
17806        &mut self,
17807        _: &GoToNextDocumentHighlight,
17808        window: &mut Window,
17809        cx: &mut Context<Self>,
17810    ) {
17811        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17812    }
17813
17814    pub fn go_to_prev_document_highlight(
17815        &mut self,
17816        _: &GoToPreviousDocumentHighlight,
17817        window: &mut Window,
17818        cx: &mut Context<Self>,
17819    ) {
17820        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17821    }
17822
17823    pub fn go_to_document_highlight_before_or_after_position(
17824        &mut self,
17825        direction: Direction,
17826        window: &mut Window,
17827        cx: &mut Context<Editor>,
17828    ) {
17829        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17830        let snapshot = self.snapshot(window, cx);
17831        let buffer = &snapshot.buffer_snapshot();
17832        let position = self
17833            .selections
17834            .newest::<Point>(&snapshot.display_snapshot)
17835            .head();
17836        let anchor_position = buffer.anchor_after(position);
17837
17838        // Get all document highlights (both read and write)
17839        let mut all_highlights = Vec::new();
17840
17841        if let Some((_, read_highlights)) = self
17842            .background_highlights
17843            .get(&HighlightKey::DocumentHighlightRead)
17844        {
17845            all_highlights.extend(read_highlights.iter());
17846        }
17847
17848        if let Some((_, write_highlights)) = self
17849            .background_highlights
17850            .get(&HighlightKey::DocumentHighlightWrite)
17851        {
17852            all_highlights.extend(write_highlights.iter());
17853        }
17854
17855        if all_highlights.is_empty() {
17856            return;
17857        }
17858
17859        // Sort highlights by position
17860        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17861
17862        let target_highlight = match direction {
17863            Direction::Next => {
17864                // Find the first highlight after the current position
17865                all_highlights
17866                    .iter()
17867                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17868            }
17869            Direction::Prev => {
17870                // Find the last highlight before the current position
17871                all_highlights
17872                    .iter()
17873                    .rev()
17874                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17875            }
17876        };
17877
17878        if let Some(highlight) = target_highlight {
17879            let destination = highlight.start.to_point(buffer);
17880            let autoscroll = Autoscroll::center();
17881
17882            self.unfold_ranges(&[destination..destination], false, false, cx);
17883            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17884                s.select_ranges([destination..destination]);
17885            });
17886        }
17887    }
17888
17889    fn go_to_line<T: 'static>(
17890        &mut self,
17891        position: Anchor,
17892        highlight_color: Option<Hsla>,
17893        window: &mut Window,
17894        cx: &mut Context<Self>,
17895    ) {
17896        let snapshot = self.snapshot(window, cx).display_snapshot;
17897        let position = position.to_point(&snapshot.buffer_snapshot());
17898        let start = snapshot
17899            .buffer_snapshot()
17900            .clip_point(Point::new(position.row, 0), Bias::Left);
17901        let end = start + Point::new(1, 0);
17902        let start = snapshot.buffer_snapshot().anchor_before(start);
17903        let end = snapshot.buffer_snapshot().anchor_before(end);
17904
17905        self.highlight_rows::<T>(
17906            start..end,
17907            highlight_color
17908                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17909            Default::default(),
17910            cx,
17911        );
17912
17913        if self.buffer.read(cx).is_singleton() {
17914            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17915        }
17916    }
17917
17918    pub fn go_to_definition(
17919        &mut self,
17920        _: &GoToDefinition,
17921        window: &mut Window,
17922        cx: &mut Context<Self>,
17923    ) -> Task<Result<Navigated>> {
17924        let definition =
17925            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17926        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17927        cx.spawn_in(window, async move |editor, cx| {
17928            if definition.await? == Navigated::Yes {
17929                return Ok(Navigated::Yes);
17930            }
17931            match fallback_strategy {
17932                GoToDefinitionFallback::None => Ok(Navigated::No),
17933                GoToDefinitionFallback::FindAllReferences => {
17934                    match editor.update_in(cx, |editor, window, cx| {
17935                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17936                    })? {
17937                        Some(references) => references.await,
17938                        None => Ok(Navigated::No),
17939                    }
17940                }
17941            }
17942        })
17943    }
17944
17945    pub fn go_to_declaration(
17946        &mut self,
17947        _: &GoToDeclaration,
17948        window: &mut Window,
17949        cx: &mut Context<Self>,
17950    ) -> Task<Result<Navigated>> {
17951        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17952    }
17953
17954    pub fn go_to_declaration_split(
17955        &mut self,
17956        _: &GoToDeclaration,
17957        window: &mut Window,
17958        cx: &mut Context<Self>,
17959    ) -> Task<Result<Navigated>> {
17960        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17961    }
17962
17963    pub fn go_to_implementation(
17964        &mut self,
17965        _: &GoToImplementation,
17966        window: &mut Window,
17967        cx: &mut Context<Self>,
17968    ) -> Task<Result<Navigated>> {
17969        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17970    }
17971
17972    pub fn go_to_implementation_split(
17973        &mut self,
17974        _: &GoToImplementationSplit,
17975        window: &mut Window,
17976        cx: &mut Context<Self>,
17977    ) -> Task<Result<Navigated>> {
17978        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17979    }
17980
17981    pub fn go_to_type_definition(
17982        &mut self,
17983        _: &GoToTypeDefinition,
17984        window: &mut Window,
17985        cx: &mut Context<Self>,
17986    ) -> Task<Result<Navigated>> {
17987        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17988    }
17989
17990    pub fn go_to_definition_split(
17991        &mut self,
17992        _: &GoToDefinitionSplit,
17993        window: &mut Window,
17994        cx: &mut Context<Self>,
17995    ) -> Task<Result<Navigated>> {
17996        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17997    }
17998
17999    pub fn go_to_type_definition_split(
18000        &mut self,
18001        _: &GoToTypeDefinitionSplit,
18002        window: &mut Window,
18003        cx: &mut Context<Self>,
18004    ) -> Task<Result<Navigated>> {
18005        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
18006    }
18007
18008    fn go_to_definition_of_kind(
18009        &mut self,
18010        kind: GotoDefinitionKind,
18011        split: bool,
18012        window: &mut Window,
18013        cx: &mut Context<Self>,
18014    ) -> Task<Result<Navigated>> {
18015        let Some(provider) = self.semantics_provider.clone() else {
18016            return Task::ready(Ok(Navigated::No));
18017        };
18018        let head = self
18019            .selections
18020            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
18021            .head();
18022        let buffer = self.buffer.read(cx);
18023        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
18024            return Task::ready(Ok(Navigated::No));
18025        };
18026        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
18027            return Task::ready(Ok(Navigated::No));
18028        };
18029
18030        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
18031
18032        cx.spawn_in(window, async move |editor, cx| {
18033            let Some(definitions) = definitions.await? else {
18034                return Ok(Navigated::No);
18035            };
18036            let navigated = editor
18037                .update_in(cx, |editor, window, cx| {
18038                    editor.navigate_to_hover_links(
18039                        Some(kind),
18040                        definitions
18041                            .into_iter()
18042                            .filter(|location| {
18043                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18044                            })
18045                            .map(HoverLink::Text)
18046                            .collect::<Vec<_>>(),
18047                        nav_entry,
18048                        split,
18049                        window,
18050                        cx,
18051                    )
18052                })?
18053                .await?;
18054            anyhow::Ok(navigated)
18055        })
18056    }
18057
18058    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18059        let selection = self.selections.newest_anchor();
18060        let head = selection.head();
18061        let tail = selection.tail();
18062
18063        let Some((buffer, start_position)) =
18064            self.buffer.read(cx).text_anchor_for_position(head, cx)
18065        else {
18066            return;
18067        };
18068
18069        let end_position = if head != tail {
18070            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18071                return;
18072            };
18073            Some(pos)
18074        } else {
18075            None
18076        };
18077
18078        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18079            let url = if let Some(end_pos) = end_position {
18080                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18081            } else {
18082                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18083            };
18084
18085            if let Some(url) = url {
18086                cx.update(|window, cx| {
18087                    if parse_zed_link(&url, cx).is_some() {
18088                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18089                    } else {
18090                        cx.open_url(&url);
18091                    }
18092                })?;
18093            }
18094
18095            anyhow::Ok(())
18096        });
18097
18098        url_finder.detach();
18099    }
18100
18101    pub fn open_selected_filename(
18102        &mut self,
18103        _: &OpenSelectedFilename,
18104        window: &mut Window,
18105        cx: &mut Context<Self>,
18106    ) {
18107        let Some(workspace) = self.workspace() else {
18108            return;
18109        };
18110
18111        let position = self.selections.newest_anchor().head();
18112
18113        let Some((buffer, buffer_position)) =
18114            self.buffer.read(cx).text_anchor_for_position(position, cx)
18115        else {
18116            return;
18117        };
18118
18119        let project = self.project.clone();
18120
18121        cx.spawn_in(window, async move |_, cx| {
18122            let result = find_file(&buffer, project, buffer_position, cx).await;
18123
18124            if let Some((_, path)) = result {
18125                workspace
18126                    .update_in(cx, |workspace, window, cx| {
18127                        workspace.open_resolved_path(path, window, cx)
18128                    })?
18129                    .await?;
18130            }
18131            anyhow::Ok(())
18132        })
18133        .detach();
18134    }
18135
18136    pub(crate) fn navigate_to_hover_links(
18137        &mut self,
18138        kind: Option<GotoDefinitionKind>,
18139        definitions: Vec<HoverLink>,
18140        origin: Option<NavigationEntry>,
18141        split: bool,
18142        window: &mut Window,
18143        cx: &mut Context<Editor>,
18144    ) -> Task<Result<Navigated>> {
18145        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18146        let mut first_url_or_file = None;
18147        let definitions: Vec<_> = definitions
18148            .into_iter()
18149            .filter_map(|def| match def {
18150                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18151                HoverLink::InlayHint(lsp_location, server_id) => {
18152                    let computation =
18153                        self.compute_target_location(lsp_location, server_id, window, cx);
18154                    Some(cx.background_spawn(computation))
18155                }
18156                HoverLink::Url(url) => {
18157                    first_url_or_file = Some(Either::Left(url));
18158                    None
18159                }
18160                HoverLink::File(path) => {
18161                    first_url_or_file = Some(Either::Right(path));
18162                    None
18163                }
18164            })
18165            .collect();
18166
18167        let workspace = self.workspace();
18168
18169        let excerpt_context_lines = multi_buffer::excerpt_context_lines(cx);
18170        cx.spawn_in(window, async move |editor, cx| {
18171            let locations: Vec<Location> = future::join_all(definitions)
18172                .await
18173                .into_iter()
18174                .filter_map(|location| location.transpose())
18175                .collect::<Result<_>>()
18176                .context("location tasks")?;
18177            let mut locations = cx.update(|_, cx| {
18178                locations
18179                    .into_iter()
18180                    .map(|location| {
18181                        let buffer = location.buffer.read(cx);
18182                        (location.buffer, location.range.to_point(buffer))
18183                    })
18184                    .into_group_map()
18185            })?;
18186            let mut num_locations = 0;
18187            for ranges in locations.values_mut() {
18188                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18189                ranges.dedup();
18190                let fits_in_one_excerpt = ranges
18191                    .iter()
18192                    .tuple_windows()
18193                    .all(|(a, b)| b.start.row - a.end.row <= 2 * excerpt_context_lines);
18194                num_locations += if fits_in_one_excerpt { 1 } else { ranges.len() };
18195            }
18196
18197            if num_locations > 1 {
18198                let tab_kind = match kind {
18199                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18200                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18201                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18202                    Some(GotoDefinitionKind::Type) => "Types",
18203                };
18204                let title = editor
18205                    .update_in(cx, |_, _, cx| {
18206                        let target = locations
18207                            .iter()
18208                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18209                            .map(|(buffer, location)| {
18210                                buffer
18211                                    .read(cx)
18212                                    .text_for_range(location.clone())
18213                                    .collect::<String>()
18214                            })
18215                            .filter(|text| !text.contains('\n'))
18216                            .unique()
18217                            .take(3)
18218                            .join(", ");
18219                        if target.is_empty() {
18220                            tab_kind.to_owned()
18221                        } else {
18222                            format!("{tab_kind} for {target}")
18223                        }
18224                    })
18225                    .context("buffer title")?;
18226
18227                let Some(workspace) = workspace else {
18228                    return Ok(Navigated::No);
18229                };
18230
18231                let opened = workspace
18232                    .update_in(cx, |workspace, window, cx| {
18233                        let allow_preview = PreviewTabsSettings::get_global(cx)
18234                            .enable_preview_multibuffer_from_code_navigation;
18235                        if let Some((target_editor, target_pane)) =
18236                            Self::open_locations_in_multibuffer(
18237                                workspace,
18238                                locations,
18239                                title,
18240                                split,
18241                                allow_preview,
18242                                MultibufferSelectionMode::First,
18243                                window,
18244                                cx,
18245                            )
18246                        {
18247                            // We create our own nav history instead of using
18248                            // `target_editor.nav_history` because `nav_history`
18249                            // seems to be populated asynchronously when an item
18250                            // is added to a pane
18251                            let mut nav_history = target_pane
18252                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18253                            target_editor.update(cx, |editor, cx| {
18254                                let nav_data = editor
18255                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18256                                let target =
18257                                    Some(nav_history.navigation_entry(Some(
18258                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18259                                    )));
18260                                nav_history.push_tag(origin, target);
18261                            })
18262                        }
18263                    })
18264                    .is_ok();
18265
18266                anyhow::Ok(Navigated::from_bool(opened))
18267            } else if num_locations == 0 {
18268                // If there is one url or file, open it directly
18269                match first_url_or_file {
18270                    Some(Either::Left(url)) => {
18271                        cx.update(|window, cx| {
18272                            if parse_zed_link(&url, cx).is_some() {
18273                                window
18274                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18275                            } else {
18276                                cx.open_url(&url);
18277                            }
18278                        })?;
18279                        Ok(Navigated::Yes)
18280                    }
18281                    Some(Either::Right(path)) => {
18282                        // TODO(andrew): respect preview tab settings
18283                        //               `enable_keep_preview_on_code_navigation` and
18284                        //               `enable_preview_file_from_code_navigation`
18285                        let Some(workspace) = workspace else {
18286                            return Ok(Navigated::No);
18287                        };
18288                        workspace
18289                            .update_in(cx, |workspace, window, cx| {
18290                                workspace.open_resolved_path(path, window, cx)
18291                            })?
18292                            .await?;
18293                        Ok(Navigated::Yes)
18294                    }
18295                    None => Ok(Navigated::No),
18296                }
18297            } else {
18298                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18299
18300                editor.update_in(cx, |editor, window, cx| {
18301                    let target_ranges = target_ranges
18302                        .into_iter()
18303                        .map(|r| editor.range_for_match(&r))
18304                        .map(collapse_multiline_range)
18305                        .collect::<Vec<_>>();
18306                    if !split
18307                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18308                    {
18309                        let multibuffer = editor.buffer.read(cx);
18310                        let target_ranges = target_ranges
18311                            .into_iter()
18312                            .filter_map(|r| {
18313                                let start = multibuffer.buffer_point_to_anchor(
18314                                    &target_buffer,
18315                                    r.start,
18316                                    cx,
18317                                )?;
18318                                let end = multibuffer.buffer_point_to_anchor(
18319                                    &target_buffer,
18320                                    r.end,
18321                                    cx,
18322                                )?;
18323                                Some(start..end)
18324                            })
18325                            .collect::<Vec<_>>();
18326                        if target_ranges.is_empty() {
18327                            return Navigated::No;
18328                        }
18329
18330                        editor.change_selections(
18331                            SelectionEffects::default().nav_history(true),
18332                            window,
18333                            cx,
18334                            |s| s.select_anchor_ranges(target_ranges),
18335                        );
18336
18337                        let target =
18338                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18339                        if let Some(mut nav_history) = editor.nav_history.clone() {
18340                            nav_history.push_tag(origin, target);
18341                        }
18342                    } else {
18343                        let Some(workspace) = workspace else {
18344                            return Navigated::No;
18345                        };
18346                        let pane = workspace.read(cx).active_pane().clone();
18347                        window.defer(cx, move |window, cx| {
18348                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18349                                workspace.update(cx, |workspace, cx| {
18350                                    let pane = if split {
18351                                        workspace.adjacent_pane(window, cx)
18352                                    } else {
18353                                        workspace.active_pane().clone()
18354                                    };
18355
18356                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18357                                    let keep_old_preview = preview_tabs_settings
18358                                        .enable_keep_preview_on_code_navigation;
18359                                    let allow_new_preview = preview_tabs_settings
18360                                        .enable_preview_file_from_code_navigation;
18361
18362                                    let editor = workspace.open_project_item(
18363                                        pane.clone(),
18364                                        target_buffer.clone(),
18365                                        true,
18366                                        true,
18367                                        keep_old_preview,
18368                                        allow_new_preview,
18369                                        window,
18370                                        cx,
18371                                    );
18372                                    (editor, pane)
18373                                });
18374                            // We create our own nav history instead of using
18375                            // `target_editor.nav_history` because `nav_history`
18376                            // seems to be populated asynchronously when an item
18377                            // is added to a pane
18378                            let mut nav_history = target_pane
18379                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18380                            target_editor.update(cx, |target_editor, cx| {
18381                                // When selecting a definition in a different buffer, disable the nav history
18382                                // to avoid creating a history entry at the previous cursor location.
18383                                pane.update(cx, |pane, _| pane.disable_history());
18384
18385                                let multibuffer = target_editor.buffer.read(cx);
18386                                let Some(target_buffer) = multibuffer.as_singleton() else {
18387                                    return Navigated::No;
18388                                };
18389                                let target_ranges = target_ranges
18390                                    .into_iter()
18391                                    .filter_map(|r| {
18392                                        let start = multibuffer.buffer_point_to_anchor(
18393                                            &target_buffer,
18394                                            r.start,
18395                                            cx,
18396                                        )?;
18397                                        let end = multibuffer.buffer_point_to_anchor(
18398                                            &target_buffer,
18399                                            r.end,
18400                                            cx,
18401                                        )?;
18402                                        Some(start..end)
18403                                    })
18404                                    .collect::<Vec<_>>();
18405                                if target_ranges.is_empty() {
18406                                    return Navigated::No;
18407                                }
18408
18409                                target_editor.change_selections(
18410                                    SelectionEffects::default().nav_history(true),
18411                                    window,
18412                                    cx,
18413                                    |s| s.select_anchor_ranges(target_ranges),
18414                                );
18415
18416                                let nav_data = target_editor.navigation_data(
18417                                    target_editor.selections.newest_anchor().head(),
18418                                    cx,
18419                                );
18420                                let target =
18421                                    Some(nav_history.navigation_entry(Some(
18422                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18423                                    )));
18424                                nav_history.push_tag(origin, target);
18425                                pane.update(cx, |pane, _| pane.enable_history());
18426                                Navigated::Yes
18427                            });
18428                        });
18429                    }
18430                    Navigated::Yes
18431                })
18432            }
18433        })
18434    }
18435
18436    fn compute_target_location(
18437        &self,
18438        lsp_location: lsp::Location,
18439        server_id: LanguageServerId,
18440        window: &mut Window,
18441        cx: &mut Context<Self>,
18442    ) -> Task<anyhow::Result<Option<Location>>> {
18443        let Some(project) = self.project.clone() else {
18444            return Task::ready(Ok(None));
18445        };
18446
18447        cx.spawn_in(window, async move |editor, cx| {
18448            let location_task = editor.update(cx, |_, cx| {
18449                project.update(cx, |project, cx| {
18450                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18451                })
18452            })?;
18453            let location = Some({
18454                let target_buffer_handle = location_task.await.context("open local buffer")?;
18455                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18456                    let target_start = target_buffer
18457                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18458                    let target_end = target_buffer
18459                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18460                    target_buffer.anchor_after(target_start)
18461                        ..target_buffer.anchor_before(target_end)
18462                });
18463                Location {
18464                    buffer: target_buffer_handle,
18465                    range,
18466                }
18467            });
18468            Ok(location)
18469        })
18470    }
18471
18472    fn go_to_next_reference(
18473        &mut self,
18474        _: &GoToNextReference,
18475        window: &mut Window,
18476        cx: &mut Context<Self>,
18477    ) {
18478        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18479        if let Some(task) = task {
18480            task.detach();
18481        };
18482    }
18483
18484    fn go_to_prev_reference(
18485        &mut self,
18486        _: &GoToPreviousReference,
18487        window: &mut Window,
18488        cx: &mut Context<Self>,
18489    ) {
18490        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18491        if let Some(task) = task {
18492            task.detach();
18493        };
18494    }
18495
18496    fn go_to_symbol_by_offset(
18497        &mut self,
18498        window: &mut Window,
18499        cx: &mut Context<Self>,
18500        offset: i8,
18501    ) -> Task<Result<()>> {
18502        let editor_snapshot = self.snapshot(window, cx);
18503
18504        // We don't care about multi-buffer symbols
18505        let Some((excerpt_id, _, _)) = editor_snapshot.as_singleton() else {
18506            return Task::ready(Ok(()));
18507        };
18508
18509        let cursor_offset = self
18510            .selections
18511            .newest::<MultiBufferOffset>(&editor_snapshot.display_snapshot)
18512            .head();
18513
18514        cx.spawn_in(window, async move |editor, wcx| -> Result<()> {
18515            let Ok(Some(remote_id)) = editor.update(wcx, |ed, cx| {
18516                let buffer = ed.buffer.read(cx).as_singleton()?;
18517                Some(buffer.read(cx).remote_id())
18518            }) else {
18519                return Ok(());
18520            };
18521
18522            let task = editor.update(wcx, |ed, cx| ed.buffer_outline_items(remote_id, cx))?;
18523            let outline_items: Vec<OutlineItem<text::Anchor>> = task.await;
18524
18525            let multi_snapshot = editor_snapshot.buffer();
18526            let buffer_range = |range: &Range<_>| {
18527                Anchor::range_in_buffer(excerpt_id, range.clone()).to_offset(multi_snapshot)
18528            };
18529
18530            wcx.update_window(wcx.window_handle(), |_, window, acx| {
18531                let current_idx = outline_items
18532                    .iter()
18533                    .enumerate()
18534                    .filter_map(|(idx, item)| {
18535                        // Find the closest outline item by distance between outline text and cursor location
18536                        let source_range = buffer_range(&item.source_range_for_text);
18537                        let distance_to_closest_endpoint = cmp::min(
18538                            (source_range.start.0 as isize - cursor_offset.0 as isize).abs(),
18539                            (source_range.end.0 as isize - cursor_offset.0 as isize).abs(),
18540                        );
18541
18542                        let item_towards_offset =
18543                            (source_range.start.0 as isize - cursor_offset.0 as isize).signum()
18544                                == (offset as isize).signum();
18545
18546                        let source_range_contains_cursor = source_range.contains(&cursor_offset);
18547
18548                        // To pick the next outline to jump to, we should jump in the direction of the offset, and
18549                        // we should not already be within the outline's source range. We then pick the closest outline
18550                        // item.
18551                        (item_towards_offset && !source_range_contains_cursor)
18552                            .then_some((distance_to_closest_endpoint, idx))
18553                    })
18554                    .min()
18555                    .map(|(_, idx)| idx);
18556
18557                let Some(idx) = current_idx else {
18558                    return;
18559                };
18560
18561                let range = buffer_range(&outline_items[idx].source_range_for_text);
18562                let selection = [range.start..range.start];
18563
18564                let _ = editor
18565                    .update(acx, |editor, ecx| {
18566                        editor.change_selections(
18567                            SelectionEffects::scroll(Autoscroll::newest()),
18568                            window,
18569                            ecx,
18570                            |s| s.select_ranges(selection),
18571                        );
18572                    })
18573                    .ok();
18574            })?;
18575
18576            Ok(())
18577        })
18578    }
18579
18580    fn go_to_next_symbol(
18581        &mut self,
18582        _: &GoToNextSymbol,
18583        window: &mut Window,
18584        cx: &mut Context<Self>,
18585    ) {
18586        self.go_to_symbol_by_offset(window, cx, 1).detach();
18587    }
18588
18589    fn go_to_previous_symbol(
18590        &mut self,
18591        _: &GoToPreviousSymbol,
18592        window: &mut Window,
18593        cx: &mut Context<Self>,
18594    ) {
18595        self.go_to_symbol_by_offset(window, cx, -1).detach();
18596    }
18597
18598    pub fn go_to_reference_before_or_after_position(
18599        &mut self,
18600        direction: Direction,
18601        count: usize,
18602        window: &mut Window,
18603        cx: &mut Context<Self>,
18604    ) -> Option<Task<Result<()>>> {
18605        let selection = self.selections.newest_anchor();
18606        let head = selection.head();
18607
18608        let multi_buffer = self.buffer.read(cx);
18609
18610        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18611        let workspace = self.workspace()?;
18612        let project = workspace.read(cx).project().clone();
18613        let references =
18614            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18615        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18616            let Some(locations) = references.await? else {
18617                return Ok(());
18618            };
18619
18620            if locations.is_empty() {
18621                // totally normal - the cursor may be on something which is not
18622                // a symbol (e.g. a keyword)
18623                log::info!("no references found under cursor");
18624                return Ok(());
18625            }
18626
18627            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18628
18629            let (locations, current_location_index) =
18630                multi_buffer.update(cx, |multi_buffer, cx| {
18631                    let mut locations = locations
18632                        .into_iter()
18633                        .filter_map(|loc| {
18634                            let start = multi_buffer.buffer_anchor_to_anchor(
18635                                &loc.buffer,
18636                                loc.range.start,
18637                                cx,
18638                            )?;
18639                            let end = multi_buffer.buffer_anchor_to_anchor(
18640                                &loc.buffer,
18641                                loc.range.end,
18642                                cx,
18643                            )?;
18644                            Some(start..end)
18645                        })
18646                        .collect::<Vec<_>>();
18647
18648                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18649                    // There is an O(n) implementation, but given this list will be
18650                    // small (usually <100 items), the extra O(log(n)) factor isn't
18651                    // worth the (surprisingly large amount of) extra complexity.
18652                    locations
18653                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18654
18655                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18656
18657                    let current_location_index = locations.iter().position(|loc| {
18658                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18659                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18660                    });
18661
18662                    (locations, current_location_index)
18663                });
18664
18665            let Some(current_location_index) = current_location_index else {
18666                // This indicates something has gone wrong, because we already
18667                // handle the "no references" case above
18668                log::error!(
18669                    "failed to find current reference under cursor. Total references: {}",
18670                    locations.len()
18671                );
18672                return Ok(());
18673            };
18674
18675            let destination_location_index = match direction {
18676                Direction::Next => (current_location_index + count) % locations.len(),
18677                Direction::Prev => {
18678                    (current_location_index + locations.len() - count % locations.len())
18679                        % locations.len()
18680                }
18681            };
18682
18683            // TODO(cameron): is this needed?
18684            // the thinking is to avoid "jumping to the current location" (avoid
18685            // polluting "jumplist" in vim terms)
18686            if current_location_index == destination_location_index {
18687                return Ok(());
18688            }
18689
18690            let Range { start, end } = locations[destination_location_index];
18691
18692            editor.update_in(cx, |editor, window, cx| {
18693                let effects = SelectionEffects::default();
18694
18695                editor.unfold_ranges(&[start..end], false, false, cx);
18696                editor.change_selections(effects, window, cx, |s| {
18697                    s.select_ranges([start..start]);
18698                });
18699            })?;
18700
18701            Ok(())
18702        }))
18703    }
18704
18705    pub fn find_all_references(
18706        &mut self,
18707        action: &FindAllReferences,
18708        window: &mut Window,
18709        cx: &mut Context<Self>,
18710    ) -> Option<Task<Result<Navigated>>> {
18711        let always_open_multibuffer = action.always_open_multibuffer;
18712        let selection = self.selections.newest_anchor();
18713        let multi_buffer = self.buffer.read(cx);
18714        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18715        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18716        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18717        let head = selection_offset.head();
18718
18719        let head_anchor = multi_buffer_snapshot.anchor_at(
18720            head,
18721            if head < selection_offset.tail() {
18722                Bias::Right
18723            } else {
18724                Bias::Left
18725            },
18726        );
18727
18728        match self
18729            .find_all_references_task_sources
18730            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18731        {
18732            Ok(_) => {
18733                log::info!(
18734                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18735                );
18736                return None;
18737            }
18738            Err(i) => {
18739                self.find_all_references_task_sources.insert(i, head_anchor);
18740            }
18741        }
18742
18743        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18744        let workspace = self.workspace()?;
18745        let project = workspace.read(cx).project().clone();
18746        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18747        Some(cx.spawn_in(window, async move |editor, cx| {
18748            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18749                if let Ok(i) = editor
18750                    .find_all_references_task_sources
18751                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18752                {
18753                    editor.find_all_references_task_sources.remove(i);
18754                }
18755            });
18756
18757            let Some(locations) = references.await? else {
18758                return anyhow::Ok(Navigated::No);
18759            };
18760            let mut locations = cx.update(|_, cx| {
18761                locations
18762                    .into_iter()
18763                    .map(|location| {
18764                        let buffer = location.buffer.read(cx);
18765                        (location.buffer, location.range.to_point(buffer))
18766                    })
18767                    // if special-casing the single-match case, remove ranges
18768                    // that intersect current selection
18769                    .filter(|(location_buffer, location)| {
18770                        if always_open_multibuffer || &buffer != location_buffer {
18771                            return true;
18772                        }
18773
18774                        !location.contains_inclusive(&selection_point.range())
18775                    })
18776                    .into_group_map()
18777            })?;
18778            if locations.is_empty() {
18779                return anyhow::Ok(Navigated::No);
18780            }
18781            for ranges in locations.values_mut() {
18782                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18783                ranges.dedup();
18784            }
18785            let mut num_locations = 0;
18786            for ranges in locations.values_mut() {
18787                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18788                ranges.dedup();
18789                num_locations += ranges.len();
18790            }
18791
18792            if num_locations == 1 && !always_open_multibuffer {
18793                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18794                let target_range = target_ranges.first().unwrap().clone();
18795
18796                return editor.update_in(cx, |editor, window, cx| {
18797                    let range = target_range.to_point(target_buffer.read(cx));
18798                    let range = editor.range_for_match(&range);
18799                    let range = range.start..range.start;
18800
18801                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18802                        editor.go_to_singleton_buffer_range(range, window, cx);
18803                    } else {
18804                        let pane = workspace.read(cx).active_pane().clone();
18805                        window.defer(cx, move |window, cx| {
18806                            let target_editor: Entity<Self> =
18807                                workspace.update(cx, |workspace, cx| {
18808                                    let pane = workspace.active_pane().clone();
18809
18810                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18811                                    let keep_old_preview = preview_tabs_settings
18812                                        .enable_keep_preview_on_code_navigation;
18813                                    let allow_new_preview = preview_tabs_settings
18814                                        .enable_preview_file_from_code_navigation;
18815
18816                                    workspace.open_project_item(
18817                                        pane,
18818                                        target_buffer.clone(),
18819                                        true,
18820                                        true,
18821                                        keep_old_preview,
18822                                        allow_new_preview,
18823                                        window,
18824                                        cx,
18825                                    )
18826                                });
18827                            target_editor.update(cx, |target_editor, cx| {
18828                                // When selecting a definition in a different buffer, disable the nav history
18829                                // to avoid creating a history entry at the previous cursor location.
18830                                pane.update(cx, |pane, _| pane.disable_history());
18831                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18832                                pane.update(cx, |pane, _| pane.enable_history());
18833                            });
18834                        });
18835                    }
18836                    Navigated::No
18837                });
18838            }
18839
18840            workspace.update_in(cx, |workspace, window, cx| {
18841                let target = locations
18842                    .iter()
18843                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18844                    .map(|(buffer, location)| {
18845                        buffer
18846                            .read(cx)
18847                            .text_for_range(location.clone())
18848                            .collect::<String>()
18849                    })
18850                    .filter(|text| !text.contains('\n'))
18851                    .unique()
18852                    .take(3)
18853                    .join(", ");
18854                let title = if target.is_empty() {
18855                    "References".to_owned()
18856                } else {
18857                    format!("References to {target}")
18858                };
18859                let allow_preview = PreviewTabsSettings::get_global(cx)
18860                    .enable_preview_multibuffer_from_code_navigation;
18861                Self::open_locations_in_multibuffer(
18862                    workspace,
18863                    locations,
18864                    title,
18865                    false,
18866                    allow_preview,
18867                    MultibufferSelectionMode::First,
18868                    window,
18869                    cx,
18870                );
18871                Navigated::Yes
18872            })
18873        }))
18874    }
18875
18876    /// Opens a multibuffer with the given project locations in it.
18877    pub fn open_locations_in_multibuffer(
18878        workspace: &mut Workspace,
18879        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18880        title: String,
18881        split: bool,
18882        allow_preview: bool,
18883        multibuffer_selection_mode: MultibufferSelectionMode,
18884        window: &mut Window,
18885        cx: &mut Context<Workspace>,
18886    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18887        if locations.is_empty() {
18888            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18889            return None;
18890        }
18891
18892        let capability = workspace.project().read(cx).capability();
18893        let mut ranges = <Vec<Range<Anchor>>>::new();
18894
18895        // a key to find existing multibuffer editors with the same set of locations
18896        // to prevent us from opening more and more multibuffer tabs for searches and the like
18897        let mut key = (title.clone(), vec![]);
18898        let excerpt_buffer = cx.new(|cx| {
18899            let key = &mut key.1;
18900            let mut multibuffer = MultiBuffer::new(capability);
18901            for (buffer, mut ranges_for_buffer) in locations {
18902                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18903                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18904                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18905                    PathKey::for_buffer(&buffer, cx),
18906                    buffer.clone(),
18907                    ranges_for_buffer,
18908                    multibuffer_context_lines(cx),
18909                    cx,
18910                );
18911                ranges.extend(new_ranges)
18912            }
18913
18914            multibuffer.with_title(title)
18915        });
18916        let existing = workspace.active_pane().update(cx, |pane, cx| {
18917            pane.items()
18918                .filter_map(|item| item.downcast::<Editor>())
18919                .find(|editor| {
18920                    editor
18921                        .read(cx)
18922                        .lookup_key
18923                        .as_ref()
18924                        .and_then(|it| {
18925                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18926                        })
18927                        .is_some_and(|it| *it == key)
18928                })
18929        });
18930        let was_existing = existing.is_some();
18931        let editor = existing.unwrap_or_else(|| {
18932            cx.new(|cx| {
18933                let mut editor = Editor::for_multibuffer(
18934                    excerpt_buffer,
18935                    Some(workspace.project().clone()),
18936                    window,
18937                    cx,
18938                );
18939                editor.lookup_key = Some(Box::new(key));
18940                editor
18941            })
18942        });
18943        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18944            MultibufferSelectionMode::First => {
18945                if let Some(first_range) = ranges.first() {
18946                    editor.change_selections(
18947                        SelectionEffects::no_scroll(),
18948                        window,
18949                        cx,
18950                        |selections| {
18951                            selections.clear_disjoint();
18952                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18953                        },
18954                    );
18955                }
18956                editor.highlight_background(
18957                    HighlightKey::Editor,
18958                    &ranges,
18959                    |_, theme| theme.colors().editor_highlighted_line_background,
18960                    cx,
18961                );
18962            }
18963            MultibufferSelectionMode::All => {
18964                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18965                    selections.clear_disjoint();
18966                    selections.select_anchor_ranges(ranges);
18967                });
18968            }
18969        });
18970
18971        let item = Box::new(editor.clone());
18972
18973        let pane = if split {
18974            workspace.adjacent_pane(window, cx)
18975        } else {
18976            workspace.active_pane().clone()
18977        };
18978        let activate_pane = split;
18979
18980        let mut destination_index = None;
18981        pane.update(cx, |pane, cx| {
18982            if allow_preview && !was_existing {
18983                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18984            }
18985            if was_existing && !allow_preview {
18986                pane.unpreview_item_if_preview(item.item_id());
18987            }
18988            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18989        });
18990
18991        Some((editor, pane))
18992    }
18993
18994    pub fn rename(
18995        &mut self,
18996        _: &Rename,
18997        window: &mut Window,
18998        cx: &mut Context<Self>,
18999    ) -> Option<Task<Result<()>>> {
19000        use language::ToOffset as _;
19001
19002        let provider = self.semantics_provider.clone()?;
19003        let selection = self.selections.newest_anchor().clone();
19004        let (cursor_buffer, cursor_buffer_position) = self
19005            .buffer
19006            .read(cx)
19007            .text_anchor_for_position(selection.head(), cx)?;
19008        let (tail_buffer, cursor_buffer_position_end) = self
19009            .buffer
19010            .read(cx)
19011            .text_anchor_for_position(selection.tail(), cx)?;
19012        if tail_buffer != cursor_buffer {
19013            return None;
19014        }
19015
19016        let snapshot = cursor_buffer.read(cx).snapshot();
19017        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
19018        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
19019        let prepare_rename = provider
19020            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
19021            .unwrap_or_else(|| Task::ready(Ok(None)));
19022        drop(snapshot);
19023
19024        Some(cx.spawn_in(window, async move |this, cx| {
19025            let rename_range = if let Some(range) = prepare_rename.await? {
19026                Some(range)
19027            } else {
19028                this.update(cx, |this, cx| {
19029                    let buffer = this.buffer.read(cx).snapshot(cx);
19030                    let mut buffer_highlights = this
19031                        .document_highlights_for_position(selection.head(), &buffer)
19032                        .filter(|highlight| {
19033                            highlight.start.excerpt_id == selection.head().excerpt_id
19034                                && highlight.end.excerpt_id == selection.head().excerpt_id
19035                        });
19036                    buffer_highlights
19037                        .next()
19038                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
19039                })?
19040            };
19041            if let Some(rename_range) = rename_range {
19042                this.update_in(cx, |this, window, cx| {
19043                    let snapshot = cursor_buffer.read(cx).snapshot();
19044                    let rename_buffer_range = rename_range.to_offset(&snapshot);
19045                    let cursor_offset_in_rename_range =
19046                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
19047                    let cursor_offset_in_rename_range_end =
19048                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
19049
19050                    this.take_rename(false, window, cx);
19051                    let buffer = this.buffer.read(cx).read(cx);
19052                    let cursor_offset = selection.head().to_offset(&buffer);
19053                    let rename_start =
19054                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
19055                    let rename_end = rename_start + rename_buffer_range.len();
19056                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
19057                    let mut old_highlight_id = None;
19058                    let old_name: Arc<str> = buffer
19059                        .chunks(rename_start..rename_end, true)
19060                        .map(|chunk| {
19061                            if old_highlight_id.is_none() {
19062                                old_highlight_id = chunk.syntax_highlight_id;
19063                            }
19064                            chunk.text
19065                        })
19066                        .collect::<String>()
19067                        .into();
19068
19069                    drop(buffer);
19070
19071                    // Position the selection in the rename editor so that it matches the current selection.
19072                    this.show_local_selections = false;
19073                    let rename_editor = cx.new(|cx| {
19074                        let mut editor = Editor::single_line(window, cx);
19075                        editor.buffer.update(cx, |buffer, cx| {
19076                            buffer.edit(
19077                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
19078                                None,
19079                                cx,
19080                            )
19081                        });
19082                        let cursor_offset_in_rename_range =
19083                            MultiBufferOffset(cursor_offset_in_rename_range);
19084                        let cursor_offset_in_rename_range_end =
19085                            MultiBufferOffset(cursor_offset_in_rename_range_end);
19086                        let rename_selection_range = match cursor_offset_in_rename_range
19087                            .cmp(&cursor_offset_in_rename_range_end)
19088                        {
19089                            Ordering::Equal => {
19090                                editor.select_all(&SelectAll, window, cx);
19091                                return editor;
19092                            }
19093                            Ordering::Less => {
19094                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
19095                            }
19096                            Ordering::Greater => {
19097                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
19098                            }
19099                        };
19100                        if rename_selection_range.end.0 > old_name.len() {
19101                            editor.select_all(&SelectAll, window, cx);
19102                        } else {
19103                            editor.change_selections(Default::default(), window, cx, |s| {
19104                                s.select_ranges([rename_selection_range]);
19105                            });
19106                        }
19107                        editor
19108                    });
19109                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
19110                        if e == &EditorEvent::Focused {
19111                            cx.emit(EditorEvent::FocusedIn)
19112                        }
19113                    })
19114                    .detach();
19115
19116                    let write_highlights =
19117                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
19118                    let read_highlights =
19119                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
19120                    let ranges = write_highlights
19121                        .iter()
19122                        .flat_map(|(_, ranges)| ranges.iter())
19123                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
19124                        .cloned()
19125                        .collect();
19126
19127                    this.highlight_text(
19128                        HighlightKey::Rename,
19129                        ranges,
19130                        HighlightStyle {
19131                            fade_out: Some(0.6),
19132                            ..Default::default()
19133                        },
19134                        cx,
19135                    );
19136                    let rename_focus_handle = rename_editor.focus_handle(cx);
19137                    window.focus(&rename_focus_handle, cx);
19138                    let block_id = this.insert_blocks(
19139                        [BlockProperties {
19140                            style: BlockStyle::Flex,
19141                            placement: BlockPlacement::Below(range.start),
19142                            height: Some(1),
19143                            render: Arc::new({
19144                                let rename_editor = rename_editor.clone();
19145                                move |cx: &mut BlockContext| {
19146                                    let mut text_style = cx.editor_style.text.clone();
19147                                    if let Some(highlight_style) = old_highlight_id
19148                                        .and_then(|h| h.style(&cx.editor_style.syntax))
19149                                    {
19150                                        text_style = text_style.highlight(highlight_style);
19151                                    }
19152                                    div()
19153                                        .block_mouse_except_scroll()
19154                                        .pl(cx.anchor_x)
19155                                        .child(EditorElement::new(
19156                                            &rename_editor,
19157                                            EditorStyle {
19158                                                background: cx.theme().system().transparent,
19159                                                local_player: cx.editor_style.local_player,
19160                                                text: text_style,
19161                                                scrollbar_width: cx.editor_style.scrollbar_width,
19162                                                syntax: cx.editor_style.syntax.clone(),
19163                                                status: cx.editor_style.status.clone(),
19164                                                inlay_hints_style: HighlightStyle {
19165                                                    font_weight: Some(FontWeight::BOLD),
19166                                                    ..make_inlay_hints_style(cx.app)
19167                                                },
19168                                                edit_prediction_styles: make_suggestion_styles(
19169                                                    cx.app,
19170                                                ),
19171                                                ..EditorStyle::default()
19172                                            },
19173                                        ))
19174                                        .into_any_element()
19175                                }
19176                            }),
19177                            priority: 0,
19178                        }],
19179                        Some(Autoscroll::fit()),
19180                        cx,
19181                    )[0];
19182                    this.pending_rename = Some(RenameState {
19183                        range,
19184                        old_name,
19185                        editor: rename_editor,
19186                        block_id,
19187                    });
19188                })?;
19189            }
19190
19191            Ok(())
19192        }))
19193    }
19194
19195    pub fn confirm_rename(
19196        &mut self,
19197        _: &ConfirmRename,
19198        window: &mut Window,
19199        cx: &mut Context<Self>,
19200    ) -> Option<Task<Result<()>>> {
19201        let rename = self.take_rename(false, window, cx)?;
19202        let workspace = self.workspace()?.downgrade();
19203        let (buffer, start) = self
19204            .buffer
19205            .read(cx)
19206            .text_anchor_for_position(rename.range.start, cx)?;
19207        let (end_buffer, _) = self
19208            .buffer
19209            .read(cx)
19210            .text_anchor_for_position(rename.range.end, cx)?;
19211        if buffer != end_buffer {
19212            return None;
19213        }
19214
19215        let old_name = rename.old_name;
19216        let new_name = rename.editor.read(cx).text(cx);
19217
19218        let rename = self.semantics_provider.as_ref()?.perform_rename(
19219            &buffer,
19220            start,
19221            new_name.clone(),
19222            cx,
19223        )?;
19224
19225        Some(cx.spawn_in(window, async move |editor, cx| {
19226            let project_transaction = rename.await?;
19227            Self::open_project_transaction(
19228                &editor,
19229                workspace,
19230                project_transaction,
19231                format!("Rename: {}{}", old_name, new_name),
19232                cx,
19233            )
19234            .await?;
19235
19236            editor.update(cx, |editor, cx| {
19237                editor.refresh_document_highlights(cx);
19238            })?;
19239            Ok(())
19240        }))
19241    }
19242
19243    fn take_rename(
19244        &mut self,
19245        moving_cursor: bool,
19246        window: &mut Window,
19247        cx: &mut Context<Self>,
19248    ) -> Option<RenameState> {
19249        let rename = self.pending_rename.take()?;
19250        if rename.editor.focus_handle(cx).is_focused(window) {
19251            window.focus(&self.focus_handle, cx);
19252        }
19253
19254        self.remove_blocks(
19255            [rename.block_id].into_iter().collect(),
19256            Some(Autoscroll::fit()),
19257            cx,
19258        );
19259        self.clear_highlights(HighlightKey::Rename, cx);
19260        self.show_local_selections = true;
19261
19262        if moving_cursor {
19263            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19264                editor
19265                    .selections
19266                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19267                    .head()
19268            });
19269
19270            // Update the selection to match the position of the selection inside
19271            // the rename editor.
19272            let snapshot = self.buffer.read(cx).read(cx);
19273            let rename_range = rename.range.to_offset(&snapshot);
19274            let cursor_in_editor = snapshot
19275                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19276                .min(rename_range.end);
19277            drop(snapshot);
19278
19279            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19280                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19281            });
19282        } else {
19283            self.refresh_document_highlights(cx);
19284        }
19285
19286        Some(rename)
19287    }
19288
19289    pub fn pending_rename(&self) -> Option<&RenameState> {
19290        self.pending_rename.as_ref()
19291    }
19292
19293    fn format(
19294        &mut self,
19295        _: &Format,
19296        window: &mut Window,
19297        cx: &mut Context<Self>,
19298    ) -> Option<Task<Result<()>>> {
19299        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19300
19301        let project = match &self.project {
19302            Some(project) => project.clone(),
19303            None => return None,
19304        };
19305
19306        Some(self.perform_format(
19307            project,
19308            FormatTrigger::Manual,
19309            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19310            window,
19311            cx,
19312        ))
19313    }
19314
19315    fn format_selections(
19316        &mut self,
19317        _: &FormatSelections,
19318        window: &mut Window,
19319        cx: &mut Context<Self>,
19320    ) -> Option<Task<Result<()>>> {
19321        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19322
19323        let project = match &self.project {
19324            Some(project) => project.clone(),
19325            None => return None,
19326        };
19327
19328        let ranges = self
19329            .selections
19330            .all_adjusted(&self.display_snapshot(cx))
19331            .into_iter()
19332            .map(|selection| selection.range())
19333            .collect_vec();
19334
19335        Some(self.perform_format(
19336            project,
19337            FormatTrigger::Manual,
19338            FormatTarget::Ranges(ranges),
19339            window,
19340            cx,
19341        ))
19342    }
19343
19344    fn perform_format(
19345        &mut self,
19346        project: Entity<Project>,
19347        trigger: FormatTrigger,
19348        target: FormatTarget,
19349        window: &mut Window,
19350        cx: &mut Context<Self>,
19351    ) -> Task<Result<()>> {
19352        let buffer = self.buffer.clone();
19353        let (buffers, target) = match target {
19354            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19355            FormatTarget::Ranges(selection_ranges) => {
19356                let multi_buffer = buffer.read(cx);
19357                let snapshot = multi_buffer.read(cx);
19358                let mut buffers = HashSet::default();
19359                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19360                    BTreeMap::new();
19361                for selection_range in selection_ranges {
19362                    for (buffer, buffer_range, _) in
19363                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19364                    {
19365                        let buffer_id = buffer.remote_id();
19366                        let start = buffer.anchor_before(buffer_range.start);
19367                        let end = buffer.anchor_after(buffer_range.end);
19368                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19369                        buffer_id_to_ranges
19370                            .entry(buffer_id)
19371                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19372                            .or_insert_with(|| vec![start..end]);
19373                    }
19374                }
19375                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19376            }
19377        };
19378
19379        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19380        let selections_prev = transaction_id_prev
19381            .and_then(|transaction_id_prev| {
19382                // default to selections as they were after the last edit, if we have them,
19383                // instead of how they are now.
19384                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19385                // will take you back to where you made the last edit, instead of staying where you scrolled
19386                self.selection_history
19387                    .transaction(transaction_id_prev)
19388                    .map(|t| t.0.clone())
19389            })
19390            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19391
19392        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19393        let format = project.update(cx, |project, cx| {
19394            project.format(buffers, target, true, trigger, cx)
19395        });
19396
19397        cx.spawn_in(window, async move |editor, cx| {
19398            let transaction = futures::select_biased! {
19399                transaction = format.log_err().fuse() => transaction,
19400                () = timeout => {
19401                    log::warn!("timed out waiting for formatting");
19402                    None
19403                }
19404            };
19405
19406            buffer.update(cx, |buffer, cx| {
19407                if let Some(transaction) = transaction
19408                    && !buffer.is_singleton()
19409                {
19410                    buffer.push_transaction(&transaction.0, cx);
19411                }
19412                cx.notify();
19413            });
19414
19415            if let Some(transaction_id_now) =
19416                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19417            {
19418                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19419                if has_new_transaction {
19420                    editor
19421                        .update(cx, |editor, _| {
19422                            editor
19423                                .selection_history
19424                                .insert_transaction(transaction_id_now, selections_prev);
19425                        })
19426                        .ok();
19427                }
19428            }
19429
19430            Ok(())
19431        })
19432    }
19433
19434    fn organize_imports(
19435        &mut self,
19436        _: &OrganizeImports,
19437        window: &mut Window,
19438        cx: &mut Context<Self>,
19439    ) -> Option<Task<Result<()>>> {
19440        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19441        let project = match &self.project {
19442            Some(project) => project.clone(),
19443            None => return None,
19444        };
19445        Some(self.perform_code_action_kind(
19446            project,
19447            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19448            window,
19449            cx,
19450        ))
19451    }
19452
19453    fn perform_code_action_kind(
19454        &mut self,
19455        project: Entity<Project>,
19456        kind: CodeActionKind,
19457        window: &mut Window,
19458        cx: &mut Context<Self>,
19459    ) -> Task<Result<()>> {
19460        let buffer = self.buffer.clone();
19461        let buffers = buffer.read(cx).all_buffers();
19462        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19463        let apply_action = project.update(cx, |project, cx| {
19464            project.apply_code_action_kind(buffers, kind, true, cx)
19465        });
19466        cx.spawn_in(window, async move |_, cx| {
19467            let transaction = futures::select_biased! {
19468                () = timeout => {
19469                    log::warn!("timed out waiting for executing code action");
19470                    None
19471                }
19472                transaction = apply_action.log_err().fuse() => transaction,
19473            };
19474            buffer.update(cx, |buffer, cx| {
19475                // check if we need this
19476                if let Some(transaction) = transaction
19477                    && !buffer.is_singleton()
19478                {
19479                    buffer.push_transaction(&transaction.0, cx);
19480                }
19481                cx.notify();
19482            });
19483            Ok(())
19484        })
19485    }
19486
19487    pub fn restart_language_server(
19488        &mut self,
19489        _: &RestartLanguageServer,
19490        _: &mut Window,
19491        cx: &mut Context<Self>,
19492    ) {
19493        if let Some(project) = self.project.clone() {
19494            self.buffer.update(cx, |multi_buffer, cx| {
19495                project.update(cx, |project, cx| {
19496                    project.restart_language_servers_for_buffers(
19497                        multi_buffer.all_buffers().into_iter().collect(),
19498                        HashSet::default(),
19499                        cx,
19500                    );
19501                });
19502            })
19503        }
19504    }
19505
19506    pub fn stop_language_server(
19507        &mut self,
19508        _: &StopLanguageServer,
19509        _: &mut Window,
19510        cx: &mut Context<Self>,
19511    ) {
19512        if let Some(project) = self.project.clone() {
19513            self.buffer.update(cx, |multi_buffer, cx| {
19514                project.update(cx, |project, cx| {
19515                    project.stop_language_servers_for_buffers(
19516                        multi_buffer.all_buffers().into_iter().collect(),
19517                        HashSet::default(),
19518                        cx,
19519                    );
19520                });
19521            });
19522        }
19523    }
19524
19525    fn cancel_language_server_work(
19526        workspace: &mut Workspace,
19527        _: &actions::CancelLanguageServerWork,
19528        _: &mut Window,
19529        cx: &mut Context<Workspace>,
19530    ) {
19531        let project = workspace.project();
19532        let buffers = workspace
19533            .active_item(cx)
19534            .and_then(|item| item.act_as::<Editor>(cx))
19535            .map_or(HashSet::default(), |editor| {
19536                editor.read(cx).buffer.read(cx).all_buffers()
19537            });
19538        project.update(cx, |project, cx| {
19539            project.cancel_language_server_work_for_buffers(buffers, cx);
19540        });
19541    }
19542
19543    fn show_character_palette(
19544        &mut self,
19545        _: &ShowCharacterPalette,
19546        window: &mut Window,
19547        _: &mut Context<Self>,
19548    ) {
19549        window.show_character_palette();
19550    }
19551
19552    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19553        if !self.diagnostics_enabled() {
19554            return;
19555        }
19556
19557        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19558            let buffer = self.buffer.read(cx).snapshot(cx);
19559            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19560            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19561            let is_valid = buffer
19562                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19563                .any(|entry| {
19564                    entry.diagnostic.is_primary
19565                        && !entry.range.is_empty()
19566                        && entry.range.start == primary_range_start
19567                        && entry.diagnostic.message == active_diagnostics.active_message
19568                });
19569
19570            if !is_valid {
19571                self.dismiss_diagnostics(cx);
19572            }
19573        }
19574    }
19575
19576    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19577        match &self.active_diagnostics {
19578            ActiveDiagnostic::Group(group) => Some(group),
19579            _ => None,
19580        }
19581    }
19582
19583    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19584        if !self.diagnostics_enabled() {
19585            return;
19586        }
19587        self.dismiss_diagnostics(cx);
19588        self.active_diagnostics = ActiveDiagnostic::All;
19589    }
19590
19591    fn activate_diagnostics(
19592        &mut self,
19593        buffer_id: BufferId,
19594        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19595        window: &mut Window,
19596        cx: &mut Context<Self>,
19597    ) {
19598        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19599            return;
19600        }
19601        self.dismiss_diagnostics(cx);
19602        let snapshot = self.snapshot(window, cx);
19603        let buffer = self.buffer.read(cx).snapshot(cx);
19604        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19605            return;
19606        };
19607
19608        let diagnostic_group = buffer
19609            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19610            .collect::<Vec<_>>();
19611
19612        let language_registry = self
19613            .project()
19614            .map(|project| project.read(cx).languages().clone());
19615
19616        let blocks = renderer.render_group(
19617            diagnostic_group,
19618            buffer_id,
19619            snapshot,
19620            cx.weak_entity(),
19621            language_registry,
19622            cx,
19623        );
19624
19625        let blocks = self.display_map.update(cx, |display_map, cx| {
19626            display_map.insert_blocks(blocks, cx).into_iter().collect()
19627        });
19628        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19629            active_range: buffer.anchor_before(diagnostic.range.start)
19630                ..buffer.anchor_after(diagnostic.range.end),
19631            active_message: diagnostic.diagnostic.message.clone(),
19632            group_id: diagnostic.diagnostic.group_id,
19633            blocks,
19634        });
19635        cx.notify();
19636    }
19637
19638    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19639        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19640            return;
19641        };
19642
19643        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19644        if let ActiveDiagnostic::Group(group) = prev {
19645            self.display_map.update(cx, |display_map, cx| {
19646                display_map.remove_blocks(group.blocks, cx);
19647            });
19648            cx.notify();
19649        }
19650    }
19651
19652    /// Disable inline diagnostics rendering for this editor.
19653    pub fn disable_inline_diagnostics(&mut self) {
19654        self.inline_diagnostics_enabled = false;
19655        self.inline_diagnostics_update = Task::ready(());
19656        self.inline_diagnostics.clear();
19657    }
19658
19659    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19660        self.diagnostics_enabled = false;
19661        self.dismiss_diagnostics(cx);
19662        self.inline_diagnostics_update = Task::ready(());
19663        self.inline_diagnostics.clear();
19664    }
19665
19666    pub fn disable_word_completions(&mut self) {
19667        self.word_completions_enabled = false;
19668    }
19669
19670    pub fn diagnostics_enabled(&self) -> bool {
19671        self.diagnostics_enabled && self.lsp_data_enabled()
19672    }
19673
19674    pub fn inline_diagnostics_enabled(&self) -> bool {
19675        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19676    }
19677
19678    pub fn show_inline_diagnostics(&self) -> bool {
19679        self.show_inline_diagnostics
19680    }
19681
19682    pub fn toggle_inline_diagnostics(
19683        &mut self,
19684        _: &ToggleInlineDiagnostics,
19685        window: &mut Window,
19686        cx: &mut Context<Editor>,
19687    ) {
19688        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19689        self.refresh_inline_diagnostics(false, window, cx);
19690    }
19691
19692    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19693        self.diagnostics_max_severity = severity;
19694        self.display_map.update(cx, |display_map, _| {
19695            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19696        });
19697    }
19698
19699    pub fn toggle_diagnostics(
19700        &mut self,
19701        _: &ToggleDiagnostics,
19702        window: &mut Window,
19703        cx: &mut Context<Editor>,
19704    ) {
19705        if !self.diagnostics_enabled() {
19706            return;
19707        }
19708
19709        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19710            EditorSettings::get_global(cx)
19711                .diagnostics_max_severity
19712                .filter(|severity| severity != &DiagnosticSeverity::Off)
19713                .unwrap_or(DiagnosticSeverity::Hint)
19714        } else {
19715            DiagnosticSeverity::Off
19716        };
19717        self.set_max_diagnostics_severity(new_severity, cx);
19718        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19719            self.active_diagnostics = ActiveDiagnostic::None;
19720            self.inline_diagnostics_update = Task::ready(());
19721            self.inline_diagnostics.clear();
19722        } else {
19723            self.refresh_inline_diagnostics(false, window, cx);
19724        }
19725
19726        cx.notify();
19727    }
19728
19729    pub fn toggle_minimap(
19730        &mut self,
19731        _: &ToggleMinimap,
19732        window: &mut Window,
19733        cx: &mut Context<Editor>,
19734    ) {
19735        if self.supports_minimap(cx) {
19736            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19737        }
19738    }
19739
19740    fn refresh_inline_diagnostics(
19741        &mut self,
19742        debounce: bool,
19743        window: &mut Window,
19744        cx: &mut Context<Self>,
19745    ) {
19746        let max_severity = ProjectSettings::get_global(cx)
19747            .diagnostics
19748            .inline
19749            .max_severity
19750            .unwrap_or(self.diagnostics_max_severity);
19751
19752        if !self.inline_diagnostics_enabled()
19753            || !self.diagnostics_enabled()
19754            || !self.show_inline_diagnostics
19755            || max_severity == DiagnosticSeverity::Off
19756        {
19757            self.inline_diagnostics_update = Task::ready(());
19758            self.inline_diagnostics.clear();
19759            return;
19760        }
19761
19762        let debounce_ms = ProjectSettings::get_global(cx)
19763            .diagnostics
19764            .inline
19765            .update_debounce_ms;
19766        let debounce = if debounce && debounce_ms > 0 {
19767            Some(Duration::from_millis(debounce_ms))
19768        } else {
19769            None
19770        };
19771        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19772            if let Some(debounce) = debounce {
19773                cx.background_executor().timer(debounce).await;
19774            }
19775            let Some(snapshot) = editor.upgrade().map(|editor| {
19776                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19777            }) else {
19778                return;
19779            };
19780
19781            let new_inline_diagnostics = cx
19782                .background_spawn(async move {
19783                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19784                    for diagnostic_entry in
19785                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19786                    {
19787                        let message = diagnostic_entry
19788                            .diagnostic
19789                            .message
19790                            .split_once('\n')
19791                            .map(|(line, _)| line)
19792                            .map(SharedString::new)
19793                            .unwrap_or_else(|| {
19794                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19795                            });
19796                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19797                        let (Ok(i) | Err(i)) = inline_diagnostics
19798                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19799                        inline_diagnostics.insert(
19800                            i,
19801                            (
19802                                start_anchor,
19803                                InlineDiagnostic {
19804                                    message,
19805                                    group_id: diagnostic_entry.diagnostic.group_id,
19806                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19807                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19808                                    severity: diagnostic_entry.diagnostic.severity,
19809                                },
19810                            ),
19811                        );
19812                    }
19813                    inline_diagnostics
19814                })
19815                .await;
19816
19817            editor
19818                .update(cx, |editor, cx| {
19819                    editor.inline_diagnostics = new_inline_diagnostics;
19820                    cx.notify();
19821                })
19822                .ok();
19823        });
19824    }
19825
19826    fn pull_diagnostics(
19827        &mut self,
19828        buffer_id: BufferId,
19829        _window: &Window,
19830        cx: &mut Context<Self>,
19831    ) -> Option<()> {
19832        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19833        // skip any LSP updates for it.
19834
19835        if self.active_diagnostics == ActiveDiagnostic::All || !self.diagnostics_enabled() {
19836            return None;
19837        }
19838        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19839            .diagnostics
19840            .lsp_pull_diagnostics;
19841        if !pull_diagnostics_settings.enabled {
19842            return None;
19843        }
19844        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19845        let project = self.project()?.downgrade();
19846        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19847
19848        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19849            cx.background_executor().timer(debounce).await;
19850            if let Ok(task) = project.update(cx, |project, cx| {
19851                project.lsp_store().update(cx, |lsp_store, cx| {
19852                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19853                })
19854            }) {
19855                task.await.log_err();
19856            }
19857            project
19858                .update(cx, |project, cx| {
19859                    project.lsp_store().update(cx, |lsp_store, cx| {
19860                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19861                    })
19862                })
19863                .log_err();
19864        });
19865
19866        Some(())
19867    }
19868
19869    pub fn set_selections_from_remote(
19870        &mut self,
19871        selections: Vec<Selection<Anchor>>,
19872        pending_selection: Option<Selection<Anchor>>,
19873        window: &mut Window,
19874        cx: &mut Context<Self>,
19875    ) {
19876        let old_cursor_position = self.selections.newest_anchor().head();
19877        self.selections
19878            .change_with(&self.display_snapshot(cx), |s| {
19879                s.select_anchors(selections);
19880                if let Some(pending_selection) = pending_selection {
19881                    s.set_pending(pending_selection, SelectMode::Character);
19882                } else {
19883                    s.clear_pending();
19884                }
19885            });
19886        self.selections_did_change(
19887            false,
19888            &old_cursor_position,
19889            SelectionEffects::default(),
19890            window,
19891            cx,
19892        );
19893    }
19894
19895    pub fn transact(
19896        &mut self,
19897        window: &mut Window,
19898        cx: &mut Context<Self>,
19899        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19900    ) -> Option<TransactionId> {
19901        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19902            this.start_transaction_at(Instant::now(), window, cx);
19903            update(this, window, cx);
19904            this.end_transaction_at(Instant::now(), cx)
19905        })
19906    }
19907
19908    pub fn start_transaction_at(
19909        &mut self,
19910        now: Instant,
19911        window: &mut Window,
19912        cx: &mut Context<Self>,
19913    ) -> Option<TransactionId> {
19914        self.end_selection(window, cx);
19915        if let Some(tx_id) = self
19916            .buffer
19917            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19918        {
19919            self.selection_history
19920                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19921            cx.emit(EditorEvent::TransactionBegun {
19922                transaction_id: tx_id,
19923            });
19924            Some(tx_id)
19925        } else {
19926            None
19927        }
19928    }
19929
19930    pub fn end_transaction_at(
19931        &mut self,
19932        now: Instant,
19933        cx: &mut Context<Self>,
19934    ) -> Option<TransactionId> {
19935        if let Some(transaction_id) = self
19936            .buffer
19937            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19938        {
19939            if let Some((_, end_selections)) =
19940                self.selection_history.transaction_mut(transaction_id)
19941            {
19942                *end_selections = Some(self.selections.disjoint_anchors_arc());
19943            } else {
19944                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19945            }
19946
19947            cx.emit(EditorEvent::Edited { transaction_id });
19948            Some(transaction_id)
19949        } else {
19950            None
19951        }
19952    }
19953
19954    pub fn modify_transaction_selection_history(
19955        &mut self,
19956        transaction_id: TransactionId,
19957        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19958    ) -> bool {
19959        self.selection_history
19960            .transaction_mut(transaction_id)
19961            .map(modify)
19962            .is_some()
19963    }
19964
19965    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19966        if self.selection_mark_mode {
19967            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19968                s.move_with(&mut |_, sel| {
19969                    sel.collapse_to(sel.head(), SelectionGoal::None);
19970                });
19971            })
19972        }
19973        self.selection_mark_mode = true;
19974        cx.notify();
19975    }
19976
19977    pub fn swap_selection_ends(
19978        &mut self,
19979        _: &actions::SwapSelectionEnds,
19980        window: &mut Window,
19981        cx: &mut Context<Self>,
19982    ) {
19983        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19984            s.move_with(&mut |_, sel| {
19985                if sel.start != sel.end {
19986                    sel.reversed = !sel.reversed
19987                }
19988            });
19989        });
19990        self.request_autoscroll(Autoscroll::newest(), cx);
19991        cx.notify();
19992    }
19993
19994    pub fn toggle_focus(
19995        workspace: &mut Workspace,
19996        _: &actions::ToggleFocus,
19997        window: &mut Window,
19998        cx: &mut Context<Workspace>,
19999    ) {
20000        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
20001            return;
20002        };
20003        workspace.activate_item(&item, true, true, window, cx);
20004    }
20005
20006    pub fn toggle_fold(
20007        &mut self,
20008        _: &actions::ToggleFold,
20009        window: &mut Window,
20010        cx: &mut Context<Self>,
20011    ) {
20012        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20013            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20014            let selection = self.selections.newest::<Point>(&display_map);
20015
20016            let range = if selection.is_empty() {
20017                let point = selection.head().to_display_point(&display_map);
20018                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20019                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20020                    .to_point(&display_map);
20021                start..end
20022            } else {
20023                selection.range()
20024            };
20025            if display_map.folds_in_range(range).next().is_some() {
20026                self.unfold_lines(&Default::default(), window, cx)
20027            } else {
20028                self.fold(&Default::default(), window, cx)
20029            }
20030        } else {
20031            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20032            let buffer_ids: HashSet<_> = self
20033                .selections
20034                .disjoint_anchor_ranges()
20035                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20036                .collect();
20037
20038            let should_unfold = buffer_ids
20039                .iter()
20040                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20041
20042            for buffer_id in buffer_ids {
20043                if should_unfold {
20044                    self.unfold_buffer(buffer_id, cx);
20045                } else {
20046                    self.fold_buffer(buffer_id, cx);
20047                }
20048            }
20049        }
20050    }
20051
20052    pub fn toggle_fold_recursive(
20053        &mut self,
20054        _: &actions::ToggleFoldRecursive,
20055        window: &mut Window,
20056        cx: &mut Context<Self>,
20057    ) {
20058        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
20059
20060        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20061        let range = if selection.is_empty() {
20062            let point = selection.head().to_display_point(&display_map);
20063            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20064            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20065                .to_point(&display_map);
20066            start..end
20067        } else {
20068            selection.range()
20069        };
20070        if display_map.folds_in_range(range).next().is_some() {
20071            self.unfold_recursive(&Default::default(), window, cx)
20072        } else {
20073            self.fold_recursive(&Default::default(), window, cx)
20074        }
20075    }
20076
20077    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
20078        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20079            let mut to_fold = Vec::new();
20080            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20081            let selections = self.selections.all_adjusted(&display_map);
20082
20083            for selection in selections {
20084                let range = selection.range().sorted();
20085                let buffer_start_row = range.start.row;
20086
20087                if range.start.row != range.end.row {
20088                    let mut found = false;
20089                    let mut row = range.start.row;
20090                    while row <= range.end.row {
20091                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20092                        {
20093                            found = true;
20094                            row = crease.range().end.row + 1;
20095                            to_fold.push(crease);
20096                        } else {
20097                            row += 1
20098                        }
20099                    }
20100                    if found {
20101                        continue;
20102                    }
20103                }
20104
20105                for row in (0..=range.start.row).rev() {
20106                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20107                        && crease.range().end.row >= buffer_start_row
20108                    {
20109                        to_fold.push(crease);
20110                        if row <= range.start.row {
20111                            break;
20112                        }
20113                    }
20114                }
20115            }
20116
20117            self.fold_creases(to_fold, true, window, cx);
20118        } else {
20119            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20120            let buffer_ids = self
20121                .selections
20122                .disjoint_anchor_ranges()
20123                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20124                .collect::<HashSet<_>>();
20125            for buffer_id in buffer_ids {
20126                self.fold_buffer(buffer_id, cx);
20127            }
20128        }
20129    }
20130
20131    pub fn toggle_fold_all(
20132        &mut self,
20133        _: &actions::ToggleFoldAll,
20134        window: &mut Window,
20135        cx: &mut Context<Self>,
20136    ) {
20137        let has_folds = if self.buffer.read(cx).is_singleton() {
20138            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20139            let has_folds = display_map
20140                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
20141                .next()
20142                .is_some();
20143            has_folds
20144        } else {
20145            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
20146            let has_folds = buffer_ids
20147                .iter()
20148                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20149            has_folds
20150        };
20151
20152        if has_folds {
20153            self.unfold_all(&actions::UnfoldAll, window, cx);
20154        } else {
20155            self.fold_all(&actions::FoldAll, window, cx);
20156        }
20157    }
20158
20159    fn fold_at_level(
20160        &mut self,
20161        fold_at: &FoldAtLevel,
20162        window: &mut Window,
20163        cx: &mut Context<Self>,
20164    ) {
20165        if !self.buffer.read(cx).is_singleton() {
20166            return;
20167        }
20168
20169        let fold_at_level = fold_at.0;
20170        let snapshot = self.buffer.read(cx).snapshot(cx);
20171        let mut to_fold = Vec::new();
20172        let mut stack = vec![(0, snapshot.max_row().0, 1)];
20173
20174        let row_ranges_to_keep: Vec<Range<u32>> = self
20175            .selections
20176            .all::<Point>(&self.display_snapshot(cx))
20177            .into_iter()
20178            .map(|sel| sel.start.row..sel.end.row)
20179            .collect();
20180
20181        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20182            while start_row < end_row {
20183                match self
20184                    .snapshot(window, cx)
20185                    .crease_for_buffer_row(MultiBufferRow(start_row))
20186                {
20187                    Some(crease) => {
20188                        let nested_start_row = crease.range().start.row + 1;
20189                        let nested_end_row = crease.range().end.row;
20190
20191                        if current_level < fold_at_level {
20192                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20193                        } else if current_level == fold_at_level {
20194                            // Fold iff there is no selection completely contained within the fold region
20195                            if !row_ranges_to_keep.iter().any(|selection| {
20196                                selection.end >= nested_start_row
20197                                    && selection.start <= nested_end_row
20198                            }) {
20199                                to_fold.push(crease);
20200                            }
20201                        }
20202
20203                        start_row = nested_end_row + 1;
20204                    }
20205                    None => start_row += 1,
20206                }
20207            }
20208        }
20209
20210        self.fold_creases(to_fold, true, window, cx);
20211    }
20212
20213    pub fn fold_at_level_1(
20214        &mut self,
20215        _: &actions::FoldAtLevel1,
20216        window: &mut Window,
20217        cx: &mut Context<Self>,
20218    ) {
20219        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20220    }
20221
20222    pub fn fold_at_level_2(
20223        &mut self,
20224        _: &actions::FoldAtLevel2,
20225        window: &mut Window,
20226        cx: &mut Context<Self>,
20227    ) {
20228        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20229    }
20230
20231    pub fn fold_at_level_3(
20232        &mut self,
20233        _: &actions::FoldAtLevel3,
20234        window: &mut Window,
20235        cx: &mut Context<Self>,
20236    ) {
20237        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20238    }
20239
20240    pub fn fold_at_level_4(
20241        &mut self,
20242        _: &actions::FoldAtLevel4,
20243        window: &mut Window,
20244        cx: &mut Context<Self>,
20245    ) {
20246        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20247    }
20248
20249    pub fn fold_at_level_5(
20250        &mut self,
20251        _: &actions::FoldAtLevel5,
20252        window: &mut Window,
20253        cx: &mut Context<Self>,
20254    ) {
20255        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20256    }
20257
20258    pub fn fold_at_level_6(
20259        &mut self,
20260        _: &actions::FoldAtLevel6,
20261        window: &mut Window,
20262        cx: &mut Context<Self>,
20263    ) {
20264        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20265    }
20266
20267    pub fn fold_at_level_7(
20268        &mut self,
20269        _: &actions::FoldAtLevel7,
20270        window: &mut Window,
20271        cx: &mut Context<Self>,
20272    ) {
20273        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20274    }
20275
20276    pub fn fold_at_level_8(
20277        &mut self,
20278        _: &actions::FoldAtLevel8,
20279        window: &mut Window,
20280        cx: &mut Context<Self>,
20281    ) {
20282        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20283    }
20284
20285    pub fn fold_at_level_9(
20286        &mut self,
20287        _: &actions::FoldAtLevel9,
20288        window: &mut Window,
20289        cx: &mut Context<Self>,
20290    ) {
20291        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20292    }
20293
20294    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20295        if self.buffer.read(cx).is_singleton() {
20296            let mut fold_ranges = Vec::new();
20297            let snapshot = self.buffer.read(cx).snapshot(cx);
20298
20299            for row in 0..snapshot.max_row().0 {
20300                if let Some(foldable_range) = self
20301                    .snapshot(window, cx)
20302                    .crease_for_buffer_row(MultiBufferRow(row))
20303                {
20304                    fold_ranges.push(foldable_range);
20305                }
20306            }
20307
20308            self.fold_creases(fold_ranges, true, window, cx);
20309        } else {
20310            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20311                editor
20312                    .update_in(cx, |editor, _, cx| {
20313                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20314                            editor.fold_buffer(buffer_id, cx);
20315                        }
20316                    })
20317                    .ok();
20318            });
20319        }
20320    }
20321
20322    pub fn fold_function_bodies(
20323        &mut self,
20324        _: &actions::FoldFunctionBodies,
20325        window: &mut Window,
20326        cx: &mut Context<Self>,
20327    ) {
20328        let snapshot = self.buffer.read(cx).snapshot(cx);
20329
20330        let ranges = snapshot
20331            .text_object_ranges(
20332                MultiBufferOffset(0)..snapshot.len(),
20333                TreeSitterOptions::default(),
20334            )
20335            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20336            .collect::<Vec<_>>();
20337
20338        let creases = ranges
20339            .into_iter()
20340            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20341            .collect();
20342
20343        self.fold_creases(creases, true, window, cx);
20344    }
20345
20346    pub fn fold_recursive(
20347        &mut self,
20348        _: &actions::FoldRecursive,
20349        window: &mut Window,
20350        cx: &mut Context<Self>,
20351    ) {
20352        let mut to_fold = Vec::new();
20353        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20354        let selections = self.selections.all_adjusted(&display_map);
20355
20356        for selection in selections {
20357            let range = selection.range().sorted();
20358            let buffer_start_row = range.start.row;
20359
20360            if range.start.row != range.end.row {
20361                let mut found = false;
20362                for row in range.start.row..=range.end.row {
20363                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20364                        found = true;
20365                        to_fold.push(crease);
20366                    }
20367                }
20368                if found {
20369                    continue;
20370                }
20371            }
20372
20373            for row in (0..=range.start.row).rev() {
20374                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20375                    if crease.range().end.row >= buffer_start_row {
20376                        to_fold.push(crease);
20377                    } else {
20378                        break;
20379                    }
20380                }
20381            }
20382        }
20383
20384        self.fold_creases(to_fold, true, window, cx);
20385    }
20386
20387    pub fn fold_at(
20388        &mut self,
20389        buffer_row: MultiBufferRow,
20390        window: &mut Window,
20391        cx: &mut Context<Self>,
20392    ) {
20393        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20394
20395        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20396            let autoscroll = self
20397                .selections
20398                .all::<Point>(&display_map)
20399                .iter()
20400                .any(|selection| crease.range().overlaps(&selection.range()));
20401
20402            self.fold_creases(vec![crease], autoscroll, window, cx);
20403        }
20404    }
20405
20406    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20407        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20408            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20409            let buffer = display_map.buffer_snapshot();
20410            let selections = self.selections.all::<Point>(&display_map);
20411            let ranges = selections
20412                .iter()
20413                .map(|s| {
20414                    let range = s.display_range(&display_map).sorted();
20415                    let mut start = range.start.to_point(&display_map);
20416                    let mut end = range.end.to_point(&display_map);
20417                    start.column = 0;
20418                    end.column = buffer.line_len(MultiBufferRow(end.row));
20419                    start..end
20420                })
20421                .collect::<Vec<_>>();
20422
20423            self.unfold_ranges(&ranges, true, true, cx);
20424        } else {
20425            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20426            let buffer_ids = self
20427                .selections
20428                .disjoint_anchor_ranges()
20429                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20430                .collect::<HashSet<_>>();
20431            for buffer_id in buffer_ids {
20432                self.unfold_buffer(buffer_id, cx);
20433            }
20434        }
20435    }
20436
20437    pub fn unfold_recursive(
20438        &mut self,
20439        _: &UnfoldRecursive,
20440        _window: &mut Window,
20441        cx: &mut Context<Self>,
20442    ) {
20443        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20444        let selections = self.selections.all::<Point>(&display_map);
20445        let ranges = selections
20446            .iter()
20447            .map(|s| {
20448                let mut range = s.display_range(&display_map).sorted();
20449                *range.start.column_mut() = 0;
20450                *range.end.column_mut() = display_map.line_len(range.end.row());
20451                let start = range.start.to_point(&display_map);
20452                let end = range.end.to_point(&display_map);
20453                start..end
20454            })
20455            .collect::<Vec<_>>();
20456
20457        self.unfold_ranges(&ranges, true, true, cx);
20458    }
20459
20460    pub fn unfold_at(
20461        &mut self,
20462        buffer_row: MultiBufferRow,
20463        _window: &mut Window,
20464        cx: &mut Context<Self>,
20465    ) {
20466        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20467
20468        let intersection_range = Point::new(buffer_row.0, 0)
20469            ..Point::new(
20470                buffer_row.0,
20471                display_map.buffer_snapshot().line_len(buffer_row),
20472            );
20473
20474        let autoscroll = self
20475            .selections
20476            .all::<Point>(&display_map)
20477            .iter()
20478            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20479
20480        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20481    }
20482
20483    pub fn unfold_all(
20484        &mut self,
20485        _: &actions::UnfoldAll,
20486        _window: &mut Window,
20487        cx: &mut Context<Self>,
20488    ) {
20489        if self.buffer.read(cx).is_singleton() {
20490            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20491            self.unfold_ranges(
20492                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20493                true,
20494                true,
20495                cx,
20496            );
20497        } else {
20498            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20499                editor
20500                    .update(cx, |editor, cx| {
20501                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20502                            editor.unfold_buffer(buffer_id, cx);
20503                        }
20504                    })
20505                    .ok();
20506            });
20507        }
20508    }
20509
20510    pub fn fold_selected_ranges(
20511        &mut self,
20512        _: &FoldSelectedRanges,
20513        window: &mut Window,
20514        cx: &mut Context<Self>,
20515    ) {
20516        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20517        let selections = self.selections.all_adjusted(&display_map);
20518        let ranges = selections
20519            .into_iter()
20520            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20521            .collect::<Vec<_>>();
20522        self.fold_creases(ranges, true, window, cx);
20523    }
20524
20525    pub fn fold_ranges<T: ToOffset + Clone>(
20526        &mut self,
20527        ranges: Vec<Range<T>>,
20528        auto_scroll: bool,
20529        window: &mut Window,
20530        cx: &mut Context<Self>,
20531    ) {
20532        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20533        let ranges = ranges
20534            .into_iter()
20535            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20536            .collect::<Vec<_>>();
20537        self.fold_creases(ranges, auto_scroll, window, cx);
20538    }
20539
20540    pub fn fold_creases<T: ToOffset + Clone>(
20541        &mut self,
20542        creases: Vec<Crease<T>>,
20543        auto_scroll: bool,
20544        window: &mut Window,
20545        cx: &mut Context<Self>,
20546    ) {
20547        if creases.is_empty() {
20548            return;
20549        }
20550
20551        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20552
20553        if auto_scroll {
20554            self.request_autoscroll(Autoscroll::fit(), cx);
20555        }
20556
20557        cx.notify();
20558
20559        self.scrollbar_marker_state.dirty = true;
20560        self.update_data_on_scroll(window, cx);
20561        self.folds_did_change(cx);
20562    }
20563
20564    /// Removes any folds whose ranges intersect any of the given ranges.
20565    pub fn unfold_ranges<T: ToOffset + Clone>(
20566        &mut self,
20567        ranges: &[Range<T>],
20568        inclusive: bool,
20569        auto_scroll: bool,
20570        cx: &mut Context<Self>,
20571    ) {
20572        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20573            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20574        });
20575        self.folds_did_change(cx);
20576    }
20577
20578    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20579        self.fold_buffers([buffer_id], cx);
20580    }
20581
20582    pub fn fold_buffers(
20583        &mut self,
20584        buffer_ids: impl IntoIterator<Item = BufferId>,
20585        cx: &mut Context<Self>,
20586    ) {
20587        if self.buffer().read(cx).is_singleton() {
20588            return;
20589        }
20590
20591        let ids_to_fold: Vec<BufferId> = buffer_ids
20592            .into_iter()
20593            .filter(|id| !self.is_buffer_folded(*id, cx))
20594            .collect();
20595
20596        if ids_to_fold.is_empty() {
20597            return;
20598        }
20599
20600        let mut all_folded_excerpt_ids = Vec::new();
20601        for buffer_id in &ids_to_fold {
20602            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20603            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
20604        }
20605
20606        self.display_map.update(cx, |display_map, cx| {
20607            display_map.fold_buffers(ids_to_fold.clone(), cx)
20608        });
20609
20610        let snapshot = self.display_snapshot(cx);
20611        self.selections.change_with(&snapshot, |selections| {
20612            for buffer_id in ids_to_fold {
20613                selections.remove_selections_from_buffer(buffer_id);
20614            }
20615        });
20616
20617        cx.emit(EditorEvent::BufferFoldToggled {
20618            ids: all_folded_excerpt_ids,
20619            folded: true,
20620        });
20621        cx.notify();
20622    }
20623
20624    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20625        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20626            return;
20627        }
20628        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20629        self.display_map.update(cx, |display_map, cx| {
20630            display_map.unfold_buffers([buffer_id], cx);
20631        });
20632        cx.emit(EditorEvent::BufferFoldToggled {
20633            ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
20634            folded: false,
20635        });
20636        cx.notify();
20637    }
20638
20639    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20640        self.display_map.read(cx).is_buffer_folded(buffer)
20641    }
20642
20643    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20644        if self.buffer().read(cx).is_singleton() {
20645            return false;
20646        }
20647        !self.folded_buffers(cx).is_empty()
20648    }
20649
20650    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20651        self.display_map.read(cx).folded_buffers()
20652    }
20653
20654    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20655        self.display_map.update(cx, |display_map, cx| {
20656            display_map.disable_header_for_buffer(buffer_id, cx);
20657        });
20658        cx.notify();
20659    }
20660
20661    /// Removes any folds with the given ranges.
20662    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20663        &mut self,
20664        ranges: &[Range<T>],
20665        type_id: TypeId,
20666        auto_scroll: bool,
20667        cx: &mut Context<Self>,
20668    ) {
20669        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20670            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20671        });
20672        self.folds_did_change(cx);
20673    }
20674
20675    fn remove_folds_with<T: ToOffset + Clone>(
20676        &mut self,
20677        ranges: &[Range<T>],
20678        auto_scroll: bool,
20679        cx: &mut Context<Self>,
20680        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20681    ) {
20682        if ranges.is_empty() {
20683            return;
20684        }
20685
20686        let mut buffers_affected = HashSet::default();
20687        let multi_buffer = self.buffer().read(cx);
20688        for range in ranges {
20689            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20690                buffers_affected.insert(buffer.read(cx).remote_id());
20691            };
20692        }
20693
20694        self.display_map.update(cx, update);
20695
20696        if auto_scroll {
20697            self.request_autoscroll(Autoscroll::fit(), cx);
20698        }
20699
20700        cx.notify();
20701        self.scrollbar_marker_state.dirty = true;
20702        self.active_indent_guides_state.dirty = true;
20703    }
20704
20705    pub fn update_renderer_widths(
20706        &mut self,
20707        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20708        cx: &mut Context<Self>,
20709    ) -> bool {
20710        self.display_map
20711            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20712    }
20713
20714    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20715        self.display_map.read(cx).fold_placeholder.clone()
20716    }
20717
20718    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20719        self.buffer.update(cx, |buffer, cx| {
20720            buffer.set_all_diff_hunks_expanded(cx);
20721        });
20722    }
20723
20724    pub fn expand_all_diff_hunks(
20725        &mut self,
20726        _: &ExpandAllDiffHunks,
20727        _window: &mut Window,
20728        cx: &mut Context<Self>,
20729    ) {
20730        self.buffer.update(cx, |buffer, cx| {
20731            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20732        });
20733    }
20734
20735    pub fn collapse_all_diff_hunks(
20736        &mut self,
20737        _: &CollapseAllDiffHunks,
20738        _window: &mut Window,
20739        cx: &mut Context<Self>,
20740    ) {
20741        self.buffer.update(cx, |buffer, cx| {
20742            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20743        });
20744    }
20745
20746    pub fn toggle_selected_diff_hunks(
20747        &mut self,
20748        _: &ToggleSelectedDiffHunks,
20749        _window: &mut Window,
20750        cx: &mut Context<Self>,
20751    ) {
20752        let ranges: Vec<_> = self
20753            .selections
20754            .disjoint_anchors()
20755            .iter()
20756            .map(|s| s.range())
20757            .collect();
20758        self.toggle_diff_hunks_in_ranges(ranges, cx);
20759    }
20760
20761    pub fn diff_hunks_in_ranges<'a>(
20762        &'a self,
20763        ranges: &'a [Range<Anchor>],
20764        buffer: &'a MultiBufferSnapshot,
20765    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20766        ranges.iter().flat_map(move |range| {
20767            let end_excerpt_id = range.end.excerpt_id;
20768            let range = range.to_point(buffer);
20769            let mut peek_end = range.end;
20770            if range.end.row < buffer.max_row().0 {
20771                peek_end = Point::new(range.end.row + 1, 0);
20772            }
20773            buffer
20774                .diff_hunks_in_range(range.start..peek_end)
20775                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20776        })
20777    }
20778
20779    pub fn has_stageable_diff_hunks_in_ranges(
20780        &self,
20781        ranges: &[Range<Anchor>],
20782        snapshot: &MultiBufferSnapshot,
20783    ) -> bool {
20784        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20785        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20786    }
20787
20788    pub fn toggle_staged_selected_diff_hunks(
20789        &mut self,
20790        _: &::git::ToggleStaged,
20791        _: &mut Window,
20792        cx: &mut Context<Self>,
20793    ) {
20794        let snapshot = self.buffer.read(cx).snapshot(cx);
20795        let ranges: Vec<_> = self
20796            .selections
20797            .disjoint_anchors()
20798            .iter()
20799            .map(|s| s.range())
20800            .collect();
20801        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20802        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20803    }
20804
20805    pub fn set_render_diff_hunk_controls(
20806        &mut self,
20807        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20808        cx: &mut Context<Self>,
20809    ) {
20810        self.render_diff_hunk_controls = render_diff_hunk_controls;
20811        cx.notify();
20812    }
20813
20814    pub fn stage_and_next(
20815        &mut self,
20816        _: &::git::StageAndNext,
20817        window: &mut Window,
20818        cx: &mut Context<Self>,
20819    ) {
20820        self.do_stage_or_unstage_and_next(true, window, cx);
20821    }
20822
20823    pub fn unstage_and_next(
20824        &mut self,
20825        _: &::git::UnstageAndNext,
20826        window: &mut Window,
20827        cx: &mut Context<Self>,
20828    ) {
20829        self.do_stage_or_unstage_and_next(false, window, cx);
20830    }
20831
20832    pub fn stage_or_unstage_diff_hunks(
20833        &mut self,
20834        stage: bool,
20835        ranges: Vec<Range<Anchor>>,
20836        cx: &mut Context<Self>,
20837    ) {
20838        if self.delegate_stage_and_restore {
20839            let snapshot = self.buffer.read(cx).snapshot(cx);
20840            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20841            if !hunks.is_empty() {
20842                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20843            }
20844            return;
20845        }
20846        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20847        cx.spawn(async move |this, cx| {
20848            task.await?;
20849            this.update(cx, |this, cx| {
20850                let snapshot = this.buffer.read(cx).snapshot(cx);
20851                let chunk_by = this
20852                    .diff_hunks_in_ranges(&ranges, &snapshot)
20853                    .chunk_by(|hunk| hunk.buffer_id);
20854                for (buffer_id, hunks) in &chunk_by {
20855                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20856                }
20857            })
20858        })
20859        .detach_and_log_err(cx);
20860    }
20861
20862    fn save_buffers_for_ranges_if_needed(
20863        &mut self,
20864        ranges: &[Range<Anchor>],
20865        cx: &mut Context<Editor>,
20866    ) -> Task<Result<()>> {
20867        let multibuffer = self.buffer.read(cx);
20868        let snapshot = multibuffer.read(cx);
20869        let buffer_ids: HashSet<_> = ranges
20870            .iter()
20871            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20872            .collect();
20873        drop(snapshot);
20874
20875        let mut buffers = HashSet::default();
20876        for buffer_id in buffer_ids {
20877            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20878                let buffer = buffer_entity.read(cx);
20879                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20880                {
20881                    buffers.insert(buffer_entity);
20882                }
20883            }
20884        }
20885
20886        if let Some(project) = &self.project {
20887            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20888        } else {
20889            Task::ready(Ok(()))
20890        }
20891    }
20892
20893    fn do_stage_or_unstage_and_next(
20894        &mut self,
20895        stage: bool,
20896        window: &mut Window,
20897        cx: &mut Context<Self>,
20898    ) {
20899        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20900
20901        if ranges.iter().any(|range| range.start != range.end) {
20902            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20903            return;
20904        }
20905
20906        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20907
20908        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20909        let wrap_around = !all_diff_hunks_expanded;
20910        let snapshot = self.snapshot(window, cx);
20911        let position = self
20912            .selections
20913            .newest::<Point>(&snapshot.display_snapshot)
20914            .head();
20915
20916        self.go_to_hunk_before_or_after_position(
20917            &snapshot,
20918            position,
20919            Direction::Next,
20920            wrap_around,
20921            window,
20922            cx,
20923        );
20924    }
20925
20926    pub(crate) fn do_stage_or_unstage(
20927        &self,
20928        stage: bool,
20929        buffer_id: BufferId,
20930        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20931        cx: &mut App,
20932    ) -> Option<()> {
20933        let project = self.project()?;
20934        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20935        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20936        let buffer_snapshot = buffer.read(cx).snapshot();
20937        let file_exists = buffer_snapshot
20938            .file()
20939            .is_some_and(|file| file.disk_state().exists());
20940        diff.update(cx, |diff, cx| {
20941            diff.stage_or_unstage_hunks(
20942                stage,
20943                &hunks
20944                    .map(|hunk| buffer_diff::DiffHunk {
20945                        buffer_range: hunk.buffer_range,
20946                        // We don't need to pass in word diffs here because they're only used for rendering and
20947                        // this function changes internal state
20948                        base_word_diffs: Vec::default(),
20949                        buffer_word_diffs: Vec::default(),
20950                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20951                            ..hunk.diff_base_byte_range.end.0,
20952                        secondary_status: hunk.status.secondary,
20953                        range: Point::zero()..Point::zero(), // unused
20954                    })
20955                    .collect::<Vec<_>>(),
20956                &buffer_snapshot,
20957                file_exists,
20958                cx,
20959            )
20960        });
20961        None
20962    }
20963
20964    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20965        let ranges: Vec<_> = self
20966            .selections
20967            .disjoint_anchors()
20968            .iter()
20969            .map(|s| s.range())
20970            .collect();
20971        self.buffer
20972            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20973    }
20974
20975    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20976        self.buffer.update(cx, |buffer, cx| {
20977            let ranges = vec![Anchor::min()..Anchor::max()];
20978            if !buffer.all_diff_hunks_expanded()
20979                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20980            {
20981                buffer.collapse_diff_hunks(ranges, cx);
20982                true
20983            } else {
20984                false
20985            }
20986        })
20987    }
20988
20989    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20990        if self.buffer.read(cx).all_diff_hunks_expanded() {
20991            return true;
20992        }
20993        let ranges = vec![Anchor::min()..Anchor::max()];
20994        self.buffer
20995            .read(cx)
20996            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20997    }
20998
20999    fn toggle_diff_hunks_in_ranges(
21000        &mut self,
21001        ranges: Vec<Range<Anchor>>,
21002        cx: &mut Context<Editor>,
21003    ) {
21004        self.buffer.update(cx, |buffer, cx| {
21005            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
21006            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
21007        })
21008    }
21009
21010    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
21011        self.buffer.update(cx, |buffer, cx| {
21012            buffer.toggle_single_diff_hunk(range, cx);
21013        })
21014    }
21015
21016    pub(crate) fn apply_all_diff_hunks(
21017        &mut self,
21018        _: &ApplyAllDiffHunks,
21019        window: &mut Window,
21020        cx: &mut Context<Self>,
21021    ) {
21022        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
21023
21024        let buffers = self.buffer.read(cx).all_buffers();
21025        for branch_buffer in buffers {
21026            branch_buffer.update(cx, |branch_buffer, cx| {
21027                branch_buffer.merge_into_base(Vec::new(), cx);
21028            });
21029        }
21030
21031        if let Some(project) = self.project.clone() {
21032            self.save(
21033                SaveOptions {
21034                    format: true,
21035                    autosave: false,
21036                },
21037                project,
21038                window,
21039                cx,
21040            )
21041            .detach_and_log_err(cx);
21042        }
21043    }
21044
21045    pub(crate) fn apply_selected_diff_hunks(
21046        &mut self,
21047        _: &ApplyDiffHunk,
21048        window: &mut Window,
21049        cx: &mut Context<Self>,
21050    ) {
21051        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
21052        let snapshot = self.snapshot(window, cx);
21053        let hunks = snapshot.hunks_for_ranges(
21054            self.selections
21055                .all(&snapshot.display_snapshot)
21056                .into_iter()
21057                .map(|selection| selection.range()),
21058        );
21059        let mut ranges_by_buffer = HashMap::default();
21060        self.transact(window, cx, |editor, _window, cx| {
21061            for hunk in hunks {
21062                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
21063                    ranges_by_buffer
21064                        .entry(buffer.clone())
21065                        .or_insert_with(Vec::new)
21066                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
21067                }
21068            }
21069
21070            for (buffer, ranges) in ranges_by_buffer {
21071                buffer.update(cx, |buffer, cx| {
21072                    buffer.merge_into_base(ranges, cx);
21073                });
21074            }
21075        });
21076
21077        if let Some(project) = self.project.clone() {
21078            self.save(
21079                SaveOptions {
21080                    format: true,
21081                    autosave: false,
21082                },
21083                project,
21084                window,
21085                cx,
21086            )
21087            .detach_and_log_err(cx);
21088        }
21089    }
21090
21091    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
21092        if hovered != self.gutter_hovered {
21093            self.gutter_hovered = hovered;
21094            cx.notify();
21095        }
21096    }
21097
21098    pub fn insert_blocks(
21099        &mut self,
21100        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
21101        autoscroll: Option<Autoscroll>,
21102        cx: &mut Context<Self>,
21103    ) -> Vec<CustomBlockId> {
21104        let blocks = self
21105            .display_map
21106            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
21107        if let Some(autoscroll) = autoscroll {
21108            self.request_autoscroll(autoscroll, cx);
21109        }
21110        cx.notify();
21111        blocks
21112    }
21113
21114    pub fn resize_blocks(
21115        &mut self,
21116        heights: HashMap<CustomBlockId, u32>,
21117        autoscroll: Option<Autoscroll>,
21118        cx: &mut Context<Self>,
21119    ) {
21120        self.display_map
21121            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
21122        if let Some(autoscroll) = autoscroll {
21123            self.request_autoscroll(autoscroll, cx);
21124        }
21125        cx.notify();
21126    }
21127
21128    pub fn replace_blocks(
21129        &mut self,
21130        renderers: HashMap<CustomBlockId, RenderBlock>,
21131        autoscroll: Option<Autoscroll>,
21132        cx: &mut Context<Self>,
21133    ) {
21134        self.display_map
21135            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
21136        if let Some(autoscroll) = autoscroll {
21137            self.request_autoscroll(autoscroll, cx);
21138        }
21139        cx.notify();
21140    }
21141
21142    pub fn remove_blocks(
21143        &mut self,
21144        block_ids: HashSet<CustomBlockId>,
21145        autoscroll: Option<Autoscroll>,
21146        cx: &mut Context<Self>,
21147    ) {
21148        self.display_map.update(cx, |display_map, cx| {
21149            display_map.remove_blocks(block_ids, cx)
21150        });
21151        if let Some(autoscroll) = autoscroll {
21152            self.request_autoscroll(autoscroll, cx);
21153        }
21154        cx.notify();
21155    }
21156
21157    pub fn row_for_block(
21158        &self,
21159        block_id: CustomBlockId,
21160        cx: &mut Context<Self>,
21161    ) -> Option<DisplayRow> {
21162        self.display_map
21163            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21164    }
21165
21166    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21167        self.focused_block = Some(focused_block);
21168    }
21169
21170    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21171        self.focused_block.take()
21172    }
21173
21174    pub fn insert_creases(
21175        &mut self,
21176        creases: impl IntoIterator<Item = Crease<Anchor>>,
21177        cx: &mut Context<Self>,
21178    ) -> Vec<CreaseId> {
21179        self.display_map
21180            .update(cx, |map, cx| map.insert_creases(creases, cx))
21181    }
21182
21183    pub fn remove_creases(
21184        &mut self,
21185        ids: impl IntoIterator<Item = CreaseId>,
21186        cx: &mut Context<Self>,
21187    ) -> Vec<(CreaseId, Range<Anchor>)> {
21188        self.display_map
21189            .update(cx, |map, cx| map.remove_creases(ids, cx))
21190    }
21191
21192    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21193        self.display_map
21194            .update(cx, |map, cx| map.snapshot(cx))
21195            .longest_row()
21196    }
21197
21198    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21199        self.display_map
21200            .update(cx, |map, cx| map.snapshot(cx))
21201            .max_point()
21202    }
21203
21204    pub fn text(&self, cx: &App) -> String {
21205        self.buffer.read(cx).read(cx).text()
21206    }
21207
21208    pub fn is_empty(&self, cx: &App) -> bool {
21209        self.buffer.read(cx).read(cx).is_empty()
21210    }
21211
21212    pub fn text_option(&self, cx: &App) -> Option<String> {
21213        let text = self.text(cx);
21214        let text = text.trim();
21215
21216        if text.is_empty() {
21217            return None;
21218        }
21219
21220        Some(text.to_string())
21221    }
21222
21223    pub fn set_text(
21224        &mut self,
21225        text: impl Into<Arc<str>>,
21226        window: &mut Window,
21227        cx: &mut Context<Self>,
21228    ) {
21229        self.transact(window, cx, |this, _, cx| {
21230            this.buffer
21231                .read(cx)
21232                .as_singleton()
21233                .expect("you can only call set_text on editors for singleton buffers")
21234                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21235        });
21236    }
21237
21238    pub fn display_text(&self, cx: &mut App) -> String {
21239        self.display_map
21240            .update(cx, |map, cx| map.snapshot(cx))
21241            .text()
21242    }
21243
21244    fn create_minimap(
21245        &self,
21246        minimap_settings: MinimapSettings,
21247        window: &mut Window,
21248        cx: &mut Context<Self>,
21249    ) -> Option<Entity<Self>> {
21250        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21251            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21252    }
21253
21254    fn initialize_new_minimap(
21255        &self,
21256        minimap_settings: MinimapSettings,
21257        window: &mut Window,
21258        cx: &mut Context<Self>,
21259    ) -> Entity<Self> {
21260        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21261        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21262
21263        let mut minimap = Editor::new_internal(
21264            EditorMode::Minimap {
21265                parent: cx.weak_entity(),
21266            },
21267            self.buffer.clone(),
21268            None,
21269            Some(self.display_map.clone()),
21270            window,
21271            cx,
21272        );
21273        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21274        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21275        minimap.scroll_manager.clone_state(
21276            &self.scroll_manager,
21277            &my_snapshot,
21278            &minimap_snapshot,
21279            cx,
21280        );
21281        minimap.set_text_style_refinement(TextStyleRefinement {
21282            font_size: Some(MINIMAP_FONT_SIZE),
21283            font_weight: Some(MINIMAP_FONT_WEIGHT),
21284            font_family: Some(MINIMAP_FONT_FAMILY),
21285            ..Default::default()
21286        });
21287        minimap.update_minimap_configuration(minimap_settings, cx);
21288        cx.new(|_| minimap)
21289    }
21290
21291    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21292        let current_line_highlight = minimap_settings
21293            .current_line_highlight
21294            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21295        self.set_current_line_highlight(Some(current_line_highlight));
21296    }
21297
21298    pub fn minimap(&self) -> Option<&Entity<Self>> {
21299        self.minimap
21300            .as_ref()
21301            .filter(|_| self.minimap_visibility.visible())
21302    }
21303
21304    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21305        let mut wrap_guides = smallvec![];
21306
21307        if self.show_wrap_guides == Some(false) {
21308            return wrap_guides;
21309        }
21310
21311        let settings = self.buffer.read(cx).language_settings(cx);
21312        if settings.show_wrap_guides {
21313            match self.soft_wrap_mode(cx) {
21314                SoftWrap::Column(soft_wrap) => {
21315                    wrap_guides.push((soft_wrap as usize, true));
21316                }
21317                SoftWrap::Bounded(soft_wrap) => {
21318                    wrap_guides.push((soft_wrap as usize, true));
21319                }
21320                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21321            }
21322            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21323        }
21324
21325        wrap_guides
21326    }
21327
21328    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21329        let settings = self.buffer.read(cx).language_settings(cx);
21330        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21331        match mode {
21332            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21333                SoftWrap::None
21334            }
21335            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21336            language_settings::SoftWrap::PreferredLineLength => {
21337                SoftWrap::Column(settings.preferred_line_length)
21338            }
21339            language_settings::SoftWrap::Bounded => {
21340                SoftWrap::Bounded(settings.preferred_line_length)
21341            }
21342        }
21343    }
21344
21345    pub fn set_soft_wrap_mode(
21346        &mut self,
21347        mode: language_settings::SoftWrap,
21348        cx: &mut Context<Self>,
21349    ) {
21350        self.soft_wrap_mode_override = Some(mode);
21351        cx.notify();
21352    }
21353
21354    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21355        self.hard_wrap = hard_wrap;
21356        cx.notify();
21357    }
21358
21359    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21360        self.text_style_refinement = Some(style);
21361    }
21362
21363    /// called by the Element so we know what style we were most recently rendered with.
21364    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21365        // We intentionally do not inform the display map about the minimap style
21366        // so that wrapping is not recalculated and stays consistent for the editor
21367        // and its linked minimap.
21368        if !self.mode.is_minimap() {
21369            let font = style.text.font();
21370            let font_size = style.text.font_size.to_pixels(window.rem_size());
21371            let display_map = self
21372                .placeholder_display_map
21373                .as_ref()
21374                .filter(|_| self.is_empty(cx))
21375                .unwrap_or(&self.display_map);
21376
21377            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21378        }
21379        self.style = Some(style);
21380    }
21381
21382    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21383        if self.style.is_none() {
21384            self.style = Some(self.create_style(cx));
21385        }
21386        self.style.as_ref().unwrap()
21387    }
21388
21389    // Called by the element. This method is not designed to be called outside of the editor
21390    // element's layout code because it does not notify when rewrapping is computed synchronously.
21391    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21392        if self.is_empty(cx) {
21393            self.placeholder_display_map
21394                .as_ref()
21395                .map_or(false, |display_map| {
21396                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21397                })
21398        } else {
21399            self.display_map
21400                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21401        }
21402    }
21403
21404    pub fn set_soft_wrap(&mut self) {
21405        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21406    }
21407
21408    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21409        if self.soft_wrap_mode_override.is_some() {
21410            self.soft_wrap_mode_override.take();
21411        } else {
21412            let soft_wrap = match self.soft_wrap_mode(cx) {
21413                SoftWrap::GitDiff => return,
21414                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21415                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21416                    language_settings::SoftWrap::None
21417                }
21418            };
21419            self.soft_wrap_mode_override = Some(soft_wrap);
21420        }
21421        cx.notify();
21422    }
21423
21424    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21425        let Some(workspace) = self.workspace() else {
21426            return;
21427        };
21428        let fs = workspace.read(cx).app_state().fs.clone();
21429        let current_show = TabBarSettings::get_global(cx).show;
21430        update_settings_file(fs, cx, move |setting, _| {
21431            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21432        });
21433    }
21434
21435    pub fn toggle_indent_guides(
21436        &mut self,
21437        _: &ToggleIndentGuides,
21438        _: &mut Window,
21439        cx: &mut Context<Self>,
21440    ) {
21441        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21442            self.buffer
21443                .read(cx)
21444                .language_settings(cx)
21445                .indent_guides
21446                .enabled
21447        });
21448        self.show_indent_guides = Some(!currently_enabled);
21449        cx.notify();
21450    }
21451
21452    fn should_show_indent_guides(&self) -> Option<bool> {
21453        self.show_indent_guides
21454    }
21455
21456    pub fn disable_indent_guides_for_buffer(
21457        &mut self,
21458        buffer_id: BufferId,
21459        cx: &mut Context<Self>,
21460    ) {
21461        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21462        cx.notify();
21463    }
21464
21465    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21466        self.buffers_with_disabled_indent_guides
21467            .contains(&buffer_id)
21468    }
21469
21470    pub fn toggle_line_numbers(
21471        &mut self,
21472        _: &ToggleLineNumbers,
21473        _: &mut Window,
21474        cx: &mut Context<Self>,
21475    ) {
21476        let mut editor_settings = EditorSettings::get_global(cx).clone();
21477        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21478        EditorSettings::override_global(editor_settings, cx);
21479    }
21480
21481    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21482        if let Some(show_line_numbers) = self.show_line_numbers {
21483            return show_line_numbers;
21484        }
21485        EditorSettings::get_global(cx).gutter.line_numbers
21486    }
21487
21488    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21489        match (
21490            self.use_relative_line_numbers,
21491            EditorSettings::get_global(cx).relative_line_numbers,
21492        ) {
21493            (None, setting) => setting,
21494            (Some(false), _) => RelativeLineNumbers::Disabled,
21495            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21496            (Some(true), _) => RelativeLineNumbers::Enabled,
21497        }
21498    }
21499
21500    pub fn toggle_relative_line_numbers(
21501        &mut self,
21502        _: &ToggleRelativeLineNumbers,
21503        _: &mut Window,
21504        cx: &mut Context<Self>,
21505    ) {
21506        let is_relative = self.relative_line_numbers(cx);
21507        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21508    }
21509
21510    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21511        self.use_relative_line_numbers = is_relative;
21512        cx.notify();
21513    }
21514
21515    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21516        self.show_gutter = show_gutter;
21517        cx.notify();
21518    }
21519
21520    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21521        self.show_scrollbars = ScrollbarAxes {
21522            horizontal: show,
21523            vertical: show,
21524        };
21525        cx.notify();
21526    }
21527
21528    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21529        self.show_scrollbars.vertical = show;
21530        cx.notify();
21531    }
21532
21533    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21534        self.show_scrollbars.horizontal = show;
21535        cx.notify();
21536    }
21537
21538    pub fn set_minimap_visibility(
21539        &mut self,
21540        minimap_visibility: MinimapVisibility,
21541        window: &mut Window,
21542        cx: &mut Context<Self>,
21543    ) {
21544        if self.minimap_visibility != minimap_visibility {
21545            if minimap_visibility.visible() && self.minimap.is_none() {
21546                let minimap_settings = EditorSettings::get_global(cx).minimap;
21547                self.minimap =
21548                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21549            }
21550            self.minimap_visibility = minimap_visibility;
21551            cx.notify();
21552        }
21553    }
21554
21555    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21556        self.set_show_scrollbars(false, cx);
21557        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21558    }
21559
21560    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21561        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21562    }
21563
21564    /// Normally the text in full mode and auto height editors is padded on the
21565    /// left side by roughly half a character width for improved hit testing.
21566    ///
21567    /// Use this method to disable this for cases where this is not wanted (e.g.
21568    /// if you want to align the editor text with some other text above or below)
21569    /// or if you want to add this padding to single-line editors.
21570    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21571        self.offset_content = offset_content;
21572        cx.notify();
21573    }
21574
21575    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21576        self.show_line_numbers = Some(show_line_numbers);
21577        cx.notify();
21578    }
21579
21580    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21581        self.disable_expand_excerpt_buttons = true;
21582        cx.notify();
21583    }
21584
21585    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21586        self.number_deleted_lines = number;
21587        cx.notify();
21588    }
21589
21590    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21591        self.delegate_expand_excerpts = delegate;
21592    }
21593
21594    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21595        self.delegate_stage_and_restore = delegate;
21596    }
21597
21598    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21599        self.delegate_open_excerpts = delegate;
21600    }
21601
21602    pub fn set_on_local_selections_changed(
21603        &mut self,
21604        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21605    ) {
21606        self.on_local_selections_changed = callback;
21607    }
21608
21609    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21610        self.suppress_selection_callback = suppress;
21611    }
21612
21613    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21614        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21615        cx.notify();
21616    }
21617
21618    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21619        self.show_code_actions = Some(show_code_actions);
21620        cx.notify();
21621    }
21622
21623    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21624        self.show_runnables = Some(show_runnables);
21625        cx.notify();
21626    }
21627
21628    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21629        self.show_breakpoints = Some(show_breakpoints);
21630        cx.notify();
21631    }
21632
21633    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21634        self.show_diff_review_button = show;
21635        cx.notify();
21636    }
21637
21638    pub fn show_diff_review_button(&self) -> bool {
21639        self.show_diff_review_button
21640    }
21641
21642    pub fn render_diff_review_button(
21643        &self,
21644        display_row: DisplayRow,
21645        width: Pixels,
21646        cx: &mut Context<Self>,
21647    ) -> impl IntoElement {
21648        let text_color = cx.theme().colors().text;
21649        let icon_color = cx.theme().colors().icon_accent;
21650
21651        h_flex()
21652            .id("diff_review_button")
21653            .cursor_pointer()
21654            .w(width - px(1.))
21655            .h(relative(0.9))
21656            .justify_center()
21657            .rounded_sm()
21658            .border_1()
21659            .border_color(text_color.opacity(0.1))
21660            .bg(text_color.opacity(0.15))
21661            .hover(|s| {
21662                s.bg(icon_color.opacity(0.4))
21663                    .border_color(icon_color.opacity(0.5))
21664            })
21665            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21666            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21667            .on_mouse_down(
21668                gpui::MouseButton::Left,
21669                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21670                    editor.start_diff_review_drag(display_row, window, cx);
21671                }),
21672            )
21673    }
21674
21675    pub fn start_diff_review_drag(
21676        &mut self,
21677        display_row: DisplayRow,
21678        window: &mut Window,
21679        cx: &mut Context<Self>,
21680    ) {
21681        let snapshot = self.snapshot(window, cx);
21682        let point = snapshot
21683            .display_snapshot
21684            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21685        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21686        self.diff_review_drag_state = Some(DiffReviewDragState {
21687            start_anchor: anchor,
21688            current_anchor: anchor,
21689        });
21690        cx.notify();
21691    }
21692
21693    pub fn update_diff_review_drag(
21694        &mut self,
21695        display_row: DisplayRow,
21696        window: &mut Window,
21697        cx: &mut Context<Self>,
21698    ) {
21699        if self.diff_review_drag_state.is_none() {
21700            return;
21701        }
21702        let snapshot = self.snapshot(window, cx);
21703        let point = snapshot
21704            .display_snapshot
21705            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21706        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21707        if let Some(drag_state) = &mut self.diff_review_drag_state {
21708            drag_state.current_anchor = anchor;
21709            cx.notify();
21710        }
21711    }
21712
21713    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21714        if let Some(drag_state) = self.diff_review_drag_state.take() {
21715            let snapshot = self.snapshot(window, cx);
21716            let range = drag_state.row_range(&snapshot.display_snapshot);
21717            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21718        }
21719        cx.notify();
21720    }
21721
21722    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21723        self.diff_review_drag_state = None;
21724        cx.notify();
21725    }
21726
21727    /// Calculates the appropriate block height for the diff review overlay.
21728    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21729    /// and 2 lines per comment when expanded.
21730    fn calculate_overlay_height(
21731        &self,
21732        hunk_key: &DiffHunkKey,
21733        comments_expanded: bool,
21734        snapshot: &MultiBufferSnapshot,
21735    ) -> u32 {
21736        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21737        let base_height: u32 = 2; // Input row with avatar and buttons
21738
21739        if comment_count == 0 {
21740            base_height
21741        } else if comments_expanded {
21742            // Header (1 line) + 2 lines per comment
21743            base_height + 1 + (comment_count as u32 * 2)
21744        } else {
21745            // Just header when collapsed
21746            base_height + 1
21747        }
21748    }
21749
21750    pub fn show_diff_review_overlay(
21751        &mut self,
21752        display_range: Range<DisplayRow>,
21753        window: &mut Window,
21754        cx: &mut Context<Self>,
21755    ) {
21756        let Range { start, end } = display_range.sorted();
21757
21758        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21759        let editor_snapshot = self.snapshot(window, cx);
21760
21761        // Convert display rows to multibuffer points
21762        let start_point = editor_snapshot
21763            .display_snapshot
21764            .display_point_to_point(start.as_display_point(), Bias::Left);
21765        let end_point = editor_snapshot
21766            .display_snapshot
21767            .display_point_to_point(end.as_display_point(), Bias::Left);
21768        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21769
21770        // Create anchor range for the selected lines (start of first line to end of last line)
21771        let line_end = Point::new(
21772            end_point.row,
21773            buffer_snapshot.line_len(end_multi_buffer_row),
21774        );
21775        let anchor_range =
21776            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21777
21778        // Compute the hunk key for this display row
21779        let file_path = buffer_snapshot
21780            .file_at(start_point)
21781            .map(|file: &Arc<dyn language::File>| file.path().clone())
21782            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21783        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21784        let new_hunk_key = DiffHunkKey {
21785            file_path,
21786            hunk_start_anchor,
21787        };
21788
21789        // Check if we already have an overlay for this hunk
21790        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21791            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21792        }) {
21793            // Just focus the existing overlay's prompt editor
21794            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21795            window.focus(&focus_handle, cx);
21796            return;
21797        }
21798
21799        // Dismiss overlays that have no comments for their hunks
21800        self.dismiss_overlays_without_comments(cx);
21801
21802        // Get the current user's avatar URI from the project's user_store
21803        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21804            let user_store = project.read(cx).user_store();
21805            user_store
21806                .read(cx)
21807                .current_user()
21808                .map(|user| user.avatar_uri.clone())
21809        });
21810
21811        // Create anchor at the end of the last row so the block appears immediately below it
21812        // Use multibuffer coordinates for anchor creation
21813        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21814        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21815
21816        // Use the hunk key we already computed
21817        let hunk_key = new_hunk_key;
21818
21819        // Create the prompt editor for the review input
21820        let prompt_editor = cx.new(|cx| {
21821            let mut editor = Editor::single_line(window, cx);
21822            editor.set_placeholder_text("Add a review comment...", window, cx);
21823            editor
21824        });
21825
21826        // Register the Newline action on the prompt editor to submit the review
21827        let parent_editor = cx.entity().downgrade();
21828        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21829            prompt_editor.register_action({
21830                let parent_editor = parent_editor.clone();
21831                move |_: &crate::actions::Newline, window, cx| {
21832                    if let Some(editor) = parent_editor.upgrade() {
21833                        editor.update(cx, |editor, cx| {
21834                            editor.submit_diff_review_comment(window, cx);
21835                        });
21836                    }
21837                }
21838            })
21839        });
21840
21841        // Calculate initial height based on existing comments for this hunk
21842        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21843
21844        // Create the overlay block
21845        let prompt_editor_for_render = prompt_editor.clone();
21846        let hunk_key_for_render = hunk_key.clone();
21847        let editor_handle = cx.entity().downgrade();
21848        let block = BlockProperties {
21849            style: BlockStyle::Sticky,
21850            placement: BlockPlacement::Below(anchor),
21851            height: Some(initial_height),
21852            render: Arc::new(move |cx| {
21853                Self::render_diff_review_overlay(
21854                    &prompt_editor_for_render,
21855                    &hunk_key_for_render,
21856                    &editor_handle,
21857                    cx,
21858                )
21859            }),
21860            priority: 0,
21861        };
21862
21863        let block_ids = self.insert_blocks([block], None, cx);
21864        let Some(block_id) = block_ids.into_iter().next() else {
21865            log::error!("Failed to insert diff review overlay block");
21866            return;
21867        };
21868
21869        self.diff_review_overlays.push(DiffReviewOverlay {
21870            anchor_range,
21871            block_id,
21872            prompt_editor: prompt_editor.clone(),
21873            hunk_key,
21874            comments_expanded: true,
21875            inline_edit_editors: HashMap::default(),
21876            inline_edit_subscriptions: HashMap::default(),
21877            user_avatar_uri,
21878            _subscription: subscription,
21879        });
21880
21881        // Focus the prompt editor
21882        let focus_handle = prompt_editor.focus_handle(cx);
21883        window.focus(&focus_handle, cx);
21884
21885        cx.notify();
21886    }
21887
21888    /// Dismisses all diff review overlays.
21889    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21890        if self.diff_review_overlays.is_empty() {
21891            return;
21892        }
21893        let block_ids: HashSet<_> = self
21894            .diff_review_overlays
21895            .drain(..)
21896            .map(|overlay| overlay.block_id)
21897            .collect();
21898        self.remove_blocks(block_ids, None, cx);
21899        cx.notify();
21900    }
21901
21902    /// Dismisses overlays that have no comments stored for their hunks.
21903    /// Keeps overlays that have at least one comment.
21904    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21905        let snapshot = self.buffer.read(cx).snapshot(cx);
21906
21907        // First, compute which overlays have comments (to avoid borrow issues with retain)
21908        let overlays_with_comments: Vec<bool> = self
21909            .diff_review_overlays
21910            .iter()
21911            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21912            .collect();
21913
21914        // Now collect block IDs to remove and retain overlays
21915        let mut block_ids_to_remove = HashSet::default();
21916        let mut index = 0;
21917        self.diff_review_overlays.retain(|overlay| {
21918            let has_comments = overlays_with_comments[index];
21919            index += 1;
21920            if !has_comments {
21921                block_ids_to_remove.insert(overlay.block_id);
21922            }
21923            has_comments
21924        });
21925
21926        if !block_ids_to_remove.is_empty() {
21927            self.remove_blocks(block_ids_to_remove, None, cx);
21928            cx.notify();
21929        }
21930    }
21931
21932    /// Refreshes the diff review overlay block to update its height and render function.
21933    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21934    fn refresh_diff_review_overlay_height(
21935        &mut self,
21936        hunk_key: &DiffHunkKey,
21937        _window: &mut Window,
21938        cx: &mut Context<Self>,
21939    ) {
21940        // Extract all needed data from overlay first to avoid borrow conflicts
21941        let snapshot = self.buffer.read(cx).snapshot(cx);
21942        let (comments_expanded, block_id, prompt_editor) = {
21943            let Some(overlay) = self
21944                .diff_review_overlays
21945                .iter()
21946                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21947            else {
21948                return;
21949            };
21950
21951            (
21952                overlay.comments_expanded,
21953                overlay.block_id,
21954                overlay.prompt_editor.clone(),
21955            )
21956        };
21957
21958        // Calculate new height
21959        let snapshot = self.buffer.read(cx).snapshot(cx);
21960        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21961
21962        // Update the block height using resize_blocks (avoids flicker)
21963        let mut heights = HashMap::default();
21964        heights.insert(block_id, new_height);
21965        self.resize_blocks(heights, None, cx);
21966
21967        // Update the render function using replace_blocks (avoids flicker)
21968        let hunk_key_for_render = hunk_key.clone();
21969        let editor_handle = cx.entity().downgrade();
21970        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21971            Arc::new(move |cx| {
21972                Self::render_diff_review_overlay(
21973                    &prompt_editor,
21974                    &hunk_key_for_render,
21975                    &editor_handle,
21976                    cx,
21977                )
21978            });
21979
21980        let mut renderers = HashMap::default();
21981        renderers.insert(block_id, render);
21982        self.replace_blocks(renderers, None, cx);
21983    }
21984
21985    /// Action handler for SubmitDiffReviewComment.
21986    pub fn submit_diff_review_comment_action(
21987        &mut self,
21988        _: &SubmitDiffReviewComment,
21989        window: &mut Window,
21990        cx: &mut Context<Self>,
21991    ) {
21992        self.submit_diff_review_comment(window, cx);
21993    }
21994
21995    /// Stores the diff review comment locally.
21996    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21997    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21998        // Find the overlay that currently has focus
21999        let overlay_index = self
22000            .diff_review_overlays
22001            .iter()
22002            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
22003        let Some(overlay_index) = overlay_index else {
22004            return;
22005        };
22006        let overlay = &self.diff_review_overlays[overlay_index];
22007
22008        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
22009        if comment_text.is_empty() {
22010            return;
22011        }
22012
22013        let anchor_range = overlay.anchor_range.clone();
22014        let hunk_key = overlay.hunk_key.clone();
22015
22016        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
22017
22018        // Clear the prompt editor but keep the overlay open
22019        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
22020            overlay.prompt_editor.update(cx, |editor, cx| {
22021                editor.clear(window, cx);
22022            });
22023        }
22024
22025        // Refresh the overlay to update the block height for the new comment
22026        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22027
22028        cx.notify();
22029    }
22030
22031    /// Returns the prompt editor for the diff review overlay, if one is active.
22032    /// This is primarily used for testing.
22033    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
22034        self.diff_review_overlays
22035            .first()
22036            .map(|overlay| &overlay.prompt_editor)
22037    }
22038
22039    /// Returns the line range for the first diff review overlay, if one is active.
22040    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
22041    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
22042        let overlay = self.diff_review_overlays.first()?;
22043        let snapshot = self.buffer.read(cx).snapshot(cx);
22044        let start_point = overlay.anchor_range.start.to_point(&snapshot);
22045        let end_point = overlay.anchor_range.end.to_point(&snapshot);
22046        let start_row = snapshot
22047            .point_to_buffer_point(start_point)
22048            .map(|(_, p, _)| p.row)
22049            .unwrap_or(start_point.row);
22050        let end_row = snapshot
22051            .point_to_buffer_point(end_point)
22052            .map(|(_, p, _)| p.row)
22053            .unwrap_or(end_point.row);
22054        Some((start_row, end_row))
22055    }
22056
22057    /// Sets whether the comments section is expanded in the diff review overlay.
22058    /// This is primarily used for testing.
22059    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
22060        for overlay in &mut self.diff_review_overlays {
22061            overlay.comments_expanded = expanded;
22062        }
22063        cx.notify();
22064    }
22065
22066    /// Compares two DiffHunkKeys for equality by resolving their anchors.
22067    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
22068        a.file_path == b.file_path
22069            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
22070    }
22071
22072    /// Returns comments for a specific hunk, ordered by creation time.
22073    pub fn comments_for_hunk<'a>(
22074        &'a self,
22075        key: &DiffHunkKey,
22076        snapshot: &MultiBufferSnapshot,
22077    ) -> &'a [StoredReviewComment] {
22078        let key_point = key.hunk_start_anchor.to_point(snapshot);
22079        self.stored_review_comments
22080            .iter()
22081            .find(|(k, _)| {
22082                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22083            })
22084            .map(|(_, comments)| comments.as_slice())
22085            .unwrap_or(&[])
22086    }
22087
22088    /// Returns the total count of stored review comments across all hunks.
22089    pub fn total_review_comment_count(&self) -> usize {
22090        self.stored_review_comments
22091            .iter()
22092            .map(|(_, v)| v.len())
22093            .sum()
22094    }
22095
22096    /// Returns the count of comments for a specific hunk.
22097    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
22098        let key_point = key.hunk_start_anchor.to_point(snapshot);
22099        self.stored_review_comments
22100            .iter()
22101            .find(|(k, _)| {
22102                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22103            })
22104            .map(|(_, v)| v.len())
22105            .unwrap_or(0)
22106    }
22107
22108    /// Adds a new review comment to a specific hunk.
22109    pub fn add_review_comment(
22110        &mut self,
22111        hunk_key: DiffHunkKey,
22112        comment: String,
22113        anchor_range: Range<Anchor>,
22114        cx: &mut Context<Self>,
22115    ) -> usize {
22116        let id = self.next_review_comment_id;
22117        self.next_review_comment_id += 1;
22118
22119        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
22120
22121        let snapshot = self.buffer.read(cx).snapshot(cx);
22122        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
22123
22124        // Find existing entry for this hunk or add a new one
22125        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
22126            k.file_path == hunk_key.file_path
22127                && k.hunk_start_anchor.to_point(&snapshot) == key_point
22128        }) {
22129            comments.push(stored_comment);
22130        } else {
22131            self.stored_review_comments
22132                .push((hunk_key, vec![stored_comment]));
22133        }
22134
22135        cx.emit(EditorEvent::ReviewCommentsChanged {
22136            total_count: self.total_review_comment_count(),
22137        });
22138        cx.notify();
22139        id
22140    }
22141
22142    /// Removes a review comment by ID from any hunk.
22143    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
22144        for (_, comments) in self.stored_review_comments.iter_mut() {
22145            if let Some(index) = comments.iter().position(|c| c.id == id) {
22146                comments.remove(index);
22147                cx.emit(EditorEvent::ReviewCommentsChanged {
22148                    total_count: self.total_review_comment_count(),
22149                });
22150                cx.notify();
22151                return true;
22152            }
22153        }
22154        false
22155    }
22156
22157    /// Updates a review comment's text by ID.
22158    pub fn update_review_comment(
22159        &mut self,
22160        id: usize,
22161        new_comment: String,
22162        cx: &mut Context<Self>,
22163    ) -> bool {
22164        for (_, comments) in self.stored_review_comments.iter_mut() {
22165            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22166                comment.comment = new_comment;
22167                comment.is_editing = false;
22168                cx.emit(EditorEvent::ReviewCommentsChanged {
22169                    total_count: self.total_review_comment_count(),
22170                });
22171                cx.notify();
22172                return true;
22173            }
22174        }
22175        false
22176    }
22177
22178    /// Sets a comment's editing state.
22179    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22180        for (_, comments) in self.stored_review_comments.iter_mut() {
22181            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22182                comment.is_editing = is_editing;
22183                cx.notify();
22184                return;
22185            }
22186        }
22187    }
22188
22189    /// Takes all stored comments from all hunks, clearing the storage.
22190    /// Returns a Vec of (hunk_key, comments) pairs.
22191    pub fn take_all_review_comments(
22192        &mut self,
22193        cx: &mut Context<Self>,
22194    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22195        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22196        self.dismiss_all_diff_review_overlays(cx);
22197        let comments = std::mem::take(&mut self.stored_review_comments);
22198        // Reset the ID counter since all comments have been taken
22199        self.next_review_comment_id = 0;
22200        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22201        cx.notify();
22202        comments
22203    }
22204
22205    /// Removes review comments whose anchors are no longer valid or whose
22206    /// associated diff hunks no longer exist.
22207    ///
22208    /// This should be called when the buffer changes to prevent orphaned comments
22209    /// from accumulating.
22210    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22211        let snapshot = self.buffer.read(cx).snapshot(cx);
22212        let original_count = self.total_review_comment_count();
22213
22214        // Remove comments with invalid hunk anchors
22215        self.stored_review_comments
22216            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22217
22218        // Also clean up individual comments with invalid anchor ranges
22219        for (_, comments) in &mut self.stored_review_comments {
22220            comments.retain(|comment| {
22221                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22222            });
22223        }
22224
22225        // Remove empty hunk entries
22226        self.stored_review_comments
22227            .retain(|(_, comments)| !comments.is_empty());
22228
22229        let new_count = self.total_review_comment_count();
22230        if new_count != original_count {
22231            cx.emit(EditorEvent::ReviewCommentsChanged {
22232                total_count: new_count,
22233            });
22234            cx.notify();
22235        }
22236    }
22237
22238    /// Toggles the expanded state of the comments section in the overlay.
22239    pub fn toggle_review_comments_expanded(
22240        &mut self,
22241        _: &ToggleReviewCommentsExpanded,
22242        window: &mut Window,
22243        cx: &mut Context<Self>,
22244    ) {
22245        // Find the overlay that currently has focus, or use the first one
22246        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22247            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22248                overlay.comments_expanded = !overlay.comments_expanded;
22249                Some(overlay.hunk_key.clone())
22250            } else {
22251                None
22252            }
22253        });
22254
22255        // If no focused overlay found, toggle the first one
22256        let hunk_key = overlay_info.or_else(|| {
22257            self.diff_review_overlays.first_mut().map(|overlay| {
22258                overlay.comments_expanded = !overlay.comments_expanded;
22259                overlay.hunk_key.clone()
22260            })
22261        });
22262
22263        if let Some(hunk_key) = hunk_key {
22264            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22265            cx.notify();
22266        }
22267    }
22268
22269    /// Handles the EditReviewComment action - sets a comment into editing mode.
22270    pub fn edit_review_comment(
22271        &mut self,
22272        action: &EditReviewComment,
22273        window: &mut Window,
22274        cx: &mut Context<Self>,
22275    ) {
22276        let comment_id = action.id;
22277
22278        // Set the comment to editing mode
22279        self.set_comment_editing(comment_id, true, cx);
22280
22281        // Find the overlay that contains this comment and create an inline editor if needed
22282        // First, find which hunk this comment belongs to
22283        let hunk_key = self
22284            .stored_review_comments
22285            .iter()
22286            .find_map(|(key, comments)| {
22287                if comments.iter().any(|c| c.id == comment_id) {
22288                    Some(key.clone())
22289                } else {
22290                    None
22291                }
22292            });
22293
22294        let snapshot = self.buffer.read(cx).snapshot(cx);
22295        if let Some(hunk_key) = hunk_key {
22296            if let Some(overlay) = self
22297                .diff_review_overlays
22298                .iter_mut()
22299                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22300            {
22301                if let std::collections::hash_map::Entry::Vacant(entry) =
22302                    overlay.inline_edit_editors.entry(comment_id)
22303                {
22304                    // Find the comment text
22305                    let comment_text = self
22306                        .stored_review_comments
22307                        .iter()
22308                        .flat_map(|(_, comments)| comments)
22309                        .find(|c| c.id == comment_id)
22310                        .map(|c| c.comment.clone())
22311                        .unwrap_or_default();
22312
22313                    // Create inline editor
22314                    let parent_editor = cx.entity().downgrade();
22315                    let inline_editor = cx.new(|cx| {
22316                        let mut editor = Editor::single_line(window, cx);
22317                        editor.set_text(&*comment_text, window, cx);
22318                        // Select all text for easy replacement
22319                        editor.select_all(&crate::actions::SelectAll, window, cx);
22320                        editor
22321                    });
22322
22323                    // Register the Newline action to confirm the edit
22324                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22325                        inline_editor.register_action({
22326                            let parent_editor = parent_editor.clone();
22327                            move |_: &crate::actions::Newline, window, cx| {
22328                                if let Some(editor) = parent_editor.upgrade() {
22329                                    editor.update(cx, |editor, cx| {
22330                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22331                                    });
22332                                }
22333                            }
22334                        })
22335                    });
22336
22337                    // Store the subscription to keep the action handler alive
22338                    overlay
22339                        .inline_edit_subscriptions
22340                        .insert(comment_id, subscription);
22341
22342                    // Focus the inline editor
22343                    let focus_handle = inline_editor.focus_handle(cx);
22344                    window.focus(&focus_handle, cx);
22345
22346                    entry.insert(inline_editor);
22347                }
22348            }
22349        }
22350
22351        cx.notify();
22352    }
22353
22354    /// Confirms an inline edit of a review comment.
22355    pub fn confirm_edit_review_comment(
22356        &mut self,
22357        comment_id: usize,
22358        _window: &mut Window,
22359        cx: &mut Context<Self>,
22360    ) {
22361        // Get the new text from the inline editor
22362        // Find the overlay containing this comment's inline editor
22363        let snapshot = self.buffer.read(cx).snapshot(cx);
22364        let hunk_key = self
22365            .stored_review_comments
22366            .iter()
22367            .find_map(|(key, comments)| {
22368                if comments.iter().any(|c| c.id == comment_id) {
22369                    Some(key.clone())
22370                } else {
22371                    None
22372                }
22373            });
22374
22375        let new_text = hunk_key
22376            .as_ref()
22377            .and_then(|hunk_key| {
22378                self.diff_review_overlays
22379                    .iter()
22380                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22381            })
22382            .as_ref()
22383            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22384            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22385
22386        if let Some(new_text) = new_text {
22387            if !new_text.is_empty() {
22388                self.update_review_comment(comment_id, new_text, cx);
22389            }
22390        }
22391
22392        // Remove the inline editor and its subscription
22393        if let Some(hunk_key) = hunk_key {
22394            if let Some(overlay) = self
22395                .diff_review_overlays
22396                .iter_mut()
22397                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22398            {
22399                overlay.inline_edit_editors.remove(&comment_id);
22400                overlay.inline_edit_subscriptions.remove(&comment_id);
22401            }
22402        }
22403
22404        // Clear editing state
22405        self.set_comment_editing(comment_id, false, cx);
22406    }
22407
22408    /// Cancels an inline edit of a review comment.
22409    pub fn cancel_edit_review_comment(
22410        &mut self,
22411        comment_id: usize,
22412        _window: &mut Window,
22413        cx: &mut Context<Self>,
22414    ) {
22415        // Find which hunk this comment belongs to
22416        let hunk_key = self
22417            .stored_review_comments
22418            .iter()
22419            .find_map(|(key, comments)| {
22420                if comments.iter().any(|c| c.id == comment_id) {
22421                    Some(key.clone())
22422                } else {
22423                    None
22424                }
22425            });
22426
22427        // Remove the inline editor and its subscription
22428        if let Some(hunk_key) = hunk_key {
22429            let snapshot = self.buffer.read(cx).snapshot(cx);
22430            if let Some(overlay) = self
22431                .diff_review_overlays
22432                .iter_mut()
22433                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22434            {
22435                overlay.inline_edit_editors.remove(&comment_id);
22436                overlay.inline_edit_subscriptions.remove(&comment_id);
22437            }
22438        }
22439
22440        // Clear editing state
22441        self.set_comment_editing(comment_id, false, cx);
22442    }
22443
22444    /// Action handler for ConfirmEditReviewComment.
22445    pub fn confirm_edit_review_comment_action(
22446        &mut self,
22447        action: &ConfirmEditReviewComment,
22448        window: &mut Window,
22449        cx: &mut Context<Self>,
22450    ) {
22451        self.confirm_edit_review_comment(action.id, window, cx);
22452    }
22453
22454    /// Action handler for CancelEditReviewComment.
22455    pub fn cancel_edit_review_comment_action(
22456        &mut self,
22457        action: &CancelEditReviewComment,
22458        window: &mut Window,
22459        cx: &mut Context<Self>,
22460    ) {
22461        self.cancel_edit_review_comment(action.id, window, cx);
22462    }
22463
22464    /// Handles the DeleteReviewComment action - removes a comment.
22465    pub fn delete_review_comment(
22466        &mut self,
22467        action: &DeleteReviewComment,
22468        window: &mut Window,
22469        cx: &mut Context<Self>,
22470    ) {
22471        // Get the hunk key before removing the comment
22472        // Find the hunk key from the comment itself
22473        let comment_id = action.id;
22474        let hunk_key = self
22475            .stored_review_comments
22476            .iter()
22477            .find_map(|(key, comments)| {
22478                if comments.iter().any(|c| c.id == comment_id) {
22479                    Some(key.clone())
22480                } else {
22481                    None
22482                }
22483            });
22484
22485        // Also get it from the overlay for refresh purposes
22486        let overlay_hunk_key = self
22487            .diff_review_overlays
22488            .first()
22489            .map(|o| o.hunk_key.clone());
22490
22491        self.remove_review_comment(action.id, cx);
22492
22493        // Refresh the overlay height after removing a comment
22494        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22495            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22496        }
22497    }
22498
22499    fn render_diff_review_overlay(
22500        prompt_editor: &Entity<Editor>,
22501        hunk_key: &DiffHunkKey,
22502        editor_handle: &WeakEntity<Editor>,
22503        cx: &mut BlockContext,
22504    ) -> AnyElement {
22505        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22506            if ranges.is_empty() {
22507                return None;
22508            }
22509            let formatted: Vec<String> = ranges
22510                .iter()
22511                .map(|(start, end)| {
22512                    let start_line = start + 1;
22513                    let end_line = end + 1;
22514                    if start_line == end_line {
22515                        format!("Line {start_line}")
22516                    } else {
22517                        format!("Lines {start_line}-{end_line}")
22518                    }
22519                })
22520                .collect();
22521            // Don't show label for single line in single excerpt
22522            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22523                return None;
22524            }
22525            Some(formatted.join(""))
22526        }
22527
22528        let theme = cx.theme();
22529        let colors = theme.colors();
22530
22531        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22532            editor_handle
22533                .upgrade()
22534                .map(|editor| {
22535                    let editor = editor.read(cx);
22536                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22537                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22538                    let (expanded, editors, avatar_uri, line_ranges) = editor
22539                        .diff_review_overlays
22540                        .iter()
22541                        .find(|overlay| {
22542                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22543                        })
22544                        .map(|o| {
22545                            let start_point = o.anchor_range.start.to_point(&snapshot);
22546                            let end_point = o.anchor_range.end.to_point(&snapshot);
22547                            // Get line ranges per excerpt to detect discontinuities
22548                            let buffer_ranges =
22549                                snapshot.range_to_buffer_ranges(start_point..end_point);
22550                            let ranges: Vec<(u32, u32)> = buffer_ranges
22551                                .iter()
22552                                .map(|(buffer, range, _)| {
22553                                    let start = buffer.offset_to_point(range.start.0).row;
22554                                    let end = buffer.offset_to_point(range.end.0).row;
22555                                    (start, end)
22556                                })
22557                                .collect();
22558                            (
22559                                o.comments_expanded,
22560                                o.inline_edit_editors.clone(),
22561                                o.user_avatar_uri.clone(),
22562                                if ranges.is_empty() {
22563                                    None
22564                                } else {
22565                                    Some(ranges)
22566                                },
22567                            )
22568                        })
22569                        .unwrap_or((true, HashMap::default(), None, None));
22570                    (comments, expanded, editors, avatar_uri, line_ranges)
22571                })
22572                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22573
22574        let comment_count = comments.len();
22575        let avatar_size = px(20.);
22576        let action_icon_size = IconSize::XSmall;
22577
22578        v_flex()
22579            .w_full()
22580            .bg(colors.editor_background)
22581            .border_b_1()
22582            .border_color(colors.border)
22583            .px_2()
22584            .pb_2()
22585            .gap_2()
22586            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22587            .when_some(line_ranges, |el, ranges| {
22588                let label = format_line_ranges(&ranges);
22589                if let Some(label) = label {
22590                    el.child(
22591                        h_flex()
22592                            .w_full()
22593                            .px_2()
22594                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22595                    )
22596                } else {
22597                    el
22598                }
22599            })
22600            // Top row: editable input with user's avatar
22601            .child(
22602                h_flex()
22603                    .w_full()
22604                    .items_center()
22605                    .gap_2()
22606                    .px_2()
22607                    .py_1p5()
22608                    .rounded_md()
22609                    .bg(colors.surface_background)
22610                    .child(
22611                        div()
22612                            .size(avatar_size)
22613                            .flex_shrink_0()
22614                            .rounded_full()
22615                            .overflow_hidden()
22616                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22617                                Avatar::new(avatar_uri.clone())
22618                                    .size(avatar_size)
22619                                    .into_any_element()
22620                            } else {
22621                                Icon::new(IconName::Person)
22622                                    .size(IconSize::Small)
22623                                    .color(ui::Color::Muted)
22624                                    .into_any_element()
22625                            }),
22626                    )
22627                    .child(
22628                        div()
22629                            .flex_1()
22630                            .border_1()
22631                            .border_color(colors.border)
22632                            .rounded_md()
22633                            .bg(colors.editor_background)
22634                            .px_2()
22635                            .py_1()
22636                            .child(prompt_editor.clone()),
22637                    )
22638                    .child(
22639                        h_flex()
22640                            .flex_shrink_0()
22641                            .gap_1()
22642                            .child(
22643                                IconButton::new("diff-review-close", IconName::Close)
22644                                    .icon_color(ui::Color::Muted)
22645                                    .icon_size(action_icon_size)
22646                                    .tooltip(Tooltip::text("Close"))
22647                                    .on_click(|_, window, cx| {
22648                                        window
22649                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22650                                    }),
22651                            )
22652                            .child(
22653                                IconButton::new("diff-review-add", IconName::Return)
22654                                    .icon_color(ui::Color::Muted)
22655                                    .icon_size(action_icon_size)
22656                                    .tooltip(Tooltip::text("Add comment"))
22657                                    .on_click(|_, window, cx| {
22658                                        window.dispatch_action(
22659                                            Box::new(crate::actions::SubmitDiffReviewComment),
22660                                            cx,
22661                                        );
22662                                    }),
22663                            ),
22664                    ),
22665            )
22666            // Expandable comments section (only shown when there are comments)
22667            .when(comment_count > 0, |el| {
22668                el.child(Self::render_comments_section(
22669                    comments,
22670                    comments_expanded,
22671                    inline_editors,
22672                    user_avatar_uri,
22673                    avatar_size,
22674                    action_icon_size,
22675                    colors,
22676                ))
22677            })
22678            .into_any_element()
22679    }
22680
22681    fn render_comments_section(
22682        comments: Vec<StoredReviewComment>,
22683        expanded: bool,
22684        inline_editors: HashMap<usize, Entity<Editor>>,
22685        user_avatar_uri: Option<SharedUri>,
22686        avatar_size: Pixels,
22687        action_icon_size: IconSize,
22688        colors: &theme::ThemeColors,
22689    ) -> impl IntoElement {
22690        let comment_count = comments.len();
22691
22692        v_flex()
22693            .w_full()
22694            .gap_1()
22695            // Header with expand/collapse toggle
22696            .child(
22697                h_flex()
22698                    .id("review-comments-header")
22699                    .w_full()
22700                    .items_center()
22701                    .gap_1()
22702                    .px_2()
22703                    .py_1()
22704                    .cursor_pointer()
22705                    .rounded_md()
22706                    .hover(|style| style.bg(colors.ghost_element_hover))
22707                    .on_click(|_, window: &mut Window, cx| {
22708                        window.dispatch_action(
22709                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22710                            cx,
22711                        );
22712                    })
22713                    .child(
22714                        Icon::new(if expanded {
22715                            IconName::ChevronDown
22716                        } else {
22717                            IconName::ChevronRight
22718                        })
22719                        .size(IconSize::Small)
22720                        .color(ui::Color::Muted),
22721                    )
22722                    .child(
22723                        Label::new(format!(
22724                            "{} Comment{}",
22725                            comment_count,
22726                            if comment_count == 1 { "" } else { "s" }
22727                        ))
22728                        .size(LabelSize::Small)
22729                        .color(Color::Muted),
22730                    ),
22731            )
22732            // Comments list (when expanded)
22733            .when(expanded, |el| {
22734                el.children(comments.into_iter().map(|comment| {
22735                    let inline_editor = inline_editors.get(&comment.id).cloned();
22736                    Self::render_comment_row(
22737                        comment,
22738                        inline_editor,
22739                        user_avatar_uri.clone(),
22740                        avatar_size,
22741                        action_icon_size,
22742                        colors,
22743                    )
22744                }))
22745            })
22746    }
22747
22748    fn render_comment_row(
22749        comment: StoredReviewComment,
22750        inline_editor: Option<Entity<Editor>>,
22751        user_avatar_uri: Option<SharedUri>,
22752        avatar_size: Pixels,
22753        action_icon_size: IconSize,
22754        colors: &theme::ThemeColors,
22755    ) -> impl IntoElement {
22756        let comment_id = comment.id;
22757        let is_editing = inline_editor.is_some();
22758
22759        h_flex()
22760            .w_full()
22761            .items_center()
22762            .gap_2()
22763            .px_2()
22764            .py_1p5()
22765            .rounded_md()
22766            .bg(colors.surface_background)
22767            .child(
22768                div()
22769                    .size(avatar_size)
22770                    .flex_shrink_0()
22771                    .rounded_full()
22772                    .overflow_hidden()
22773                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22774                        Avatar::new(avatar_uri.clone())
22775                            .size(avatar_size)
22776                            .into_any_element()
22777                    } else {
22778                        Icon::new(IconName::Person)
22779                            .size(IconSize::Small)
22780                            .color(ui::Color::Muted)
22781                            .into_any_element()
22782                    }),
22783            )
22784            .child(if let Some(editor) = inline_editor {
22785                // Inline edit mode: show an editable text field
22786                div()
22787                    .flex_1()
22788                    .border_1()
22789                    .border_color(colors.border)
22790                    .rounded_md()
22791                    .bg(colors.editor_background)
22792                    .px_2()
22793                    .py_1()
22794                    .child(editor)
22795                    .into_any_element()
22796            } else {
22797                // Display mode: show the comment text
22798                div()
22799                    .flex_1()
22800                    .text_sm()
22801                    .text_color(colors.text)
22802                    .child(comment.comment)
22803                    .into_any_element()
22804            })
22805            .child(if is_editing {
22806                // Editing mode: show close and confirm buttons
22807                h_flex()
22808                    .gap_1()
22809                    .child(
22810                        IconButton::new(
22811                            format!("diff-review-cancel-edit-{comment_id}"),
22812                            IconName::Close,
22813                        )
22814                        .icon_color(ui::Color::Muted)
22815                        .icon_size(action_icon_size)
22816                        .tooltip(Tooltip::text("Cancel"))
22817                        .on_click(move |_, window, cx| {
22818                            window.dispatch_action(
22819                                Box::new(crate::actions::CancelEditReviewComment {
22820                                    id: comment_id,
22821                                }),
22822                                cx,
22823                            );
22824                        }),
22825                    )
22826                    .child(
22827                        IconButton::new(
22828                            format!("diff-review-confirm-edit-{comment_id}"),
22829                            IconName::Return,
22830                        )
22831                        .icon_color(ui::Color::Muted)
22832                        .icon_size(action_icon_size)
22833                        .tooltip(Tooltip::text("Confirm"))
22834                        .on_click(move |_, window, cx| {
22835                            window.dispatch_action(
22836                                Box::new(crate::actions::ConfirmEditReviewComment {
22837                                    id: comment_id,
22838                                }),
22839                                cx,
22840                            );
22841                        }),
22842                    )
22843                    .into_any_element()
22844            } else {
22845                // Display mode: no action buttons for now (edit/delete not yet implemented)
22846                gpui::Empty.into_any_element()
22847            })
22848    }
22849
22850    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22851        if self.display_map.read(cx).masked != masked {
22852            self.display_map.update(cx, |map, _| map.masked = masked);
22853        }
22854        cx.notify()
22855    }
22856
22857    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22858        self.show_wrap_guides = Some(show_wrap_guides);
22859        cx.notify();
22860    }
22861
22862    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22863        self.show_indent_guides = Some(show_indent_guides);
22864        cx.notify();
22865    }
22866
22867    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22868        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22869            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22870                && let Some(dir) = file.abs_path(cx).parent()
22871            {
22872                return Some(dir.to_owned());
22873            }
22874        }
22875
22876        None
22877    }
22878
22879    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22880        self.active_excerpt(cx)?
22881            .1
22882            .read(cx)
22883            .file()
22884            .and_then(|f| f.as_local())
22885    }
22886
22887    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22888        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22889            let buffer = buffer.read(cx);
22890            if let Some(project_path) = buffer.project_path(cx) {
22891                let project = self.project()?.read(cx);
22892                project.absolute_path(&project_path, cx)
22893            } else {
22894                buffer
22895                    .file()
22896                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22897            }
22898        })
22899    }
22900
22901    pub fn reveal_in_finder(
22902        &mut self,
22903        _: &RevealInFileManager,
22904        _window: &mut Window,
22905        cx: &mut Context<Self>,
22906    ) {
22907        if let Some(path) = self.target_file_abs_path(cx) {
22908            if let Some(project) = self.project() {
22909                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22910            } else {
22911                cx.reveal_path(&path);
22912            }
22913        }
22914    }
22915
22916    pub fn copy_path(
22917        &mut self,
22918        _: &zed_actions::workspace::CopyPath,
22919        _window: &mut Window,
22920        cx: &mut Context<Self>,
22921    ) {
22922        if let Some(path) = self.target_file_abs_path(cx)
22923            && let Some(path) = path.to_str()
22924        {
22925            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22926        } else {
22927            cx.propagate();
22928        }
22929    }
22930
22931    pub fn copy_relative_path(
22932        &mut self,
22933        _: &zed_actions::workspace::CopyRelativePath,
22934        _window: &mut Window,
22935        cx: &mut Context<Self>,
22936    ) {
22937        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22938            let project = self.project()?.read(cx);
22939            let path = buffer.read(cx).file()?.path();
22940            let path = path.display(project.path_style(cx));
22941            Some(path)
22942        }) {
22943            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22944        } else {
22945            cx.propagate();
22946        }
22947    }
22948
22949    /// Returns the project path for the editor's buffer, if any buffer is
22950    /// opened in the editor.
22951    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22952        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22953            buffer.read(cx).project_path(cx)
22954        } else {
22955            None
22956        }
22957    }
22958
22959    // Returns true if the editor handled a go-to-line request
22960    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22961        maybe!({
22962            let breakpoint_store = self.breakpoint_store.as_ref()?;
22963
22964            let (active_stack_frame, debug_line_pane_id) = {
22965                let store = breakpoint_store.read(cx);
22966                let active_stack_frame = store.active_position().cloned();
22967                let debug_line_pane_id = store.active_debug_line_pane_id();
22968                (active_stack_frame, debug_line_pane_id)
22969            };
22970
22971            let Some(active_stack_frame) = active_stack_frame else {
22972                self.clear_row_highlights::<ActiveDebugLine>();
22973                return None;
22974            };
22975
22976            if let Some(debug_line_pane_id) = debug_line_pane_id {
22977                if let Some(workspace) = self
22978                    .workspace
22979                    .as_ref()
22980                    .and_then(|(workspace, _)| workspace.upgrade())
22981                {
22982                    let editor_pane_id = workspace
22983                        .read(cx)
22984                        .pane_for_item_id(cx.entity_id())
22985                        .map(|pane| pane.entity_id());
22986
22987                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
22988                        self.clear_row_highlights::<ActiveDebugLine>();
22989                        return None;
22990                    }
22991                }
22992            }
22993
22994            let position = active_stack_frame.position;
22995            let buffer_id = position.buffer_id?;
22996            let snapshot = self
22997                .project
22998                .as_ref()?
22999                .read(cx)
23000                .buffer_for_id(buffer_id, cx)?
23001                .read(cx)
23002                .snapshot();
23003
23004            let mut handled = false;
23005            for (id, _, ExcerptRange { context, .. }) in
23006                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
23007            {
23008                if context.start.cmp(&position, &snapshot).is_ge()
23009                    || context.end.cmp(&position, &snapshot).is_lt()
23010                {
23011                    continue;
23012                }
23013                let snapshot = self.buffer.read(cx).snapshot(cx);
23014                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
23015
23016                handled = true;
23017                self.clear_row_highlights::<ActiveDebugLine>();
23018
23019                self.go_to_line::<ActiveDebugLine>(
23020                    multibuffer_anchor,
23021                    Some(cx.theme().colors().editor_debugger_active_line_background),
23022                    window,
23023                    cx,
23024                );
23025
23026                cx.notify();
23027            }
23028
23029            handled.then_some(())
23030        })
23031        .is_some()
23032    }
23033
23034    pub fn copy_file_name_without_extension(
23035        &mut self,
23036        _: &CopyFileNameWithoutExtension,
23037        _: &mut Window,
23038        cx: &mut Context<Self>,
23039    ) {
23040        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23041            let file = buffer.read(cx).file()?;
23042            file.path().file_stem()
23043        }) {
23044            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
23045        }
23046    }
23047
23048    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
23049        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23050            let file = buffer.read(cx).file()?;
23051            Some(file.file_name(cx))
23052        }) {
23053            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
23054        }
23055    }
23056
23057    pub fn toggle_git_blame(
23058        &mut self,
23059        _: &::git::Blame,
23060        window: &mut Window,
23061        cx: &mut Context<Self>,
23062    ) {
23063        self.show_git_blame_gutter = !self.show_git_blame_gutter;
23064
23065        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
23066            self.start_git_blame(true, window, cx);
23067        }
23068
23069        cx.notify();
23070    }
23071
23072    pub fn toggle_git_blame_inline(
23073        &mut self,
23074        _: &ToggleGitBlameInline,
23075        window: &mut Window,
23076        cx: &mut Context<Self>,
23077    ) {
23078        self.toggle_git_blame_inline_internal(true, window, cx);
23079        cx.notify();
23080    }
23081
23082    pub fn open_git_blame_commit(
23083        &mut self,
23084        _: &OpenGitBlameCommit,
23085        window: &mut Window,
23086        cx: &mut Context<Self>,
23087    ) {
23088        self.open_git_blame_commit_internal(window, cx);
23089    }
23090
23091    fn open_git_blame_commit_internal(
23092        &mut self,
23093        window: &mut Window,
23094        cx: &mut Context<Self>,
23095    ) -> Option<()> {
23096        let blame = self.blame.as_ref()?;
23097        let snapshot = self.snapshot(window, cx);
23098        let cursor = self
23099            .selections
23100            .newest::<Point>(&snapshot.display_snapshot)
23101            .head();
23102        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
23103        let (_, blame_entry) = blame
23104            .update(cx, |blame, cx| {
23105                blame
23106                    .blame_for_rows(
23107                        &[RowInfo {
23108                            buffer_id: Some(buffer.remote_id()),
23109                            buffer_row: Some(point.row),
23110                            ..Default::default()
23111                        }],
23112                        cx,
23113                    )
23114                    .next()
23115            })
23116            .flatten()?;
23117        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
23118        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
23119        let workspace = self.workspace()?.downgrade();
23120        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
23121        None
23122    }
23123
23124    pub fn git_blame_inline_enabled(&self) -> bool {
23125        self.git_blame_inline_enabled
23126    }
23127
23128    pub fn toggle_selection_menu(
23129        &mut self,
23130        _: &ToggleSelectionMenu,
23131        _: &mut Window,
23132        cx: &mut Context<Self>,
23133    ) {
23134        self.show_selection_menu = self
23135            .show_selection_menu
23136            .map(|show_selections_menu| !show_selections_menu)
23137            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
23138
23139        cx.notify();
23140    }
23141
23142    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
23143        self.show_selection_menu
23144            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
23145    }
23146
23147    fn start_git_blame(
23148        &mut self,
23149        user_triggered: bool,
23150        window: &mut Window,
23151        cx: &mut Context<Self>,
23152    ) {
23153        if let Some(project) = self.project() {
23154            if let Some(buffer) = self.buffer().read(cx).as_singleton()
23155                && buffer.read(cx).file().is_none()
23156            {
23157                return;
23158            }
23159
23160            let focused = self.focus_handle(cx).contains_focused(window, cx);
23161
23162            let project = project.clone();
23163            let blame = cx
23164                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23165            self.blame_subscription =
23166                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23167            self.blame = Some(blame);
23168        }
23169    }
23170
23171    fn toggle_git_blame_inline_internal(
23172        &mut self,
23173        user_triggered: bool,
23174        window: &mut Window,
23175        cx: &mut Context<Self>,
23176    ) {
23177        if self.git_blame_inline_enabled {
23178            self.git_blame_inline_enabled = false;
23179            self.show_git_blame_inline = false;
23180            self.show_git_blame_inline_delay_task.take();
23181        } else {
23182            self.git_blame_inline_enabled = true;
23183            self.start_git_blame_inline(user_triggered, window, cx);
23184        }
23185
23186        cx.notify();
23187    }
23188
23189    fn start_git_blame_inline(
23190        &mut self,
23191        user_triggered: bool,
23192        window: &mut Window,
23193        cx: &mut Context<Self>,
23194    ) {
23195        self.start_git_blame(user_triggered, window, cx);
23196
23197        if ProjectSettings::get_global(cx)
23198            .git
23199            .inline_blame_delay()
23200            .is_some()
23201        {
23202            self.start_inline_blame_timer(window, cx);
23203        } else {
23204            self.show_git_blame_inline = true
23205        }
23206    }
23207
23208    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23209        self.blame.as_ref()
23210    }
23211
23212    pub fn show_git_blame_gutter(&self) -> bool {
23213        self.show_git_blame_gutter
23214    }
23215
23216    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23217        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23218    }
23219
23220    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23221        self.show_git_blame_inline
23222            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23223            && !self.newest_selection_head_on_empty_line(cx)
23224            && self.has_blame_entries(cx)
23225    }
23226
23227    fn has_blame_entries(&self, cx: &App) -> bool {
23228        self.blame()
23229            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23230    }
23231
23232    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23233        let cursor_anchor = self.selections.newest_anchor().head();
23234
23235        let snapshot = self.buffer.read(cx).snapshot(cx);
23236        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23237
23238        snapshot.line_len(buffer_row) == 0
23239    }
23240
23241    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23242        let buffer_and_selection = maybe!({
23243            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23244            let selection_range = selection.range();
23245
23246            let multi_buffer = self.buffer().read(cx);
23247            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23248            let buffer_ranges = multi_buffer_snapshot
23249                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23250
23251            let (buffer, range, _) = if selection.reversed {
23252                buffer_ranges.first()
23253            } else {
23254                buffer_ranges.last()
23255            }?;
23256
23257            let buffer_range = range.to_point(buffer);
23258
23259            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23260                return Some((
23261                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23262                    buffer_range.start.row..buffer_range.end.row,
23263                ));
23264            };
23265
23266            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23267            let start =
23268                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23269            let end =
23270                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23271
23272            Some((
23273                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23274                start.row..end.row,
23275            ))
23276        });
23277
23278        let Some((buffer, selection)) = buffer_and_selection else {
23279            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23280        };
23281
23282        let Some(project) = self.project() else {
23283            return Task::ready(Err(anyhow!("editor does not have project")));
23284        };
23285
23286        project.update(cx, |project, cx| {
23287            project.get_permalink_to_line(&buffer, selection, cx)
23288        })
23289    }
23290
23291    pub fn copy_permalink_to_line(
23292        &mut self,
23293        _: &CopyPermalinkToLine,
23294        window: &mut Window,
23295        cx: &mut Context<Self>,
23296    ) {
23297        let permalink_task = self.get_permalink_to_line(cx);
23298        let workspace = self.workspace();
23299
23300        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23301            Ok(permalink) => {
23302                cx.update(|_, cx| {
23303                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23304                })
23305                .ok();
23306            }
23307            Err(err) => {
23308                let message = format!("Failed to copy permalink: {err}");
23309
23310                anyhow::Result::<()>::Err(err).log_err();
23311
23312                if let Some(workspace) = workspace {
23313                    workspace
23314                        .update_in(cx, |workspace, _, cx| {
23315                            struct CopyPermalinkToLine;
23316
23317                            workspace.show_toast(
23318                                Toast::new(
23319                                    NotificationId::unique::<CopyPermalinkToLine>(),
23320                                    message,
23321                                ),
23322                                cx,
23323                            )
23324                        })
23325                        .ok();
23326                }
23327            }
23328        })
23329        .detach();
23330    }
23331
23332    pub fn copy_file_location(
23333        &mut self,
23334        _: &CopyFileLocation,
23335        _: &mut Window,
23336        cx: &mut Context<Self>,
23337    ) {
23338        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23339
23340        let start_line = selection.start.row + 1;
23341        let end_line = selection.end.row + 1;
23342
23343        let end_line = if selection.end.column == 0 && end_line > start_line {
23344            end_line - 1
23345        } else {
23346            end_line
23347        };
23348
23349        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23350            let project = self.project()?.read(cx);
23351            let file = buffer.read(cx).file()?;
23352            let path = file.path().display(project.path_style(cx));
23353
23354            let location = if start_line == end_line {
23355                format!("{path}:{start_line}")
23356            } else {
23357                format!("{path}:{start_line}-{end_line}")
23358            };
23359            Some(location)
23360        }) {
23361            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23362        }
23363    }
23364
23365    pub fn open_permalink_to_line(
23366        &mut self,
23367        _: &OpenPermalinkToLine,
23368        window: &mut Window,
23369        cx: &mut Context<Self>,
23370    ) {
23371        let permalink_task = self.get_permalink_to_line(cx);
23372        let workspace = self.workspace();
23373
23374        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23375            Ok(permalink) => {
23376                cx.update(|_, cx| {
23377                    cx.open_url(permalink.as_ref());
23378                })
23379                .ok();
23380            }
23381            Err(err) => {
23382                let message = format!("Failed to open permalink: {err}");
23383
23384                anyhow::Result::<()>::Err(err).log_err();
23385
23386                if let Some(workspace) = workspace {
23387                    workspace.update(cx, |workspace, cx| {
23388                        struct OpenPermalinkToLine;
23389
23390                        workspace.show_toast(
23391                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23392                            cx,
23393                        )
23394                    });
23395                }
23396            }
23397        })
23398        .detach();
23399    }
23400
23401    pub fn insert_uuid_v4(
23402        &mut self,
23403        _: &InsertUuidV4,
23404        window: &mut Window,
23405        cx: &mut Context<Self>,
23406    ) {
23407        self.insert_uuid(UuidVersion::V4, window, cx);
23408    }
23409
23410    pub fn insert_uuid_v7(
23411        &mut self,
23412        _: &InsertUuidV7,
23413        window: &mut Window,
23414        cx: &mut Context<Self>,
23415    ) {
23416        self.insert_uuid(UuidVersion::V7, window, cx);
23417    }
23418
23419    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23420        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23421        self.transact(window, cx, |this, window, cx| {
23422            let edits = this
23423                .selections
23424                .all::<Point>(&this.display_snapshot(cx))
23425                .into_iter()
23426                .map(|selection| {
23427                    let uuid = match version {
23428                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23429                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23430                    };
23431
23432                    (selection.range(), uuid.to_string())
23433                });
23434            this.edit(edits, cx);
23435            this.refresh_edit_prediction(true, false, window, cx);
23436        });
23437    }
23438
23439    pub fn open_selections_in_multibuffer(
23440        &mut self,
23441        _: &OpenSelectionsInMultibuffer,
23442        window: &mut Window,
23443        cx: &mut Context<Self>,
23444    ) {
23445        let multibuffer = self.buffer.read(cx);
23446
23447        let Some(buffer) = multibuffer.as_singleton() else {
23448            return;
23449        };
23450
23451        let Some(workspace) = self.workspace() else {
23452            return;
23453        };
23454
23455        let title = multibuffer.title(cx).to_string();
23456
23457        let locations = self
23458            .selections
23459            .all_anchors(&self.display_snapshot(cx))
23460            .iter()
23461            .map(|selection| {
23462                (
23463                    buffer.clone(),
23464                    (selection.start.text_anchor..selection.end.text_anchor)
23465                        .to_point(buffer.read(cx)),
23466                )
23467            })
23468            .into_group_map();
23469
23470        cx.spawn_in(window, async move |_, cx| {
23471            workspace.update_in(cx, |workspace, window, cx| {
23472                Self::open_locations_in_multibuffer(
23473                    workspace,
23474                    locations,
23475                    format!("Selections for '{title}'"),
23476                    false,
23477                    false,
23478                    MultibufferSelectionMode::All,
23479                    window,
23480                    cx,
23481                );
23482            })
23483        })
23484        .detach();
23485    }
23486
23487    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23488    /// last highlight added will be used.
23489    ///
23490    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23491    pub fn highlight_rows<T: 'static>(
23492        &mut self,
23493        range: Range<Anchor>,
23494        color: Hsla,
23495        options: RowHighlightOptions,
23496        cx: &mut Context<Self>,
23497    ) {
23498        let snapshot = self.buffer().read(cx).snapshot(cx);
23499        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23500        let ix = row_highlights.binary_search_by(|highlight| {
23501            Ordering::Equal
23502                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23503                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23504        });
23505
23506        if let Err(mut ix) = ix {
23507            let index = post_inc(&mut self.highlight_order);
23508
23509            // If this range intersects with the preceding highlight, then merge it with
23510            // the preceding highlight. Otherwise insert a new highlight.
23511            let mut merged = false;
23512            if ix > 0 {
23513                let prev_highlight = &mut row_highlights[ix - 1];
23514                if prev_highlight
23515                    .range
23516                    .end
23517                    .cmp(&range.start, &snapshot)
23518                    .is_ge()
23519                {
23520                    ix -= 1;
23521                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23522                        prev_highlight.range.end = range.end;
23523                    }
23524                    merged = true;
23525                    prev_highlight.index = index;
23526                    prev_highlight.color = color;
23527                    prev_highlight.options = options;
23528                }
23529            }
23530
23531            if !merged {
23532                row_highlights.insert(
23533                    ix,
23534                    RowHighlight {
23535                        range,
23536                        index,
23537                        color,
23538                        options,
23539                        type_id: TypeId::of::<T>(),
23540                    },
23541                );
23542            }
23543
23544            // If any of the following highlights intersect with this one, merge them.
23545            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23546                let highlight = &row_highlights[ix];
23547                if next_highlight
23548                    .range
23549                    .start
23550                    .cmp(&highlight.range.end, &snapshot)
23551                    .is_le()
23552                {
23553                    if next_highlight
23554                        .range
23555                        .end
23556                        .cmp(&highlight.range.end, &snapshot)
23557                        .is_gt()
23558                    {
23559                        row_highlights[ix].range.end = next_highlight.range.end;
23560                    }
23561                    row_highlights.remove(ix + 1);
23562                } else {
23563                    break;
23564                }
23565            }
23566        }
23567    }
23568
23569    /// Remove any highlighted row ranges of the given type that intersect the
23570    /// given ranges.
23571    pub fn remove_highlighted_rows<T: 'static>(
23572        &mut self,
23573        ranges_to_remove: Vec<Range<Anchor>>,
23574        cx: &mut Context<Self>,
23575    ) {
23576        let snapshot = self.buffer().read(cx).snapshot(cx);
23577        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23578        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23579        row_highlights.retain(|highlight| {
23580            while let Some(range_to_remove) = ranges_to_remove.peek() {
23581                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23582                    Ordering::Less | Ordering::Equal => {
23583                        ranges_to_remove.next();
23584                    }
23585                    Ordering::Greater => {
23586                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23587                            Ordering::Less | Ordering::Equal => {
23588                                return false;
23589                            }
23590                            Ordering::Greater => break,
23591                        }
23592                    }
23593                }
23594            }
23595
23596            true
23597        })
23598    }
23599
23600    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23601    pub fn clear_row_highlights<T: 'static>(&mut self) {
23602        self.highlighted_rows.remove(&TypeId::of::<T>());
23603    }
23604
23605    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23606    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23607        self.highlighted_rows
23608            .get(&TypeId::of::<T>())
23609            .map_or(&[] as &[_], |vec| vec.as_slice())
23610            .iter()
23611            .map(|highlight| (highlight.range.clone(), highlight.color))
23612    }
23613
23614    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23615    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23616    /// Allows to ignore certain kinds of highlights.
23617    pub fn highlighted_display_rows(
23618        &self,
23619        window: &mut Window,
23620        cx: &mut App,
23621    ) -> BTreeMap<DisplayRow, LineHighlight> {
23622        let snapshot = self.snapshot(window, cx);
23623        let mut used_highlight_orders = HashMap::default();
23624        self.highlighted_rows
23625            .iter()
23626            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23627            .fold(
23628                BTreeMap::<DisplayRow, LineHighlight>::new(),
23629                |mut unique_rows, highlight| {
23630                    let start = highlight.range.start.to_display_point(&snapshot);
23631                    let end = highlight.range.end.to_display_point(&snapshot);
23632                    let start_row = start.row().0;
23633                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23634                    {
23635                        end.row().0.saturating_sub(1)
23636                    } else {
23637                        end.row().0
23638                    };
23639                    for row in start_row..=end_row {
23640                        let used_index =
23641                            used_highlight_orders.entry(row).or_insert(highlight.index);
23642                        if highlight.index >= *used_index {
23643                            *used_index = highlight.index;
23644                            unique_rows.insert(
23645                                DisplayRow(row),
23646                                LineHighlight {
23647                                    include_gutter: highlight.options.include_gutter,
23648                                    border: None,
23649                                    background: highlight.color.into(),
23650                                    type_id: Some(highlight.type_id),
23651                                },
23652                            );
23653                        }
23654                    }
23655                    unique_rows
23656                },
23657            )
23658    }
23659
23660    pub fn highlighted_display_row_for_autoscroll(
23661        &self,
23662        snapshot: &DisplaySnapshot,
23663    ) -> Option<DisplayRow> {
23664        self.highlighted_rows
23665            .values()
23666            .flat_map(|highlighted_rows| highlighted_rows.iter())
23667            .filter_map(|highlight| {
23668                if highlight.options.autoscroll {
23669                    Some(highlight.range.start.to_display_point(snapshot).row())
23670                } else {
23671                    None
23672                }
23673            })
23674            .min()
23675    }
23676
23677    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23678        self.highlight_background(
23679            HighlightKey::SearchWithinRange,
23680            ranges,
23681            |_, colors| colors.colors().editor_document_highlight_read_background,
23682            cx,
23683        )
23684    }
23685
23686    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23687        self.breadcrumb_header = Some(new_header);
23688    }
23689
23690    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23691        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23692    }
23693
23694    pub fn highlight_background(
23695        &mut self,
23696        key: HighlightKey,
23697        ranges: &[Range<Anchor>],
23698        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23699        cx: &mut Context<Self>,
23700    ) {
23701        self.background_highlights
23702            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23703        self.scrollbar_marker_state.dirty = true;
23704        cx.notify();
23705    }
23706
23707    pub fn clear_background_highlights(
23708        &mut self,
23709        key: HighlightKey,
23710        cx: &mut Context<Self>,
23711    ) -> Option<BackgroundHighlight> {
23712        let text_highlights = self.background_highlights.remove(&key)?;
23713        if !text_highlights.1.is_empty() {
23714            self.scrollbar_marker_state.dirty = true;
23715            cx.notify();
23716        }
23717        Some(text_highlights)
23718    }
23719
23720    pub fn highlight_gutter<T: 'static>(
23721        &mut self,
23722        ranges: impl Into<Vec<Range<Anchor>>>,
23723        color_fetcher: fn(&App) -> Hsla,
23724        cx: &mut Context<Self>,
23725    ) {
23726        self.gutter_highlights
23727            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23728        cx.notify();
23729    }
23730
23731    pub fn clear_gutter_highlights<T: 'static>(
23732        &mut self,
23733        cx: &mut Context<Self>,
23734    ) -> Option<GutterHighlight> {
23735        cx.notify();
23736        self.gutter_highlights.remove(&TypeId::of::<T>())
23737    }
23738
23739    pub fn insert_gutter_highlight<T: 'static>(
23740        &mut self,
23741        range: Range<Anchor>,
23742        color_fetcher: fn(&App) -> Hsla,
23743        cx: &mut Context<Self>,
23744    ) {
23745        let snapshot = self.buffer().read(cx).snapshot(cx);
23746        let mut highlights = self
23747            .gutter_highlights
23748            .remove(&TypeId::of::<T>())
23749            .map(|(_, highlights)| highlights)
23750            .unwrap_or_default();
23751        let ix = highlights.binary_search_by(|highlight| {
23752            Ordering::Equal
23753                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23754                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23755        });
23756        if let Err(ix) = ix {
23757            highlights.insert(ix, range);
23758        }
23759        self.gutter_highlights
23760            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23761    }
23762
23763    pub fn remove_gutter_highlights<T: 'static>(
23764        &mut self,
23765        ranges_to_remove: Vec<Range<Anchor>>,
23766        cx: &mut Context<Self>,
23767    ) {
23768        let snapshot = self.buffer().read(cx).snapshot(cx);
23769        let Some((color_fetcher, mut gutter_highlights)) =
23770            self.gutter_highlights.remove(&TypeId::of::<T>())
23771        else {
23772            return;
23773        };
23774        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23775        gutter_highlights.retain(|highlight| {
23776            while let Some(range_to_remove) = ranges_to_remove.peek() {
23777                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23778                    Ordering::Less | Ordering::Equal => {
23779                        ranges_to_remove.next();
23780                    }
23781                    Ordering::Greater => {
23782                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23783                            Ordering::Less | Ordering::Equal => {
23784                                return false;
23785                            }
23786                            Ordering::Greater => break,
23787                        }
23788                    }
23789                }
23790            }
23791
23792            true
23793        });
23794        self.gutter_highlights
23795            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23796    }
23797
23798    #[cfg(any(test, feature = "test-support"))]
23799    pub fn all_text_highlights(
23800        &self,
23801        window: &mut Window,
23802        cx: &mut Context<Self>,
23803    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23804        let snapshot = self.snapshot(window, cx);
23805        self.display_map.update(cx, |display_map, _| {
23806            display_map
23807                .all_text_highlights()
23808                .map(|(_, highlight)| {
23809                    let (style, ranges) = highlight.as_ref();
23810                    (
23811                        *style,
23812                        ranges
23813                            .iter()
23814                            .map(|range| range.clone().to_display_points(&snapshot))
23815                            .collect(),
23816                    )
23817                })
23818                .collect()
23819        })
23820    }
23821
23822    #[cfg(any(test, feature = "test-support"))]
23823    pub fn all_text_background_highlights(
23824        &self,
23825        window: &mut Window,
23826        cx: &mut Context<Self>,
23827    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23828        let snapshot = self.snapshot(window, cx);
23829        let buffer = &snapshot.buffer_snapshot();
23830        let start = buffer.anchor_before(MultiBufferOffset(0));
23831        let end = buffer.anchor_after(buffer.len());
23832        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23833    }
23834
23835    #[cfg(any(test, feature = "test-support"))]
23836    pub fn sorted_background_highlights_in_range(
23837        &self,
23838        search_range: Range<Anchor>,
23839        display_snapshot: &DisplaySnapshot,
23840        theme: &Theme,
23841    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23842        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23843        res.sort_by(|a, b| {
23844            a.0.start
23845                .cmp(&b.0.start)
23846                .then_with(|| a.0.end.cmp(&b.0.end))
23847                .then_with(|| a.1.cmp(&b.1))
23848        });
23849        res
23850    }
23851
23852    #[cfg(any(test, feature = "test-support"))]
23853    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23854        let snapshot = self.buffer().read(cx).snapshot(cx);
23855
23856        let highlights = self
23857            .background_highlights
23858            .get(&HighlightKey::BufferSearchHighlights);
23859
23860        if let Some((_color, ranges)) = highlights {
23861            ranges
23862                .iter()
23863                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23864                .collect_vec()
23865        } else {
23866            vec![]
23867        }
23868    }
23869
23870    fn document_highlights_for_position<'a>(
23871        &'a self,
23872        position: Anchor,
23873        buffer: &'a MultiBufferSnapshot,
23874    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23875        let read_highlights = self
23876            .background_highlights
23877            .get(&HighlightKey::DocumentHighlightRead)
23878            .map(|h| &h.1);
23879        let write_highlights = self
23880            .background_highlights
23881            .get(&HighlightKey::DocumentHighlightWrite)
23882            .map(|h| &h.1);
23883        let left_position = position.bias_left(buffer);
23884        let right_position = position.bias_right(buffer);
23885        read_highlights
23886            .into_iter()
23887            .chain(write_highlights)
23888            .flat_map(move |ranges| {
23889                let start_ix = match ranges.binary_search_by(|probe| {
23890                    let cmp = probe.end.cmp(&left_position, buffer);
23891                    if cmp.is_ge() {
23892                        Ordering::Greater
23893                    } else {
23894                        Ordering::Less
23895                    }
23896                }) {
23897                    Ok(i) | Err(i) => i,
23898                };
23899
23900                ranges[start_ix..]
23901                    .iter()
23902                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23903            })
23904    }
23905
23906    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23907        self.background_highlights
23908            .get(&key)
23909            .is_some_and(|(_, highlights)| !highlights.is_empty())
23910    }
23911
23912    /// Returns all background highlights for a given range.
23913    ///
23914    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23915    pub fn background_highlights_in_range(
23916        &self,
23917        search_range: Range<Anchor>,
23918        display_snapshot: &DisplaySnapshot,
23919        theme: &Theme,
23920    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23921        let mut results = Vec::new();
23922        for (color_fetcher, ranges) in self.background_highlights.values() {
23923            let start_ix = match ranges.binary_search_by(|probe| {
23924                let cmp = probe
23925                    .end
23926                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23927                if cmp.is_gt() {
23928                    Ordering::Greater
23929                } else {
23930                    Ordering::Less
23931                }
23932            }) {
23933                Ok(i) | Err(i) => i,
23934            };
23935            for (index, range) in ranges[start_ix..].iter().enumerate() {
23936                if range
23937                    .start
23938                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23939                    .is_ge()
23940                {
23941                    break;
23942                }
23943
23944                let color = color_fetcher(&(start_ix + index), theme);
23945                let start = range.start.to_display_point(display_snapshot);
23946                let end = range.end.to_display_point(display_snapshot);
23947                results.push((start..end, color))
23948            }
23949        }
23950        results
23951    }
23952
23953    pub fn gutter_highlights_in_range(
23954        &self,
23955        search_range: Range<Anchor>,
23956        display_snapshot: &DisplaySnapshot,
23957        cx: &App,
23958    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23959        let mut results = Vec::new();
23960        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23961            let color = color_fetcher(cx);
23962            let start_ix = match ranges.binary_search_by(|probe| {
23963                let cmp = probe
23964                    .end
23965                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23966                if cmp.is_gt() {
23967                    Ordering::Greater
23968                } else {
23969                    Ordering::Less
23970                }
23971            }) {
23972                Ok(i) | Err(i) => i,
23973            };
23974            for range in &ranges[start_ix..] {
23975                if range
23976                    .start
23977                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23978                    .is_ge()
23979                {
23980                    break;
23981                }
23982
23983                let start = range.start.to_display_point(display_snapshot);
23984                let end = range.end.to_display_point(display_snapshot);
23985                results.push((start..end, color))
23986            }
23987        }
23988        results
23989    }
23990
23991    /// Get the text ranges corresponding to the redaction query
23992    pub fn redacted_ranges(
23993        &self,
23994        search_range: Range<Anchor>,
23995        display_snapshot: &DisplaySnapshot,
23996        cx: &App,
23997    ) -> Vec<Range<DisplayPoint>> {
23998        display_snapshot
23999            .buffer_snapshot()
24000            .redacted_ranges(search_range, |file| {
24001                if let Some(file) = file {
24002                    file.is_private()
24003                        && EditorSettings::get(
24004                            Some(SettingsLocation {
24005                                worktree_id: file.worktree_id(cx),
24006                                path: file.path().as_ref(),
24007                            }),
24008                            cx,
24009                        )
24010                        .redact_private_values
24011                } else {
24012                    false
24013                }
24014            })
24015            .map(|range| {
24016                range.start.to_display_point(display_snapshot)
24017                    ..range.end.to_display_point(display_snapshot)
24018            })
24019            .collect()
24020    }
24021
24022    pub fn highlight_text_key(
24023        &mut self,
24024        key: HighlightKey,
24025        ranges: Vec<Range<Anchor>>,
24026        style: HighlightStyle,
24027        merge: bool,
24028        cx: &mut Context<Self>,
24029    ) {
24030        self.display_map.update(cx, |map, cx| {
24031            map.highlight_text(key, ranges, style, merge, cx);
24032        });
24033        cx.notify();
24034    }
24035
24036    pub fn highlight_text(
24037        &mut self,
24038        key: HighlightKey,
24039        ranges: Vec<Range<Anchor>>,
24040        style: HighlightStyle,
24041        cx: &mut Context<Self>,
24042    ) {
24043        self.display_map.update(cx, |map, cx| {
24044            map.highlight_text(key, ranges, style, false, cx)
24045        });
24046        cx.notify();
24047    }
24048
24049    pub fn text_highlights<'a>(
24050        &'a self,
24051        key: HighlightKey,
24052        cx: &'a App,
24053    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
24054        self.display_map.read(cx).text_highlights(key)
24055    }
24056
24057    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
24058        let cleared = self
24059            .display_map
24060            .update(cx, |map, _| map.clear_highlights(key));
24061        if cleared {
24062            cx.notify();
24063        }
24064    }
24065
24066    pub fn clear_highlights_with(
24067        &mut self,
24068        f: &mut dyn FnMut(&HighlightKey) -> bool,
24069        cx: &mut Context<Self>,
24070    ) {
24071        let cleared = self
24072            .display_map
24073            .update(cx, |map, _| map.clear_highlights_with(f));
24074        if cleared {
24075            cx.notify();
24076        }
24077    }
24078
24079    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
24080        (self.read_only(cx) || self.blink_manager.read(cx).visible())
24081            && self.focus_handle.is_focused(window)
24082    }
24083
24084    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
24085        self.show_cursor_when_unfocused = is_enabled;
24086        cx.notify();
24087    }
24088
24089    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
24090        cx.notify();
24091    }
24092
24093    fn on_debug_session_event(
24094        &mut self,
24095        _session: Entity<Session>,
24096        event: &SessionEvent,
24097        cx: &mut Context<Self>,
24098    ) {
24099        if let SessionEvent::InvalidateInlineValue = event {
24100            self.refresh_inline_values(cx);
24101        }
24102    }
24103
24104    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
24105        let Some(semantics) = self.semantics_provider.clone() else {
24106            return;
24107        };
24108
24109        if !self.inline_value_cache.enabled {
24110            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
24111            self.splice_inlays(&inlays, Vec::new(), cx);
24112            return;
24113        }
24114
24115        let current_execution_position = self
24116            .highlighted_rows
24117            .get(&TypeId::of::<ActiveDebugLine>())
24118            .and_then(|lines| lines.last().map(|line| line.range.end));
24119
24120        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
24121            let inline_values = editor
24122                .update(cx, |editor, cx| {
24123                    let Some(current_execution_position) = current_execution_position else {
24124                        return Some(Task::ready(Ok(Vec::new())));
24125                    };
24126
24127                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
24128                        let snapshot = buffer.snapshot(cx);
24129
24130                        let excerpt = snapshot.excerpt_containing(
24131                            current_execution_position..current_execution_position,
24132                        )?;
24133
24134                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
24135                    })?;
24136
24137                    if current_execution_position
24138                        .text_anchor
24139                        .buffer_id
24140                        .is_some_and(|id| id != buffer.read(cx).remote_id())
24141                    {
24142                        return Some(Task::ready(Ok(Vec::new())));
24143                    }
24144
24145                    let range =
24146                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
24147
24148                    semantics.inline_values(buffer, range, cx)
24149                })
24150                .ok()
24151                .flatten()?
24152                .await
24153                .context("refreshing debugger inlays")
24154                .log_err()?;
24155
24156            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
24157
24158            for (buffer_id, inline_value) in inline_values
24159                .into_iter()
24160                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
24161            {
24162                buffer_inline_values
24163                    .entry(buffer_id)
24164                    .or_default()
24165                    .push(inline_value);
24166            }
24167
24168            editor
24169                .update(cx, |editor, cx| {
24170                    let snapshot = editor.buffer.read(cx).snapshot(cx);
24171                    let mut new_inlays = Vec::default();
24172
24173                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
24174                        let buffer_id = buffer_snapshot.remote_id();
24175                        buffer_inline_values
24176                            .get(&buffer_id)
24177                            .into_iter()
24178                            .flatten()
24179                            .for_each(|hint| {
24180                                let inlay = Inlay::debugger(
24181                                    post_inc(&mut editor.next_inlay_id),
24182                                    Anchor::in_buffer(excerpt_id, hint.position),
24183                                    hint.text(),
24184                                );
24185                                if !inlay.text().chars().contains(&'\n') {
24186                                    new_inlays.push(inlay);
24187                                }
24188                            });
24189                    }
24190
24191                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24192                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24193
24194                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24195                })
24196                .ok()?;
24197            Some(())
24198        });
24199    }
24200
24201    fn on_buffer_event(
24202        &mut self,
24203        multibuffer: &Entity<MultiBuffer>,
24204        event: &multi_buffer::Event,
24205        window: &mut Window,
24206        cx: &mut Context<Self>,
24207    ) {
24208        match event {
24209            multi_buffer::Event::Edited {
24210                edited_buffer,
24211                is_local,
24212            } => {
24213                self.scrollbar_marker_state.dirty = true;
24214                self.active_indent_guides_state.dirty = true;
24215                self.refresh_active_diagnostics(cx);
24216                self.refresh_code_actions(window, cx);
24217                self.refresh_single_line_folds(window, cx);
24218                let snapshot = self.snapshot(window, cx);
24219                self.refresh_matching_bracket_highlights(&snapshot, cx);
24220                self.refresh_outline_symbols_at_cursor(cx);
24221                self.refresh_sticky_headers(&snapshot, cx);
24222                if *is_local && self.has_active_edit_prediction() {
24223                    self.update_visible_edit_prediction(window, cx);
24224                }
24225
24226                // Clean up orphaned review comments after edits
24227                self.cleanup_orphaned_review_comments(cx);
24228
24229                if let Some(buffer) = edited_buffer {
24230                    if buffer.read(cx).file().is_none() {
24231                        cx.emit(EditorEvent::TitleChanged);
24232                    }
24233
24234                    if self.project.is_some() {
24235                        let buffer_id = buffer.read(cx).remote_id();
24236                        self.register_buffer(buffer_id, cx);
24237                        self.update_lsp_data(Some(buffer_id), window, cx);
24238                        self.refresh_inlay_hints(
24239                            InlayHintRefreshReason::BufferEdited(buffer_id),
24240                            cx,
24241                        );
24242                    }
24243                }
24244
24245                cx.emit(EditorEvent::BufferEdited);
24246                cx.emit(SearchEvent::MatchesInvalidated);
24247
24248                let Some(project) = &self.project else { return };
24249                let (telemetry, is_via_ssh) = {
24250                    let project = project.read(cx);
24251                    let telemetry = project.client().telemetry().clone();
24252                    let is_via_ssh = project.is_via_remote_server();
24253                    (telemetry, is_via_ssh)
24254                };
24255                telemetry.log_edit_event("editor", is_via_ssh);
24256            }
24257            multi_buffer::Event::ExcerptsAdded {
24258                buffer,
24259                predecessor,
24260                excerpts,
24261            } => {
24262                let buffer_id = buffer.read(cx).remote_id();
24263                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24264                    && let Some(project) = &self.project
24265                {
24266                    update_uncommitted_diff_for_buffer(
24267                        cx.entity(),
24268                        project,
24269                        [buffer.clone()],
24270                        self.buffer.clone(),
24271                        cx,
24272                    )
24273                    .detach();
24274                }
24275                self.semantic_token_state
24276                    .invalidate_buffer(&buffer.read(cx).remote_id());
24277                self.update_lsp_data(Some(buffer_id), window, cx);
24278                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24279                self.refresh_runnables(None, window, cx);
24280                self.colorize_brackets(false, cx);
24281                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24282                cx.emit(EditorEvent::ExcerptsAdded {
24283                    buffer: buffer.clone(),
24284                    predecessor: *predecessor,
24285                    excerpts: excerpts.clone(),
24286                });
24287            }
24288            multi_buffer::Event::ExcerptsRemoved {
24289                ids,
24290                removed_buffer_ids,
24291            } => {
24292                if let Some(inlay_hints) = &mut self.inlay_hints {
24293                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24294                }
24295                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24296                for buffer_id in removed_buffer_ids {
24297                    self.registered_buffers.remove(buffer_id);
24298                    self.clear_runnables(Some(*buffer_id));
24299                    self.semantic_token_state.invalidate_buffer(buffer_id);
24300                    self.display_map.update(cx, |display_map, cx| {
24301                        display_map.invalidate_semantic_highlights(*buffer_id);
24302                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24303                    });
24304                }
24305
24306                self.display_map.update(cx, |display_map, cx| {
24307                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
24308                });
24309
24310                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24311                cx.emit(EditorEvent::ExcerptsRemoved {
24312                    ids: ids.clone(),
24313                    removed_buffer_ids: removed_buffer_ids.clone(),
24314                });
24315            }
24316            multi_buffer::Event::ExcerptsEdited {
24317                excerpt_ids,
24318                buffer_ids,
24319            } => {
24320                self.display_map.update(cx, |map, cx| {
24321                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24322                });
24323                cx.emit(EditorEvent::ExcerptsEdited {
24324                    ids: excerpt_ids.clone(),
24325                });
24326            }
24327            multi_buffer::Event::ExcerptsExpanded { ids } => {
24328                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24329                self.refresh_document_highlights(cx);
24330                let snapshot = multibuffer.read(cx).snapshot(cx);
24331                for id in ids {
24332                    self.bracket_fetched_tree_sitter_chunks.remove(id);
24333                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24334                        self.semantic_token_state
24335                            .invalidate_buffer(&buffer.remote_id());
24336                    }
24337                }
24338                self.colorize_brackets(false, cx);
24339                self.update_lsp_data(None, window, cx);
24340                self.refresh_runnables(None, window, cx);
24341                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24342            }
24343            multi_buffer::Event::Reparsed(buffer_id) => {
24344                self.refresh_runnables(Some(*buffer_id), window, cx);
24345                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24346                self.colorize_brackets(true, cx);
24347                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24348
24349                cx.emit(EditorEvent::Reparsed(*buffer_id));
24350            }
24351            multi_buffer::Event::DiffHunksToggled => {
24352                self.refresh_runnables(None, window, cx);
24353            }
24354            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24355                if !is_fresh_language {
24356                    self.registered_buffers.remove(&buffer_id);
24357                }
24358                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24359                cx.emit(EditorEvent::Reparsed(*buffer_id));
24360                self.update_edit_prediction_settings(cx);
24361                cx.notify();
24362            }
24363            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24364            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24365            multi_buffer::Event::FileHandleChanged
24366            | multi_buffer::Event::Reloaded
24367            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24368            multi_buffer::Event::DiagnosticsUpdated => {
24369                self.update_diagnostics_state(window, cx);
24370            }
24371            _ => {}
24372        };
24373    }
24374
24375    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24376        if !self.diagnostics_enabled() {
24377            return;
24378        }
24379        self.refresh_active_diagnostics(cx);
24380        self.refresh_inline_diagnostics(true, window, cx);
24381        self.scrollbar_marker_state.dirty = true;
24382        cx.notify();
24383    }
24384
24385    pub fn start_temporary_diff_override(&mut self) {
24386        self.load_diff_task.take();
24387        self.temporary_diff_override = true;
24388    }
24389
24390    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24391        self.temporary_diff_override = false;
24392        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24393        self.buffer.update(cx, |buffer, cx| {
24394            buffer.set_all_diff_hunks_collapsed(cx);
24395        });
24396
24397        if let Some(project) = self.project.clone() {
24398            self.load_diff_task = Some(
24399                update_uncommitted_diff_for_buffer(
24400                    cx.entity(),
24401                    &project,
24402                    self.buffer.read(cx).all_buffers(),
24403                    self.buffer.clone(),
24404                    cx,
24405                )
24406                .shared(),
24407            );
24408        }
24409    }
24410
24411    fn on_display_map_changed(
24412        &mut self,
24413        _: Entity<DisplayMap>,
24414        _: &mut Window,
24415        cx: &mut Context<Self>,
24416    ) {
24417        cx.notify();
24418    }
24419
24420    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24421        if !self.mode.is_full() {
24422            return None;
24423        }
24424
24425        let theme_settings = theme::ThemeSettings::get_global(cx);
24426        let theme = cx.theme();
24427        let accent_colors = theme.accents().clone();
24428
24429        let accent_overrides = theme_settings
24430            .theme_overrides
24431            .get(theme.name.as_ref())
24432            .map(|theme_style| &theme_style.accents)
24433            .into_iter()
24434            .flatten()
24435            .chain(
24436                theme_settings
24437                    .experimental_theme_overrides
24438                    .as_ref()
24439                    .map(|overrides| &overrides.accents)
24440                    .into_iter()
24441                    .flatten(),
24442            )
24443            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24444            .collect();
24445
24446        Some(AccentData {
24447            colors: accent_colors,
24448            overrides: accent_overrides,
24449        })
24450    }
24451
24452    fn fetch_applicable_language_settings(
24453        &self,
24454        cx: &App,
24455    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24456        if !self.mode.is_full() {
24457            return HashMap::default();
24458        }
24459
24460        self.buffer().read(cx).all_buffers().into_iter().fold(
24461            HashMap::default(),
24462            |mut acc, buffer| {
24463                let buffer = buffer.read(cx);
24464                let language = buffer.language().map(|language| language.name());
24465                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24466                    let file = buffer.file();
24467                    v.insert(language_settings(language, file, cx).into_owned());
24468                }
24469                acc
24470            },
24471        )
24472    }
24473
24474    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24475        let new_language_settings = self.fetch_applicable_language_settings(cx);
24476        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24477        self.applicable_language_settings = new_language_settings;
24478
24479        let new_accents = self.fetch_accent_data(cx);
24480        let accents_changed = new_accents != self.accent_data;
24481        self.accent_data = new_accents;
24482
24483        if self.diagnostics_enabled() {
24484            let new_severity = EditorSettings::get_global(cx)
24485                .diagnostics_max_severity
24486                .unwrap_or(DiagnosticSeverity::Hint);
24487            self.set_max_diagnostics_severity(new_severity, cx);
24488        }
24489        self.refresh_runnables(None, window, cx);
24490        self.update_edit_prediction_settings(cx);
24491        self.refresh_edit_prediction(true, false, window, cx);
24492        self.refresh_inline_values(cx);
24493
24494        let old_cursor_shape = self.cursor_shape;
24495        let old_show_breadcrumbs = self.show_breadcrumbs;
24496
24497        {
24498            let editor_settings = EditorSettings::get_global(cx);
24499            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24500            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24501            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24502            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24503        }
24504
24505        if old_cursor_shape != self.cursor_shape {
24506            cx.emit(EditorEvent::CursorShapeChanged);
24507        }
24508
24509        if old_show_breadcrumbs != self.show_breadcrumbs {
24510            cx.emit(EditorEvent::BreadcrumbsChanged);
24511        }
24512
24513        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24514            let project_settings = ProjectSettings::get_global(cx);
24515            (
24516                project_settings.session.restore_unsaved_buffers,
24517                project_settings.diagnostics.inline.enabled,
24518                project_settings.git.inline_blame.enabled,
24519            )
24520        };
24521        self.buffer_serialization = self
24522            .should_serialize_buffer()
24523            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24524
24525        if self.mode.is_full() {
24526            if self.show_inline_diagnostics != show_inline_diagnostics {
24527                self.show_inline_diagnostics = show_inline_diagnostics;
24528                self.refresh_inline_diagnostics(false, window, cx);
24529            }
24530
24531            if self.git_blame_inline_enabled != inline_blame_enabled {
24532                self.toggle_git_blame_inline_internal(false, window, cx);
24533            }
24534
24535            let minimap_settings = EditorSettings::get_global(cx).minimap;
24536            if self.minimap_visibility != MinimapVisibility::Disabled {
24537                if self.minimap_visibility.settings_visibility()
24538                    != minimap_settings.minimap_enabled()
24539                {
24540                    self.set_minimap_visibility(
24541                        MinimapVisibility::for_mode(self.mode(), cx),
24542                        window,
24543                        cx,
24544                    );
24545                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24546                    minimap_entity.update(cx, |minimap_editor, cx| {
24547                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24548                    })
24549                }
24550            }
24551
24552            if language_settings_changed || accents_changed {
24553                self.colorize_brackets(true, cx);
24554            }
24555
24556            if language_settings_changed {
24557                self.clear_disabled_lsp_folding_ranges(window, cx);
24558                self.refresh_document_symbols(None, cx);
24559            }
24560
24561            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24562                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24563            }) {
24564                if !inlay_splice.is_empty() {
24565                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24566                }
24567                self.refresh_document_colors(None, window, cx);
24568            }
24569
24570            self.refresh_inlay_hints(
24571                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24572                    self.selections.newest_anchor().head(),
24573                    &self.buffer.read(cx).snapshot(cx),
24574                    cx,
24575                )),
24576                cx,
24577            );
24578
24579            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24580                .global_lsp_settings
24581                .semantic_token_rules
24582                .clone();
24583            let semantic_token_rules_changed = self
24584                .semantic_token_state
24585                .update_rules(new_semantic_token_rules);
24586            if language_settings_changed || semantic_token_rules_changed {
24587                self.invalidate_semantic_tokens(None);
24588                self.refresh_semantic_tokens(None, None, cx);
24589            }
24590        }
24591
24592        cx.notify();
24593    }
24594
24595    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24596        if !self.mode.is_full() {
24597            return;
24598        }
24599
24600        let new_accents = self.fetch_accent_data(cx);
24601        if new_accents != self.accent_data {
24602            self.accent_data = new_accents;
24603            self.colorize_brackets(true, cx);
24604        }
24605
24606        self.invalidate_semantic_tokens(None);
24607        self.refresh_semantic_tokens(None, None, cx);
24608    }
24609
24610    pub fn set_searchable(&mut self, searchable: bool) {
24611        self.searchable = searchable;
24612    }
24613
24614    pub fn searchable(&self) -> bool {
24615        self.searchable
24616    }
24617
24618    pub fn open_excerpts_in_split(
24619        &mut self,
24620        _: &OpenExcerptsSplit,
24621        window: &mut Window,
24622        cx: &mut Context<Self>,
24623    ) {
24624        self.open_excerpts_common(None, true, window, cx)
24625    }
24626
24627    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24628        self.open_excerpts_common(None, false, window, cx)
24629    }
24630
24631    pub(crate) fn open_excerpts_common(
24632        &mut self,
24633        jump_data: Option<JumpData>,
24634        split: bool,
24635        window: &mut Window,
24636        cx: &mut Context<Self>,
24637    ) {
24638        if self.buffer.read(cx).is_singleton() {
24639            cx.propagate();
24640            return;
24641        }
24642
24643        let mut new_selections_by_buffer = HashMap::default();
24644        match &jump_data {
24645            Some(JumpData::MultiBufferPoint {
24646                excerpt_id,
24647                position,
24648                anchor,
24649                line_offset_from_top,
24650            }) => {
24651                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24652                if let Some(buffer) = multi_buffer_snapshot
24653                    .buffer_id_for_excerpt(*excerpt_id)
24654                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24655                {
24656                    let buffer_snapshot = buffer.read(cx).snapshot();
24657                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24658                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24659                    } else {
24660                        buffer_snapshot.clip_point(*position, Bias::Left)
24661                    };
24662                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24663                    new_selections_by_buffer.insert(
24664                        buffer,
24665                        (
24666                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24667                            Some(*line_offset_from_top),
24668                        ),
24669                    );
24670                }
24671            }
24672            Some(JumpData::MultiBufferRow {
24673                row,
24674                line_offset_from_top,
24675            }) => {
24676                let point = MultiBufferPoint::new(row.0, 0);
24677                if let Some((buffer, buffer_point, _)) =
24678                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24679                {
24680                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24681                    new_selections_by_buffer
24682                        .entry(buffer)
24683                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24684                        .0
24685                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24686                }
24687            }
24688            None => {
24689                let selections = self
24690                    .selections
24691                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24692                let multi_buffer = self.buffer.read(cx);
24693                for selection in selections {
24694                    for (snapshot, range, _, anchor) in multi_buffer
24695                        .snapshot(cx)
24696                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24697                    {
24698                        if let Some(anchor) = anchor {
24699                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24700                            else {
24701                                continue;
24702                            };
24703                            let offset = text::ToOffset::to_offset(
24704                                &anchor.text_anchor,
24705                                &buffer_handle.read(cx).snapshot(),
24706                            );
24707                            let range = BufferOffset(offset)..BufferOffset(offset);
24708                            new_selections_by_buffer
24709                                .entry(buffer_handle)
24710                                .or_insert((Vec::new(), None))
24711                                .0
24712                                .push(range)
24713                        } else {
24714                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24715                            else {
24716                                continue;
24717                            };
24718                            new_selections_by_buffer
24719                                .entry(buffer_handle)
24720                                .or_insert((Vec::new(), None))
24721                                .0
24722                                .push(range)
24723                        }
24724                    }
24725                }
24726            }
24727        }
24728
24729        if self.delegate_open_excerpts {
24730            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24731                .into_iter()
24732                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24733                .collect();
24734            if !selections_by_buffer.is_empty() {
24735                cx.emit(EditorEvent::OpenExcerptsRequested {
24736                    selections_by_buffer,
24737                    split,
24738                });
24739            }
24740            return;
24741        }
24742
24743        let Some(workspace) = self.workspace() else {
24744            cx.propagate();
24745            return;
24746        };
24747
24748        new_selections_by_buffer
24749            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24750
24751        if new_selections_by_buffer.is_empty() {
24752            return;
24753        }
24754
24755        Self::open_buffers_in_workspace(
24756            workspace.downgrade(),
24757            new_selections_by_buffer,
24758            split,
24759            window,
24760            cx,
24761        );
24762    }
24763
24764    pub(crate) fn open_buffers_in_workspace(
24765        workspace: WeakEntity<Workspace>,
24766        new_selections_by_buffer: HashMap<
24767            Entity<language::Buffer>,
24768            (Vec<Range<BufferOffset>>, Option<u32>),
24769        >,
24770        split: bool,
24771        window: &mut Window,
24772        cx: &mut App,
24773    ) {
24774        // We defer the pane interaction because we ourselves are a workspace item
24775        // and activating a new item causes the pane to call a method on us reentrantly,
24776        // which panics if we're on the stack.
24777        window.defer(cx, move |window, cx| {
24778            workspace
24779                .update(cx, |workspace, cx| {
24780                    let pane = if split {
24781                        workspace.adjacent_pane(window, cx)
24782                    } else {
24783                        workspace.active_pane().clone()
24784                    };
24785
24786                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24787                        let buffer_read = buffer.read(cx);
24788                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24789                            (true, project::File::from_dyn(Some(file)).is_some())
24790                        } else {
24791                            (false, false)
24792                        };
24793
24794                        // If project file is none workspace.open_project_item will fail to open the excerpt
24795                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24796                        // so we check if there's a tab match in that case first
24797                        let editor = (!has_file || !is_project_file)
24798                            .then(|| {
24799                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24800                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24801                                // Instead, we try to activate the existing editor in the pane first.
24802                                let (editor, pane_item_index, pane_item_id) =
24803                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24804                                        let editor = item.downcast::<Editor>()?;
24805                                        let singleton_buffer =
24806                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24807                                        if singleton_buffer == buffer {
24808                                            Some((editor, i, item.item_id()))
24809                                        } else {
24810                                            None
24811                                        }
24812                                    })?;
24813                                pane.update(cx, |pane, cx| {
24814                                    pane.activate_item(pane_item_index, true, true, window, cx);
24815                                    if !PreviewTabsSettings::get_global(cx)
24816                                        .enable_preview_from_multibuffer
24817                                    {
24818                                        pane.unpreview_item_if_preview(pane_item_id);
24819                                    }
24820                                });
24821                                Some(editor)
24822                            })
24823                            .flatten()
24824                            .unwrap_or_else(|| {
24825                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24826                                    .enable_keep_preview_on_code_navigation;
24827                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24828                                    .enable_preview_from_multibuffer;
24829                                workspace.open_project_item::<Self>(
24830                                    pane.clone(),
24831                                    buffer,
24832                                    true,
24833                                    true,
24834                                    keep_old_preview,
24835                                    allow_new_preview,
24836                                    window,
24837                                    cx,
24838                                )
24839                            });
24840
24841                        editor.update(cx, |editor, cx| {
24842                            if has_file && !is_project_file {
24843                                editor.set_read_only(true);
24844                            }
24845                            let autoscroll = match scroll_offset {
24846                                Some(scroll_offset) => {
24847                                    Autoscroll::top_relative(scroll_offset as usize)
24848                                }
24849                                None => Autoscroll::newest(),
24850                            };
24851                            let nav_history = editor.nav_history.take();
24852                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24853                            let Some((excerpt_id, _, buffer_snapshot)) =
24854                                multibuffer_snapshot.as_singleton()
24855                            else {
24856                                return;
24857                            };
24858                            editor.change_selections(
24859                                SelectionEffects::scroll(autoscroll),
24860                                window,
24861                                cx,
24862                                |s| {
24863                                    s.select_ranges(ranges.into_iter().map(|range| {
24864                                        let range = buffer_snapshot.anchor_before(range.start)
24865                                            ..buffer_snapshot.anchor_after(range.end);
24866                                        multibuffer_snapshot
24867                                            .anchor_range_in_excerpt(excerpt_id, range)
24868                                            .unwrap()
24869                                    }));
24870                                },
24871                            );
24872                            editor.nav_history = nav_history;
24873                        });
24874                    }
24875                })
24876                .ok();
24877        });
24878    }
24879
24880    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24881        let snapshot = self.buffer.read(cx).read(cx);
24882        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24883        Some(
24884            ranges
24885                .iter()
24886                .map(move |range| {
24887                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24888                })
24889                .collect(),
24890        )
24891    }
24892
24893    fn selection_replacement_ranges(
24894        &self,
24895        range: Range<MultiBufferOffsetUtf16>,
24896        cx: &mut App,
24897    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24898        let selections = self
24899            .selections
24900            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24901        let newest_selection = selections
24902            .iter()
24903            .max_by_key(|selection| selection.id)
24904            .unwrap();
24905        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24906        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24907        let snapshot = self.buffer.read(cx).read(cx);
24908        selections
24909            .into_iter()
24910            .map(|mut selection| {
24911                selection.start.0.0 =
24912                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24913                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24914                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24915                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24916            })
24917            .collect()
24918    }
24919
24920    fn report_editor_event(
24921        &self,
24922        reported_event: ReportEditorEvent,
24923        file_extension: Option<String>,
24924        cx: &App,
24925    ) {
24926        if cfg!(any(test, feature = "test-support")) {
24927            return;
24928        }
24929
24930        let Some(project) = &self.project else { return };
24931
24932        // If None, we are in a file without an extension
24933        let file = self
24934            .buffer
24935            .read(cx)
24936            .as_singleton()
24937            .and_then(|b| b.read(cx).file());
24938        let file_extension = file_extension.or(file
24939            .as_ref()
24940            .and_then(|file| Path::new(file.file_name(cx)).extension())
24941            .and_then(|e| e.to_str())
24942            .map(|a| a.to_string()));
24943
24944        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24945            .map(|vim_mode| vim_mode.0)
24946            .unwrap_or(false);
24947
24948        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24949        let copilot_enabled = edit_predictions_provider
24950            == language::language_settings::EditPredictionProvider::Copilot;
24951        let copilot_enabled_for_language = self
24952            .buffer
24953            .read(cx)
24954            .language_settings(cx)
24955            .show_edit_predictions;
24956
24957        let project = project.read(cx);
24958        let event_type = reported_event.event_type();
24959
24960        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24961            telemetry::event!(
24962                event_type,
24963                type = if auto_saved {"autosave"} else {"manual"},
24964                file_extension,
24965                vim_mode,
24966                copilot_enabled,
24967                copilot_enabled_for_language,
24968                edit_predictions_provider,
24969                is_via_ssh = project.is_via_remote_server(),
24970            );
24971        } else {
24972            telemetry::event!(
24973                event_type,
24974                file_extension,
24975                vim_mode,
24976                copilot_enabled,
24977                copilot_enabled_for_language,
24978                edit_predictions_provider,
24979                is_via_ssh = project.is_via_remote_server(),
24980            );
24981        };
24982    }
24983
24984    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24985    /// with each line being an array of {text, highlight} objects.
24986    fn copy_highlight_json(
24987        &mut self,
24988        _: &CopyHighlightJson,
24989        window: &mut Window,
24990        cx: &mut Context<Self>,
24991    ) {
24992        #[derive(Serialize)]
24993        struct Chunk<'a> {
24994            text: String,
24995            highlight: Option<&'a str>,
24996        }
24997
24998        let snapshot = self.buffer.read(cx).snapshot(cx);
24999        let range = self
25000            .selected_text_range(false, window, cx)
25001            .and_then(|selection| {
25002                if selection.range.is_empty() {
25003                    None
25004                } else {
25005                    Some(
25006                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
25007                            selection.range.start,
25008                        )))
25009                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
25010                                selection.range.end,
25011                            ))),
25012                    )
25013                }
25014            })
25015            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
25016
25017        let chunks = snapshot.chunks(range, true);
25018        let mut lines = Vec::new();
25019        let mut line: VecDeque<Chunk> = VecDeque::new();
25020
25021        let Some(style) = self.style.as_ref() else {
25022            return;
25023        };
25024
25025        for chunk in chunks {
25026            let highlight = chunk
25027                .syntax_highlight_id
25028                .and_then(|id| id.name(&style.syntax));
25029            let mut chunk_lines = chunk.text.split('\n').peekable();
25030            while let Some(text) = chunk_lines.next() {
25031                let mut merged_with_last_token = false;
25032                if let Some(last_token) = line.back_mut()
25033                    && last_token.highlight == highlight
25034                {
25035                    last_token.text.push_str(text);
25036                    merged_with_last_token = true;
25037                }
25038
25039                if !merged_with_last_token {
25040                    line.push_back(Chunk {
25041                        text: text.into(),
25042                        highlight,
25043                    });
25044                }
25045
25046                if chunk_lines.peek().is_some() {
25047                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
25048                        line.pop_front();
25049                    }
25050                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
25051                        line.pop_back();
25052                    }
25053
25054                    lines.push(mem::take(&mut line));
25055                }
25056            }
25057        }
25058
25059        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
25060            return;
25061        };
25062        cx.write_to_clipboard(ClipboardItem::new_string(lines));
25063    }
25064
25065    pub fn open_context_menu(
25066        &mut self,
25067        _: &OpenContextMenu,
25068        window: &mut Window,
25069        cx: &mut Context<Self>,
25070    ) {
25071        self.request_autoscroll(Autoscroll::newest(), cx);
25072        let position = self
25073            .selections
25074            .newest_display(&self.display_snapshot(cx))
25075            .start;
25076        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
25077    }
25078
25079    pub fn replay_insert_event(
25080        &mut self,
25081        text: &str,
25082        relative_utf16_range: Option<Range<isize>>,
25083        window: &mut Window,
25084        cx: &mut Context<Self>,
25085    ) {
25086        if !self.input_enabled {
25087            cx.emit(EditorEvent::InputIgnored { text: text.into() });
25088            return;
25089        }
25090        if let Some(relative_utf16_range) = relative_utf16_range {
25091            let selections = self
25092                .selections
25093                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
25094            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25095                let new_ranges = selections.into_iter().map(|range| {
25096                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
25097                        range
25098                            .head()
25099                            .0
25100                            .0
25101                            .saturating_add_signed(relative_utf16_range.start),
25102                    ));
25103                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
25104                        range
25105                            .head()
25106                            .0
25107                            .0
25108                            .saturating_add_signed(relative_utf16_range.end),
25109                    ));
25110                    start..end
25111                });
25112                s.select_ranges(new_ranges);
25113            });
25114        }
25115
25116        self.handle_input(text, window, cx);
25117    }
25118
25119    pub fn is_focused(&self, window: &Window) -> bool {
25120        self.focus_handle.is_focused(window)
25121    }
25122
25123    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25124        cx.emit(EditorEvent::Focused);
25125
25126        if let Some(descendant) = self
25127            .last_focused_descendant
25128            .take()
25129            .and_then(|descendant| descendant.upgrade())
25130        {
25131            window.focus(&descendant, cx);
25132        } else {
25133            if let Some(blame) = self.blame.as_ref() {
25134                blame.update(cx, GitBlame::focus)
25135            }
25136
25137            self.blink_manager.update(cx, BlinkManager::enable);
25138            self.show_cursor_names(window, cx);
25139            self.buffer.update(cx, |buffer, cx| {
25140                buffer.finalize_last_transaction(cx);
25141                if self.leader_id.is_none() {
25142                    buffer.set_active_selections(
25143                        &self.selections.disjoint_anchors_arc(),
25144                        self.selections.line_mode(),
25145                        self.cursor_shape,
25146                        cx,
25147                    );
25148                }
25149            });
25150
25151            if let Some(position_map) = self.last_position_map.clone()
25152                && !self.mouse_cursor_hidden
25153            {
25154                EditorElement::mouse_moved(
25155                    self,
25156                    &MouseMoveEvent {
25157                        position: window.mouse_position(),
25158                        pressed_button: None,
25159                        modifiers: window.modifiers(),
25160                    },
25161                    &position_map,
25162                    None,
25163                    window,
25164                    cx,
25165                );
25166            }
25167        }
25168    }
25169
25170    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
25171        cx.emit(EditorEvent::FocusedIn)
25172    }
25173
25174    fn handle_focus_out(
25175        &mut self,
25176        event: FocusOutEvent,
25177        _window: &mut Window,
25178        cx: &mut Context<Self>,
25179    ) {
25180        if event.blurred != self.focus_handle {
25181            self.last_focused_descendant = Some(event.blurred);
25182        }
25183        self.selection_drag_state = SelectionDragState::None;
25184        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
25185    }
25186
25187    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25188        self.blink_manager.update(cx, BlinkManager::disable);
25189        self.buffer
25190            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25191
25192        if let Some(blame) = self.blame.as_ref() {
25193            blame.update(cx, GitBlame::blur)
25194        }
25195        if !self.hover_state.focused(window, cx) {
25196            hide_hover(self, cx);
25197        }
25198        if !self
25199            .context_menu
25200            .borrow()
25201            .as_ref()
25202            .is_some_and(|context_menu| context_menu.focused(window, cx))
25203        {
25204            self.hide_context_menu(window, cx);
25205        }
25206        self.take_active_edit_prediction(cx);
25207        cx.emit(EditorEvent::Blurred);
25208        cx.notify();
25209    }
25210
25211    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25212        let mut pending: String = window
25213            .pending_input_keystrokes()
25214            .into_iter()
25215            .flatten()
25216            .filter_map(|keystroke| keystroke.key_char.clone())
25217            .collect();
25218
25219        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25220            pending = "".to_string();
25221        }
25222
25223        let existing_pending = self
25224            .text_highlights(HighlightKey::PendingInput, cx)
25225            .map(|(_, ranges)| ranges.to_vec());
25226        if existing_pending.is_none() && pending.is_empty() {
25227            return;
25228        }
25229        let transaction =
25230            self.transact(window, cx, |this, window, cx| {
25231                let selections = this
25232                    .selections
25233                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25234                let edits = selections
25235                    .iter()
25236                    .map(|selection| (selection.end..selection.end, pending.clone()));
25237                this.edit(edits, cx);
25238                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25239                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25240                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25241                    }));
25242                });
25243                if let Some(existing_ranges) = existing_pending {
25244                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25245                    this.edit(edits, cx);
25246                }
25247            });
25248
25249        let snapshot = self.snapshot(window, cx);
25250        let ranges = self
25251            .selections
25252            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25253            .into_iter()
25254            .map(|selection| {
25255                snapshot.buffer_snapshot().anchor_after(selection.end)
25256                    ..snapshot
25257                        .buffer_snapshot()
25258                        .anchor_before(selection.end + pending.len())
25259            })
25260            .collect();
25261
25262        if pending.is_empty() {
25263            self.clear_highlights(HighlightKey::PendingInput, cx);
25264        } else {
25265            self.highlight_text(
25266                HighlightKey::PendingInput,
25267                ranges,
25268                HighlightStyle {
25269                    underline: Some(UnderlineStyle {
25270                        thickness: px(1.),
25271                        color: None,
25272                        wavy: false,
25273                    }),
25274                    ..Default::default()
25275                },
25276                cx,
25277            );
25278        }
25279
25280        self.ime_transaction = self.ime_transaction.or(transaction);
25281        if let Some(transaction) = self.ime_transaction {
25282            self.buffer.update(cx, |buffer, cx| {
25283                buffer.group_until_transaction(transaction, cx);
25284            });
25285        }
25286
25287        if self
25288            .text_highlights(HighlightKey::PendingInput, cx)
25289            .is_none()
25290        {
25291            self.ime_transaction.take();
25292        }
25293    }
25294
25295    pub fn register_action_renderer(
25296        &mut self,
25297        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25298    ) -> Subscription {
25299        let id = self.next_editor_action_id.post_inc();
25300        self.editor_actions
25301            .borrow_mut()
25302            .insert(id, Box::new(listener));
25303
25304        let editor_actions = self.editor_actions.clone();
25305        Subscription::new(move || {
25306            editor_actions.borrow_mut().remove(&id);
25307        })
25308    }
25309
25310    pub fn register_action<A: Action>(
25311        &mut self,
25312        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25313    ) -> Subscription {
25314        let id = self.next_editor_action_id.post_inc();
25315        let listener = Arc::new(listener);
25316        self.editor_actions.borrow_mut().insert(
25317            id,
25318            Box::new(move |_, window, _| {
25319                let listener = listener.clone();
25320                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25321                    let action = action.downcast_ref().unwrap();
25322                    if phase == DispatchPhase::Bubble {
25323                        listener(action, window, cx)
25324                    }
25325                })
25326            }),
25327        );
25328
25329        let editor_actions = self.editor_actions.clone();
25330        Subscription::new(move || {
25331            editor_actions.borrow_mut().remove(&id);
25332        })
25333    }
25334
25335    pub fn file_header_size(&self) -> u32 {
25336        FILE_HEADER_HEIGHT
25337    }
25338
25339    pub fn restore(
25340        &mut self,
25341        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25342        window: &mut Window,
25343        cx: &mut Context<Self>,
25344    ) {
25345        self.buffer().update(cx, |multi_buffer, cx| {
25346            for (buffer_id, changes) in revert_changes {
25347                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25348                    buffer.update(cx, |buffer, cx| {
25349                        buffer.edit(
25350                            changes
25351                                .into_iter()
25352                                .map(|(range, text)| (range, text.to_string())),
25353                            None,
25354                            cx,
25355                        );
25356                    });
25357                }
25358            }
25359        });
25360        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25361            selections.refresh()
25362        });
25363    }
25364
25365    pub fn to_pixel_point(
25366        &mut self,
25367        source: Anchor,
25368        editor_snapshot: &EditorSnapshot,
25369        window: &mut Window,
25370        cx: &mut App,
25371    ) -> Option<gpui::Point<Pixels>> {
25372        let source_point = source.to_display_point(editor_snapshot);
25373        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25374    }
25375
25376    pub fn display_to_pixel_point(
25377        &mut self,
25378        source: DisplayPoint,
25379        editor_snapshot: &EditorSnapshot,
25380        window: &mut Window,
25381        cx: &mut App,
25382    ) -> Option<gpui::Point<Pixels>> {
25383        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25384        let text_layout_details = self.text_layout_details(window, cx);
25385        let scroll_top = text_layout_details
25386            .scroll_anchor
25387            .scroll_position(editor_snapshot)
25388            .y;
25389
25390        if source.row().as_f64() < scroll_top.floor() {
25391            return None;
25392        }
25393        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25394        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25395        Some(gpui::Point::new(source_x, source_y))
25396    }
25397
25398    pub fn has_visible_completions_menu(&self) -> bool {
25399        !self.edit_prediction_preview_is_active()
25400            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25401                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25402            })
25403    }
25404
25405    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25406        if self.mode.is_minimap() {
25407            return;
25408        }
25409        self.addons
25410            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25411    }
25412
25413    pub fn unregister_addon<T: Addon>(&mut self) {
25414        self.addons.remove(&std::any::TypeId::of::<T>());
25415    }
25416
25417    pub fn addon<T: Addon>(&self) -> Option<&T> {
25418        let type_id = std::any::TypeId::of::<T>();
25419        self.addons
25420            .get(&type_id)
25421            .and_then(|item| item.to_any().downcast_ref::<T>())
25422    }
25423
25424    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25425        let type_id = std::any::TypeId::of::<T>();
25426        self.addons
25427            .get_mut(&type_id)
25428            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25429    }
25430
25431    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25432        let text_layout_details = self.text_layout_details(window, cx);
25433        let style = &text_layout_details.editor_style;
25434        let font_id = window.text_system().resolve_font(&style.text.font());
25435        let font_size = style.text.font_size.to_pixels(window.rem_size());
25436        let line_height = style.text.line_height_in_pixels(window.rem_size());
25437        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25438        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25439
25440        CharacterDimensions {
25441            em_width,
25442            em_advance,
25443            line_height,
25444        }
25445    }
25446
25447    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25448        self.load_diff_task.clone()
25449    }
25450
25451    fn read_metadata_from_db(
25452        &mut self,
25453        item_id: u64,
25454        workspace_id: WorkspaceId,
25455        window: &mut Window,
25456        cx: &mut Context<Editor>,
25457    ) {
25458        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25459            && !self.mode.is_minimap()
25460            && WorkspaceSettings::get(None, cx).restore_on_startup
25461                != RestoreOnStartupBehavior::EmptyTab
25462        {
25463            let buffer_snapshot = OnceCell::new();
25464
25465            // Get file path for path-based fold lookup
25466            let file_path: Option<Arc<Path>> =
25467                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25468                    project::File::from_dyn(buffer.read(cx).file())
25469                        .map(|file| Arc::from(file.abs_path(cx)))
25470                });
25471
25472            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25473            let db = EditorDb::global(cx);
25474            let (folds, needs_migration) = if let Some(ref path) = file_path {
25475                if let Some(folds) = db.get_file_folds(workspace_id, path).log_err()
25476                    && !folds.is_empty()
25477                {
25478                    (Some(folds), false)
25479                } else if let Some(folds) = db.get_editor_folds(item_id, workspace_id).log_err()
25480                    && !folds.is_empty()
25481                {
25482                    // Found old editor_folds data, will migrate to file_folds
25483                    (Some(folds), true)
25484                } else {
25485                    (None, false)
25486                }
25487            } else {
25488                // No file path, try editor_folds as fallback
25489                let folds = db.get_editor_folds(item_id, workspace_id).log_err();
25490                (folds.filter(|f| !f.is_empty()), false)
25491            };
25492
25493            if let Some(folds) = folds {
25494                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25495                let snapshot_len = snapshot.len().0;
25496
25497                // Helper: search for fingerprint in buffer, return offset if found
25498                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25499                    // Ensure we start at a character boundary (defensive)
25500                    let search_start = snapshot
25501                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25502                        .0;
25503                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25504
25505                    let mut byte_offset = search_start;
25506                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25507                        if byte_offset > search_end {
25508                            break;
25509                        }
25510                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25511                            return Some(byte_offset);
25512                        }
25513                        byte_offset += ch.len_utf8();
25514                    }
25515                    None
25516                };
25517
25518                // Track search position to handle duplicate fingerprints correctly.
25519                // Folds are stored in document order, so we advance after each match.
25520                let mut search_start = 0usize;
25521
25522                // Collect db_folds for migration (only folds with valid fingerprints)
25523                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25524
25525                let valid_folds: Vec<_> = folds
25526                    .into_iter()
25527                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25528                        // Skip folds without fingerprints (old data before migration)
25529                        let sfp = start_fp?;
25530                        let efp = end_fp?;
25531                        let efp_len = efp.len();
25532
25533                        // Fast path: check if fingerprints match at stored offsets
25534                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25535                        let start_matches = stored_start < snapshot_len
25536                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25537                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25538                        let end_matches = efp_check_pos >= stored_start
25539                            && stored_end <= snapshot_len
25540                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25541
25542                        let (new_start, new_end) = if start_matches && end_matches {
25543                            // Offsets unchanged, use stored values
25544                            (stored_start, stored_end)
25545                        } else if sfp == efp {
25546                            // Short fold: identical fingerprints can only match once per search
25547                            // Use stored fold length to compute new_end
25548                            let new_start = find_fingerprint(&sfp, search_start)?;
25549                            let fold_len = stored_end - stored_start;
25550                            let new_end = new_start + fold_len;
25551                            (new_start, new_end)
25552                        } else {
25553                            // Slow path: search for fingerprints in buffer
25554                            let new_start = find_fingerprint(&sfp, search_start)?;
25555                            // Search for end_fp after start, then add efp_len to get actual fold end
25556                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25557                            let new_end = efp_pos + efp_len;
25558                            (new_start, new_end)
25559                        };
25560
25561                        // Advance search position for next fold
25562                        search_start = new_end;
25563
25564                        // Validate fold makes sense (end must be after start)
25565                        if new_end <= new_start {
25566                            return None;
25567                        }
25568
25569                        // Collect for migration if needed
25570                        if needs_migration {
25571                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25572                        }
25573
25574                        Some(
25575                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25576                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25577                        )
25578                    })
25579                    .collect();
25580
25581                if !valid_folds.is_empty() {
25582                    self.fold_ranges(valid_folds, false, window, cx);
25583
25584                    // Migrate from editor_folds to file_folds if we loaded from old table
25585                    if needs_migration {
25586                        if let Some(ref path) = file_path {
25587                            let path = path.clone();
25588                            let db = EditorDb::global(cx);
25589                            cx.spawn(async move |_, _| {
25590                                db.save_file_folds(workspace_id, path, db_folds_for_migration)
25591                                    .await
25592                                    .log_err();
25593                            })
25594                            .detach();
25595                        }
25596                    }
25597                }
25598            }
25599
25600            if let Some(selections) = db.get_editor_selections(item_id, workspace_id).log_err()
25601                && !selections.is_empty()
25602            {
25603                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25604                // skip adding the initial selection to selection history
25605                self.selection_history.mode = SelectionHistoryMode::Skipping;
25606                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25607                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25608                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25609                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25610                    }));
25611                });
25612                self.selection_history.mode = SelectionHistoryMode::Normal;
25613            };
25614        }
25615
25616        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25617    }
25618
25619    /// Load folds from the file_folds database table by file path.
25620    /// Used when manually opening a file that was previously closed.
25621    fn load_folds_from_db(
25622        &mut self,
25623        workspace_id: WorkspaceId,
25624        file_path: PathBuf,
25625        window: &mut Window,
25626        cx: &mut Context<Editor>,
25627    ) {
25628        if self.mode.is_minimap()
25629            || WorkspaceSettings::get(None, cx).restore_on_startup
25630                == RestoreOnStartupBehavior::EmptyTab
25631        {
25632            return;
25633        }
25634
25635        let Some(folds) = EditorDb::global(cx)
25636            .get_file_folds(workspace_id, &file_path)
25637            .log_err()
25638        else {
25639            return;
25640        };
25641        if folds.is_empty() {
25642            return;
25643        }
25644
25645        let snapshot = self.buffer.read(cx).snapshot(cx);
25646        let snapshot_len = snapshot.len().0;
25647
25648        // Helper: search for fingerprint in buffer, return offset if found
25649        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25650            let search_start = snapshot
25651                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25652                .0;
25653            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25654
25655            let mut byte_offset = search_start;
25656            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25657                if byte_offset > search_end {
25658                    break;
25659                }
25660                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25661                    return Some(byte_offset);
25662                }
25663                byte_offset += ch.len_utf8();
25664            }
25665            None
25666        };
25667
25668        let mut search_start = 0usize;
25669
25670        let valid_folds: Vec<_> = folds
25671            .into_iter()
25672            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25673                let sfp = start_fp?;
25674                let efp = end_fp?;
25675                let efp_len = efp.len();
25676
25677                let start_matches = stored_start < snapshot_len
25678                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25679                let efp_check_pos = stored_end.saturating_sub(efp_len);
25680                let end_matches = efp_check_pos >= stored_start
25681                    && stored_end <= snapshot_len
25682                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25683
25684                let (new_start, new_end) = if start_matches && end_matches {
25685                    (stored_start, stored_end)
25686                } else if sfp == efp {
25687                    let new_start = find_fingerprint(&sfp, search_start)?;
25688                    let fold_len = stored_end - stored_start;
25689                    let new_end = new_start + fold_len;
25690                    (new_start, new_end)
25691                } else {
25692                    let new_start = find_fingerprint(&sfp, search_start)?;
25693                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25694                    let new_end = efp_pos + efp_len;
25695                    (new_start, new_end)
25696                };
25697
25698                search_start = new_end;
25699
25700                if new_end <= new_start {
25701                    return None;
25702                }
25703
25704                Some(
25705                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25706                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25707                )
25708            })
25709            .collect();
25710
25711        if !valid_folds.is_empty() {
25712            self.fold_ranges(valid_folds, false, window, cx);
25713        }
25714    }
25715
25716    fn lsp_data_enabled(&self) -> bool {
25717        self.enable_lsp_data && self.mode().is_full()
25718    }
25719
25720    fn update_lsp_data(
25721        &mut self,
25722        for_buffer: Option<BufferId>,
25723        window: &mut Window,
25724        cx: &mut Context<'_, Self>,
25725    ) {
25726        if !self.lsp_data_enabled() {
25727            return;
25728        }
25729
25730        if let Some(buffer_id) = for_buffer {
25731            self.pull_diagnostics(buffer_id, window, cx);
25732        }
25733        self.refresh_semantic_tokens(for_buffer, None, cx);
25734        self.refresh_document_colors(for_buffer, window, cx);
25735        self.refresh_folding_ranges(for_buffer, window, cx);
25736        self.refresh_document_symbols(for_buffer, cx);
25737    }
25738
25739    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25740        if !self.lsp_data_enabled() {
25741            return;
25742        }
25743        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25744            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25745        }
25746    }
25747
25748    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25749        if !self.lsp_data_enabled() {
25750            return;
25751        }
25752
25753        if !self.registered_buffers.contains_key(&buffer_id)
25754            && let Some(project) = self.project.as_ref()
25755        {
25756            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25757                project.update(cx, |project, cx| {
25758                    self.registered_buffers.insert(
25759                        buffer_id,
25760                        project.register_buffer_with_language_servers(&buffer, cx),
25761                    );
25762                });
25763            } else {
25764                self.registered_buffers.remove(&buffer_id);
25765            }
25766        }
25767    }
25768
25769    fn create_style(&self, cx: &App) -> EditorStyle {
25770        let settings = ThemeSettings::get_global(cx);
25771
25772        let mut text_style = match self.mode {
25773            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25774                color: cx.theme().colors().editor_foreground,
25775                font_family: settings.ui_font.family.clone(),
25776                font_features: settings.ui_font.features.clone(),
25777                font_fallbacks: settings.ui_font.fallbacks.clone(),
25778                font_size: rems(0.875).into(),
25779                font_weight: settings.ui_font.weight,
25780                line_height: relative(settings.buffer_line_height.value()),
25781                ..Default::default()
25782            },
25783            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25784                color: cx.theme().colors().editor_foreground,
25785                font_family: settings.buffer_font.family.clone(),
25786                font_features: settings.buffer_font.features.clone(),
25787                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25788                font_size: settings.buffer_font_size(cx).into(),
25789                font_weight: settings.buffer_font.weight,
25790                line_height: relative(settings.buffer_line_height.value()),
25791                ..Default::default()
25792            },
25793        };
25794        if let Some(text_style_refinement) = &self.text_style_refinement {
25795            text_style.refine(text_style_refinement)
25796        }
25797
25798        let background = match self.mode {
25799            EditorMode::SingleLine => cx.theme().system().transparent,
25800            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25801            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25802            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25803        };
25804
25805        EditorStyle {
25806            background,
25807            border: cx.theme().colors().border,
25808            local_player: cx.theme().players().local(),
25809            text: text_style,
25810            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25811            syntax: cx.theme().syntax().clone(),
25812            status: cx.theme().status().clone(),
25813            inlay_hints_style: make_inlay_hints_style(cx),
25814            edit_prediction_styles: make_suggestion_styles(cx),
25815            unnecessary_code_fade: settings.unnecessary_code_fade,
25816            show_underlines: self.diagnostics_enabled(),
25817        }
25818    }
25819
25820    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<HighlightedText>> {
25821        let multibuffer = self.buffer().read(cx);
25822        let is_singleton = multibuffer.is_singleton();
25823        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25824        let buffer = multibuffer.buffer(*buffer_id)?;
25825
25826        let buffer = buffer.read(cx);
25827        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25828        let mut breadcrumbs = if is_singleton {
25829            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25830                buffer
25831                    .snapshot()
25832                    .resolve_file_path(
25833                        self.project
25834                            .as_ref()
25835                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25836                            .unwrap_or_default(),
25837                        cx,
25838                    )
25839                    .unwrap_or_else(|| {
25840                        if multibuffer.is_singleton() {
25841                            multibuffer.title(cx).to_string()
25842                        } else {
25843                            "untitled".to_string()
25844                        }
25845                    })
25846            });
25847            vec![HighlightedText {
25848                text: text.into(),
25849                highlights: vec![],
25850            }]
25851        } else {
25852            vec![]
25853        };
25854
25855        breadcrumbs.extend(symbols.iter().map(|symbol| HighlightedText {
25856            text: symbol.text.clone().into(),
25857            highlights: symbol.highlight_ranges.clone(),
25858        }));
25859        Some(breadcrumbs)
25860    }
25861
25862    fn disable_lsp_data(&mut self) {
25863        self.enable_lsp_data = false;
25864    }
25865
25866    fn disable_runnables(&mut self) {
25867        self.enable_runnables = false;
25868    }
25869
25870    fn update_data_on_scroll(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) {
25871        self.register_visible_buffers(cx);
25872        self.colorize_brackets(false, cx);
25873        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
25874        if !self.buffer().read(cx).is_singleton() {
25875            self.update_lsp_data(None, window, cx);
25876            self.refresh_runnables(None, window, cx);
25877        }
25878    }
25879}
25880
25881fn edit_for_markdown_paste<'a>(
25882    buffer: &MultiBufferSnapshot,
25883    range: Range<MultiBufferOffset>,
25884    to_insert: &'a str,
25885    url: Option<url::Url>,
25886) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25887    if url.is_none() {
25888        return (range, Cow::Borrowed(to_insert));
25889    };
25890
25891    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25892
25893    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25894        Cow::Borrowed(to_insert)
25895    } else {
25896        Cow::Owned(format!("[{old_text}]({to_insert})"))
25897    };
25898    (range, new_text)
25899}
25900
25901fn process_completion_for_edit(
25902    completion: &Completion,
25903    intent: CompletionIntent,
25904    buffer: &Entity<Buffer>,
25905    cursor_position: &text::Anchor,
25906    cx: &mut Context<Editor>,
25907) -> CompletionEdit {
25908    let buffer = buffer.read(cx);
25909    let buffer_snapshot = buffer.snapshot();
25910    let (snippet, new_text) = if completion.is_snippet() {
25911        let mut snippet_source = completion.new_text.clone();
25912        // Workaround for typescript language server issues so that methods don't expand within
25913        // strings and functions with type expressions. The previous point is used because the query
25914        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25915        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25916        let previous_point = if previous_point.column > 0 {
25917            cursor_position.to_previous_offset(&buffer_snapshot)
25918        } else {
25919            cursor_position.to_offset(&buffer_snapshot)
25920        };
25921        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25922            && scope.prefers_label_for_snippet_in_completion()
25923            && let Some(label) = completion.label()
25924            && matches!(
25925                completion.kind(),
25926                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25927            )
25928        {
25929            snippet_source = label;
25930        }
25931        match Snippet::parse(&snippet_source).log_err() {
25932            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25933            None => (None, completion.new_text.clone()),
25934        }
25935    } else {
25936        (None, completion.new_text.clone())
25937    };
25938
25939    let mut range_to_replace = {
25940        let replace_range = &completion.replace_range;
25941        if let CompletionSource::Lsp {
25942            insert_range: Some(insert_range),
25943            ..
25944        } = &completion.source
25945        {
25946            debug_assert_eq!(
25947                insert_range.start, replace_range.start,
25948                "insert_range and replace_range should start at the same position"
25949            );
25950            debug_assert!(
25951                insert_range
25952                    .start
25953                    .cmp(cursor_position, &buffer_snapshot)
25954                    .is_le(),
25955                "insert_range should start before or at cursor position"
25956            );
25957            debug_assert!(
25958                replace_range
25959                    .start
25960                    .cmp(cursor_position, &buffer_snapshot)
25961                    .is_le(),
25962                "replace_range should start before or at cursor position"
25963            );
25964
25965            let should_replace = match intent {
25966                CompletionIntent::CompleteWithInsert => false,
25967                CompletionIntent::CompleteWithReplace => true,
25968                CompletionIntent::Complete | CompletionIntent::Compose => {
25969                    let insert_mode =
25970                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25971                            .completions
25972                            .lsp_insert_mode;
25973                    match insert_mode {
25974                        LspInsertMode::Insert => false,
25975                        LspInsertMode::Replace => true,
25976                        LspInsertMode::ReplaceSubsequence => {
25977                            let mut text_to_replace = buffer.chars_for_range(
25978                                buffer.anchor_before(replace_range.start)
25979                                    ..buffer.anchor_after(replace_range.end),
25980                            );
25981                            let mut current_needle = text_to_replace.next();
25982                            for haystack_ch in completion.label.text.chars() {
25983                                if let Some(needle_ch) = current_needle
25984                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25985                                {
25986                                    current_needle = text_to_replace.next();
25987                                }
25988                            }
25989                            current_needle.is_none()
25990                        }
25991                        LspInsertMode::ReplaceSuffix => {
25992                            if replace_range
25993                                .end
25994                                .cmp(cursor_position, &buffer_snapshot)
25995                                .is_gt()
25996                            {
25997                                let range_after_cursor = *cursor_position..replace_range.end;
25998                                let text_after_cursor = buffer
25999                                    .text_for_range(
26000                                        buffer.anchor_before(range_after_cursor.start)
26001                                            ..buffer.anchor_after(range_after_cursor.end),
26002                                    )
26003                                    .collect::<String>()
26004                                    .to_ascii_lowercase();
26005                                completion
26006                                    .label
26007                                    .text
26008                                    .to_ascii_lowercase()
26009                                    .ends_with(&text_after_cursor)
26010                            } else {
26011                                true
26012                            }
26013                        }
26014                    }
26015                }
26016            };
26017
26018            if should_replace {
26019                replace_range.clone()
26020            } else {
26021                insert_range.clone()
26022            }
26023        } else {
26024            replace_range.clone()
26025        }
26026    };
26027
26028    if range_to_replace
26029        .end
26030        .cmp(cursor_position, &buffer_snapshot)
26031        .is_lt()
26032    {
26033        range_to_replace.end = *cursor_position;
26034    }
26035
26036    let replace_range = range_to_replace.to_offset(buffer);
26037    CompletionEdit {
26038        new_text,
26039        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
26040        snippet,
26041    }
26042}
26043
26044struct CompletionEdit {
26045    new_text: String,
26046    replace_range: Range<BufferOffset>,
26047    snippet: Option<Snippet>,
26048}
26049
26050fn comment_delimiter_for_newline(
26051    start_point: &Point,
26052    buffer: &MultiBufferSnapshot,
26053    language: &LanguageScope,
26054) -> Option<Arc<str>> {
26055    let delimiters = language.line_comment_prefixes();
26056    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
26057    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26058
26059    let num_of_whitespaces = snapshot
26060        .chars_for_range(range.clone())
26061        .take_while(|c| c.is_whitespace())
26062        .count();
26063    let comment_candidate = snapshot
26064        .chars_for_range(range.clone())
26065        .skip(num_of_whitespaces)
26066        .take(max_len_of_delimiter + 2)
26067        .collect::<String>();
26068    let (delimiter, trimmed_len, is_repl) = delimiters
26069        .iter()
26070        .filter_map(|delimiter| {
26071            let prefix = delimiter.trim_end();
26072            if comment_candidate.starts_with(prefix) {
26073                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
26074                {
26075                    stripped_comment.starts_with(" %%")
26076                } else {
26077                    false
26078                };
26079                Some((delimiter, prefix.len(), is_repl))
26080            } else {
26081                None
26082            }
26083        })
26084        .max_by_key(|(_, len, _)| *len)?;
26085
26086    if let Some(BlockCommentConfig {
26087        start: block_start, ..
26088    }) = language.block_comment()
26089    {
26090        let block_start_trimmed = block_start.trim_end();
26091        if block_start_trimmed.starts_with(delimiter.trim_end()) {
26092            let line_content = snapshot
26093                .chars_for_range(range.clone())
26094                .skip(num_of_whitespaces)
26095                .take(block_start_trimmed.len())
26096                .collect::<String>();
26097
26098            if line_content.starts_with(block_start_trimmed) {
26099                return None;
26100            }
26101        }
26102    }
26103
26104    let cursor_is_placed_after_comment_marker =
26105        num_of_whitespaces + trimmed_len <= start_point.column as usize;
26106    if cursor_is_placed_after_comment_marker {
26107        if !is_repl {
26108            return Some(delimiter.clone());
26109        }
26110
26111        let line_content_after_cursor: String = snapshot
26112            .chars_for_range(range)
26113            .skip(start_point.column as usize)
26114            .collect();
26115
26116        if line_content_after_cursor.trim().is_empty() {
26117            return None;
26118        } else {
26119            return Some(delimiter.clone());
26120        }
26121    } else {
26122        None
26123    }
26124}
26125
26126fn documentation_delimiter_for_newline(
26127    start_point: &Point,
26128    buffer: &MultiBufferSnapshot,
26129    language: &LanguageScope,
26130    newline_config: &mut NewlineConfig,
26131) -> Option<Arc<str>> {
26132    let BlockCommentConfig {
26133        start: start_tag,
26134        end: end_tag,
26135        prefix: delimiter,
26136        tab_size: len,
26137    } = language.documentation_comment()?;
26138    let is_within_block_comment = buffer
26139        .language_scope_at(*start_point)
26140        .is_some_and(|scope| scope.override_name() == Some("comment"));
26141    if !is_within_block_comment {
26142        return None;
26143    }
26144
26145    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26146
26147    let num_of_whitespaces = snapshot
26148        .chars_for_range(range.clone())
26149        .take_while(|c| c.is_whitespace())
26150        .count();
26151
26152    // 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.
26153    let column = start_point.column;
26154    let cursor_is_after_start_tag = {
26155        let start_tag_len = start_tag.len();
26156        let start_tag_line = snapshot
26157            .chars_for_range(range.clone())
26158            .skip(num_of_whitespaces)
26159            .take(start_tag_len)
26160            .collect::<String>();
26161        if start_tag_line.starts_with(start_tag.as_ref()) {
26162            num_of_whitespaces + start_tag_len <= column as usize
26163        } else {
26164            false
26165        }
26166    };
26167
26168    let cursor_is_after_delimiter = {
26169        let delimiter_trim = delimiter.trim_end();
26170        let delimiter_line = snapshot
26171            .chars_for_range(range.clone())
26172            .skip(num_of_whitespaces)
26173            .take(delimiter_trim.len())
26174            .collect::<String>();
26175        if delimiter_line.starts_with(delimiter_trim) {
26176            num_of_whitespaces + delimiter_trim.len() <= column as usize
26177        } else {
26178            false
26179        }
26180    };
26181
26182    let mut needs_extra_line = false;
26183    let mut extra_line_additional_indent = IndentSize::spaces(0);
26184
26185    let cursor_is_before_end_tag_if_exists = {
26186        let mut char_position = 0u32;
26187        let mut end_tag_offset = None;
26188
26189        'outer: for chunk in snapshot.text_for_range(range) {
26190            if let Some(byte_pos) = chunk.find(&**end_tag) {
26191                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
26192                end_tag_offset = Some(char_position + chars_before_match);
26193                break 'outer;
26194            }
26195            char_position += chunk.chars().count() as u32;
26196        }
26197
26198        if let Some(end_tag_offset) = end_tag_offset {
26199            let cursor_is_before_end_tag = column <= end_tag_offset;
26200            if cursor_is_after_start_tag {
26201                if cursor_is_before_end_tag {
26202                    needs_extra_line = true;
26203                }
26204                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26205                if cursor_is_at_start_of_end_tag {
26206                    extra_line_additional_indent.len = *len;
26207                }
26208            }
26209            cursor_is_before_end_tag
26210        } else {
26211            true
26212        }
26213    };
26214
26215    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26216        && cursor_is_before_end_tag_if_exists
26217    {
26218        let additional_indent = if cursor_is_after_start_tag {
26219            IndentSize::spaces(*len)
26220        } else {
26221            IndentSize::spaces(0)
26222        };
26223
26224        *newline_config = NewlineConfig::Newline {
26225            additional_indent,
26226            extra_line_additional_indent: if needs_extra_line {
26227                Some(extra_line_additional_indent)
26228            } else {
26229                None
26230            },
26231            prevent_auto_indent: true,
26232        };
26233        Some(delimiter.clone())
26234    } else {
26235        None
26236    }
26237}
26238
26239const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26240
26241fn list_delimiter_for_newline(
26242    start_point: &Point,
26243    buffer: &MultiBufferSnapshot,
26244    language: &LanguageScope,
26245    newline_config: &mut NewlineConfig,
26246) -> Option<Arc<str>> {
26247    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26248
26249    let num_of_whitespaces = snapshot
26250        .chars_for_range(range.clone())
26251        .take_while(|c| c.is_whitespace())
26252        .count();
26253
26254    let task_list_entries: Vec<_> = language
26255        .task_list()
26256        .into_iter()
26257        .flat_map(|config| {
26258            config
26259                .prefixes
26260                .iter()
26261                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26262        })
26263        .collect();
26264    let unordered_list_entries: Vec<_> = language
26265        .unordered_list()
26266        .iter()
26267        .map(|marker| (marker.as_ref(), marker.as_ref()))
26268        .collect();
26269
26270    let all_entries: Vec<_> = task_list_entries
26271        .into_iter()
26272        .chain(unordered_list_entries)
26273        .collect();
26274
26275    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26276        let candidate: String = snapshot
26277            .chars_for_range(range.clone())
26278            .skip(num_of_whitespaces)
26279            .take(max_prefix_len)
26280            .collect();
26281
26282        if let Some((prefix, continuation)) = all_entries
26283            .iter()
26284            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26285            .max_by_key(|(prefix, _)| prefix.len())
26286        {
26287            let end_of_prefix = num_of_whitespaces + prefix.len();
26288            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26289            let has_content_after_marker = snapshot
26290                .chars_for_range(range)
26291                .skip(end_of_prefix)
26292                .any(|c| !c.is_whitespace());
26293
26294            if has_content_after_marker && cursor_is_after_prefix {
26295                return Some((*continuation).into());
26296            }
26297
26298            if start_point.column as usize == end_of_prefix {
26299                if num_of_whitespaces == 0 {
26300                    *newline_config = NewlineConfig::ClearCurrentLine;
26301                } else {
26302                    *newline_config = NewlineConfig::UnindentCurrentLine {
26303                        continuation: (*continuation).into(),
26304                    };
26305                }
26306            }
26307
26308            return None;
26309        }
26310    }
26311
26312    let candidate: String = snapshot
26313        .chars_for_range(range.clone())
26314        .skip(num_of_whitespaces)
26315        .take(ORDERED_LIST_MAX_MARKER_LEN)
26316        .collect();
26317
26318    for ordered_config in language.ordered_list() {
26319        let regex = match Regex::new(&ordered_config.pattern) {
26320            Ok(r) => r,
26321            Err(_) => continue,
26322        };
26323
26324        if let Some(captures) = regex.captures(&candidate) {
26325            let full_match = captures.get(0)?;
26326            let marker_len = full_match.len();
26327            let end_of_prefix = num_of_whitespaces + marker_len;
26328            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26329
26330            let has_content_after_marker = snapshot
26331                .chars_for_range(range)
26332                .skip(end_of_prefix)
26333                .any(|c| !c.is_whitespace());
26334
26335            if has_content_after_marker && cursor_is_after_prefix {
26336                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26337                let continuation = ordered_config
26338                    .format
26339                    .replace("{1}", &(number + 1).to_string());
26340                return Some(continuation.into());
26341            }
26342
26343            if start_point.column as usize == end_of_prefix {
26344                let continuation = ordered_config.format.replace("{1}", "1");
26345                if num_of_whitespaces == 0 {
26346                    *newline_config = NewlineConfig::ClearCurrentLine;
26347                } else {
26348                    *newline_config = NewlineConfig::UnindentCurrentLine {
26349                        continuation: continuation.into(),
26350                    };
26351                }
26352            }
26353
26354            return None;
26355        }
26356    }
26357
26358    None
26359}
26360
26361fn is_list_prefix_row(
26362    row: MultiBufferRow,
26363    buffer: &MultiBufferSnapshot,
26364    language: &LanguageScope,
26365) -> bool {
26366    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26367        return false;
26368    };
26369
26370    let num_of_whitespaces = snapshot
26371        .chars_for_range(range.clone())
26372        .take_while(|c| c.is_whitespace())
26373        .count();
26374
26375    let task_list_prefixes: Vec<_> = language
26376        .task_list()
26377        .into_iter()
26378        .flat_map(|config| {
26379            config
26380                .prefixes
26381                .iter()
26382                .map(|p| p.as_ref())
26383                .collect::<Vec<_>>()
26384        })
26385        .collect();
26386    let unordered_list_markers: Vec<_> = language
26387        .unordered_list()
26388        .iter()
26389        .map(|marker| marker.as_ref())
26390        .collect();
26391    let all_prefixes: Vec<_> = task_list_prefixes
26392        .into_iter()
26393        .chain(unordered_list_markers)
26394        .collect();
26395    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26396        let candidate: String = snapshot
26397            .chars_for_range(range.clone())
26398            .skip(num_of_whitespaces)
26399            .take(max_prefix_len)
26400            .collect();
26401        if all_prefixes
26402            .iter()
26403            .any(|prefix| candidate.starts_with(*prefix))
26404        {
26405            return true;
26406        }
26407    }
26408
26409    let ordered_list_candidate: String = snapshot
26410        .chars_for_range(range)
26411        .skip(num_of_whitespaces)
26412        .take(ORDERED_LIST_MAX_MARKER_LEN)
26413        .collect();
26414    for ordered_config in language.ordered_list() {
26415        let regex = match Regex::new(&ordered_config.pattern) {
26416            Ok(r) => r,
26417            Err(_) => continue,
26418        };
26419        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26420            return captures.get(0).is_some();
26421        }
26422    }
26423
26424    false
26425}
26426
26427#[derive(Debug)]
26428enum NewlineConfig {
26429    /// Insert newline with optional additional indent and optional extra blank line
26430    Newline {
26431        additional_indent: IndentSize,
26432        extra_line_additional_indent: Option<IndentSize>,
26433        prevent_auto_indent: bool,
26434    },
26435    /// Clear the current line
26436    ClearCurrentLine,
26437    /// Unindent the current line and add continuation
26438    UnindentCurrentLine { continuation: Arc<str> },
26439}
26440
26441impl NewlineConfig {
26442    fn has_extra_line(&self) -> bool {
26443        matches!(
26444            self,
26445            Self::Newline {
26446                extra_line_additional_indent: Some(_),
26447                ..
26448            }
26449        )
26450    }
26451
26452    fn insert_extra_newline_brackets(
26453        buffer: &MultiBufferSnapshot,
26454        range: Range<MultiBufferOffset>,
26455        language: &language::LanguageScope,
26456    ) -> bool {
26457        let leading_whitespace_len = buffer
26458            .reversed_chars_at(range.start)
26459            .take_while(|c| c.is_whitespace() && *c != '\n')
26460            .map(|c| c.len_utf8())
26461            .sum::<usize>();
26462        let trailing_whitespace_len = buffer
26463            .chars_at(range.end)
26464            .take_while(|c| c.is_whitespace() && *c != '\n')
26465            .map(|c| c.len_utf8())
26466            .sum::<usize>();
26467        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26468
26469        language.brackets().any(|(pair, enabled)| {
26470            let pair_start = pair.start.trim_end();
26471            let pair_end = pair.end.trim_start();
26472
26473            enabled
26474                && pair.newline
26475                && buffer.contains_str_at(range.end, pair_end)
26476                && buffer.contains_str_at(
26477                    range.start.saturating_sub_usize(pair_start.len()),
26478                    pair_start,
26479                )
26480        })
26481    }
26482
26483    fn insert_extra_newline_tree_sitter(
26484        buffer: &MultiBufferSnapshot,
26485        range: Range<MultiBufferOffset>,
26486    ) -> bool {
26487        let (buffer, range) = match buffer
26488            .range_to_buffer_ranges(range.start..=range.end)
26489            .as_slice()
26490        {
26491            [(buffer, range, _)] => (*buffer, range.clone()),
26492            _ => return false,
26493        };
26494        let pair = {
26495            let mut result: Option<BracketMatch<usize>> = None;
26496
26497            for pair in buffer
26498                .all_bracket_ranges(range.start.0..range.end.0)
26499                .filter(move |pair| {
26500                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26501                })
26502            {
26503                let len = pair.close_range.end - pair.open_range.start;
26504
26505                if let Some(existing) = &result {
26506                    let existing_len = existing.close_range.end - existing.open_range.start;
26507                    if len > existing_len {
26508                        continue;
26509                    }
26510                }
26511
26512                result = Some(pair);
26513            }
26514
26515            result
26516        };
26517        let Some(pair) = pair else {
26518            return false;
26519        };
26520        pair.newline_only
26521            && buffer
26522                .chars_for_range(pair.open_range.end..range.start.0)
26523                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26524                .all(|c| c.is_whitespace() && c != '\n')
26525    }
26526}
26527
26528fn update_uncommitted_diff_for_buffer(
26529    editor: Entity<Editor>,
26530    project: &Entity<Project>,
26531    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26532    buffer: Entity<MultiBuffer>,
26533    cx: &mut App,
26534) -> Task<()> {
26535    let mut tasks = Vec::new();
26536    project.update(cx, |project, cx| {
26537        for buffer in buffers {
26538            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26539                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26540            }
26541        }
26542    });
26543    cx.spawn(async move |cx| {
26544        let diffs = future::join_all(tasks).await;
26545        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26546            return;
26547        }
26548
26549        buffer.update(cx, |buffer, cx| {
26550            for diff in diffs.into_iter().flatten() {
26551                buffer.add_diff(diff, cx);
26552            }
26553        });
26554    })
26555}
26556
26557fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26558    let tab_size = tab_size.get() as usize;
26559    let mut width = offset;
26560
26561    for ch in text.chars() {
26562        width += if ch == '\t' {
26563            tab_size - (width % tab_size)
26564        } else {
26565            1
26566        };
26567    }
26568
26569    width - offset
26570}
26571
26572#[cfg(test)]
26573mod tests {
26574    use super::*;
26575
26576    #[test]
26577    fn test_string_size_with_expanded_tabs() {
26578        let nz = |val| NonZeroU32::new(val).unwrap();
26579        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26580        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26581        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26582        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26583        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26584        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26585        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26586        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26587    }
26588}
26589
26590/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26591struct WordBreakingTokenizer<'a> {
26592    input: &'a str,
26593}
26594
26595impl<'a> WordBreakingTokenizer<'a> {
26596    fn new(input: &'a str) -> Self {
26597        Self { input }
26598    }
26599}
26600
26601fn is_char_ideographic(ch: char) -> bool {
26602    use unicode_script::Script::*;
26603    use unicode_script::UnicodeScript;
26604    matches!(ch.script(), Han | Tangut | Yi)
26605}
26606
26607fn is_grapheme_ideographic(text: &str) -> bool {
26608    text.chars().any(is_char_ideographic)
26609}
26610
26611fn is_grapheme_whitespace(text: &str) -> bool {
26612    text.chars().any(|x| x.is_whitespace())
26613}
26614
26615fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26616    text.chars()
26617        .next()
26618        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26619}
26620
26621#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26622enum WordBreakToken<'a> {
26623    Word { token: &'a str, grapheme_len: usize },
26624    InlineWhitespace { token: &'a str, grapheme_len: usize },
26625    Newline,
26626}
26627
26628impl<'a> Iterator for WordBreakingTokenizer<'a> {
26629    /// Yields a span, the count of graphemes in the token, and whether it was
26630    /// whitespace. Note that it also breaks at word boundaries.
26631    type Item = WordBreakToken<'a>;
26632
26633    fn next(&mut self) -> Option<Self::Item> {
26634        use unicode_segmentation::UnicodeSegmentation;
26635        if self.input.is_empty() {
26636            return None;
26637        }
26638
26639        let mut iter = self.input.graphemes(true).peekable();
26640        let mut offset = 0;
26641        let mut grapheme_len = 0;
26642        if let Some(first_grapheme) = iter.next() {
26643            let is_newline = first_grapheme == "\n";
26644            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26645            offset += first_grapheme.len();
26646            grapheme_len += 1;
26647            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26648                if let Some(grapheme) = iter.peek().copied()
26649                    && should_stay_with_preceding_ideograph(grapheme)
26650                {
26651                    offset += grapheme.len();
26652                    grapheme_len += 1;
26653                }
26654            } else {
26655                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26656                let mut next_word_bound = words.peek().copied();
26657                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26658                    next_word_bound = words.next();
26659                }
26660                while let Some(grapheme) = iter.peek().copied() {
26661                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26662                        break;
26663                    };
26664                    if is_grapheme_whitespace(grapheme) != is_whitespace
26665                        || (grapheme == "\n") != is_newline
26666                    {
26667                        break;
26668                    };
26669                    offset += grapheme.len();
26670                    grapheme_len += 1;
26671                    iter.next();
26672                }
26673            }
26674            let token = &self.input[..offset];
26675            self.input = &self.input[offset..];
26676            if token == "\n" {
26677                Some(WordBreakToken::Newline)
26678            } else if is_whitespace {
26679                Some(WordBreakToken::InlineWhitespace {
26680                    token,
26681                    grapheme_len,
26682                })
26683            } else {
26684                Some(WordBreakToken::Word {
26685                    token,
26686                    grapheme_len,
26687                })
26688            }
26689        } else {
26690            None
26691        }
26692    }
26693}
26694
26695#[test]
26696fn test_word_breaking_tokenizer() {
26697    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26698        ("", &[]),
26699        ("  ", &[whitespace("  ", 2)]),
26700        ("Ʒ", &[word("Ʒ", 1)]),
26701        ("Ǽ", &[word("Ǽ", 1)]),
26702        ("", &[word("", 1)]),
26703        ("⋑⋑", &[word("⋑⋑", 2)]),
26704        (
26705            "原理,进而",
26706            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26707        ),
26708        (
26709            "hello world",
26710            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26711        ),
26712        (
26713            "hello, world",
26714            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26715        ),
26716        (
26717            "  hello world",
26718            &[
26719                whitespace("  ", 2),
26720                word("hello", 5),
26721                whitespace(" ", 1),
26722                word("world", 5),
26723            ],
26724        ),
26725        (
26726            "这是什么 \n 钢笔",
26727            &[
26728                word("", 1),
26729                word("", 1),
26730                word("", 1),
26731                word("", 1),
26732                whitespace(" ", 1),
26733                newline(),
26734                whitespace(" ", 1),
26735                word("", 1),
26736                word("", 1),
26737            ],
26738        ),
26739        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26740    ];
26741
26742    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26743        WordBreakToken::Word {
26744            token,
26745            grapheme_len,
26746        }
26747    }
26748
26749    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26750        WordBreakToken::InlineWhitespace {
26751            token,
26752            grapheme_len,
26753        }
26754    }
26755
26756    fn newline() -> WordBreakToken<'static> {
26757        WordBreakToken::Newline
26758    }
26759
26760    for (input, result) in tests {
26761        assert_eq!(
26762            WordBreakingTokenizer::new(input)
26763                .collect::<Vec<_>>()
26764                .as_slice(),
26765            *result,
26766        );
26767    }
26768}
26769
26770fn wrap_with_prefix(
26771    first_line_prefix: String,
26772    subsequent_lines_prefix: String,
26773    unwrapped_text: String,
26774    wrap_column: usize,
26775    tab_size: NonZeroU32,
26776    preserve_existing_whitespace: bool,
26777) -> String {
26778    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26779    let subsequent_lines_prefix_len =
26780        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26781    let mut wrapped_text = String::new();
26782    let mut current_line = first_line_prefix;
26783    let mut is_first_line = true;
26784
26785    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26786    let mut current_line_len = first_line_prefix_len;
26787    let mut in_whitespace = false;
26788    for token in tokenizer {
26789        let have_preceding_whitespace = in_whitespace;
26790        match token {
26791            WordBreakToken::Word {
26792                token,
26793                grapheme_len,
26794            } => {
26795                in_whitespace = false;
26796                let current_prefix_len = if is_first_line {
26797                    first_line_prefix_len
26798                } else {
26799                    subsequent_lines_prefix_len
26800                };
26801                if current_line_len + grapheme_len > wrap_column
26802                    && current_line_len != current_prefix_len
26803                {
26804                    wrapped_text.push_str(current_line.trim_end());
26805                    wrapped_text.push('\n');
26806                    is_first_line = false;
26807                    current_line = subsequent_lines_prefix.clone();
26808                    current_line_len = subsequent_lines_prefix_len;
26809                }
26810                current_line.push_str(token);
26811                current_line_len += grapheme_len;
26812            }
26813            WordBreakToken::InlineWhitespace {
26814                mut token,
26815                mut grapheme_len,
26816            } => {
26817                in_whitespace = true;
26818                if have_preceding_whitespace && !preserve_existing_whitespace {
26819                    continue;
26820                }
26821                if !preserve_existing_whitespace {
26822                    // Keep a single whitespace grapheme as-is
26823                    if let Some(first) =
26824                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26825                    {
26826                        token = first;
26827                    } else {
26828                        token = " ";
26829                    }
26830                    grapheme_len = 1;
26831                }
26832                let current_prefix_len = if is_first_line {
26833                    first_line_prefix_len
26834                } else {
26835                    subsequent_lines_prefix_len
26836                };
26837                if current_line_len + grapheme_len > wrap_column {
26838                    wrapped_text.push_str(current_line.trim_end());
26839                    wrapped_text.push('\n');
26840                    is_first_line = false;
26841                    current_line = subsequent_lines_prefix.clone();
26842                    current_line_len = subsequent_lines_prefix_len;
26843                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26844                    current_line.push_str(token);
26845                    current_line_len += grapheme_len;
26846                }
26847            }
26848            WordBreakToken::Newline => {
26849                in_whitespace = true;
26850                let current_prefix_len = if is_first_line {
26851                    first_line_prefix_len
26852                } else {
26853                    subsequent_lines_prefix_len
26854                };
26855                if preserve_existing_whitespace {
26856                    wrapped_text.push_str(current_line.trim_end());
26857                    wrapped_text.push('\n');
26858                    is_first_line = false;
26859                    current_line = subsequent_lines_prefix.clone();
26860                    current_line_len = subsequent_lines_prefix_len;
26861                } else if have_preceding_whitespace {
26862                    continue;
26863                } else if current_line_len + 1 > wrap_column
26864                    && current_line_len != current_prefix_len
26865                {
26866                    wrapped_text.push_str(current_line.trim_end());
26867                    wrapped_text.push('\n');
26868                    is_first_line = false;
26869                    current_line = subsequent_lines_prefix.clone();
26870                    current_line_len = subsequent_lines_prefix_len;
26871                } else if current_line_len != current_prefix_len {
26872                    current_line.push(' ');
26873                    current_line_len += 1;
26874                }
26875            }
26876        }
26877    }
26878
26879    if !current_line.is_empty() {
26880        wrapped_text.push_str(&current_line);
26881    }
26882    wrapped_text
26883}
26884
26885#[test]
26886fn test_wrap_with_prefix() {
26887    assert_eq!(
26888        wrap_with_prefix(
26889            "# ".to_string(),
26890            "# ".to_string(),
26891            "abcdefg".to_string(),
26892            4,
26893            NonZeroU32::new(4).unwrap(),
26894            false,
26895        ),
26896        "# abcdefg"
26897    );
26898    assert_eq!(
26899        wrap_with_prefix(
26900            "".to_string(),
26901            "".to_string(),
26902            "\thello world".to_string(),
26903            8,
26904            NonZeroU32::new(4).unwrap(),
26905            false,
26906        ),
26907        "hello\nworld"
26908    );
26909    assert_eq!(
26910        wrap_with_prefix(
26911            "// ".to_string(),
26912            "// ".to_string(),
26913            "xx \nyy zz aa bb cc".to_string(),
26914            12,
26915            NonZeroU32::new(4).unwrap(),
26916            false,
26917        ),
26918        "// xx yy zz\n// aa bb cc"
26919    );
26920    assert_eq!(
26921        wrap_with_prefix(
26922            String::new(),
26923            String::new(),
26924            "这是什么 \n 钢笔".to_string(),
26925            3,
26926            NonZeroU32::new(4).unwrap(),
26927            false,
26928        ),
26929        "这是什\n么 钢\n"
26930    );
26931    assert_eq!(
26932        wrap_with_prefix(
26933            String::new(),
26934            String::new(),
26935            format!("foo{}bar", '\u{2009}'), // thin space
26936            80,
26937            NonZeroU32::new(4).unwrap(),
26938            false,
26939        ),
26940        format!("foo{}bar", '\u{2009}')
26941    );
26942}
26943
26944pub trait CollaborationHub {
26945    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26946    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26947    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26948}
26949
26950impl CollaborationHub for Entity<Project> {
26951    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26952        self.read(cx).collaborators()
26953    }
26954
26955    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26956        self.read(cx).user_store().read(cx).participant_indices()
26957    }
26958
26959    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26960        let this = self.read(cx);
26961        let user_ids = this.collaborators().values().map(|c| c.user_id);
26962        this.user_store().read(cx).participant_names(user_ids, cx)
26963    }
26964}
26965
26966pub trait SemanticsProvider {
26967    fn hover(
26968        &self,
26969        buffer: &Entity<Buffer>,
26970        position: text::Anchor,
26971        cx: &mut App,
26972    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26973
26974    fn inline_values(
26975        &self,
26976        buffer_handle: Entity<Buffer>,
26977        range: Range<text::Anchor>,
26978        cx: &mut App,
26979    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26980
26981    fn applicable_inlay_chunks(
26982        &self,
26983        buffer: &Entity<Buffer>,
26984        ranges: &[Range<text::Anchor>],
26985        cx: &mut App,
26986    ) -> Vec<Range<BufferRow>>;
26987
26988    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26989
26990    fn inlay_hints(
26991        &self,
26992        invalidate: InvalidationStrategy,
26993        buffer: Entity<Buffer>,
26994        ranges: Vec<Range<text::Anchor>>,
26995        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26996        cx: &mut App,
26997    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26998
26999    fn semantic_tokens(
27000        &self,
27001        buffer: Entity<Buffer>,
27002        refresh: Option<RefreshForServer>,
27003        cx: &mut App,
27004    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
27005
27006    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
27007
27008    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
27009
27010    fn document_highlights(
27011        &self,
27012        buffer: &Entity<Buffer>,
27013        position: text::Anchor,
27014        cx: &mut App,
27015    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
27016
27017    fn definitions(
27018        &self,
27019        buffer: &Entity<Buffer>,
27020        position: text::Anchor,
27021        kind: GotoDefinitionKind,
27022        cx: &mut App,
27023    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
27024
27025    fn range_for_rename(
27026        &self,
27027        buffer: &Entity<Buffer>,
27028        position: text::Anchor,
27029        cx: &mut App,
27030    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
27031
27032    fn perform_rename(
27033        &self,
27034        buffer: &Entity<Buffer>,
27035        position: text::Anchor,
27036        new_name: String,
27037        cx: &mut App,
27038    ) -> Option<Task<Result<ProjectTransaction>>>;
27039}
27040
27041pub trait CompletionProvider {
27042    fn completions(
27043        &self,
27044        excerpt_id: ExcerptId,
27045        buffer: &Entity<Buffer>,
27046        buffer_position: text::Anchor,
27047        trigger: CompletionContext,
27048        window: &mut Window,
27049        cx: &mut Context<Editor>,
27050    ) -> Task<Result<Vec<CompletionResponse>>>;
27051
27052    fn resolve_completions(
27053        &self,
27054        _buffer: Entity<Buffer>,
27055        _completion_indices: Vec<usize>,
27056        _completions: Rc<RefCell<Box<[Completion]>>>,
27057        _cx: &mut Context<Editor>,
27058    ) -> Task<Result<bool>> {
27059        Task::ready(Ok(false))
27060    }
27061
27062    fn apply_additional_edits_for_completion(
27063        &self,
27064        _buffer: Entity<Buffer>,
27065        _completions: Rc<RefCell<Box<[Completion]>>>,
27066        _completion_index: usize,
27067        _push_to_history: bool,
27068        _all_commit_ranges: Vec<Range<language::Anchor>>,
27069        _cx: &mut Context<Editor>,
27070    ) -> Task<Result<Option<language::Transaction>>> {
27071        Task::ready(Ok(None))
27072    }
27073
27074    fn is_completion_trigger(
27075        &self,
27076        buffer: &Entity<Buffer>,
27077        position: language::Anchor,
27078        text: &str,
27079        trigger_in_words: bool,
27080        cx: &mut Context<Editor>,
27081    ) -> bool;
27082
27083    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
27084
27085    fn sort_completions(&self) -> bool {
27086        true
27087    }
27088
27089    fn filter_completions(&self) -> bool {
27090        true
27091    }
27092
27093    fn show_snippets(&self) -> bool {
27094        false
27095    }
27096}
27097
27098pub trait CodeActionProvider {
27099    fn id(&self) -> Arc<str>;
27100
27101    fn code_actions(
27102        &self,
27103        buffer: &Entity<Buffer>,
27104        range: Range<text::Anchor>,
27105        window: &mut Window,
27106        cx: &mut App,
27107    ) -> Task<Result<Vec<CodeAction>>>;
27108
27109    fn apply_code_action(
27110        &self,
27111        buffer_handle: Entity<Buffer>,
27112        action: CodeAction,
27113        excerpt_id: ExcerptId,
27114        push_to_history: bool,
27115        window: &mut Window,
27116        cx: &mut App,
27117    ) -> Task<Result<ProjectTransaction>>;
27118}
27119
27120impl CodeActionProvider for Entity<Project> {
27121    fn id(&self) -> Arc<str> {
27122        "project".into()
27123    }
27124
27125    fn code_actions(
27126        &self,
27127        buffer: &Entity<Buffer>,
27128        range: Range<text::Anchor>,
27129        _window: &mut Window,
27130        cx: &mut App,
27131    ) -> Task<Result<Vec<CodeAction>>> {
27132        self.update(cx, |project, cx| {
27133            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
27134            let code_actions = project.code_actions(buffer, range, None, cx);
27135            cx.background_spawn(async move {
27136                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
27137                Ok(code_lens_actions
27138                    .context("code lens fetch")?
27139                    .into_iter()
27140                    .flatten()
27141                    .chain(
27142                        code_actions
27143                            .context("code action fetch")?
27144                            .into_iter()
27145                            .flatten(),
27146                    )
27147                    .collect())
27148            })
27149        })
27150    }
27151
27152    fn apply_code_action(
27153        &self,
27154        buffer_handle: Entity<Buffer>,
27155        action: CodeAction,
27156        _excerpt_id: ExcerptId,
27157        push_to_history: bool,
27158        _window: &mut Window,
27159        cx: &mut App,
27160    ) -> Task<Result<ProjectTransaction>> {
27161        self.update(cx, |project, cx| {
27162            project.apply_code_action(buffer_handle, action, push_to_history, cx)
27163        })
27164    }
27165}
27166
27167fn snippet_completions(
27168    project: &Project,
27169    buffer: &Entity<Buffer>,
27170    buffer_anchor: text::Anchor,
27171    classifier: CharClassifier,
27172    cx: &mut App,
27173) -> Task<Result<CompletionResponse>> {
27174    let languages = buffer.read(cx).languages_at(buffer_anchor);
27175    let snippet_store = project.snippets().read(cx);
27176
27177    let scopes: Vec<_> = languages
27178        .iter()
27179        .filter_map(|language| {
27180            let language_name = language.lsp_id();
27181            let snippets = snippet_store.snippets_for(Some(language_name), cx);
27182
27183            if snippets.is_empty() {
27184                None
27185            } else {
27186                Some((language.default_scope(), snippets))
27187            }
27188        })
27189        .collect();
27190
27191    if scopes.is_empty() {
27192        return Task::ready(Ok(CompletionResponse {
27193            completions: vec![],
27194            display_options: CompletionDisplayOptions::default(),
27195            is_incomplete: false,
27196        }));
27197    }
27198
27199    let snapshot = buffer.read(cx).text_snapshot();
27200    let executor = cx.background_executor().clone();
27201
27202    cx.background_spawn(async move {
27203        let is_word_char = |c| classifier.is_word(c);
27204
27205        let mut is_incomplete = false;
27206        let mut completions: Vec<Completion> = Vec::new();
27207
27208        const MAX_PREFIX_LEN: usize = 128;
27209        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27210        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27211        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27212
27213        let max_buffer_window: String = snapshot
27214            .text_for_range(window_start..buffer_offset)
27215            .collect();
27216
27217        if max_buffer_window.is_empty() {
27218            return Ok(CompletionResponse {
27219                completions: vec![],
27220                display_options: CompletionDisplayOptions::default(),
27221                is_incomplete: true,
27222            });
27223        }
27224
27225        for (_scope, snippets) in scopes.into_iter() {
27226            // Sort snippets by word count to match longer snippet prefixes first.
27227            let mut sorted_snippet_candidates = snippets
27228                .iter()
27229                .enumerate()
27230                .flat_map(|(snippet_ix, snippet)| {
27231                    snippet
27232                        .prefix
27233                        .iter()
27234                        .enumerate()
27235                        .map(move |(prefix_ix, prefix)| {
27236                            let word_count =
27237                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27238                            ((snippet_ix, prefix_ix), prefix, word_count)
27239                        })
27240                })
27241                .collect_vec();
27242            sorted_snippet_candidates
27243                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27244
27245            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27246
27247            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27248                .take(
27249                    sorted_snippet_candidates
27250                        .first()
27251                        .map(|(_, _, word_count)| *word_count)
27252                        .unwrap_or_default(),
27253                )
27254                .collect_vec();
27255
27256            const MAX_RESULTS: usize = 100;
27257            // Each match also remembers how many characters from the buffer it consumed
27258            let mut matches: Vec<(StringMatch, usize)> = vec![];
27259
27260            let mut snippet_list_cutoff_index = 0;
27261            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27262                let word_count = buffer_index + 1;
27263                // Increase `snippet_list_cutoff_index` until we have all of the
27264                // snippets with sufficiently many words.
27265                while sorted_snippet_candidates
27266                    .get(snippet_list_cutoff_index)
27267                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27268                        *snippet_word_count >= word_count
27269                    })
27270                {
27271                    snippet_list_cutoff_index += 1;
27272                }
27273
27274                // Take only the candidates with at least `word_count` many words
27275                let snippet_candidates_at_word_len =
27276                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27277
27278                let candidates = snippet_candidates_at_word_len
27279                    .iter()
27280                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27281                    .enumerate() // index in `sorted_snippet_candidates`
27282                    // First char must match
27283                    .filter(|(_ix, prefix)| {
27284                        itertools::equal(
27285                            prefix
27286                                .chars()
27287                                .next()
27288                                .into_iter()
27289                                .flat_map(|c| c.to_lowercase()),
27290                            buffer_window
27291                                .chars()
27292                                .next()
27293                                .into_iter()
27294                                .flat_map(|c| c.to_lowercase()),
27295                        )
27296                    })
27297                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27298                    .collect::<Vec<StringMatchCandidate>>();
27299
27300                matches.extend(
27301                    fuzzy::match_strings(
27302                        &candidates,
27303                        &buffer_window,
27304                        buffer_window.chars().any(|c| c.is_uppercase()),
27305                        true,
27306                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27307                        &Default::default(),
27308                        executor.clone(),
27309                    )
27310                    .await
27311                    .into_iter()
27312                    .map(|string_match| (string_match, buffer_window.len())),
27313                );
27314
27315                if matches.len() >= MAX_RESULTS {
27316                    break;
27317                }
27318            }
27319
27320            let to_lsp = |point: &text::Anchor| {
27321                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27322                point_to_lsp(end)
27323            };
27324            let lsp_end = to_lsp(&buffer_anchor);
27325
27326            if matches.len() >= MAX_RESULTS {
27327                is_incomplete = true;
27328            }
27329
27330            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27331                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27332                    sorted_snippet_candidates[string_match.candidate_id];
27333                let snippet = &snippets[snippet_index];
27334                let start = buffer_offset - buffer_window_len;
27335                let start = snapshot.anchor_before(start);
27336                let range = start..buffer_anchor;
27337                let lsp_start = to_lsp(&start);
27338                let lsp_range = lsp::Range {
27339                    start: lsp_start,
27340                    end: lsp_end,
27341                };
27342                Completion {
27343                    replace_range: range,
27344                    new_text: snippet.body.clone(),
27345                    source: CompletionSource::Lsp {
27346                        insert_range: None,
27347                        server_id: LanguageServerId(usize::MAX),
27348                        resolved: true,
27349                        lsp_completion: Box::new(lsp::CompletionItem {
27350                            label: snippet.prefix.first().unwrap().clone(),
27351                            kind: Some(CompletionItemKind::SNIPPET),
27352                            label_details: snippet.description.as_ref().map(|description| {
27353                                lsp::CompletionItemLabelDetails {
27354                                    detail: Some(description.clone()),
27355                                    description: None,
27356                                }
27357                            }),
27358                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27359                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27360                                lsp::InsertReplaceEdit {
27361                                    new_text: snippet.body.clone(),
27362                                    insert: lsp_range,
27363                                    replace: lsp_range,
27364                                },
27365                            )),
27366                            filter_text: Some(snippet.body.clone()),
27367                            sort_text: Some(char::MAX.to_string()),
27368                            ..lsp::CompletionItem::default()
27369                        }),
27370                        lsp_defaults: None,
27371                    },
27372                    label: CodeLabel {
27373                        text: matching_prefix.clone(),
27374                        runs: Vec::new(),
27375                        filter_range: 0..matching_prefix.len(),
27376                    },
27377                    icon_path: None,
27378                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27379                        single_line: snippet.name.clone().into(),
27380                        plain_text: snippet
27381                            .description
27382                            .clone()
27383                            .map(|description| description.into()),
27384                    }),
27385                    insert_text_mode: None,
27386                    confirm: None,
27387                    match_start: Some(start),
27388                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27389                }
27390            }));
27391        }
27392
27393        Ok(CompletionResponse {
27394            completions,
27395            display_options: CompletionDisplayOptions::default(),
27396            is_incomplete,
27397        })
27398    })
27399}
27400
27401impl CompletionProvider for Entity<Project> {
27402    fn completions(
27403        &self,
27404        _excerpt_id: ExcerptId,
27405        buffer: &Entity<Buffer>,
27406        buffer_position: text::Anchor,
27407        options: CompletionContext,
27408        _window: &mut Window,
27409        cx: &mut Context<Editor>,
27410    ) -> Task<Result<Vec<CompletionResponse>>> {
27411        self.update(cx, |project, cx| {
27412            let task = project.completions(buffer, buffer_position, options, cx);
27413            cx.background_spawn(task)
27414        })
27415    }
27416
27417    fn resolve_completions(
27418        &self,
27419        buffer: Entity<Buffer>,
27420        completion_indices: Vec<usize>,
27421        completions: Rc<RefCell<Box<[Completion]>>>,
27422        cx: &mut Context<Editor>,
27423    ) -> Task<Result<bool>> {
27424        self.update(cx, |project, cx| {
27425            project.lsp_store().update(cx, |lsp_store, cx| {
27426                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27427            })
27428        })
27429    }
27430
27431    fn apply_additional_edits_for_completion(
27432        &self,
27433        buffer: Entity<Buffer>,
27434        completions: Rc<RefCell<Box<[Completion]>>>,
27435        completion_index: usize,
27436        push_to_history: bool,
27437        all_commit_ranges: Vec<Range<language::Anchor>>,
27438        cx: &mut Context<Editor>,
27439    ) -> Task<Result<Option<language::Transaction>>> {
27440        self.update(cx, |project, cx| {
27441            project.lsp_store().update(cx, |lsp_store, cx| {
27442                lsp_store.apply_additional_edits_for_completion(
27443                    buffer,
27444                    completions,
27445                    completion_index,
27446                    push_to_history,
27447                    all_commit_ranges,
27448                    cx,
27449                )
27450            })
27451        })
27452    }
27453
27454    fn is_completion_trigger(
27455        &self,
27456        buffer: &Entity<Buffer>,
27457        position: language::Anchor,
27458        text: &str,
27459        trigger_in_words: bool,
27460        cx: &mut Context<Editor>,
27461    ) -> bool {
27462        let mut chars = text.chars();
27463        let char = if let Some(char) = chars.next() {
27464            char
27465        } else {
27466            return false;
27467        };
27468        if chars.next().is_some() {
27469            return false;
27470        }
27471
27472        let buffer = buffer.read(cx);
27473        let snapshot = buffer.snapshot();
27474        let classifier = snapshot
27475            .char_classifier_at(position)
27476            .scope_context(Some(CharScopeContext::Completion));
27477        if trigger_in_words && classifier.is_word(char) {
27478            return true;
27479        }
27480
27481        buffer.completion_triggers().contains(text)
27482    }
27483
27484    fn show_snippets(&self) -> bool {
27485        true
27486    }
27487}
27488
27489impl SemanticsProvider for WeakEntity<Project> {
27490    fn hover(
27491        &self,
27492        buffer: &Entity<Buffer>,
27493        position: text::Anchor,
27494        cx: &mut App,
27495    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27496        self.update(cx, |project, cx| project.hover(buffer, position, cx))
27497            .ok()
27498    }
27499
27500    fn document_highlights(
27501        &self,
27502        buffer: &Entity<Buffer>,
27503        position: text::Anchor,
27504        cx: &mut App,
27505    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27506        self.update(cx, |project, cx| {
27507            project.document_highlights(buffer, position, cx)
27508        })
27509        .ok()
27510    }
27511
27512    fn definitions(
27513        &self,
27514        buffer: &Entity<Buffer>,
27515        position: text::Anchor,
27516        kind: GotoDefinitionKind,
27517        cx: &mut App,
27518    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27519        self.update(cx, |project, cx| match kind {
27520            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27521            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27522            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27523            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27524        })
27525        .ok()
27526    }
27527
27528    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27529        self.update(cx, |project, cx| {
27530            if project
27531                .active_debug_session(cx)
27532                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27533            {
27534                return true;
27535            }
27536
27537            buffer.update(cx, |buffer, cx| {
27538                project.any_language_server_supports_inlay_hints(buffer, cx)
27539            })
27540        })
27541        .unwrap_or(false)
27542    }
27543
27544    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27545        self.update(cx, |project, cx| {
27546            buffer.update(cx, |buffer, cx| {
27547                project.any_language_server_supports_semantic_tokens(buffer, cx)
27548            })
27549        })
27550        .unwrap_or(false)
27551    }
27552
27553    fn inline_values(
27554        &self,
27555        buffer_handle: Entity<Buffer>,
27556        range: Range<text::Anchor>,
27557        cx: &mut App,
27558    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27559        self.update(cx, |project, cx| {
27560            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27561
27562            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27563        })
27564        .ok()
27565        .flatten()
27566    }
27567
27568    fn applicable_inlay_chunks(
27569        &self,
27570        buffer: &Entity<Buffer>,
27571        ranges: &[Range<text::Anchor>],
27572        cx: &mut App,
27573    ) -> Vec<Range<BufferRow>> {
27574        self.update(cx, |project, cx| {
27575            project.lsp_store().update(cx, |lsp_store, cx| {
27576                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27577            })
27578        })
27579        .unwrap_or_default()
27580    }
27581
27582    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27583        self.update(cx, |project, cx| {
27584            project.lsp_store().update(cx, |lsp_store, _| {
27585                lsp_store.invalidate_inlay_hints(for_buffers)
27586            })
27587        })
27588        .ok();
27589    }
27590
27591    fn inlay_hints(
27592        &self,
27593        invalidate: InvalidationStrategy,
27594        buffer: Entity<Buffer>,
27595        ranges: Vec<Range<text::Anchor>>,
27596        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27597        cx: &mut App,
27598    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27599        self.update(cx, |project, cx| {
27600            project.lsp_store().update(cx, |lsp_store, cx| {
27601                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27602            })
27603        })
27604        .ok()
27605    }
27606
27607    fn semantic_tokens(
27608        &self,
27609        buffer: Entity<Buffer>,
27610        refresh: Option<RefreshForServer>,
27611        cx: &mut App,
27612    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27613        self.update(cx, |this, cx| {
27614            this.lsp_store().update(cx, |lsp_store, cx| {
27615                lsp_store.semantic_tokens(buffer, refresh, cx)
27616            })
27617        })
27618        .ok()
27619    }
27620
27621    fn range_for_rename(
27622        &self,
27623        buffer: &Entity<Buffer>,
27624        position: text::Anchor,
27625        cx: &mut App,
27626    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27627        self.update(cx, |project, cx| {
27628            let buffer = buffer.clone();
27629            let task = project.prepare_rename(buffer.clone(), position, cx);
27630            cx.spawn(async move |_, cx| {
27631                Ok(match task.await? {
27632                    PrepareRenameResponse::Success(range) => Some(range),
27633                    PrepareRenameResponse::InvalidPosition => None,
27634                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27635                        // Fallback on using TreeSitter info to determine identifier range
27636                        buffer.read_with(cx, |buffer, _| {
27637                            let snapshot = buffer.snapshot();
27638                            let (range, kind) = snapshot.surrounding_word(position, None);
27639                            if kind != Some(CharKind::Word) {
27640                                return None;
27641                            }
27642                            Some(
27643                                snapshot.anchor_before(range.start)
27644                                    ..snapshot.anchor_after(range.end),
27645                            )
27646                        })
27647                    }
27648                })
27649            })
27650        })
27651        .ok()
27652    }
27653
27654    fn perform_rename(
27655        &self,
27656        buffer: &Entity<Buffer>,
27657        position: text::Anchor,
27658        new_name: String,
27659        cx: &mut App,
27660    ) -> Option<Task<Result<ProjectTransaction>>> {
27661        self.update(cx, |project, cx| {
27662            project.perform_rename(buffer.clone(), position, new_name, cx)
27663        })
27664        .ok()
27665    }
27666}
27667
27668fn consume_contiguous_rows(
27669    contiguous_row_selections: &mut Vec<Selection<Point>>,
27670    selection: &Selection<Point>,
27671    display_map: &DisplaySnapshot,
27672    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27673) -> (MultiBufferRow, MultiBufferRow) {
27674    contiguous_row_selections.push(selection.clone());
27675    let start_row = starting_row(selection, display_map);
27676    let mut end_row = ending_row(selection, display_map);
27677
27678    while let Some(next_selection) = selections.peek() {
27679        if next_selection.start.row <= end_row.0 {
27680            end_row = ending_row(next_selection, display_map);
27681            contiguous_row_selections.push(selections.next().unwrap().clone());
27682        } else {
27683            break;
27684        }
27685    }
27686    (start_row, end_row)
27687}
27688
27689fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27690    if selection.start.column > 0 {
27691        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27692    } else {
27693        MultiBufferRow(selection.start.row)
27694    }
27695}
27696
27697fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27698    if next_selection.end.column > 0 || next_selection.is_empty() {
27699        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27700    } else {
27701        MultiBufferRow(next_selection.end.row)
27702    }
27703}
27704
27705impl EditorSnapshot {
27706    pub fn remote_selections_in_range<'a>(
27707        &'a self,
27708        range: &'a Range<Anchor>,
27709        collaboration_hub: &dyn CollaborationHub,
27710        cx: &'a App,
27711    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27712        let participant_names = collaboration_hub.user_names(cx);
27713        let participant_indices = collaboration_hub.user_participant_indices(cx);
27714        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27715        let collaborators_by_replica_id = collaborators_by_peer_id
27716            .values()
27717            .map(|collaborator| (collaborator.replica_id, collaborator))
27718            .collect::<HashMap<_, _>>();
27719        self.buffer_snapshot()
27720            .selections_in_range(range, false)
27721            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27722                if replica_id == ReplicaId::AGENT {
27723                    Some(RemoteSelection {
27724                        replica_id,
27725                        selection,
27726                        cursor_shape,
27727                        line_mode,
27728                        collaborator_id: CollaboratorId::Agent,
27729                        user_name: Some("Agent".into()),
27730                        color: cx.theme().players().agent(),
27731                    })
27732                } else {
27733                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27734                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27735                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27736                    Some(RemoteSelection {
27737                        replica_id,
27738                        selection,
27739                        cursor_shape,
27740                        line_mode,
27741                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27742                        user_name,
27743                        color: if let Some(index) = participant_index {
27744                            cx.theme().players().color_for_participant(index.0)
27745                        } else {
27746                            cx.theme().players().absent()
27747                        },
27748                    })
27749                }
27750            })
27751    }
27752
27753    pub fn hunks_for_ranges(
27754        &self,
27755        ranges: impl IntoIterator<Item = Range<Point>>,
27756    ) -> Vec<MultiBufferDiffHunk> {
27757        let mut hunks = Vec::new();
27758        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27759            HashMap::default();
27760        for query_range in ranges {
27761            let query_rows =
27762                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27763            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27764                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27765            ) {
27766                // Include deleted hunks that are adjacent to the query range, because
27767                // otherwise they would be missed.
27768                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27769                if hunk.status().is_deleted() {
27770                    intersects_range |= hunk.row_range.start == query_rows.end;
27771                    intersects_range |= hunk.row_range.end == query_rows.start;
27772                }
27773                if intersects_range {
27774                    if !processed_buffer_rows
27775                        .entry(hunk.buffer_id)
27776                        .or_default()
27777                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27778                    {
27779                        continue;
27780                    }
27781                    hunks.push(hunk);
27782                }
27783            }
27784        }
27785
27786        hunks
27787    }
27788
27789    fn display_diff_hunks_for_rows<'a>(
27790        &'a self,
27791        display_rows: Range<DisplayRow>,
27792        folded_buffers: &'a HashSet<BufferId>,
27793    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27794        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27795        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27796
27797        self.buffer_snapshot()
27798            .diff_hunks_in_range(buffer_start..buffer_end)
27799            .filter_map(|hunk| {
27800                if folded_buffers.contains(&hunk.buffer_id)
27801                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27802                {
27803                    return None;
27804                }
27805
27806                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27807                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27808                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27809                    let line_len = self.buffer_snapshot().line_len(last_row);
27810                    Point::new(last_row.0, line_len)
27811                } else {
27812                    Point::new(hunk.row_range.end.0, 0)
27813                };
27814
27815                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27816                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27817
27818                let display_hunk = if hunk_display_start.column() != 0 {
27819                    DisplayDiffHunk::Folded {
27820                        display_row: hunk_display_start.row(),
27821                    }
27822                } else {
27823                    let mut end_row = hunk_display_end.row();
27824                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27825                        end_row.0 += 1;
27826                    }
27827                    let is_created_file = hunk.is_created_file();
27828
27829                    DisplayDiffHunk::Unfolded {
27830                        status: hunk.status(),
27831                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27832                            ..hunk.diff_base_byte_range.end.0,
27833                        word_diffs: hunk.word_diffs,
27834                        display_row_range: hunk_display_start.row()..end_row,
27835                        multi_buffer_range: Anchor::range_in_buffer(
27836                            hunk.excerpt_id,
27837                            hunk.buffer_range,
27838                        ),
27839                        is_created_file,
27840                    }
27841                };
27842
27843                Some(display_hunk)
27844            })
27845    }
27846
27847    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27848        self.display_snapshot
27849            .buffer_snapshot()
27850            .language_at(position)
27851    }
27852
27853    pub fn is_focused(&self) -> bool {
27854        self.is_focused
27855    }
27856
27857    pub fn placeholder_text(&self) -> Option<String> {
27858        self.placeholder_display_snapshot
27859            .as_ref()
27860            .map(|display_map| display_map.text())
27861    }
27862
27863    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27864        self.scroll_anchor.scroll_position(&self.display_snapshot)
27865    }
27866
27867    pub fn gutter_dimensions(
27868        &self,
27869        font_id: FontId,
27870        font_size: Pixels,
27871        style: &EditorStyle,
27872        window: &mut Window,
27873        cx: &App,
27874    ) -> GutterDimensions {
27875        if self.show_gutter
27876            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27877            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27878        {
27879            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27880                matches!(
27881                    ProjectSettings::get_global(cx).git.git_gutter,
27882                    GitGutterSetting::TrackedFiles
27883                )
27884            });
27885            let gutter_settings = EditorSettings::get_global(cx).gutter;
27886            let show_line_numbers = self
27887                .show_line_numbers
27888                .unwrap_or(gutter_settings.line_numbers);
27889            let line_gutter_width = if show_line_numbers {
27890                // Avoid flicker-like gutter resizes when the line number gains another digit by
27891                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27892                let min_width_for_number_on_gutter =
27893                    ch_advance * gutter_settings.min_line_number_digits as f32;
27894                self.max_line_number_width(style, window)
27895                    .max(min_width_for_number_on_gutter)
27896            } else {
27897                0.0.into()
27898            };
27899
27900            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27901            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27902
27903            let git_blame_entries_width =
27904                self.git_blame_gutter_max_author_length
27905                    .map(|max_author_length| {
27906                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27907                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27908
27909                        /// The number of characters to dedicate to gaps and margins.
27910                        const SPACING_WIDTH: usize = 4;
27911
27912                        let max_char_count = max_author_length.min(renderer.max_author_length())
27913                            + ::git::SHORT_SHA_LENGTH
27914                            + MAX_RELATIVE_TIMESTAMP.len()
27915                            + SPACING_WIDTH;
27916
27917                        ch_advance * max_char_count
27918                    });
27919
27920            let is_singleton = self.buffer_snapshot().is_singleton();
27921
27922            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27923            left_padding += if !is_singleton {
27924                ch_width * 4.0
27925            } else if show_runnables || show_breakpoints {
27926                ch_width * 3.0
27927            } else if show_git_gutter && show_line_numbers {
27928                ch_width * 2.0
27929            } else if show_git_gutter || show_line_numbers {
27930                ch_width
27931            } else {
27932                px(0.)
27933            };
27934
27935            let shows_folds = is_singleton && gutter_settings.folds;
27936
27937            let right_padding = if shows_folds && show_line_numbers {
27938                ch_width * 4.0
27939            } else if shows_folds || (!is_singleton && show_line_numbers) {
27940                ch_width * 3.0
27941            } else if show_line_numbers {
27942                ch_width
27943            } else {
27944                px(0.)
27945            };
27946
27947            GutterDimensions {
27948                left_padding,
27949                right_padding,
27950                width: line_gutter_width + left_padding + right_padding,
27951                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27952                git_blame_entries_width,
27953            }
27954        } else if self.offset_content {
27955            GutterDimensions::default_with_margin(font_id, font_size, cx)
27956        } else {
27957            GutterDimensions::default()
27958        }
27959    }
27960
27961    pub fn render_crease_toggle(
27962        &self,
27963        buffer_row: MultiBufferRow,
27964        row_contains_cursor: bool,
27965        editor: Entity<Editor>,
27966        window: &mut Window,
27967        cx: &mut App,
27968    ) -> Option<AnyElement> {
27969        let folded = self.is_line_folded(buffer_row);
27970        let mut is_foldable = false;
27971
27972        if let Some(crease) = self
27973            .crease_snapshot
27974            .query_row(buffer_row, self.buffer_snapshot())
27975        {
27976            is_foldable = true;
27977            match crease {
27978                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27979                    if let Some(render_toggle) = render_toggle {
27980                        let toggle_callback =
27981                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27982                                if folded {
27983                                    editor.update(cx, |editor, cx| {
27984                                        editor.fold_at(buffer_row, window, cx)
27985                                    });
27986                                } else {
27987                                    editor.update(cx, |editor, cx| {
27988                                        editor.unfold_at(buffer_row, window, cx)
27989                                    });
27990                                }
27991                            });
27992                        return Some((render_toggle)(
27993                            buffer_row,
27994                            folded,
27995                            toggle_callback,
27996                            window,
27997                            cx,
27998                        ));
27999                    }
28000                }
28001            }
28002        }
28003
28004        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
28005
28006        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
28007            Some(
28008                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
28009                    .toggle_state(folded)
28010                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
28011                        if folded {
28012                            this.unfold_at(buffer_row, window, cx);
28013                        } else {
28014                            this.fold_at(buffer_row, window, cx);
28015                        }
28016                    }))
28017                    .into_any_element(),
28018            )
28019        } else {
28020            None
28021        }
28022    }
28023
28024    pub fn render_crease_trailer(
28025        &self,
28026        buffer_row: MultiBufferRow,
28027        window: &mut Window,
28028        cx: &mut App,
28029    ) -> Option<AnyElement> {
28030        let folded = self.is_line_folded(buffer_row);
28031        if let Crease::Inline { render_trailer, .. } = self
28032            .crease_snapshot
28033            .query_row(buffer_row, self.buffer_snapshot())?
28034        {
28035            let render_trailer = render_trailer.as_ref()?;
28036            Some(render_trailer(buffer_row, folded, window, cx))
28037        } else {
28038            None
28039        }
28040    }
28041
28042    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
28043        let digit_count = self.widest_line_number().ilog10() + 1;
28044        column_pixels(style, digit_count as usize, window)
28045    }
28046
28047    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
28048    ///
28049    /// This is positive if `base` is before `line`.
28050    fn relative_line_delta(
28051        &self,
28052        current_selection_head: DisplayRow,
28053        first_visible_row: DisplayRow,
28054        consider_wrapped_lines: bool,
28055    ) -> i64 {
28056        let current_selection_head = current_selection_head.as_display_point().to_point(self);
28057        let first_visible_row = first_visible_row.as_display_point().to_point(self);
28058
28059        if consider_wrapped_lines {
28060            let wrap_snapshot = self.wrap_snapshot();
28061            let base_wrap_row = wrap_snapshot
28062                .make_wrap_point(current_selection_head, Bias::Left)
28063                .row();
28064            let wrap_row = wrap_snapshot
28065                .make_wrap_point(first_visible_row, Bias::Left)
28066                .row();
28067
28068            wrap_row.0 as i64 - base_wrap_row.0 as i64
28069        } else {
28070            let fold_snapshot = self.fold_snapshot();
28071            let base_fold_row = fold_snapshot
28072                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
28073                .row();
28074            let fold_row = fold_snapshot
28075                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
28076                .row();
28077
28078            fold_row as i64 - base_fold_row as i64
28079        }
28080    }
28081
28082    /// Returns the unsigned relative line number to display for each row in `rows`.
28083    ///
28084    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
28085    pub fn calculate_relative_line_numbers(
28086        &self,
28087        rows: &Range<DisplayRow>,
28088        current_selection_head: DisplayRow,
28089        count_wrapped_lines: bool,
28090    ) -> HashMap<DisplayRow, u32> {
28091        let initial_offset =
28092            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
28093
28094        self.row_infos(rows.start)
28095            .take(rows.len())
28096            .enumerate()
28097            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
28098            .filter(|(_row, row_info)| {
28099                row_info.buffer_row.is_some()
28100                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
28101            })
28102            .enumerate()
28103            .filter_map(|(i, (row, row_info))| {
28104                // We want to ensure here that the current line has absolute
28105                // numbering, even if we are in a soft-wrapped line. With the
28106                // exception that if we are in a deleted line, we should number this
28107                // relative with 0, as otherwise it would have no line number at all
28108                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
28109
28110                (relative_line_number != 0
28111                    || row_info
28112                        .diff_status
28113                        .is_some_and(|status| status.is_deleted()))
28114                .then_some((row, relative_line_number))
28115            })
28116            .collect()
28117    }
28118}
28119
28120pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
28121    let font_size = style.text.font_size.to_pixels(window.rem_size());
28122    let layout = window.text_system().shape_line(
28123        SharedString::from(" ".repeat(column)),
28124        font_size,
28125        &[TextRun {
28126            len: column,
28127            font: style.text.font(),
28128            color: Hsla::default(),
28129            ..Default::default()
28130        }],
28131        None,
28132    );
28133
28134    layout.width
28135}
28136
28137impl Deref for EditorSnapshot {
28138    type Target = DisplaySnapshot;
28139
28140    fn deref(&self) -> &Self::Target {
28141        &self.display_snapshot
28142    }
28143}
28144
28145#[derive(Clone, Debug, PartialEq, Eq)]
28146pub enum EditorEvent {
28147    /// Emitted when the stored review comments change (added, removed, or updated).
28148    ReviewCommentsChanged {
28149        /// The new total count of review comments.
28150        total_count: usize,
28151    },
28152    InputIgnored {
28153        text: Arc<str>,
28154    },
28155    InputHandled {
28156        utf16_range_to_replace: Option<Range<isize>>,
28157        text: Arc<str>,
28158    },
28159    ExcerptsAdded {
28160        buffer: Entity<Buffer>,
28161        predecessor: ExcerptId,
28162        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
28163    },
28164    ExcerptsRemoved {
28165        ids: Vec<ExcerptId>,
28166        removed_buffer_ids: Vec<BufferId>,
28167    },
28168    BufferFoldToggled {
28169        ids: Vec<ExcerptId>,
28170        folded: bool,
28171    },
28172    ExcerptsEdited {
28173        ids: Vec<ExcerptId>,
28174    },
28175    ExcerptsExpanded {
28176        ids: Vec<ExcerptId>,
28177    },
28178    ExpandExcerptsRequested {
28179        excerpt_ids: Vec<ExcerptId>,
28180        lines: u32,
28181        direction: ExpandExcerptDirection,
28182    },
28183    StageOrUnstageRequested {
28184        stage: bool,
28185        hunks: Vec<MultiBufferDiffHunk>,
28186    },
28187    OpenExcerptsRequested {
28188        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
28189        split: bool,
28190    },
28191    RestoreRequested {
28192        hunks: Vec<MultiBufferDiffHunk>,
28193    },
28194    BufferEdited,
28195    Edited {
28196        transaction_id: clock::Lamport,
28197    },
28198    Reparsed(BufferId),
28199    Focused,
28200    FocusedIn,
28201    Blurred,
28202    DirtyChanged,
28203    Saved,
28204    TitleChanged,
28205    SelectionsChanged {
28206        local: bool,
28207    },
28208    ScrollPositionChanged {
28209        local: bool,
28210        autoscroll: bool,
28211    },
28212    TransactionUndone {
28213        transaction_id: clock::Lamport,
28214    },
28215    TransactionBegun {
28216        transaction_id: clock::Lamport,
28217    },
28218    CursorShapeChanged,
28219    BreadcrumbsChanged,
28220    OutlineSymbolsChanged,
28221    PushedToNavHistory {
28222        anchor: Anchor,
28223        is_deactivate: bool,
28224    },
28225}
28226
28227impl EventEmitter<EditorEvent> for Editor {}
28228
28229impl Focusable for Editor {
28230    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28231        self.focus_handle.clone()
28232    }
28233}
28234
28235impl Render for Editor {
28236    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28237        EditorElement::new(&cx.entity(), self.create_style(cx))
28238    }
28239}
28240
28241impl EntityInputHandler for Editor {
28242    fn text_for_range(
28243        &mut self,
28244        range_utf16: Range<usize>,
28245        adjusted_range: &mut Option<Range<usize>>,
28246        _: &mut Window,
28247        cx: &mut Context<Self>,
28248    ) -> Option<String> {
28249        let snapshot = self.buffer.read(cx).read(cx);
28250        let start = snapshot.clip_offset_utf16(
28251            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28252            Bias::Left,
28253        );
28254        let end = snapshot.clip_offset_utf16(
28255            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28256            Bias::Right,
28257        );
28258        if (start.0.0..end.0.0) != range_utf16 {
28259            adjusted_range.replace(start.0.0..end.0.0);
28260        }
28261        Some(snapshot.text_for_range(start..end).collect())
28262    }
28263
28264    fn selected_text_range(
28265        &mut self,
28266        ignore_disabled_input: bool,
28267        _: &mut Window,
28268        cx: &mut Context<Self>,
28269    ) -> Option<UTF16Selection> {
28270        // Prevent the IME menu from appearing when holding down an alphabetic key
28271        // while input is disabled.
28272        if !ignore_disabled_input && !self.input_enabled {
28273            return None;
28274        }
28275
28276        let selection = self
28277            .selections
28278            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28279        let range = selection.range();
28280
28281        Some(UTF16Selection {
28282            range: range.start.0.0..range.end.0.0,
28283            reversed: selection.reversed,
28284        })
28285    }
28286
28287    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28288        let snapshot = self.buffer.read(cx).read(cx);
28289        let range = self
28290            .text_highlights(HighlightKey::InputComposition, cx)?
28291            .1
28292            .first()?;
28293        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28294    }
28295
28296    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28297        self.clear_highlights(HighlightKey::InputComposition, cx);
28298        self.ime_transaction.take();
28299    }
28300
28301    fn replace_text_in_range(
28302        &mut self,
28303        range_utf16: Option<Range<usize>>,
28304        text: &str,
28305        window: &mut Window,
28306        cx: &mut Context<Self>,
28307    ) {
28308        if !self.input_enabled {
28309            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28310            return;
28311        }
28312
28313        self.transact(window, cx, |this, window, cx| {
28314            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28315                if let Some(marked_ranges) = this.marked_text_ranges(cx) {
28316                    // During IME composition, macOS reports the replacement range
28317                    // relative to the first marked region (the only one visible via
28318                    // marked_text_range). The correct targets for replacement are the
28319                    // marked ranges themselves — one per cursor — so use them directly.
28320                    Some(marked_ranges)
28321                } else if range_utf16.start == range_utf16.end {
28322                    // An empty replacement range means "insert at cursor" with no text
28323                    // to replace. macOS reports the cursor position from its own
28324                    // (single-cursor) view of the buffer, which diverges from our actual
28325                    // cursor positions after multi-cursor edits have shifted offsets.
28326                    // Treating this as range_utf16=None lets each cursor insert in place.
28327                    None
28328                } else {
28329                    // Outside of IME composition (e.g. Accessibility Keyboard word
28330                    // completion), the range is an absolute document offset for the
28331                    // newest cursor. Fan it out to all cursors via
28332                    // selection_replacement_ranges, which applies the delta relative
28333                    // to the newest selection to every cursor.
28334                    let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28335                        ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28336                    Some(this.selection_replacement_ranges(range_utf16, cx))
28337                }
28338            } else {
28339                this.marked_text_ranges(cx)
28340            };
28341
28342            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28343                let newest_selection_id = this.selections.newest_anchor().id;
28344                this.selections
28345                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28346                    .iter()
28347                    .zip(ranges_to_replace.iter())
28348                    .find_map(|(selection, range)| {
28349                        if selection.id == newest_selection_id {
28350                            Some(
28351                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28352                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28353                            )
28354                        } else {
28355                            None
28356                        }
28357                    })
28358            });
28359
28360            cx.emit(EditorEvent::InputHandled {
28361                utf16_range_to_replace: range_to_replace,
28362                text: text.into(),
28363            });
28364
28365            if let Some(new_selected_ranges) = new_selected_ranges {
28366                // Only backspace if at least one range covers actual text. When all
28367                // ranges are empty (e.g. a trailing-space insertion from Accessibility
28368                // Keyboard sends replacementRange=cursor..cursor), backspace would
28369                // incorrectly delete the character just before the cursor.
28370                let should_backspace = new_selected_ranges.iter().any(|r| r.start != r.end);
28371                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28372                    selections.select_ranges(new_selected_ranges)
28373                });
28374                if should_backspace {
28375                    this.backspace(&Default::default(), window, cx);
28376                }
28377            }
28378
28379            this.handle_input(text, window, cx);
28380        });
28381
28382        if let Some(transaction) = self.ime_transaction {
28383            self.buffer.update(cx, |buffer, cx| {
28384                buffer.group_until_transaction(transaction, cx);
28385            });
28386        }
28387
28388        self.unmark_text(window, cx);
28389    }
28390
28391    fn replace_and_mark_text_in_range(
28392        &mut self,
28393        range_utf16: Option<Range<usize>>,
28394        text: &str,
28395        new_selected_range_utf16: Option<Range<usize>>,
28396        window: &mut Window,
28397        cx: &mut Context<Self>,
28398    ) {
28399        if !self.input_enabled {
28400            return;
28401        }
28402
28403        let transaction = self.transact(window, cx, |this, window, cx| {
28404            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28405                let snapshot = this.buffer.read(cx).read(cx);
28406                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28407                    for marked_range in &mut marked_ranges {
28408                        marked_range.end = marked_range.start + relative_range_utf16.end;
28409                        marked_range.start += relative_range_utf16.start;
28410                        marked_range.start =
28411                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28412                        marked_range.end =
28413                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28414                    }
28415                }
28416                Some(marked_ranges)
28417            } else if let Some(range_utf16) = range_utf16 {
28418                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28419                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28420                Some(this.selection_replacement_ranges(range_utf16, cx))
28421            } else {
28422                None
28423            };
28424
28425            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28426                let newest_selection_id = this.selections.newest_anchor().id;
28427                this.selections
28428                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28429                    .iter()
28430                    .zip(ranges_to_replace.iter())
28431                    .find_map(|(selection, range)| {
28432                        if selection.id == newest_selection_id {
28433                            Some(
28434                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28435                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28436                            )
28437                        } else {
28438                            None
28439                        }
28440                    })
28441            });
28442
28443            cx.emit(EditorEvent::InputHandled {
28444                utf16_range_to_replace: range_to_replace,
28445                text: text.into(),
28446            });
28447
28448            if let Some(ranges) = ranges_to_replace {
28449                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28450                    s.select_ranges(ranges)
28451                });
28452            }
28453
28454            let marked_ranges = {
28455                let snapshot = this.buffer.read(cx).read(cx);
28456                this.selections
28457                    .disjoint_anchors_arc()
28458                    .iter()
28459                    .map(|selection| {
28460                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28461                    })
28462                    .collect::<Vec<_>>()
28463            };
28464
28465            if text.is_empty() {
28466                this.unmark_text(window, cx);
28467            } else {
28468                this.highlight_text(
28469                    HighlightKey::InputComposition,
28470                    marked_ranges.clone(),
28471                    HighlightStyle {
28472                        underline: Some(UnderlineStyle {
28473                            thickness: px(1.),
28474                            color: None,
28475                            wavy: false,
28476                        }),
28477                        ..Default::default()
28478                    },
28479                    cx,
28480                );
28481            }
28482
28483            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28484            let use_autoclose = this.use_autoclose;
28485            let use_auto_surround = this.use_auto_surround;
28486            this.set_use_autoclose(false);
28487            this.set_use_auto_surround(false);
28488            this.handle_input(text, window, cx);
28489            this.set_use_autoclose(use_autoclose);
28490            this.set_use_auto_surround(use_auto_surround);
28491
28492            if let Some(new_selected_range) = new_selected_range_utf16 {
28493                let snapshot = this.buffer.read(cx).read(cx);
28494                let new_selected_ranges = marked_ranges
28495                    .into_iter()
28496                    .map(|marked_range| {
28497                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28498                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28499                            insertion_start.0 + new_selected_range.start,
28500                        ));
28501                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28502                            insertion_start.0 + new_selected_range.end,
28503                        ));
28504                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28505                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28506                    })
28507                    .collect::<Vec<_>>();
28508
28509                drop(snapshot);
28510                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28511                    selections.select_ranges(new_selected_ranges)
28512                });
28513            }
28514        });
28515
28516        self.ime_transaction = self.ime_transaction.or(transaction);
28517        if let Some(transaction) = self.ime_transaction {
28518            self.buffer.update(cx, |buffer, cx| {
28519                buffer.group_until_transaction(transaction, cx);
28520            });
28521        }
28522
28523        if self
28524            .text_highlights(HighlightKey::InputComposition, cx)
28525            .is_none()
28526        {
28527            self.ime_transaction.take();
28528        }
28529    }
28530
28531    fn bounds_for_range(
28532        &mut self,
28533        range_utf16: Range<usize>,
28534        element_bounds: gpui::Bounds<Pixels>,
28535        window: &mut Window,
28536        cx: &mut Context<Self>,
28537    ) -> Option<gpui::Bounds<Pixels>> {
28538        let text_layout_details = self.text_layout_details(window, cx);
28539        let CharacterDimensions {
28540            em_width,
28541            em_advance,
28542            line_height,
28543        } = self.character_dimensions(window, cx);
28544
28545        let snapshot = self.snapshot(window, cx);
28546        let scroll_position = snapshot.scroll_position();
28547        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28548
28549        let start =
28550            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28551        let x = Pixels::from(
28552            ScrollOffset::from(
28553                snapshot.x_for_display_point(start, &text_layout_details)
28554                    + self.gutter_dimensions.full_width(),
28555            ) - scroll_left,
28556        );
28557        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28558
28559        Some(Bounds {
28560            origin: element_bounds.origin + point(x, y),
28561            size: size(em_width, line_height),
28562        })
28563    }
28564
28565    fn character_index_for_point(
28566        &mut self,
28567        point: gpui::Point<Pixels>,
28568        _window: &mut Window,
28569        _cx: &mut Context<Self>,
28570    ) -> Option<usize> {
28571        let position_map = self.last_position_map.as_ref()?;
28572        if !position_map.text_hitbox.contains(&point) {
28573            return None;
28574        }
28575        let display_point = position_map.point_for_position(point).previous_valid;
28576        let anchor = position_map
28577            .snapshot
28578            .display_point_to_anchor(display_point, Bias::Left);
28579        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28580        Some(utf16_offset.0.0)
28581    }
28582
28583    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28584        self.expects_character_input
28585    }
28586}
28587
28588trait SelectionExt {
28589    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28590    fn spanned_rows(
28591        &self,
28592        include_end_if_at_line_start: bool,
28593        map: &DisplaySnapshot,
28594    ) -> Range<MultiBufferRow>;
28595}
28596
28597impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28598    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28599        let start = self
28600            .start
28601            .to_point(map.buffer_snapshot())
28602            .to_display_point(map);
28603        let end = self
28604            .end
28605            .to_point(map.buffer_snapshot())
28606            .to_display_point(map);
28607        if self.reversed {
28608            end..start
28609        } else {
28610            start..end
28611        }
28612    }
28613
28614    fn spanned_rows(
28615        &self,
28616        include_end_if_at_line_start: bool,
28617        map: &DisplaySnapshot,
28618    ) -> Range<MultiBufferRow> {
28619        let start = self.start.to_point(map.buffer_snapshot());
28620        let mut end = self.end.to_point(map.buffer_snapshot());
28621        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28622            end.row -= 1;
28623        }
28624
28625        let buffer_start = map.prev_line_boundary(start).0;
28626        let buffer_end = map.next_line_boundary(end).0;
28627        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28628    }
28629}
28630
28631impl<T: InvalidationRegion> InvalidationStack<T> {
28632    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28633    where
28634        S: Clone + ToOffset,
28635    {
28636        while let Some(region) = self.last() {
28637            let all_selections_inside_invalidation_ranges =
28638                if selections.len() == region.ranges().len() {
28639                    selections
28640                        .iter()
28641                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28642                        .all(|(selection, invalidation_range)| {
28643                            let head = selection.head().to_offset(buffer);
28644                            invalidation_range.start <= head && invalidation_range.end >= head
28645                        })
28646                } else {
28647                    false
28648                };
28649
28650            if all_selections_inside_invalidation_ranges {
28651                break;
28652            } else {
28653                self.pop();
28654            }
28655        }
28656    }
28657}
28658
28659#[derive(Clone)]
28660struct ErasedEditorImpl(Entity<Editor>);
28661
28662impl ui_input::ErasedEditor for ErasedEditorImpl {
28663    fn text(&self, cx: &App) -> String {
28664        self.0.read(cx).text(cx)
28665    }
28666
28667    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28668        self.0.update(cx, |this, cx| {
28669            this.set_text(text, window, cx);
28670        })
28671    }
28672
28673    fn clear(&self, window: &mut Window, cx: &mut App) {
28674        self.0.update(cx, |this, cx| this.clear(window, cx));
28675    }
28676
28677    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28678        self.0.update(cx, |this, cx| {
28679            this.set_placeholder_text(text, window, cx);
28680        });
28681    }
28682
28683    fn focus_handle(&self, cx: &App) -> FocusHandle {
28684        self.0.read(cx).focus_handle(cx)
28685    }
28686
28687    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28688        let settings = ThemeSettings::get_global(cx);
28689        let theme_color = cx.theme().colors();
28690
28691        let text_style = TextStyle {
28692            font_family: settings.ui_font.family.clone(),
28693            font_features: settings.ui_font.features.clone(),
28694            font_size: rems(0.875).into(),
28695            font_weight: settings.ui_font.weight,
28696            font_style: FontStyle::Normal,
28697            line_height: relative(1.2),
28698            color: theme_color.text,
28699            ..Default::default()
28700        };
28701        let editor_style = EditorStyle {
28702            background: theme_color.ghost_element_background,
28703            local_player: cx.theme().players().local(),
28704            syntax: cx.theme().syntax().clone(),
28705            text: text_style,
28706            ..Default::default()
28707        };
28708        EditorElement::new(&self.0, editor_style).into_any()
28709    }
28710
28711    fn as_any(&self) -> &dyn Any {
28712        &self.0
28713    }
28714
28715    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28716        self.0.update(cx, |editor, cx| {
28717            let editor_offset = editor.buffer().read(cx).len(cx);
28718            editor.change_selections(
28719                SelectionEffects::scroll(Autoscroll::Next),
28720                window,
28721                cx,
28722                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28723            );
28724        });
28725    }
28726
28727    fn subscribe(
28728        &self,
28729        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28730        window: &mut Window,
28731        cx: &mut App,
28732    ) -> Subscription {
28733        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28734            let event = match event {
28735                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28736                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28737                _ => return,
28738            };
28739            (callback)(event, window, cx);
28740        })
28741    }
28742
28743    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28744        self.0.update(cx, |editor, cx| {
28745            editor.set_masked(masked, cx);
28746        });
28747    }
28748}
28749impl<T> Default for InvalidationStack<T> {
28750    fn default() -> Self {
28751        Self(Default::default())
28752    }
28753}
28754
28755impl<T> Deref for InvalidationStack<T> {
28756    type Target = Vec<T>;
28757
28758    fn deref(&self) -> &Self::Target {
28759        &self.0
28760    }
28761}
28762
28763impl<T> DerefMut for InvalidationStack<T> {
28764    fn deref_mut(&mut self) -> &mut Self::Target {
28765        &mut self.0
28766    }
28767}
28768
28769impl InvalidationRegion for SnippetState {
28770    fn ranges(&self) -> &[Range<Anchor>] {
28771        &self.ranges[self.active_index]
28772    }
28773}
28774
28775fn edit_prediction_edit_text(
28776    current_snapshot: &BufferSnapshot,
28777    edits: &[(Range<Anchor>, impl AsRef<str>)],
28778    edit_preview: &EditPreview,
28779    include_deletions: bool,
28780    cx: &App,
28781) -> HighlightedText {
28782    let edits = edits
28783        .iter()
28784        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28785        .collect::<Vec<_>>();
28786
28787    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28788}
28789
28790fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28791    // Fallback for providers that don't provide edit_preview (like Copilot)
28792    // Just show the raw edit text with basic styling
28793    let mut text = String::new();
28794    let mut highlights = Vec::new();
28795
28796    let insertion_highlight_style = HighlightStyle {
28797        color: Some(cx.theme().colors().text),
28798        ..Default::default()
28799    };
28800
28801    for (_, edit_text) in edits {
28802        let start_offset = text.len();
28803        text.push_str(edit_text);
28804        let end_offset = text.len();
28805
28806        if start_offset < end_offset {
28807            highlights.push((start_offset..end_offset, insertion_highlight_style));
28808        }
28809    }
28810
28811    HighlightedText {
28812        text: text.into(),
28813        highlights,
28814    }
28815}
28816
28817pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28818    match severity {
28819        lsp::DiagnosticSeverity::ERROR => colors.error,
28820        lsp::DiagnosticSeverity::WARNING => colors.warning,
28821        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28822        lsp::DiagnosticSeverity::HINT => colors.info,
28823        _ => colors.ignored,
28824    }
28825}
28826
28827pub fn styled_runs_for_code_label<'a>(
28828    label: &'a CodeLabel,
28829    syntax_theme: &'a theme::SyntaxTheme,
28830    local_player: &'a theme::PlayerColor,
28831) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28832    let fade_out = HighlightStyle {
28833        fade_out: Some(0.35),
28834        ..Default::default()
28835    };
28836
28837    let mut prev_end = label.filter_range.end;
28838    label
28839        .runs
28840        .iter()
28841        .enumerate()
28842        .flat_map(move |(ix, (range, highlight_id))| {
28843            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28844                HighlightStyle {
28845                    color: Some(local_player.cursor),
28846                    ..Default::default()
28847                }
28848            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28849                HighlightStyle {
28850                    background_color: Some(local_player.selection),
28851                    ..Default::default()
28852                }
28853            } else if let Some(style) = highlight_id.style(syntax_theme) {
28854                style
28855            } else {
28856                return Default::default();
28857            };
28858            let muted_style = style.highlight(fade_out);
28859
28860            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28861            if range.start >= label.filter_range.end {
28862                if range.start > prev_end {
28863                    runs.push((prev_end..range.start, fade_out));
28864                }
28865                runs.push((range.clone(), muted_style));
28866            } else if range.end <= label.filter_range.end {
28867                runs.push((range.clone(), style));
28868            } else {
28869                runs.push((range.start..label.filter_range.end, style));
28870                runs.push((label.filter_range.end..range.end, muted_style));
28871            }
28872            prev_end = cmp::max(prev_end, range.end);
28873
28874            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28875                runs.push((prev_end..label.text.len(), fade_out));
28876            }
28877
28878            runs
28879        })
28880}
28881
28882pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28883    let mut prev_index = 0;
28884    let mut prev_codepoint: Option<char> = None;
28885    text.char_indices()
28886        .chain([(text.len(), '\0')])
28887        .filter_map(move |(index, codepoint)| {
28888            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28889            let is_boundary = index == text.len()
28890                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28891                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28892            if is_boundary {
28893                let chunk = &text[prev_index..index];
28894                prev_index = index;
28895                Some(chunk)
28896            } else {
28897                None
28898            }
28899        })
28900}
28901
28902/// Given a string of text immediately before the cursor, iterates over possible
28903/// strings a snippet could match to. More precisely: returns an iterator over
28904/// suffixes of `text` created by splitting at word boundaries (before & after
28905/// every non-word character).
28906///
28907/// Shorter suffixes are returned first.
28908pub(crate) fn snippet_candidate_suffixes<'a>(
28909    text: &'a str,
28910    is_word_char: &'a dyn Fn(char) -> bool,
28911) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28912    let mut prev_index = text.len();
28913    let mut prev_codepoint = None;
28914    text.char_indices()
28915        .rev()
28916        .chain([(0, '\0')])
28917        .filter_map(move |(index, codepoint)| {
28918            let prev_index = std::mem::replace(&mut prev_index, index);
28919            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28920            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28921                None
28922            } else {
28923                let chunk = &text[prev_index..]; // go to end of string
28924                Some(chunk)
28925            }
28926        })
28927}
28928
28929pub trait RangeToAnchorExt: Sized {
28930    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28931
28932    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28933        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28934        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28935    }
28936}
28937
28938impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28939    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28940        let start_offset = self.start.to_offset(snapshot);
28941        let end_offset = self.end.to_offset(snapshot);
28942        if start_offset == end_offset {
28943            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28944        } else {
28945            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28946        }
28947    }
28948}
28949
28950pub trait RowExt {
28951    fn as_f64(&self) -> f64;
28952
28953    fn next_row(&self) -> Self;
28954
28955    fn previous_row(&self) -> Self;
28956
28957    fn minus(&self, other: Self) -> u32;
28958}
28959
28960impl RowExt for DisplayRow {
28961    fn as_f64(&self) -> f64 {
28962        self.0 as _
28963    }
28964
28965    fn next_row(&self) -> Self {
28966        Self(self.0 + 1)
28967    }
28968
28969    fn previous_row(&self) -> Self {
28970        Self(self.0.saturating_sub(1))
28971    }
28972
28973    fn minus(&self, other: Self) -> u32 {
28974        self.0 - other.0
28975    }
28976}
28977
28978impl RowExt for MultiBufferRow {
28979    fn as_f64(&self) -> f64 {
28980        self.0 as _
28981    }
28982
28983    fn next_row(&self) -> Self {
28984        Self(self.0 + 1)
28985    }
28986
28987    fn previous_row(&self) -> Self {
28988        Self(self.0.saturating_sub(1))
28989    }
28990
28991    fn minus(&self, other: Self) -> u32 {
28992        self.0 - other.0
28993    }
28994}
28995
28996trait RowRangeExt {
28997    type Row;
28998
28999    fn len(&self) -> usize;
29000
29001    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
29002}
29003
29004impl RowRangeExt for Range<MultiBufferRow> {
29005    type Row = MultiBufferRow;
29006
29007    fn len(&self) -> usize {
29008        (self.end.0 - self.start.0) as usize
29009    }
29010
29011    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
29012        (self.start.0..self.end.0).map(MultiBufferRow)
29013    }
29014}
29015
29016impl RowRangeExt for Range<DisplayRow> {
29017    type Row = DisplayRow;
29018
29019    fn len(&self) -> usize {
29020        (self.end.0 - self.start.0) as usize
29021    }
29022
29023    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
29024        (self.start.0..self.end.0).map(DisplayRow)
29025    }
29026}
29027
29028/// If select range has more than one line, we
29029/// just point the cursor to range.start.
29030fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
29031    if range.start.row == range.end.row {
29032        range
29033    } else {
29034        range.start..range.start
29035    }
29036}
29037pub struct KillRing(ClipboardItem);
29038impl Global for KillRing {}
29039
29040const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
29041
29042enum BreakpointPromptEditAction {
29043    Log,
29044    Condition,
29045    HitCondition,
29046}
29047
29048struct BreakpointPromptEditor {
29049    pub(crate) prompt: Entity<Editor>,
29050    editor: WeakEntity<Editor>,
29051    breakpoint_anchor: Anchor,
29052    breakpoint: Breakpoint,
29053    edit_action: BreakpointPromptEditAction,
29054    block_ids: HashSet<CustomBlockId>,
29055    editor_margins: Arc<Mutex<EditorMargins>>,
29056    _subscriptions: Vec<Subscription>,
29057}
29058
29059impl BreakpointPromptEditor {
29060    const MAX_LINES: u8 = 4;
29061
29062    fn new(
29063        editor: WeakEntity<Editor>,
29064        breakpoint_anchor: Anchor,
29065        breakpoint: Breakpoint,
29066        edit_action: BreakpointPromptEditAction,
29067        window: &mut Window,
29068        cx: &mut Context<Self>,
29069    ) -> Self {
29070        let base_text = match edit_action {
29071            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
29072            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
29073            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
29074        }
29075        .map(|msg| msg.to_string())
29076        .unwrap_or_default();
29077
29078        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
29079        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
29080
29081        let prompt = cx.new(|cx| {
29082            let mut prompt = Editor::new(
29083                EditorMode::AutoHeight {
29084                    min_lines: 1,
29085                    max_lines: Some(Self::MAX_LINES as usize),
29086                },
29087                buffer,
29088                None,
29089                window,
29090                cx,
29091            );
29092            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
29093            prompt.set_show_cursor_when_unfocused(false, cx);
29094            prompt.set_placeholder_text(
29095                match edit_action {
29096                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
29097                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
29098                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
29099                },
29100                window,
29101                cx,
29102            );
29103
29104            prompt
29105        });
29106
29107        Self {
29108            prompt,
29109            editor,
29110            breakpoint_anchor,
29111            breakpoint,
29112            edit_action,
29113            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
29114            block_ids: Default::default(),
29115            _subscriptions: vec![],
29116        }
29117    }
29118
29119    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
29120        self.block_ids.extend(block_ids)
29121    }
29122
29123    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
29124        if let Some(editor) = self.editor.upgrade() {
29125            let message = self
29126                .prompt
29127                .read(cx)
29128                .buffer
29129                .read(cx)
29130                .as_singleton()
29131                .expect("A multi buffer in breakpoint prompt isn't possible")
29132                .read(cx)
29133                .as_rope()
29134                .to_string();
29135
29136            editor.update(cx, |editor, cx| {
29137                editor.edit_breakpoint_at_anchor(
29138                    self.breakpoint_anchor,
29139                    self.breakpoint.clone(),
29140                    match self.edit_action {
29141                        BreakpointPromptEditAction::Log => {
29142                            BreakpointEditAction::EditLogMessage(message.into())
29143                        }
29144                        BreakpointPromptEditAction::Condition => {
29145                            BreakpointEditAction::EditCondition(message.into())
29146                        }
29147                        BreakpointPromptEditAction::HitCondition => {
29148                            BreakpointEditAction::EditHitCondition(message.into())
29149                        }
29150                    },
29151                    cx,
29152                );
29153
29154                editor.remove_blocks(self.block_ids.clone(), None, cx);
29155                cx.focus_self(window);
29156            });
29157        }
29158    }
29159
29160    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
29161        self.editor
29162            .update(cx, |editor, cx| {
29163                editor.remove_blocks(self.block_ids.clone(), None, cx);
29164                window.focus(&editor.focus_handle, cx);
29165            })
29166            .log_err();
29167    }
29168
29169    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
29170        let settings = ThemeSettings::get_global(cx);
29171        let text_style = TextStyle {
29172            color: if self.prompt.read(cx).read_only(cx) {
29173                cx.theme().colors().text_disabled
29174            } else {
29175                cx.theme().colors().text
29176            },
29177            font_family: settings.buffer_font.family.clone(),
29178            font_fallbacks: settings.buffer_font.fallbacks.clone(),
29179            font_size: settings.buffer_font_size(cx).into(),
29180            font_weight: settings.buffer_font.weight,
29181            line_height: relative(settings.buffer_line_height.value()),
29182            ..Default::default()
29183        };
29184        EditorElement::new(
29185            &self.prompt,
29186            EditorStyle {
29187                background: cx.theme().colors().editor_background,
29188                local_player: cx.theme().players().local(),
29189                text: text_style,
29190                ..Default::default()
29191            },
29192        )
29193    }
29194
29195    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29196        let focus_handle = self.prompt.focus_handle(cx);
29197        IconButton::new("cancel", IconName::Close)
29198            .icon_color(Color::Muted)
29199            .shape(IconButtonShape::Square)
29200            .tooltip(move |_window, cx| {
29201                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
29202            })
29203            .on_click(cx.listener(|this, _, window, cx| {
29204                this.cancel(&menu::Cancel, window, cx);
29205            }))
29206    }
29207
29208    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29209        let focus_handle = self.prompt.focus_handle(cx);
29210        IconButton::new("confirm", IconName::Return)
29211            .icon_color(Color::Muted)
29212            .shape(IconButtonShape::Square)
29213            .tooltip(move |_window, cx| {
29214                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
29215            })
29216            .on_click(cx.listener(|this, _, window, cx| {
29217                this.confirm(&menu::Confirm, window, cx);
29218            }))
29219    }
29220}
29221
29222impl Render for BreakpointPromptEditor {
29223    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29224        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
29225        let editor_margins = *self.editor_margins.lock();
29226        let gutter_dimensions = editor_margins.gutter;
29227        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
29228        let right_padding = editor_margins.right + px(9.);
29229        h_flex()
29230            .key_context("Editor")
29231            .bg(cx.theme().colors().editor_background)
29232            .border_y_1()
29233            .border_color(cx.theme().status().info_border)
29234            .size_full()
29235            .py(window.line_height() / 2.5)
29236            .pr(right_padding)
29237            .on_action(cx.listener(Self::confirm))
29238            .on_action(cx.listener(Self::cancel))
29239            .child(
29240                WithRemSize::new(ui_font_size)
29241                    .h_full()
29242                    .w(left_gutter_width)
29243                    .flex()
29244                    .flex_row()
29245                    .flex_shrink_0()
29246                    .items_center()
29247                    .justify_center()
29248                    .gap_1()
29249                    .child(self.render_close_button(cx)),
29250            )
29251            .child(
29252                h_flex()
29253                    .w_full()
29254                    .justify_between()
29255                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
29256                    .child(
29257                        WithRemSize::new(ui_font_size)
29258                            .flex()
29259                            .flex_row()
29260                            .items_center()
29261                            .child(self.render_confirm_button(cx)),
29262                    ),
29263            )
29264    }
29265}
29266
29267impl Focusable for BreakpointPromptEditor {
29268    fn focus_handle(&self, cx: &App) -> FocusHandle {
29269        self.prompt.focus_handle(cx)
29270    }
29271}
29272
29273fn all_edits_insertions_or_deletions(
29274    edits: &Vec<(Range<Anchor>, Arc<str>)>,
29275    snapshot: &MultiBufferSnapshot,
29276) -> bool {
29277    let mut all_insertions = true;
29278    let mut all_deletions = true;
29279
29280    for (range, new_text) in edits.iter() {
29281        let range_is_empty = range.to_offset(snapshot).is_empty();
29282        let text_is_empty = new_text.is_empty();
29283
29284        if range_is_empty != text_is_empty {
29285            if range_is_empty {
29286                all_deletions = false;
29287            } else {
29288                all_insertions = false;
29289            }
29290        } else {
29291            return false;
29292        }
29293
29294        if !all_insertions && !all_deletions {
29295            return false;
29296        }
29297    }
29298    all_insertions || all_deletions
29299}
29300
29301struct MissingEditPredictionKeybindingTooltip;
29302
29303impl Render for MissingEditPredictionKeybindingTooltip {
29304    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29305        ui::tooltip_container(cx, |container, cx| {
29306            container
29307                .flex_shrink_0()
29308                .max_w_80()
29309                .min_h(rems_from_px(124.))
29310                .justify_between()
29311                .child(
29312                    v_flex()
29313                        .flex_1()
29314                        .text_ui_sm(cx)
29315                        .child(Label::new("Conflict with Accept Keybinding"))
29316                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29317                )
29318                .child(
29319                    h_flex()
29320                        .pb_1()
29321                        .gap_1()
29322                        .items_end()
29323                        .w_full()
29324                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29325                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29326                        }))
29327                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29328                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29329                        })),
29330                )
29331        })
29332    }
29333}
29334
29335#[derive(Debug, Clone, Copy, PartialEq)]
29336pub struct LineHighlight {
29337    pub background: Background,
29338    pub border: Option<gpui::Hsla>,
29339    pub include_gutter: bool,
29340    pub type_id: Option<TypeId>,
29341}
29342
29343struct LineManipulationResult {
29344    pub new_text: String,
29345    pub line_count_before: usize,
29346    pub line_count_after: usize,
29347}
29348
29349fn render_diff_hunk_controls(
29350    row: u32,
29351    status: &DiffHunkStatus,
29352    hunk_range: Range<Anchor>,
29353    is_created_file: bool,
29354    line_height: Pixels,
29355    editor: &Entity<Editor>,
29356    _window: &mut Window,
29357    cx: &mut App,
29358) -> AnyElement {
29359    h_flex()
29360        .h(line_height)
29361        .mr_1()
29362        .gap_1()
29363        .px_0p5()
29364        .pb_1()
29365        .border_x_1()
29366        .border_b_1()
29367        .border_color(cx.theme().colors().border_variant)
29368        .rounded_b_lg()
29369        .bg(cx.theme().colors().editor_background)
29370        .gap_1()
29371        .block_mouse_except_scroll()
29372        .shadow_md()
29373        .child(if status.has_secondary_hunk() {
29374            Button::new(("stage", row as u64), "Stage")
29375                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29376                .tooltip({
29377                    let focus_handle = editor.focus_handle(cx);
29378                    move |_window, cx| {
29379                        Tooltip::for_action_in(
29380                            "Stage Hunk",
29381                            &::git::ToggleStaged,
29382                            &focus_handle,
29383                            cx,
29384                        )
29385                    }
29386                })
29387                .on_click({
29388                    let editor = editor.clone();
29389                    move |_event, _window, cx| {
29390                        editor.update(cx, |editor, cx| {
29391                            editor.stage_or_unstage_diff_hunks(
29392                                true,
29393                                vec![hunk_range.start..hunk_range.start],
29394                                cx,
29395                            );
29396                        });
29397                    }
29398                })
29399        } else {
29400            Button::new(("unstage", row as u64), "Unstage")
29401                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29402                .tooltip({
29403                    let focus_handle = editor.focus_handle(cx);
29404                    move |_window, cx| {
29405                        Tooltip::for_action_in(
29406                            "Unstage Hunk",
29407                            &::git::ToggleStaged,
29408                            &focus_handle,
29409                            cx,
29410                        )
29411                    }
29412                })
29413                .on_click({
29414                    let editor = editor.clone();
29415                    move |_event, _window, cx| {
29416                        editor.update(cx, |editor, cx| {
29417                            editor.stage_or_unstage_diff_hunks(
29418                                false,
29419                                vec![hunk_range.start..hunk_range.start],
29420                                cx,
29421                            );
29422                        });
29423                    }
29424                })
29425        })
29426        .child(
29427            Button::new(("restore", row as u64), "Restore")
29428                .tooltip({
29429                    let focus_handle = editor.focus_handle(cx);
29430                    move |_window, cx| {
29431                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29432                    }
29433                })
29434                .on_click({
29435                    let editor = editor.clone();
29436                    move |_event, window, cx| {
29437                        editor.update(cx, |editor, cx| {
29438                            let snapshot = editor.snapshot(window, cx);
29439                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29440                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29441                        });
29442                    }
29443                })
29444                .disabled(is_created_file),
29445        )
29446        .when(
29447            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29448            |el| {
29449                el.child(
29450                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29451                        .shape(IconButtonShape::Square)
29452                        .icon_size(IconSize::Small)
29453                        // .disabled(!has_multiple_hunks)
29454                        .tooltip({
29455                            let focus_handle = editor.focus_handle(cx);
29456                            move |_window, cx| {
29457                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29458                            }
29459                        })
29460                        .on_click({
29461                            let editor = editor.clone();
29462                            move |_event, window, cx| {
29463                                editor.update(cx, |editor, cx| {
29464                                    let snapshot = editor.snapshot(window, cx);
29465                                    let position =
29466                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29467                                    editor.go_to_hunk_before_or_after_position(
29468                                        &snapshot,
29469                                        position,
29470                                        Direction::Next,
29471                                        true,
29472                                        window,
29473                                        cx,
29474                                    );
29475                                    editor.expand_selected_diff_hunks(cx);
29476                                });
29477                            }
29478                        }),
29479                )
29480                .child(
29481                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29482                        .shape(IconButtonShape::Square)
29483                        .icon_size(IconSize::Small)
29484                        // .disabled(!has_multiple_hunks)
29485                        .tooltip({
29486                            let focus_handle = editor.focus_handle(cx);
29487                            move |_window, cx| {
29488                                Tooltip::for_action_in(
29489                                    "Previous Hunk",
29490                                    &GoToPreviousHunk,
29491                                    &focus_handle,
29492                                    cx,
29493                                )
29494                            }
29495                        })
29496                        .on_click({
29497                            let editor = editor.clone();
29498                            move |_event, window, cx| {
29499                                editor.update(cx, |editor, cx| {
29500                                    let snapshot = editor.snapshot(window, cx);
29501                                    let point =
29502                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29503                                    editor.go_to_hunk_before_or_after_position(
29504                                        &snapshot,
29505                                        point,
29506                                        Direction::Prev,
29507                                        true,
29508                                        window,
29509                                        cx,
29510                                    );
29511                                    editor.expand_selected_diff_hunks(cx);
29512                                });
29513                            }
29514                        }),
29515                )
29516            },
29517        )
29518        .into_any_element()
29519}
29520
29521pub fn multibuffer_context_lines(cx: &App) -> u32 {
29522    EditorSettings::try_get(cx)
29523        .map(|settings| settings.excerpt_context_lines)
29524        .unwrap_or(2)
29525        .min(32)
29526}